I am just forking the awesome turtles of @llemarie Mandelbrot through Curl Noise
This one uses the smooth Mandelbrot iteration count formula: iquilezles.org/www/a…ooth/mset_smooth.htm
I made some changes to the PoissonDiscGrid (using a queue), so I could improve precision without getting incorrect self-intersection tests.
#fractal #mandelbrot
Log in to post a comment.
// Smooth Mandelbrot. Created by Reinder Nijhoff 2021 // @reindernijhoff // // https://turtletoy.net/turtle/a1b031fb9e // // Forked from "Mandelbrot through Curl Noise" by llemarie // https://turtletoy.net/turtle/48b30c6521 Canvas.setpenopacity(.6); const turtle = new Turtle(); turtle.traveled = 0; const radius = 0.58; // min=0.1, max=5, step=0.01 const minRadius = 0.02; // min=0.01, max=1, step=0.01 const maxPathLength = 50; // min=1, max=100, step=0.1 const happyAccident = 1; // min=0, max=1, step=1 (No, Yes) const maxTries = 35; const max_iterations = 19; /// min = 1, max = 100, step = 1 const m_scale = 80 /// min = 10, max = 10000, step = 0.01 const offset_x = 0.7 /// min = -10, max = 10, step = 0.001 const offset_y = 0 /// min = -10, max = 10, step = 0.001 // const max_iterations = 190 /// min = 1, max = 100, step = 1 // const m_scale = 4000 /// min = 10, max = 10000, step = 0.01 // const offset_x = 0.72; /// min = -10, max = 10, step = 0.001 // const offset_y = -0.25; /// min = -10, max = 10, step = 0.001 const grid = new PoissonDiscGrid(radius); function sqrlen(r, i) { return happyAccident ? r*r*15 - i*i : r*r + i*i; } function mandelbrot(x, y) { let cr = x; let ci = y; let zr = 0; let zi = 0; let iterations = 0; const B = 8; while (sqrlen(zr, zi) < B*B) { [zr, zi] = [zr * zr - zi * zi + cr, 2 * zr * zi + ci]; iterations += 1; if (iterations >= max_iterations) break; } iterations = iterations - Math.log(Math.log(Math.sqrt(sqrlen(zr, zi)))/Math.log(B))/Math.log(2) + 1; return Math.min(Math.max(0,iterations), max_iterations) / max_iterations; } function get_image_intensity(x,y) { var center_x = x; var center_y = y; const m_x = (center_x) / m_scale - offset_x; const m_y = (center_y) / m_scale - offset_y; return mandelbrot(m_x, m_y); } function curlNoiseM(x, y) { const eps = 0.01; const dx = (get_image_intensity(x, y + eps) - get_image_intensity(x, y - eps))/(2 * eps); const dy = (get_image_intensity(x + eps, y) - get_image_intensity(x - eps, y))/(2 * eps); const l = Math.hypot(dx, dy) * 10; const c = [dx / l, -dy / l]; return c; } function getRadius(p2) { const l = get_image_intensity(p2[0], p2[1]); return (minRadius * l + radius * (1-l)) / 2; } function walk(i) { const p = turtle.pos(); const curl = curlNoiseM(p[0], p[1]); const dest = [p[0]+curl[0], p[1]+curl[1]]; dest[2] = getRadius(dest); if (turtle.traveled < maxPathLength && Math.abs(dest[0]) < 110 && Math.abs(dest[1]) < 110 && grid.insert(dest)) { turtle.goto(dest); turtle.traveled += Math.hypot(curl[0], curl[1]); } else { turtle.traveled = 0; let r, i = 0; do { r =[Math.random()*200-100, Math.random()*200-100]; r[2] = getRadius(r); i ++; } while(!grid.insert(r) && i < maxTries); if (i >= maxTries) { return false; } turtle.jump(r); } return true; } //////////////////////////////////////////////////////////////// // Poisson-Disc utility code. Created by Reinder Nijhoff 2019 // https://turtletoy.net/turtle/b5510898dc //////////////////////////////////////////////////////////////// function PoissonDiscGrid(radius) { class PoissonDiscGrid { constructor(radius) { this.cellSize = 1/Math.sqrt(2)/radius; this.cells = []; this.queue = []; } insert(p) { const x = p[0]*this.cellSize|0, y=p[1]*this.cellSize|0; for (let xi = x-1; xi<=x+1; xi++) { for (let yi = y-1; yi<=y+1; yi++) { const ps = this.cell(xi,yi); for (let i=0; i<ps.length; i++) { if ((ps[i][0]-p[0])**2 + (ps[i][1]-p[1])**2 < (ps[i][2]+p[2])**2) { return false; } } } } this.queue.push([p, x, y]); if (this.queue.length > 10) { const d = this.queue.shift(); this.cell(d[1], d[2]).push(d[0]); } return true; } cell(x,y) { const c = this.cells; return (c[x]?c[x]:c[x]=[])[y]?c[x][y]:c[x][y]=[]; } } return new PoissonDiscGrid(radius); }