basic drawing on a quadtree

no zoom yet, also no figuring out when tree nodes can merge without
losing information.
This commit is contained in:
crystal heart 2024-06-04 12:39:07 +02:00
commit a0d42e7709
6 changed files with 207 additions and 0 deletions

10
index.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<head>
<title>quadtree drawer</title>
<link rel="stylesheet" href="style.css"></link>
<script defer type="module" src="js/main.js"></script>
<link rel="icon" href="data:,">
</head>
<body>
<canvas id="canvas" width="512" height="512" />
</body>

13
js/canvas.js Normal file
View file

@ -0,0 +1,13 @@
// CORE GRAPHICS
export const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
export function fill_rect(x, y, w, h, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
}
export function clear(color) {
fill_rect(0, 0, canvas.width, canvas.height, color);
}

27
js/cursor.js Normal file
View file

@ -0,0 +1,27 @@
import {canvas} from "./canvas.js";
const state = {
x: 0,
y: 0,
buttons: 0,
};
function update(e) {
const elem_root = e.target.getBoundingClientRect();
state.x = e.clientX - elem_root.left;
state.y = e.clientY - elem_root.top;
state.buttons = e.buttons;
}
canvas.addEventListener("mousedown", update);
canvas.addEventListener("mouseup", update);
canvas.addEventListener("mousemove", update);
export function get_x() {
return state.x;
}
export function get_y() {
return state.y;
}
export function button_pressed(button) {
return ((state.buttons >>> button) & 1) !== 0;
}

129
js/main.js Normal file
View file

@ -0,0 +1,129 @@
// IMPORTS
import {None, Some} from "./option.js";
import {canvas, fill_rect, clear} from "./canvas.js";
import * as cursor from "./cursor.js";
// CONSTANTS
const MIN_SCALE = 4;
// QUADTREE
function Tree() {
let parent = None();
let children = None();
let color = "#000000";
const node = {
set_parent: p => { parent = Some(p); },
get_parent: () => parent,
create_children: () => {
children.none(() => {
let _children = Array(4);
for (let i = 0; i < 4; i++) {
_children[i] = Tree();
_children[i].set_color(color);
_children[i].set_parent(node);
}
children = Some(_children);
});
},
get_children: () => children,
set_color: c => { color = c; },
get_color: () => color,
to_string: () => {
return children
.some(cs => {
return Some(`{color: "${color}", children: [${
cs.map(c => c.to_string()).join(", ")
}]}`);
})
.unwrap(`{color: "${color}"}`);
},
};
return node;
}
function draw_tree(tree, x, y, size) {
fill_rect(x, y, size, size, tree.get_color());
if (size < MIN_SCALE) { return; }
tree.get_children()
.none(None)
.some(c => {
const new_size = size / 2;
draw_tree(c[0], x, y, new_size);
draw_tree(c[1], x + new_size, y, new_size);
draw_tree(c[2], x, y + new_size, new_size);
draw_tree(c[3], x + new_size, y + new_size, new_size);
});
}
function find_containing_node(node, root_x, root_y, x, y, scale) {
if (scale <= MIN_SCALE) {
return node;
}
node.create_children();
return node.get_children().none(None).some(c => {
let index = 0;
scale /= 2;
if (x >= scale) {
index += 1;
root_x += scale;
x -= scale;
}
if (y >= scale) {
index += 2;
root_y += scale;
y -= scale;
}
return find_containing_node(c[index], root_x, root_y, x, y, scale);
});
}
function color_pixel(tree, root_x, root_y, x, y, scale, color) {
const node = find_containing_node(tree, 0, 0, x, y, 512);
node.set_color(color);
}
// MAIN
const root = Tree();
function render(tree) {
clear("#000000");
draw_tree(tree, 0, 0, 512);
}
function init() {
root.set_color("#0f0");
root.create_children();
root.get_children().none(None).some(c => {
c[0].set_color("#f80");
});
console.log(root.to_string());
}
function main() {
if (cursor.button_pressed(0)) {
color_pixel(root, 0, 0, cursor.get_x(), cursor.get_y(), 512, "#000");
render(root);
}
render(root);
requestAnimationFrame(main);
}
init();
main();

24
js/option.js Normal file
View file

@ -0,0 +1,24 @@
// OPTION TYPE
/*
usage:
None().none(f1).some(f2) // returns f1()
Some(x).none(f1).some(f2) // returns f2(x)
None(x).unwrap(y) // returns y
Some(x).unwrap(y) // returns x
*/
export function None() {
return {
none: (f) => f(),
some: (f) => None(),
unwrap: (y) => y,
};
}
export function Some(x) {
return {
none: (f) => Some(x),
some: (f) => f(x),
unwrap: (y) => x,
};
}

4
style.css Normal file
View file

@ -0,0 +1,4 @@
body {
background-color: #222;
color: #fff;
}