Самые короткие и простые способы генерации различных фракталов или других изображений
Я интересуюсь компьютерной графикой и хотел бы провести своего рода конкурс.
Я хотел бы узнать о новых (для себя) способах генерации различных фракталов или других изображений, полученных по достаточно простой формуле.
То есть критерием соревнования является, - использование простой базовой формул
для получения интересных картинок.
Например есть такая реализация цикла по всем пикселям на картинке:
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
let formula = (x, y, cx, cy, m) => {
return [x/w+cx/w, y/h+cy/h, 0]
}
canvas.onmousemove = e => {
var img = c.getImageData(0, 0, w, h)
for(var x = 0; x
Необходимо реализовать функцию formula для получения "интересного" изображения, дополнительными аргументами выступают координаты мыши
Язык - любой, но желательно js, из-за возможности онлайн визуализации.
PS: рекурсивные методы мне менее интересны, особенно если рекурсия не хвостовая
так какпортировать это на glsl будет сложно если не невозможно.
P.P.S. для привлечения более широкой аудитории мной был выбран cpu и код цикла п
картинке я написал для него, однако если Вам угодно, мне больше импонирует webgl, по этому ниже сниппет, где цикл по всем пикселям делает видеокарта, когда я рисую один треугольник, закрывающий весь экран, а функция formula - это фрагментный шейдер :)
let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 3, -1, -1, 3, -1]), gl.STATIC_DRAW);
let pid = gl.createProgram();
shader('vertex', gl.VERTEX_SHADER);
shader('fragment', gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);
let coords = gl.getAttribLocation(pid, "coords");
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);
let mouse = gl.getUniformLocation(pid, 'mouse');
let resolution = gl.getUniformLocation(pid, 'resolution');
gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight);
let changeCenter = e => {
e = e.touches ? e.touches[0] : e;
gl.uniform2f(mouse, e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
draw();
}
window.addEventListener('mousemove', changeCenter);
window.addEventListener('touchmove', changeCenter);
draw();
function draw() {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
function shader(src, type) {
let sid = gl.createShader(type);
gl.shaderSource(sid, document.querySelector(`script[type="glsl/${src}"]`).textContent);
gl.compileShader(sid);
var message = gl.getShaderInfoLog(sid);
gl.attachShader(pid, sid);
if (message.length > 0) {
console.log(src.split('\n').map(function (str, i) {
return ("" + (1 + i)).padStart(4, "0") + ": " + str
}).join('\n'));
throw message;
}
}
Ответы
Ответ 1
Пример с бассейном Ньютона.
Области подкрашиваются в соответствии с близостью к корням уравнения z3-1 = 0
class Complex {
constructor({algebraic,trigonometric} = {}) {
if (algebraic) {
this.initAlgebraic(algebraic);
} else if (trigonometric) {
this.initTrigonometric(trigonometric)
} else {
throw new Error('Invalid arguments');
}
}
static fromReal(r) {
return Complex.fromAlgebraic(r, 0);
}
static fromAlgebraic(r, i) {
return new Complex({algebraic: {r,i}});
}
static fromTrigonometric(modulus, arg) {
return new Complex({trigonometric: {modulus,arg}});
}
initAlgebraic({r,i}) {
this.r = r;
this.i = i;
this.modulus = Math.sqrt(r * r + i * i);
this.arg = Math.atan2(i, r)
}
initTrigonometric({modulus,arg}) {
this.modulus = modulus,
this.arg = arg;
this.r = this.modulus * Math.cos(arg);
this.i = this.modulus * Math.sin(arg);
}
pow(exp) {
return Complex.fromTrigonometric(Math.pow(this.modulus, exp), exp * this.arg);
}
add(c) {
return Complex.fromAlgebraic(this.r + c.r, this.i + c.i);
}
conjugate(){
return Complex.fromAlgebraic(this.r, -this.i);
}
sub(c){
return Complex.fromAlgebraic(this.r - c.r, this.i - c.i);
}
mul(c){
return Complex.fromAlgebraic(this.r*c.r-this.i*c.i, this.r*c.i+this.i*c.r);
}
div(c){
var cConjugate = c.conjugate();
var mulToConjugate = this.mul(cConjugate);
var divider = c.mul(cConjugate).r;
return Complex.fromAlgebraic(mulToConjugate.r/divider, mulToConjugate.i/divider);
}
}
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
function formula(x,y, maxIter, max, min){
var z = Complex.fromAlgebraic(x,y);
var d = z;
for(var i=0;imin;i++){
var z1 = z.sub(z.pow(3).add(Complex.fromReal(-1)).div(z.pow(2).mul(Complex.fromReal(3))));
//var z1 = z.sub(z.pow(5).add(Complex.fromReal(-1)).div(z.pow(4).mul(Complex.fromReal(5))));
var z2 = z1.sub(z);
d = Complex.fromAlgebraic(Math.abs(z2.r),Math.abs(z2.i));
z = z1;
}
return [i, z];
}
var input = document.getElementById('coef');
input.addEventListener('change',function(){
draw(this.value);
});
var roots = [
Complex.fromReal(1),
Complex.fromAlgebraic(-1, Math.sqrt(3)/2),
Complex.fromAlgebraic(-1, -Math.sqrt(3)/2),
]
var maxDistance = input.max;
function draw(distance){
var xc = w/2;
var yc = h/2;
var coef = 0.0015 + distance * ((0.015-0.0015)/maxDistance);
var img = c.getImageData(0, 0, w, h);
var aaa = 0;
for(var y = -h/2; y
Для урaвнения z5-1 = 0
class Complex {
constructor({algebraic,trigonometric} = {}) {
if (algebraic) {
this.initAlgebraic(algebraic);
} else if (trigonometric) {
this.initTrigonometric(trigonometric)
} else {
throw new Error('Invalid arguments');
}
}
static fromReal(r) {
return Complex.fromAlgebraic(r, 0);
}
static fromAlgebraic(r, i) {
return new Complex({algebraic: {r,i}});
}
static fromTrigonometric(modulus, arg) {
return new Complex({trigonometric: {modulus,arg}});
}
initAlgebraic({r,i}) {
this.r = r;
this.i = i;
this.modulus = Math.sqrt(r * r + i * i);
this.arg = Math.atan2(i, r)
}
initTrigonometric({modulus,arg}) {
this.modulus = modulus,
this.arg = arg;
this.r = this.modulus * Math.cos(arg);
this.i = this.modulus * Math.sin(arg);
}
pow(exp) {
return Complex.fromTrigonometric(Math.pow(this.modulus, exp), exp * this.arg);
}
add(c) {
return Complex.fromAlgebraic(this.r + c.r, this.i + c.i);
}
conjugate(){
return Complex.fromAlgebraic(this.r, -this.i);
}
sub(c){
return Complex.fromAlgebraic(this.r - c.r, this.i - c.i);
}
mul(c){
return Complex.fromAlgebraic(this.r*c.r-this.i*c.i, this.r*c.i+this.i*c.r);
}
div(c){
var cConjugate = c.conjugate();
var mulToConjugate = this.mul(cConjugate);
var divider = c.mul(cConjugate).r;
return Complex.fromAlgebraic(mulToConjugate.r/divider, mulToConjugate.i/divider);
}
}
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
function formula(x,y, maxIter, max, min){
var z = Complex.fromAlgebraic(x,y);
var d = z;
for(var i=0;imin;i++){
//var z1 = z.sub(z.pow(3).add(Complex.fromReal(-1)).div(z.pow(2).mul(Complex.fromReal(3))));
var z1 = z.sub(z.pow(5).add(Complex.fromReal(-1)).div(z.pow(4).mul(Complex.fromReal(5))));
var z2 = z1.sub(z);
d = Complex.fromAlgebraic(Math.abs(z2.r),Math.abs(z2.i));
z = z1;
}
return [i, z];
}
var input = document.getElementById('coef');
input.addEventListener('change',function(){
draw(this.value);
});
var maxDistance = input.max;
function draw(distance){
var xc = w/2;
var yc = h/2;
var coef = 0.0015 + distance * ((0.015-0.0015)/maxDistance);
var img = c.getImageData(0, 0, w, h);
var aaa = 0;
for(var y = -h/2; y
Ответ 2
Предлагаю Вашему вниманию Суперформулу
Суперформула является обобщением суперэллипса и впервые была выведена Йоханом Гиелисо
в 2003 году. Гиелис предположил использовать формулу для описания сложных форм и кривых, которые встречаются в природе.
В полярной системе координат, с радиусом, и углом, суперформула выглядит так:
Выбирая различные значения параметров , получаются различные формы.
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
// функция возвращает расстояние в полярной системе координат для угла phi, идущего первым аргументом
function superformula(phi, m, n1, a, b, n2, n3) {
with (Math) {
m = m*phi/4;
a = pow(abs(cos(m))/a, n2);
b = pow(abs(sin(m))/b, n3);
return pow(a + b, -1/n1);
}
}
let formula = (x, y, cx, cy) => {
x = (2*x-w)/w*(3-cy/h);
y = (2*y-h)/w*(3-cy/h);
let a = Math.atan2(y, x) - cx/w*Math.PI;
let d = superformula(a, t.m, t.n1, t.a, t.b, t.n2, t.n3);
let l = Math.sqrt(x*x + y*y);
let c = Math.min(d-l)*10.;
return [c, c, c]
}
let types = {
asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1},
butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1},
circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1},
clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1},
cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1},
diamond: {m: 4, n1: 1, n2: 1, n3: 1, a: 1, b: 1},
drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1},
gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1},
heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18},
heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1},
pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1},
rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1},
roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1},
square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1},
star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1},
triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1}
};
let t = Object.values(types)[0];
let draw = e => {
var img = c.getImageData(0, 0, w, h)
for(var x = 0; x {
draw(e);
}
canvas.onclick = e => {
let vals = Object.values(types);
let i = Math.floor(Math.random()*vals.length);
t = vals[i];
document.querySelector('span').textContent = 'click to change: ' + Object.keys(types)[i]
draw(e)
}
click to change: asterisk
Ответ 3
Самое простое что известно мне, помимо фракталов в комплексной плоскости - это фрактал kali, назван в честь опубликовавшего эту формулу на fractalforums
формула в glsl для него вообще простая:
vec2 q = vec2(x, y);
for(var i=0; i<10; i++)
q = abs(q)/dot(q,q) - vec2(cx, cy);
на js чуть посложнее из-за отсутствия операций над векторами, но все равно очен
простая:
let formula = (x, y, cx, cy, m) => {
x = (2*x-w)/w;
y = (2*y-h)/w;
for (var i=0; i<10; i++) {
x = Math.abs(x)
y = Math.abs(y)
m = x*x + y*y
x = x/m - cx/w
y = y/m - cy/h
}
return [x, y, Math.sqrt(x*x+y*y)/2.]
}
Вот результат для cpu версии, мышка меняет переменные в формуле для получения другого изображения:
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
let formula = (x, y, cx, cy, m) => {
x = (2*x-w)/w;
y = (2*y-h)/w;
for (var i=0; i<10; i++) {
x = Math.abs(x)
y = Math.abs(y)
m = x*x + y*y
x = x/m - cx/w
y = y/m - cy/h
}
return [x, y, Math.sqrt(x*x+y*y)/2.]
}
canvas.onmousemove = e => {
var img = c.getImageData(0, 0, w, h)
for(var x = 0; x
GPU вариация и другая палитра
let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 3, -1, -1, 3, -1]), gl.STATIC_DRAW);
let pid = gl.createProgram();
shader('vertex', gl.VERTEX_SHADER);
shader('fragment', gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);
let coords = gl.getAttribLocation(pid, "coords");
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);
let mouse = gl.getUniformLocation(pid, 'mouse');
let resolution = gl.getUniformLocation(pid, 'resolution');
gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight);
let changeCenter = e => {
e = e.touches ? e.touches[0] : e;
gl.uniform2f(mouse, e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
draw();
}
window.addEventListener('mousemove', changeCenter);
window.addEventListener('touchmove', changeCenter);
draw();
function draw() {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
function shader(src, type) {
let sid = gl.createShader(type);
gl.shaderSource(sid, document.querySelector(`script[type="glsl/${src}"]`).textContent);
gl.compileShader(sid);
var message = gl.getShaderInfoLog(sid);
gl.attachShader(pid, sid);
if (message.length > 0) {
console.log(src.split('\n').map(function (str, i) {
return ("" + (1 + i)).padStart(4, "0") + ": " + str
}).join('\n'));
throw message;
}
}
Еще одна вариация фрактала
let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 3, -1, -1, 3, -1]), gl.STATIC_DRAW);
let pid = gl.createProgram();
shader('vertex', gl.VERTEX_SHADER);
shader('fragment', gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);
let coords = gl.getAttribLocation(pid, "coords");
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);
let mouse = gl.getUniformLocation(pid, 'mouse');
let resolution = gl.getUniformLocation(pid, 'resolution');
gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight);
let changeCenter = e => {
e = e.touches ? e.touches[0] : e;
gl.uniform2f(mouse, e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
draw();
}
window.addEventListener('mousemove', changeCenter);
window.addEventListener('touchmove', changeCenter);
draw();
function draw() {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
function shader(src, type) {
let sid = gl.createShader(type);
gl.shaderSource(sid, document.querySelector(`script[type="glsl/${src}"]`).textContent);
gl.compileShader(sid);
var message = gl.getShaderInfoLog(sid);
gl.attachShader(pid, sid);
if (message.length > 0) {
console.log(src.split('\n').map(function (str, i) {
return ("" + (1 + i)).padStart(4, "0") + ": " + str
}).join('\n'));
throw message;
}
}
Ответ 4
Множество Жюлиа (Julia set)
Формула для него выглядит вот так:
где z это комплексное число:
В этом примере значение c зависит от координат мышки, что позволяет одновременн
наблюдать великое множество различных изображений множества Жюлиа
Все эти изображения получены при помощи нижеприведенного сниппета с минимальным
изменениями
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
let formula = (x, y, cx, cy, m) => {
let z = [(2*y-h)/w*1.5,(2*x-w)/w*1.5];
for (var i = 0; i < 32; ++i) {
z = [ z[0] * z[0] - z[1] * z[1] + cy/h, 2. * z[0] * z[1] + cx/w];
if (z[0]*z[0] + z[1]*z[1] > 4.)
return i;
}
return 0
}
canvas.onmousemove = e => {
var img = c.getImageData(0, 0, w, h)
for(var x = 0; x
По очень похожей формуле получается обобщение всех множеств Жюлиа - Множество Мандельброта (Mandelbrot set)
Ответ 5
Сетка Аполлона.
let c = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height;
var x = 0.2,
y = 0.3,
a = 0,
b = 0,
r = Math.sqrt(3),
points = Array(1000),
counter = 0;
(function starter() {
draw();
requestAnimationFrame(starter);
}())
function sqr(x) {
return x * x;
}
function putPixels(x, y) {
var img = c.getImageData(0, 0, w, h)
let offset = (y * w + x) * 4;
img.data[offset] = 255;
img.data[offset + 1] = 0;
img.data[offset + 2] = 0;
img.data[offset + 3] = 255;
c.putImageData(img, 0, 0)
}
function draw() {
a = Math.random();
var div = sqr(1 + r - x) + sqr(y);
var a0 = 3 * (1 + r - x) / (div) - (1 + r) / (2 + r);
var b0 = 3 * y / (div);
if (a <= 1 / 3 && a >= 0) {
x1 = a0;
y1 = b0;
} else {
var a1 = -1 / 2;
var b1 = r / 2;
var a2 = -1 / 2;
var b2 = -r / 2;
var d =sqr(a0) + sqr(b0);
var f1x = a0 / (d);
var f1y = -b0 / (d);
if (a <= 2 / 3 && a > 1 / 3) {
x1 = f1x * a1 - f1y * b1;
y1 = f1x * b1 + f1y * a1;
}
if (a <= 3 / 3 && a > 2 / 3) {
x1 = f1x * a2 - f1y * b2;
y1 = f1x * b2 + f1y * a2;
}
}
x = x1;
y = y1;
putPixels(320 + x * 50 | 0, 240 + y * 50 | 0);
}
Ответ 6
При возведении комплексного аргумента в 4 степень можно получить фрактал по форме напоминающий живой организм, такие фракталы называют биоморфами.
class Complex {
constructor({algebraic,trigonometric} = {}) {
if (algebraic) {
this.initAlgebraic(algebraic);
} else if (trigonometric) {
this.initTrigonometric(trigonometric)
} else {
throw new Error('Invalid arguments');
}
}
static fromReal(r) {
return Complex.fromAlgebraic(r, 0);
}
static fromAlgebraic(r, i) {
return new Complex({algebraic: {r,i}});
}
static fromTrigonometric(modulus, arg) {
return new Complex({trigonometric: {modulus,arg}});
}
initAlgebraic({r,i}) {
this.r = r;
this.i = i;
this.modulus = Math.sqrt(r * r + i * i);
this.arg = Math.atan2(i, r)
}
initTrigonometric({modulus,arg}) {
this.modulus = modulus,
this.arg = arg;
this.r = this.modulus * Math.cos(arg);
this.i = this.modulus * Math.sin(arg);
}
pow(exp) {
return Complex.fromTrigonometric(Math.pow(this.modulus, exp), exp * this.arg);
}
add(c) {
return Complex.fromAlgebraic(this.r + c.r, this.i + c.i);
}
}
let c = canvas.getContext('2d'), w = canvas.width, h = canvas.height
function formula(x,y,cx,cy, maxIter){
var z = Complex.fromAlgebraic(x,y);
var c = Complex.fromAlgebraic(cx,cy);
for(var i=0;i 10 || Math.abs(z.i) > 1000) {
return i;
}
}
return 0;
}
canvas.onmousemove = e => {
var xc = w/2;
var yc = h/2;
var img = c.getImageData(0, 0, w, h)
for(var y = -h/2; y
Ответ 7
Фрактал Ляпунова
Фракталы Ляпунова строятся отображением областей стабильного и хаотического поведения
измеряемых экспонентой Ляпунова, в плоскости a-b для данной периодической последовательности a и b.
Форма зависит от введенной строки включающей символы A и B, а так же от выбранного квадрата в диапазоне [0x4, 0x4]
Приведенный пример с конкретными параметрами также известен как Zircon City.
let c = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height
var i = 0;
function formula(a, b, N, S) {
var xn = 0.5;
var sum = 0;
for (var n = 1; n < N; n++) {
var rn = S[n % S.length] == 'A' ? a : b;
xn = rn * xn * (1 - xn);
sum += Math.log2(Math.abs(rn * (1 - 2 * xn)));
if (sum > 10000) return 50;
if (sum < -10000) return -50;
}
return sum / N;
}
function getRect() {
var xmin = +document.getElementById('xmin').value;
var xmax = +document.getElementById('xmax').value;
var ymin = +document.getElementById('ymin').value;
var ymax = +document.getElementById('ymax').value;
return [{
start: Math.min(xmin, xmax),
range: Math.abs(xmax - xmin) / w
}, {
start: Math.min(ymin, ymax),
range: Math.abs(ymax - ymin) / h
}];
}
function draw() {
var img = c.getImageData(0, 0, w, h)
var S = document.getElementById('S').value;
var [p1, p2] = getRect();
for (var x = 0; x < w; x++) {
for (var y = 0; y < h; y++) {
let La = formula(p1.start + x * p1.range, p2.start + y * p2.range, 1000, S);
let offset = ((h - y) * w + x) * 4
if (La > 0) {
img.data[offset] = (La * 25 + 10) % 256;
img.data[offset + 1] = (La * 25 + 10) % 216;
} else {
img.data[offset] = 255 - (-La * 20 + 50) % 256
img.data[offset + 1] = 215 - (-La * 20 + 50) % 216
}
img.data[offset + 2] = 0
img.data[offset + 3] = 255
}
}
c.putImageData(img, 0, 0)
}
draw();
#c {
display: flex;
}
#settings{
margin-left:10px;
}
Ответ 8
В продолжение темы фракталов в комплексной плоскости - вариация множества Мандельброта
но в каждой итерации компоненты комплексного z берутся по модулю - имеет название Burning Ship, за визуальную похожесть деталей фрактала на корабль. Интересен очень высокой вариативностью геометрических узоров при большом увеличении.
...
z[0] = Math.abs(z[0]);
z[1] = Math.abs(z[1]);
...
Если приближаться в район мачт кораблей можно найти интересные участки
let c = canvas.getContext('2d'), w=canvas.width, h=canvas.height, cx=0, cy=0, z=0.25;
let formula = (x, y, cx, cy, it) => {
let z = [0,0];
for (var i = 0; i < it; ++i) {
z[0] = Math.abs(z[0]);
z[1] = Math.abs(z[1]);
z = [ z[0] * z[0] - z[1] * z[1] - x+cx, 2. * z[0] * z[1] + y-cy];
if (z[0]*z[0] + z[1]*z[1] > 4.1)
return i;
}
return 0
}
let p, ct;
canvas.addEventListener('mousedown',function(e){
p = {x:e.pageX, y:e.pageY};
ct = {x:cx, y:cy};
})
canvas.addEventListener('mousemove',function(e){
if (!p) return;
cx = ct.x + (e.pageX - p.x)/w/z;
cy = ct.y + (e.pageY - p.y)/h/z;
render()
})
canvas.addEventListener('mouseup',function(e){
p = null
})
canvas.addEventListener('wheel',function(e){
z *= 1 + Math.sign(e.wheelDeltaY)*0.1
e.preventDefault();
render()
})
render()
function render() {
let red = r.value;
let green = g.value;
let blue = b.value;
let it = iter.value;
var img = c.getImageData(0, 0, w, h)
for(var x = 0; x
PS: В сниппете работает колесо мышки и драг, при помощи слайдеров можно поменят
кол-во итерация и палитру
Комментариев нет:
Отправить комментарий