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:
commit
a0d42e7709
6 changed files with 207 additions and 0 deletions
10
index.html
Normal file
10
index.html
Normal 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
13
js/canvas.js
Normal 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
27
js/cursor.js
Normal 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
129
js/main.js
Normal 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
24
js/option.js
Normal 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
4
style.css
Normal file
|
@ -0,0 +1,4 @@
|
|||
body {
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
Loading…
Reference in a new issue