### Smooth Mandelbrot

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);
}