Published
Edited
Mar 8, 2020
Insert cell
Insert cell
// A place to hold it all
container = {
let container = d3
.create('div')
.style('width', width + 'px')
.style('height', 0.6 * width + 'px');
container.append(() => mandel_canvas.node());
container.append(() => julia_canvas);
container.append(() => c.node());
return container.node();
}
Insert cell
// A function to draw the Julia set
function draw_julia_set(c, canvas, bail = 200) {
let xmin = -2;
let xmax = 2;
let ymin = -2;
let ymax = 2;
let context = canvas.getContext("2d");
let canvasData = context.createImageData(canvas.width, canvas.height);

for (let i = 0; i < canvas.width; i++) {
for (let j = 0; j < canvas.height; j++) {
let xy = canvas_to_xy([i, j], xmin, xmax, ymin, ymax, canvas, bail);
let it_cnt = juliaOrbitCount(c.re, c.im, xy[0], xy[1], bail);
let color = 255 - (255 * it_cnt) / (bail + 1);
let idx = (i + j * canvas.width) * 4;
canvasData.data[idx + 0] = color;
canvasData.data[idx + 1] = color;
canvasData.data[idx + 2] = color;
canvasData.data[idx + 3] = 255;
}
}
context.putImageData(canvasData, 0, 0);
}
Insert cell
// A function to draw the mandelbrot set
function draw_mandelbrot_set(canvas, xmin, xmax, ymin, ymax) {
let bail = 100;
let mandel_context = canvas.getContext("2d");
let canvasData = mandel_context.createImageData(canvas.width, canvas.height);
for (let i = 0; i < canvas.width; i++) {
for (let j = 0; j < canvas.height; j++) {
var c = canvas_to_xy([i, j], xmin, xmax, ymin, ymax, canvas);
var it_cnt = orbitCnt(c[0], c[1], bail);
var scaled_it_cnt = 255 - (255 * it_cnt) / (bail + 1);
var idx = (i + j * canvas.width) * 4;
canvasData.data[idx + 0] = scaled_it_cnt;
canvasData.data[idx + 1] = scaled_it_cnt;
canvasData.data[idx + 2] = scaled_it_cnt;
canvasData.data[idx + 3] = 255;
}
}
mandel_context.putImageData(canvasData, 0, 0);
}
Insert cell
// A place to draw the Mandelbrot set
mandel_canvas = {
let xmin = -.7
let xmax = 0.3
let ymin = -.5
let ymax = .5
let canvas = d3
.create('canvas')
.attr('width', 0.5 * width)
.attr('height', 0.5 * width)
.on('click', function() {
let ij = d3.mouse(this);
let xy = canvas_to_xy(ij, xmin, xmax, ymin, ymax, canvas.node());
draw_julia_set({ re: xy[0], im: xy[1] }, julia_canvas, 200);
c.html(`c = ${d3.format('.2f')(xy[0])} + ${d3.format('.2f')(xy[1])} i`);
});
draw_mandelbrot_set(canvas.node(), xmin, xmax, ymin, ymax);

return canvas;
}
Insert cell
// A place to display c value choosen by user click
c = d3
.create('div')
.style('width', width + 'px')
.style('text-align', 'center')
.style('background-color', '#eee')
.text('c = -1')
Insert cell
// A place to draw the Julia set
julia_canvas = {
let xmin = -2;
let xmax = 2;
let ymin = -2;
let ymax = 2;
let canvas = d3
.create('canvas')
.attr('width', 0.5 * width)
.attr('height', 0.5 * width);
return canvas.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function isProblemNum(x){
if (x == Infinity || x == Number.MAX_SAFE_INTEGER || x == Number.MIN_SAFE_INTEGER || x == Number.MIN_VALUE || x == Number.MAX_VALUE || x == Number.NEGATIVE_INFINITY || x == Number.NaN)
{
return(true);
}
}
Insert cell
function isProblematic(x){
if (isProblemNum(x**4) || isProblemNum(x**3) || isProblemNum(x**2)) {return(true)};}
Insert cell
F_z = function F_z(z, c){
z = math.divide((math.add(math.cube(z),c)),z) // Sets z to F(z) = (z^3 + c)/z
/*
var a = z.re;
var b = z.im;
var e = c.re;
var g = c.im;
if (isProblematic(a) || isProblematic(b) || isProblematic(e) || isProblematic(g) || (a ==0 && b == 0)){
return Infinity;
}
a = math.divide((a**4- b**4 +g*a+e*b), (a*a + b*b));
b = math.divide((2*a**3*b+2*a*b**3 + a*e-g*b),(a*a+b*b));
z = math.complex(a,b);
*/
// this code creates ugly pictures. Hypothesis: its because of use of the complex conjugate (denied, tried the code below).
// tried assigning numbers with decimals. Nothing. Hypothesis: js doesnt do division well (denied-- tried mathjs for division instead).
/*
var p = math.complex();
var a = z.re;
var b = z.im;
z = math.divide(c,z);
a = a*a - b*b;
b = 2*a*b;
p = math.complex(a,b);
z = math.add(z,p);
This code also creates an ugly picture, though slightly more held together. Hypothesis: some complexity is being truncated when the
*/
/*
Perhaps the problem has something to do with whatever problem in complex division that is addressed by this code from mathjs, which is applied when dividing a complex a+bi by another complex c+di
/var x,t;
if (Math.abs(c) < Math.abs(d)) {
x = c / d;
t = c * x + d;

return new Complex(
(a * x + b) / t,
(b * x - a) / t);

} else {

x = d / c;
t = d * x + c;

return new Complex(
(a + b * x) / t,
(b - a * x) / t);
}

*/
return z;
}
Insert cell
Insert cell
cPoint = math.complex(.2,.2);

Insert cell
z = math.complex(.1,.1);
Insert cell
Insert cell
orbitCnt =
function paramSpace_iteration_count(cre, cim, bail) {
var c = math.complex(cre, cim);
"use strict";
var cnt = 0;
//var c = math.complex(x0,y0); // Initial value of c, the point choosen to try iteration from. Later on, this point will be colored based on the result of its coorisponding critical orbit (found by this function)
// Note that z = (c/2)^(1/3) is the most basic of the three critical points from which to find critical orbits in F_z
var Re = math.re(math.cbrt(math.divide(c,2)));
var Im = math.im(math.cbrt(math.divide(c,2)));
var T = math.complex(Re, Im); // now T is set as the most basic of the three critical points of the function [T = (c/2)^(1/3)]

while((math.abs(T) <= 3) && (cnt++ < bail) && !isProblematic(T.re) && !isProblematic(T.im)){
T = F_z(T,c);
}
return cnt
}

Insert cell
juliaOrbitCount =
function julia_iteration_count(cre, cim, x0, y0, bail) {
var c = math.complex(cre, cim);
var z = math.complex(x0, y0);
var cnt = 0;
while(math.abs(z) < 4 && cnt++ < bail) {
z = F_z(z,c);
}
return cnt;
}
Insert cell
// Convert canvas coordinates to xy coordinates
// - i.e. real and imaginary parts
function canvas_to_xy(ij, xmin, xmax, ymin, ymax, canvas) {
return [
((xmax - xmin) / (canvas.width - 1)) * ij[0] + xmin,
((ymin - ymax) / (canvas.height - 1)) * ij[1] + ymax
];
}
Insert cell
function printJulia(c) {

let height = 0.8 * d3.min([width, window.screen.height]);
let container = d3
.create('div')
.style('width', width.toString() + 'px')
.style('height', height.toString() + 'px')
.append("svg")
.attr("width", width)
.attr("height", height);
let scene = container
.append('x3d')
.attr('width', width.toString() + 'px')
.attr('height', height.toString() + 'px')
.append('scene')
.append("line")
.attr("x1", 100)
.attr("y1", 100)
.attr("x2", 200)
.attr("y2", 200)
.style("stroke", "rgb(255,0,0)")
.style("stroke-width", 2);

}
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