RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 983698
Accepted
Stranger in the Q
Stranger in the Q
Asked:2020-05-21 03:41:07 +0000 UTC2020-05-21 03:41:07 +0000 UTC 2020-05-21 03:41:07 +0000 UTC

生成各种分形或其他图像的最短和最简单的方法

  • 772

我对计算机图形学很感兴趣,想参加一些比赛。

我想了解新的(对我自己而言)方法来生成从相当简单的公式获得的各种分形或其他图像。

也就是说,比赛的标准是——使用简单的基本公式来获得有趣的图片。


比如有这样一个循环遍历图片中所有像素的实现:

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<w; x++) {
        for(var y = 0; y<h; y++) {
            let value = formula(x, y, e.x, e.y)
            let offset = (y*w + x)*4    
            img.data[offset]     = value[0]*255
            img.data[offset + 1] = value[1]*255
            img.data[offset + 2] = value[2]*255
            img.data[offset + 3] = 255
        }
    }
    c.putImageData(img, 0, 0) 
}

canvas.onmousemove({x: 456, y: 123})
<canvas width="600" height="175" id="canvas"/>

有必要实现一个函数formula来获得一个“有趣”的图像,鼠标坐标是额外的参数

语言 - 任何,但最好js是,因为在线可视化的可能性。

PS:递归方法对我来说不太有趣,特别是如果递归不是尾,因为将其移植到 glsl 将很困难,如果不是不可能的话。

PPS,为了吸引更多的观众,我选择了cpu,我为它写了图片的循环代码,但是如果你喜欢,我更喜欢webgl,所以下面是我绘制时显卡循环遍历所有像素的片段一个关闭整个屏幕的三角形和公式函数是片段着色器:)

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;
  }
}
<canvas width="600" height="175" id="canvas"/>

<script type="glsl/vertex">

attribute vec2 coords;

void main(void) {
  gl_Position = vec4(coords.xy, 0.0, 1.0);
}

</script>

<script type="glsl/fragment">

precision highp float;

uniform vec2 mouse;
uniform vec2 resolution;

void main(void) {
    vec2 m = mouse/resolution;
    vec2 p = gl_FragCoord.xy/resolution - 0.5;
    gl_FragColor = vec4(p, m); 
}

</script>

javascript
  • 9 9 个回答
  • 10 Views

