lexys-labyrinth/js/util.js
2020-09-05 16:21:31 -06:00

159 lines
4.9 KiB
JavaScript

export function mk(tag_selector, ...children) {
let [tag, ...classes] = tag_selector.split('.');
let el = document.createElement(tag);
el.classList = classes.join(' ');
if (children.length > 0) {
if (!(children[0] instanceof Node) && children[0] !== undefined && typeof(children[0]) !== "string" && typeof(children[0]) !== "number") {
let [attrs] = children.splice(0, 1);
for (let [key, value] of Object.entries(attrs)) {
el.setAttribute(key, value);
}
}
el.append(...children);
}
return el;
}
export function promise_event(element, success_event, failure_event) {
let resolve, reject;
let promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
let success_handler = e => {
element.removeEventListener(success_event, success_handler);
if (failure_event) {
element.removeEventListener(failure_event, failure_handler);
}
resolve(e);
};
let failure_handler = e => {
element.removeEventListener(success_event, success_handler);
if (failure_event) {
element.removeEventListener(failure_event, failure_handler);
}
reject(e);
};
element.addEventListener(success_event, success_handler);
if (failure_event) {
element.addEventListener(failure_event, failure_handler);
}
return promise;
}
export async function fetch(url) {
let xhr = new XMLHttpRequest;
let promise = promise_event(xhr, 'load', 'error');
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.send();
await promise;
return xhr.response;
}
// Cast a line through a grid and yield every cell it touches
export function* walk_grid(x0, y0, x1, y1) {
// TODO if the ray starts outside the grid (extremely unlikely), we should
// find the point where it ENTERS the grid, otherwise the 'while'
// conditions below will stop immediately
let a = Math.floor(x0);
let b = Math.floor(y0);
let dx = x1 - x0;
let dy = y1 - y0;
if (dx === 0 && dy === 0) {
// Special case: the ray goes nowhere, so only return this block
yield [a, b];
return;
}
let goal_x = Math.floor(x1);
let goal_y = Math.floor(y1);
// Use a modified Bresenham. Use mirroring to move everything into the
// first quadrant, then split it into two octants depending on whether dx
// or dy increases faster, and call that the main axis. Track an "error"
// value, which is the (negative) distance between the ray and the next
// grid line parallel to the main axis, but scaled up by dx. Every
// iteration, we move one cell along the main axis and increase the error
// value by dy (the ray's slope, scaled up by dx); when it becomes
// positive, we can subtract dx (1) and move one cell along the minor axis
// as well. Since the main axis is the faster one, we'll never traverse
// more than one cell on the minor axis for one cell on the main axis, and
// this readily provides every cell the ray hits in order.
// Based on: http://www.idav.ucdavis.edu/education/GraphicsNotes/Bresenhams-Algorithm/Bresenhams-Algorithm.html
// Setup: map to the first quadrant. The "offsets" are the distance
// between the starting point and the next grid point.
let step_a = 1;
let offset_x = 1 - (x0 - a);
if (dx < 0) {
dx = -dx;
step_a = -step_a;
offset_x = 1 - offset_x;
}
// Zero offset means we're on a grid line, so we're actually a full cell
// away from the next grid line
if (offset_x === 0) {
offset_x = 1;
}
let step_b = 1;
let offset_y = 1 - (y0 - b);
if (dy < 0) {
dy = -dy;
step_b = -step_b;
offset_y = 1 - offset_y;
}
if (offset_y === 0) {
offset_y = 1;
}
let err = dy * offset_x - dx * offset_y;
let min_a = 0, min_b = 0;
// TODO get these passed in fool
let max_a = 31, max_b = 31;
if (dx > dy) {
// Main axis is x/a
while (min_a <= a && a <= max_a && min_b <= b && b <= max_b) {
yield [a, b];
if (a === goal_x && b === goal_y)
return;
if (err > 0) {
err -= dx;
b += step_b;
yield [a, b];
if (a === goal_x && b === goal_y)
return;
}
err += dy;
a += step_a;
}
}
else {
err = -err;
// Main axis is y/b
while (min_a <= a && a <= max_a && min_b <= b && b <= max_b) {
yield [a, b];
if (a === goal_x && b === goal_y)
return;
if (err > 0) {
err -= dy;
a += step_a;
yield [a, b];
if (a === goal_x && b === goal_y)
return;
}
err += dx;
b += step_b;
}
}
}