Published
Edited
Aug 1, 2022
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function mandelbrot_period_cnt(c, opts = {}) {
let { bail = 1000, max_orbit_length = 50, tolerance = 10 ** -15 } = opts;
let zi = new Complex(0, 0);
let i = 0;
let iterates = [zi];
while (i++ < bail) {
zi = zi.squared().plus(c);
if (zi.abs2() > 4) {
return [-1, i];
}
let idx = zi.indexOf(iterates, tolerance);
if (idx > -1) {
idx = zi.indexOf(iterates, tolerance * 10 ** 4);
return [iterates.length - idx, i];
} else {
iterates.push(zi);
}
if (iterates.length == max_orbit_length) {
iterates.shift();
}
}
return [0, i];
}
Insert cell
Insert cell
function draw(S, opts = {}) {
let { bail = 1000, max_orbit_length = 50, tolerance = 10 ** -15 } = opts;
let xmin = -2;
let xmax = 0.6;
let ymin = -1.3;
let ymax = 1.3;
let xScale = d3.scaleLinear().range([xmin, xmax]).domain([0, S]);
let yScale = d3.scaleLinear().range([ymin, ymax]).domain([S, 0]);
let canvas = d3.create("canvas").attr("width", S).attr("height", S);
let context = canvas.node().getContext("2d");

for (let i = 0; i < S; i++) {
Promises.delay().then(() => draw_row(i));
}
return canvas.node();

function draw_row(i) {
for (let j = 0; j < S; j++) {
let c = new Complex(xScale(j), yScale(i));
let [p, it] = mandelbrot_period_cnt(c, { bail, max_orbit_length, tolerance });
context.fillStyle = d3
.color(d3.schemeCategory10[(p + 1) % 10])
.darker(it / 100)
.toString();
context.fillRect(j, i, 1, 1);
}
}
}
Insert cell
Insert cell
//draw(500)
Insert cell
Insert cell
Complex = {
let complex = class Complex {
constructor(a, b) {
if (b) {
this.re = a;
this.im = b;
} else if (a) {
this.re = a;
this.im = 0;
} else {
this.re = 0;
this.im = 0;
}
}

// Magnitude squared, magnitude, and squared.
abs2() {
return this.re ** 2 + this.im ** 2;
}
abs() {
return Math.sqrt(this.abs2());
}
squared() {
return new Complex(this.re ** 2 - this.im ** 2, 2 * this.re * this.im);
}

// Algebra
plus(z) {
if (z instanceof Complex) {
return new Complex(this.re + z.re, this.im + z.im);
} else if (typeof z == "number") {
return new Complex(this.re + z, this.im);
} else {
return NaN;
}
}
minus(z) {
if (z instanceof Complex) {
return new Complex(this.re - z.re, this.im - z.im);
} else if (typeof z == "number") {
return new Complex(this.re - z, this.im);
} else {
return NaN;
}
}
times(z) {
if (z instanceof Complex) {
return new Complex(
this.re * z.re - this.im * z.im,
this.re * z.im + this.im * z.re
);
} else if (typeof z == "number") {
return new Complex(z * this.re, z * this.im);
} else {
return NaN;
}
}

// Approximate comparison
almost_equal(z, tolerance = 10 ** -20) {
return this.minus(z).abs2() < tolerance;
}
// Check for occurence in an array
indexOf(A, tolerance = 10 ** -10) {
let i = A.length - 1;
while (i >= 0) {
let z = A[i];
if (this.almost_equal(z, tolerance)) {
break;
}
i--;
}
if (i == 0) {
i = -1;
}
return i;
}
};

return complex;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more