Public
Edited
Dec 31, 2023
1 fork
Importers
4 stars
Insert cell
Insert cell
Insert cell
new QuadraticJuliaSet(0).draw_simple()
Insert cell
Insert cell
Insert cell
new QuadraticJuliaSet(math.complex(-0.62, 0.41)).draw_simple({ depth })
Insert cell
Insert cell
new QuadraticJuliaSet(math.complex(-0.62, 0.41)).draw()
Insert cell
Insert cell
Insert cell
QuadraticJuliaSet = {
let QuadraticJuliaSet = class QuadraticJuliaSet {
constructor(c) {
this.c = c;
}
};

QuadraticJuliaSet.prototype.draw_simple = function (opts = {}) {
let {
depth = 12,
width = 512,
height = 512,
extent = [
[-2, 2],
[-2, 2]
]
} = opts;

let c = this.c;
let xmin = extent[0][0];
let xmax = extent[0][1];
let ymin = extent[1][0];
let ymax = extent[1][1];
let pts = [math.complex(0.123, -0.0456)];
for (let i = 0; i < depth; i++) {
pts = pts
.map(function (z) {
let z1 = math.sqrt(math.add(z, math.multiply(c, -1)));
let z2 = math.multiply(z1, -1);
return [z1, z2];
})
.flat();
}
let canvas = d3
.create("canvas")
.attr("width", width)
.attr("height", height);

let ctx = canvas.node().getContext("2d");
ctx.fillStyle = "black";
pts = pts.map(function (z) {
let ij = complex_to_canvas(z, xmin, xmax, ymin, ymax, width, height);
let i = ij[0];
let j = ij[1];
ctx.fillRect(i, j, 1, 1);
});
return canvas.node();
};

QuadraticJuliaSet.prototype.generate_inverse_iterate_matrix = function (
opts = {}
) {
let {
bailout = 5,
extent = [
[-2, 2],
[-2, 2]
],
width = 512,
height = 512
} = opts;

let c = this.c;
let xmin = extent[0][0];
let xmax = extent[0][1];
let ymin = extent[1][0];
let ymax = extent[1][1];

// Set up a matrix to keep track of which
// points have been plotted.
let plot_record = new Array(width + 1);
for (let i = 0; i <= width; i++) {
plot_record[i] = new Array(height + 1);
}
// Initialize the values in the matrix
// to be zero.
for (let row = 0; row <= width; row++) {
for (let col = 0; col <= height; col++) {
plot_record[row][col] = 0;
}
}
// Start the iteration from an intial point.
let z0 = math.complex(0.123, -0.0456);
// And inverse iterate a few times to ensure
// the initial point is close to the Julia set.
for (let i = 0; i < 5; i++) {
z0 = math.sqrt(math.add(z0, math.multiply(c, -1)));
z0 = math.multiply(math.sqrt(math.add(z0, math.multiply(c, -1))), -1);
}

// Now, we're going to use a standard tree construction
// to perform the inverse iteration.
let zNode = new JuliaNode(z0);
let queue = [zNode];
let cnt = 0;
while (queue.length > 0) {
zNode = queue.pop();
let z = zNode.z;
let ij = complex_to_canvas(z, xmin, xmax, ymin, ymax, width, height);
let i = ij[0];
let j = ij[1];
if (plot_record[i][j] < bailout) {
let z1 = math.sqrt(math.add(z, math.multiply(c, -1)));
let z2 = math.multiply(z1, -1);
let left = new JuliaNode(z1);
let right = new JuliaNode(z2);
zNode.left = left;
zNode.right = right;
queue.push(left);
queue.push(right);
plot_record[i][j] = plot_record[i][j] + 1;
}
}

// The output is matrix that tells us which pixels
// are on (value>0) or off (value=0).
return plot_record;
};

QuadraticJuliaSet.prototype.draw = function (opts = {}) {
let {
bailout = 5,
extent = [
[-2, 2],
[-2, 2]
],
width = 512,
height = 512
} = opts;
let all_opts = { bailout, extent, width, height };

let plot_record = this.generate_inverse_iterate_matrix(all_opts);
let canvas = d3
.create("canvas")
.attr("width", width)
.attr("height", height);

let ctx = canvas.node().getContext("2d");
ctx.fillStyle = "black";
let cnts = [];
for (let j = 0; j <= height; j++) {
for (let i = 0; i <= width; i++) {
if (plot_record[i][j] > 0) {
ctx.fillRect(i, j, 1, 1);
}
}
}
return canvas.node();
};

QuadraticJuliaSet.prototype.box_count = function () {
return box_count(
this.generate_inverse_iterate_matrix({
bailout: 5,
width: 2 ** 10,
height: 2 ** 10
})
);
};

return QuadraticJuliaSet;

function xy_to_canvas(x, y, xmin, xmax, ymin, ymax, width, height) {
return [
Math.min(Math.floor(((x - xmin) * width) / (xmax - xmin)), width - 1),
Math.min(Math.floor(((ymax - y) * height) / (ymax - ymin)), height - 1)
];
}
function complex_to_canvas(z, xmin, xmax, ymin, ymax, width, height) {
return xy_to_canvas(z.re, z.im, xmin, xmax, ymin, ymax, width, height);
}

function box_count(matrix) {
let w = matrix.length;
let cnts = [];
for (let s = 1; s <= w; s = 2 * s) {
let boxes = [];
let cnt = 0;
for (let i = 0; s * (i + 1) < w; i++) {
for (let j = 0; s * (j + 1) < w; j++) {
if (check_box(i, j, s)) {
cnt = cnt + 1;
boxes.push([i, j]);
}
}
}
cnts.push({ s: s, cnt: cnt, boxes: boxes });
}

function check_box(i, j, s) {
for (let i0 = i * s; i0 < (i + 1) * s; i0++) {
for (let j0 = j * s; j0 < (j + 1) * s; j0++) {
if (matrix[i0][j0] > 0) {
return true;
}
}
}
return false;
}

return cnts;
}
}
Insert cell
JuliaNode = class JuliaNode {
constructor(z) {
this.z = z;
}
}
Insert cell
math = require("mathjs")
Insert cell
// julia_dim2 = julia_dim
// .filter((a) => a.C < 0.2501)
// .concat([{ C: 0.2505, Dim: undefined }])
// .concat(julia_dim.filter((a) => a.C > 0.2505))
Insert cell
// julia_dim = FileAttachment("julia_dim.csv").csv({ typed: true })
Insert cell
ss = require("simple-statistics")
Insert cell
{
// let div = d3.create("div").style("width", "600px");
let canvas = J.draw({
bailout: 25,
width: 1000,
height: 250,
extent: [
[-2, 2],
[-0.5, 0.5]
]
});
d3.select(canvas).style("max-width", "100%");
//div.append(() => canvas);
return canvas;
}
Insert cell
counts
? ss.linearRegression(counts.map((o) => [-Math.log(o.s), Math.log(o.cnt)]))
: 0
Insert cell
counts = J.box_count({
bailout: 25,
width: 1000,
height: 1000
})
Insert cell
J = new QuadraticJuliaSet(c)
Insert cell
// c = math.complex(-0.122561, 0.744861)

// c = math.complex(0.3, 0.025)

c = math.complex(-1.76, 0.005)
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