Public
Edited
May 7, 2020
1 fork
2 stars
Insert cell
Insert cell
Insert cell
make_images()

Insert cell
Insert cell
// z=x+iy and c=a+bi, so
// arrays [x,y] and [a,b] and do the complex arithmetic there.

F_z = function([x, y], [a, b]) {
let denominator = x * x + y * y;
let re = (x ** 4 - y ** 4 + x * a + y * b) / denominator;
let im = (2 * x ** 3 * y + 2 * x * y ** 3 - y * a + x * b) / denominator;
return [re, im];
}
Insert cell
Insert cell
// Here's the file upload of the parameter space image.
parameter_pic = FileAttachment('parameter_pic.png').image()
Insert cell
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
Insert cell
function juliaOrbitCount(cre, cim, x0, y0, bail) {
var c = [cre, cim];
var z = [x0, y0];
var cnt = 0;
while (z[0] * z[0] + z[1] * z[1] < 16 && cnt++ < bail) {
z = F_z(z, c);
}
return cnt;
}
Insert cell
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
Insert cell
// A fixed width is used here, because the parameter space is an uploaded image (not generated by this code)
width = 1000
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
d3 = require('d3@5')
Insert cell
parameter_div = {
let xmin = -.7;
let xmax = 0.3;
let ymin = -.5;
let ymax = .5;
let x_scale = d3
.scaleLinear()
.domain([0, width / 2])
.range([xmin, xmax]);
let y_scale = d3
.scaleLinear()
.domain([width / 2, 0])
.range([ymin, ymax]);

let div = d3
.create('div')
.style('width', `${width / 2}px`)
.style('height', `${width / 2}px`)
.style('display', 'inline-block')
.style('float', 'left');
div.append(() => parameter_pic);
div.on('click', function() {
let [i, j] = d3.mouse(this);
let x = x_scale(i);
let y = y_scale(j);
draw_julia_set({ re: x, im: y }, julia_canvas, 200);
c_display.html(`c = ${d3.format('.2f')(x)} + ${d3.format('.2f')(y)} i`);
});
return div.node();
}
Insert cell
function make_images() {
let container = d3
.create('div')
.style('width', `${width}px`)
.style('height', `${30 + width / 2}px`);
let julia_container = d3
.create('div')
.style('width', `${width / 2}px`)
.style('height', `${width / 2}px`)
.style('display', 'inline-block');
julia_container.append(() => julia_canvas);
container.append(() => parameter_div);
container.append(() => julia_container.node());
container.append(() => c_display.node());
return container.node();
}
Insert cell
// A place to display c value chosen by user click
c_display = d3
.create('div')
.style('width', width + 'px')
.style('height', '30px') // Added a height specification
.style('text-align', 'center')
.style('background-color', '#eee')
.text('c = ?') // c isn't defined yet, will be defined after the user clicks a point in the parameter space.
Insert cell
Insert cell
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
// 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) {
// T = F_z(T, c);
// }
// return cnt;
// }
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
// return z;
// }
Insert cell
// This is required to run the code above but I've
// math = require('mathjs')
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