9 个回答

  • Voted
  1. Stranger in the Q
    2020-05-22T05:49:43Z2020-05-22T05:49:43Z

    我提请您注意超公式

    超级公式是超级​​椭圆的推广,由 Johan Gielis 于 2003 年首次提出。Gielis 建议使用该公式来描述自然界中发现的复杂形状和曲线。

    在极坐标中,具有在此处输入图像描述半径和在此处输入图像描述 角度,超公式如下所示:

    在此处输入图像描述

    通过选择不同的参数值,在此处输入图像描述得到不同的形状。

    在此处输入图像描述

    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<w; x++) {
            for(var y = 0; y<h; y++) {
                let value = formula(x, y, e.x, e.y)
                let offset = (y*w + x)*4    
                img.data[offset]     = value[0]*255
                img.data[offset + 1] = value[1]*255
                img.data[offset + 2] = value[2]*255
                img.data[offset + 3] = 255
            }
        }
        c.putImageData(img, 0, 0) 
    }
    
    draw({x:100, y:100})
    
    canvas.onmousemove = e => {
        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)
    }
    <body style="margin:0;user-select:none"><canvas width="175" height="175" id="canvas"></canvas><br><span>click to change: asterisk</span></body>

    • 17
  2. Stranger in the Q
    2020-05-21T03:46:18Z2020-05-21T03:46:18Z

    我知道的最简单的事情,除了复平面中的分形,是分形kali,以在分形论坛上发布此公式的人命名

    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<w; x++) {
            for(var y = 0; y<h; y++) {
                let value = formula(x, y, e.x, e.y)
                let offset = (y*w + x)*4    
                img.data[offset]     = value[0]*255
                img.data[offset + 1] = value[1]*255
                img.data[offset + 2] = value[2]*255
                img.data[offset + 3] = 255
            }
        }
        c.putImageData(img, 0, 0) 
    }
    
    canvas.onmousemove({x: 456, y: 123})
    <canvas width="600" height="175" id="canvas"/>


    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;
      }
    }
    <canvas width="600" height="175" id="canvas"/>
    
    <script type="glsl/vertex">
    
    attribute vec2 coords;
    
    void main(void) {
      gl_Position = vec4(coords.xy, 0.0, 1.0);
    }
    
    </script>
    
    <script type="glsl/fragment">
    
    precision highp float;
    
    uniform vec2 mouse;
    uniform vec2 resolution;
    
    void main(void) {
    
        vec2 m = mouse/resolution;
        vec2 p = gl_FragCoord.xy;
        
        // вот собственно сама формула реализована в этих 3 строчках
        vec2 q = (p + p - resolution) / resolution.y;
        for(int i = 0; i < 10; i++)
             q = abs(q)/dot(q,q) -  m;
             
             
        gl_FragColor = vec4(q, q.x/q.y, 1.0);
    }
    
    </script>


    分形的另一种变体

    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;
      }
    }
    <canvas width="600" height="175" id="canvas"/>
    
    <script type="glsl/vertex">
    
    attribute vec2 coords;
    
    void main(void) {
      gl_Position = vec4(coords.xy, 0.0, 1.0);
    }
    
    </script>
    
    <script type="glsl/fragment">
    
    precision highp float;
    
    uniform vec2 mouse;
    uniform vec2 resolution;
    
    void main(void) {
    
        vec2 m = mouse/resolution;
        vec2 p = gl_FragCoord.xy;
        
        // вот собственно сама формула реализована в этих 3 строчках
        vec2 q = (p + p - resolution) / resolution.y/m.y;
        for(int i = 0; i < 11; i++)
             q = -abs(q)/dot(q,q) + m;
             
             
        gl_FragColor = vec4(q, 1.-dot(q,q), 1.0);
    }
    
    </script>

    • 15
  3. Stranger in the Q
    2020-05-23T21:02:18Z2020-05-23T21:02:18Z

    朱莉娅集

    它的公式如下所示:

    z其中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<w; x++) {
            for(var y = 0; y<h; y++) {
                let v = formula(x, y, e.x, e.y)
                let o = (y*w + x)*4    
                img.data[o++] = Math.sin(v/5)*255
                img.data[o++] = Math.sin(v/6)*255
                img.data[o++] = Math.sin(v/7)*255
                img.data[o++] = 255
            }
        }
        c.putImageData(img, 0, 0) 
    }
    
    canvas.onmousemove({x: 111, y: 123})
    <canvas width="400" height="400" id="canvas"/>

    По очень похожей формуле получается обобщение всех множеств Жюлиа - Множество Мандельброта (Mandelbrot set)

    在此处输入图像描述

    • 13
  4. Best Answer
    Grundy
    2020-05-29T22:45:20Z2020-05-29T22:45:20Z

    Пример с бассейном Ньютона.

    Области подкрашиваются в соответствии с близостью к корням уравнения 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;i<maxIter && z.modulus<max && d.modulus>min;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<h/2; y++) {
          for(var x = -w/2; x<w/2; x++) {
            let [value,root] = formula(x*coef, y*coef, 50,1e6, 1e-6);
            
            
            let offset = ((y+yc)*w + x+xc)*4 
            img.data[offset]     = (value*roots[0].sub(root).modulus*7)%255;
            img.data[offset + 1] = (value*roots[1].sub(root).modulus*5)%255;
            img.data[offset + 2] = (value*roots[2].sub(root).modulus*11)%255;
            img.data[offset + 3] = 255
          }
        }
        c.putImageData(img, 0, 0) ;
    };
    draw(100);
    <canvas width="400" height="400" id="canvas"></canvas>
    <input type="range" min="0" max="200" id="coef" value="100">

    Для ур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;i<maxIter && z.modulus<max && d.modulus>min;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<h/2; y++) {
          for(var x = -w/2; x<w/2; x++) {
            let [value,root] = formula(x*coef, y*coef, 50,1e6, 1e-6);       
            
            let offset = ((y+yc)*w + x+xc)*4 
            img.data[offset]     = (value*7)%255;
            img.data[offset + 1] = (value*5)%255;
            img.data[offset + 2] = (value*11)%255;
            img.data[offset + 3] = 255
          }
        }
        c.putImageData(img, 0, 0) ;
    };
    draw(100);
    <canvas width="400" height="400" id="canvas"></canvas>
    <input type="range" min="0" max="400" id="coef" value="100">

    • 9
  5. Grundy
    2020-05-29T16:18:57Z2020-05-29T16:18:57Z

    Сетка Аполлона.

    在此处输入图像描述

    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);
    }
    <canvas width="650" height="500" id="canvas" />

    • 7
  6. Grundy
    2020-05-29T18:09:39Z2020-05-29T18:09:39Z

    При возведении комплексного аргумента в 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<maxIter || z.modulus<50;i++){
        z = z.pow(4).add(c);
        if(Math.abs(z.r) > 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<h/2; y++) {
          for(var x = -w/2; x<w/2; x++) {
            let value = formula(x*0.01, y*0.01, e.x/399, 1+e.y/398, 70)
            let offset = ((y+yc)*w + x+xc)*4 
            img.data[offset]     = Math.sin(value)*255;
            img.data[offset + 1] = Math.cos(value)*255;
            img.data[offset + 2] = Math.random()*value*255;
            img.data[offset + 3] = 255
          }
        }
        c.putImageData(img, 0, 0) 
    }
    console.log(1);
    canvas.onmousemove({x: 111, y: 123})
    <canvas width="400" height="400" id="canvas"/>

    • 6
  7. Stranger in the Q
    2020-05-29T21:25:33Z2020-05-29T21:25:33Z

    В продолжение темы фракталов в комплексной плоскости - вариация множества Мандельброта, но в каждой итерации компоненты комплексного 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<w; x++) {
          for(var y = 0; y<h; y++) {
              let v = formula((x/w-0.5)/z, (y/h-0.5)/z,cx,cy,it)
              let o = (y*w + x)*4    
              img.data[o++] = Math.sin(v/red)*255
              img.data[o++] = Math.sin(v/green)*255
              img.data[o++] = Math.sin(v/blue)*255
              img.data[o++] = 255
          }
      }
      c.putImageData(img, 0, 0)
    }
    <input type="range" id="iter" min=16 max=512 value=32 onchange="render()"/>
    <input type="range" id="r" min=1 max=33 value=11 onchange="render()"/>
    <input type="range" id="g" min=1 max=33 value=7 onchange="render()" />
    <input type="range" id="b" min=1 max=33 value=22 onchange="render()" />
    <br>
    <canvas width="400" height="400" id="canvas"/>

    PS: В сниппете работает колесо мышки и драг, при помощи слайдеров можно поменять кол-во итерация и палитру

    • 4
  8. Grundy
    2020-06-01T03:27:09Z2020-06-01T03:27:09Z

    Фрактал Ляпунова

    Фракталы Ляпунова строятся отображением областей стабильного и хаотического поведения, измеряемых экспонентой Ляпунова, в плоскости 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;
    }
    <div id="c">
      <div>
        <canvas width="400" height="400" id="canvas"></canvas>
      </div>
      <div id="settings">
        <label>xmin <input type="number" id="xmin" min="0" max="4" step="0.1" value="2.5"/></label>
        <label>xmax <input type="number" id="xmax" min="0" max="4" step="0.1" value="3.4"/></label><br>
        <label>ymin <input type="number" id="ymin" min="0" max="4" step="0.1" value="3.4"/></label>
        <label>ymax <input type="number" id="ymax" min="0" max="4" step="0.1" value="4"/></label><br>
        <label>Seq <input type="text" id="S" value="BBBBBBAAAAAA"/></label>
        <input type="button" value="apply" onclick="draw()" />
      </div>
    </div>

    • 3
  9. barsik34566
    2022-09-15T21:24:47Z2022-09-15T21:24:47Z

    1)Модификация кривой Леви.

    Посмотрите тут новые фракталы, модификация кривой Леви, есть онлайн построение. Формула одна меняется только кол-во углов. Скорей это уже больше L системы. https://habr.com/ru/post/328568/

    2).Странные аттракторы с симметрией.

    Обычно странные аттракторы бесформенная каша пикселей или траекторий, тут фотки как из хаоса можно получить симметрию. Вроде в той проге и коды есть…да и pdf можно нагуглить при желании. http://www.fractalsciencekit.com/tutorial/examples/symicon.htm

    3). Снежинки из параметрических кривых Безье.

    Формы и сложность можно создать бесконечную. Суть формула одна и также только разное количество кривых и координат опорных точек. Вот тут делал давно на GeoGebra 5. https://www.cyberforum.ru/mathcad/thread1271478.html#post6735492

    Там еще где-то в теме была ссылка люди скидывали параметрические уравнения рисующие сложные цветки, форма менялась от параметров. Вот эти делал. https://www.cyberforum.ru/mathcad/thread1271478-page2.html#post6766654

    4)Вообще по фракталам

    Есть прога Fractal Science Kit, не видел больше нигде такого количества всяких таких штук, несколько сотен наверно с настройками.

    5)Новый алгоритм генерации типа снежинок или лабиринтов.

    Вот тут случайно открыл новый алгоритм генерации типа снежинок или лабиринтов, алгоритм чем-то похоже на игру жизнь. Делал реакцию диффузии и алгоритмически ошибся =). Получилось интересно. Можно генерировать всякие узоры типа вышиванки крестиком, алгоритм останавливает граница в 1 пиксель. Рост начинается с 1 пикселя. Вот тут делал 5 анимаций, а код там есть в теме чего-то вылетает после компиляции =(. Делал вроде на старом glut. https://www.cyberforum.ru/opengl/thread2749102-page3.html#post15131849

    Вспомнил еще два новых для меня метода, насколько помню почти не упомянуты в литературе по фракталам.

    6)Chaos_game.

    最近我为自己发现了一种方法,我还没有尝试过任何方法。理论上,潜力是巨大的。 https://en.wikipedia.org/wiki/Chaos_game

    7)循环对称多尺度图灵模式我的一个新算法是基于扩散反应。实际上,对她来说,我想通过基本扩散5分来接近。直到热情消退。但他以一种非常不寻常的方式画在那里,就像电子显微镜上的照片一样。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5