Skip to content

Commit

Permalink
More color palettes and better performance
Browse files Browse the repository at this point in the history
  • Loading branch information
rosslh committed Feb 4, 2024
1 parent a50a71d commit 33b1447
Show file tree
Hide file tree
Showing 24 changed files with 624 additions and 496 deletions.
207 changes: 122 additions & 85 deletions client/app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ interface WorkerContainer {
}

interface MandelbrotConfig {
iterations: number;
exponent: number;
[key: string]: number | string | boolean;
}

interface Input {
interface NumberInput {
id: "iterations" | "exponent";
map: MandelbrotMap;
minValue: number;
Expand All @@ -25,6 +24,16 @@ interface Input {
resetView?: boolean;
}

interface SelectInput {
id: "colorScheme";
map: MandelbrotMap;
}

interface CheckboxInput {
id: "reverseColors";
map: MandelbrotMap;
}

interface MessageFromWorker {
data: {
image: Uint8ClampedArray;
Expand All @@ -39,6 +48,8 @@ interface Done {
const config: MandelbrotConfig = {
iterations: 200,
exponent: 2,
colorScheme: "inferno",
reverseColors: false,
};

class WorkerManager {
Expand Down Expand Up @@ -86,87 +97,6 @@ class WorkerManager {
}
}

function handleInput({
id,
map,
defaultValue,
minValue,
maxValue,
resetView,
}: Input) {
const input = <HTMLInputElement>document.getElementById(id);
input.value = String(config[id]);
input.oninput = debounce(({ target }) => {
let parsedValue = Number.parseInt((<HTMLInputElement>target).value, 10);
if (
isNaN(parsedValue) ||
parsedValue < minValue ||
parsedValue > maxValue
) {
parsedValue = defaultValue;
}
input.value = String(parsedValue);
config[id] = parsedValue;
map.refresh(resetView);
}, 1000);
}

function handleInputs(map: MandelbrotMap) {
handleInput({
id: "iterations",
map,
minValue: 1,
defaultValue: 200,
maxValue: 10 ** 9,
});
handleInput({
id: "exponent",
map,
minValue: 2,
defaultValue: config.exponent,
maxValue: 10 ** 9,
resetView: true,
});

const refreshButton: HTMLButtonElement = document.querySelector("#refresh");
refreshButton.onclick = () => map.refresh();

const fullScreenButton: HTMLButtonElement =
document.querySelector("#full-screen");
if (document.fullscreenEnabled) {
fullScreenButton.onclick = toggleFullScreen;
} else {
fullScreenButton.style.display = "none";
}

const saveButton: HTMLButtonElement = document.querySelector("#save-image");
try {
// eslint-disable-next-line no-constant-condition
if (new Blob()) {
saveButton.onclick = () => map.saveImage();
} else {
throw "FileSaver not supported";
}
} catch {
saveButton.style.display = "none";
}

function toggleFullScreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
document.body.requestFullscreen();
}
}

document.addEventListener("fullscreenchange", () => {
const button: HTMLButtonElement = document.querySelector("#full-screen");
button.innerText = document.fullscreenElement
? "Exit Full Screen"
: "Full Screen";
});
}

class MandelbrotLayer extends L.GridLayer {
tileSize: number;
taskQueue: Array<{
Expand Down Expand Up @@ -259,6 +189,8 @@ class MandelbrotLayer extends L.GridLayer {
maxIterations: config.iterations,
exponent: config.exponent,
tileSize: this.getTileSize().x,
colorScheme: config.colorScheme,
reverseColors: config.reverseColors,
});
}

Expand All @@ -277,7 +209,7 @@ class MandelbrotLayer extends L.GridLayer {

debounceTileGeneration = debounce(() => {
this.processTileGenerationQueue();
}, 500);
}, 400);

createTile(coords: L.Coords, done: Done) {
const canvas = L.DomUtil.create(
Expand Down Expand Up @@ -318,6 +250,10 @@ class MandelbrotMap extends L.Map {
this.defaultZoom = 2;
this.setView(this.defaultPosition, this.defaultZoom);
this.mandelbrotLayer.refresh();

this.on("dragend", function () {
this.mandelbrotLayer.processTileGenerationQueue();
});
}

async refresh(resetView = false) {
Expand All @@ -343,6 +279,107 @@ class MandelbrotMap extends L.Map {
}
}

function handleNumberInput({
id,
map,
defaultValue,
minValue,
maxValue,
resetView,
}: NumberInput) {
const input = <HTMLInputElement>document.getElementById(id);
input.value = String(config[id]);
input.oninput = debounce(({ target }) => {
let parsedValue = Number.parseInt((<HTMLInputElement>target).value, 10);
if (
isNaN(parsedValue) ||
parsedValue < minValue ||
parsedValue > maxValue
) {
parsedValue = defaultValue;
}
input.value = String(parsedValue);
config[id] = parsedValue;
map.refresh(resetView);
}, 1000);
}

function handleSelectInput({ id, map }: SelectInput) {
const select = <HTMLSelectElement>document.getElementById(id);
select.value = String(config[id]);
select.onchange = ({ target }) => {
config[id] = (<HTMLSelectElement>target).value;
map.refresh();
};
}

function handleCheckboxInput({ id, map }: CheckboxInput) {
const checkbox = <HTMLInputElement>document.getElementById(id);
checkbox.checked = Boolean(config[id]);
checkbox.onchange = ({ target }) => {
config[id] = (<HTMLInputElement>target).checked;
map.refresh();
};
}

function handleInputs(map: MandelbrotMap) {
handleNumberInput({
id: "iterations",
map,
minValue: 1,
defaultValue: 200,
maxValue: 10 ** 9,
});
handleNumberInput({
id: "exponent",
map,
minValue: 2,
defaultValue: Number(config.exponent),
maxValue: 10 ** 9,
resetView: true,
});
handleSelectInput({ id: "colorScheme", map });
handleCheckboxInput({ id: "reverseColors", map });

const refreshButton: HTMLButtonElement = document.querySelector("#refresh");
refreshButton.onclick = () => map.refresh();

const fullScreenButton: HTMLButtonElement =
document.querySelector("#full-screen");
if (document.fullscreenEnabled) {
fullScreenButton.onclick = toggleFullScreen;
} else {
fullScreenButton.style.display = "none";
}

const saveButton: HTMLButtonElement = document.querySelector("#save-image");
try {
// eslint-disable-next-line no-constant-condition
if (new Blob()) {
saveButton.onclick = () => map.saveImage();
} else {
throw "FileSaver not supported";
}
} catch {
saveButton.style.display = "none";
}

function toggleFullScreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
document.body.requestFullscreen();
}
}

document.addEventListener("fullscreenchange", () => {
const button: HTMLButtonElement = document.querySelector("#full-screen");
button.innerText = document.fullscreenElement
? "Exit Full Screen"
: "Full Screen";
});
}

const workerManager = new WorkerManager();
workerManager.resetWorkers().then(() => {
const map = new MandelbrotMap("leaflet-map");
Expand Down
17 changes: 13 additions & 4 deletions client/app/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@ import("../../mandelbrot/pkg").then((wasm) => {
wasm.init();
self.addEventListener("message", (ev) => {
try {
const { coords, maxIterations, exponent, tileSize } = ev.data;
const data = wasm.get_tile_js(
const {
coords,
maxIterations,
exponent,
tileSize,
colorScheme,
reverseColors,
} = ev.data;
const data = wasm.get_tile(
coords.re_min,
coords.re_max,
coords.im_min,
coords.im_max,
maxIterations,
exponent,
tileSize
tileSize,
colorScheme,
reverseColors
);
self.postMessage({
...data,
image: data,
coords: stringify(coords),
});
} catch (err) {
Expand Down
Loading

0 comments on commit 33b1447

Please sign in to comment.