- click and drag to zoom in on an area
- click the reset zoombutton to get back to the start
- press the ikey to view info about the zoomed area
- I used this article as a starting point. Source code available here
- TODO: this article has some documentation of how to do mandelbrot deep zoom
 
  
  
import { fmandelbrot } from "./components/mandelbrot.js";
const MAX_ITER = 512;
const W = 800;
const H = 800;
const state = {
  xscale: d3.scaleLinear([0, W], [-2, 0.6]),
  yscale: d3.scaleLinear([0, H], [1.25, -1.25]),
  coords: [0, 0],
  infoEnabled: false,
};
const colors = new Map();
const colorscale = d3
  .scaleSequentialLog(d3.interpolateInferno)
  .domain([1, MAX_ITER]);
for (let i = 1; i < MAX_ITER; i++) {
  colors.set(i, colorscale(i));
}
colors.set(MAX_ITER, `rgb(0, 0, 0)`);
const ctx = document.querySelector("canvas#mcanv").getContext("2d");
const drawRow = (ctx, xscale, yscale, y = 0, rows = 20) => {
  for (let i = 0; i < rows; i++) {
    for (let x = 0; x <= W; x++) {
      const l = fmandelbrot([xscale(x), yscale(y + i)], MAX_ITER);
      ctx.fillStyle = colors.get(l);
      ctx.fillRect(x, y + i, 1, 1);
    }
  }
  if (y < H) {
    requestAnimationFrame(() => drawRow(ctx, xscale, yscale, y + rows, rows));
  }
};
requestAnimationFrame(() => drawRow(ctx, state.xscale, state.yscale));
const ocanvas = document.querySelector("canvas#overlay");
const overlay = ocanvas.getContext("2d");
const bbox = ocanvas.getBoundingClientRect();
overlay.font = "24px Arial";
let mousedown = false;
const drawDebug = () => {
  overlay.fillStyle = "white";
  overlay.strokeStyle = "black";
  overlay.lineWidth = 2;
  overlay.strokeText(`x scale: ${state.xscale.range()}`, 10, 30);
  overlay.fillText(`x scale: ${state.xscale.range()}`, 10, 30);
  overlay.strokeText(`y scale: ${state.yscale.range()}`, 10, 60);
  overlay.fillText(`y scale: ${state.yscale.range()}`, 10, 60);
};
const clearOverlay = () => {
  overlay.clearRect(0, 0, W, H);
};
document.querySelector("#overlay").addEventListener("mousedown", (e) => {
  const mx = e.clientX - bbox.left;
  const my = e.clientY - bbox.top;
  state.coords = [mx, my];
  mousedown = true;
});
document.querySelector("#overlay").addEventListener("mousemove", (e) => {
  if (mousedown) {
    const mx = e.clientX - bbox.left;
    const my = e.clientY - bbox.top;
    clearOverlay();
    overlay.strokeStyle = "white";
    overlay.strokeRect(
      state.coords[0],
      state.coords[1],
      mx - state.coords[0],
      my - state.coords[1],
    );
    console.log(state.infoEnabled);
    if (state.infoEnabled) drawDebug();
  }
});
document.querySelector("#overlay").addEventListener("mouseup", (e) => {
  if (!mousedown) {
    return;
  }
  mousedown = false;
  clearOverlay();
  const mx = e.clientX - bbox.left;
  const my = e.clientY - bbox.top;
  const minx = Math.min(state.coords[0], mx);
  const maxx = Math.max(state.coords[0], mx);
  const miny = Math.min(state.coords[1], my);
  const maxy = Math.max(state.coords[1], my);
  const maxDiff = Math.max(maxx - minx, maxy - miny);
  state.xscale = d3.scaleLinear(
    [0, W],
    [state.xscale(minx), state.xscale(minx + maxDiff)],
  );
  state.yscale = d3.scaleLinear(
    [0, H],
    [state.yscale(miny), state.yscale(miny + maxDiff)],
  );
  if (state.infoEnabled) drawDebug();
  requestAnimationFrame(() => drawRow(ctx, state.xscale, state.yscale));
});
document.addEventListener("keydown", (event) => {
  
  if (event.key === "i") {
    if (!state.infoEnabled) {
      state.infoEnabled = true;
      drawDebug();
    } else {
      state.infoEnabled = false;
      clearOverlay();
    }
  }
});