Fix some subtle bugs with some octants in walk_grid

This commit is contained in:
Eevee (Evelyn Woods) 2024-05-09 19:02:01 -06:00
parent 6d003287e4
commit 2fa84e0477

View File

@ -285,52 +285,56 @@ export function* walk_grid(x0, y0, x1, y1, min_a, min_b, max_a, max_b) {
let goal_x = Math.floor(x1); let goal_x = Math.floor(x1);
let goal_y = Math.floor(y1); let goal_y = Math.floor(y1);
// Use a modified Bresenham. Use mirroring to move everything into the // Use a modified Bresenham. Use mirroring to move everything into the first quadrant, then
// first quadrant, then split it into two octants depending on whether dx // split it into two octants depending on whether dx or dy increases faster, and call that the
// or dy increases faster, and call that the main axis. Track an "error" // main axis. Track an "error" value, which is the (negative) distance between the ray and the
// value, which is the (negative) distance between the ray and the next // next grid line parallel to the main axis, but scaled up by dx. Every iteration, we move one
// grid line parallel to the main axis, but scaled up by dx. Every // cell along the main axis and increase the error value by dy (the ray's slope, scaled up by
// iteration, we move one cell along the main axis and increase the error // dx); when it becomes positive, we can subtract dx (1) and move one cell along the minor axis
// value by dy (the ray's slope, scaled up by dx); when it becomes // as well. Since the main axis is the faster one, we'll never traverse more than one cell on
// positive, we can subtract dx (1) and move one cell along the minor axis // the minor axis for one cell on the main axis, and this readily provides every cell the ray
// as well. Since the main axis is the faster one, we'll never traverse // hits in order.
// 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 // 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 // Setup: map to the first quadrant. The "offsets" are the distance between the starting point
// between the starting point and the next grid point. // and the next grid point.
let step_a = 1; let step_a = 1;
let offset_x = 1 - (x0 - a); let offset_x = 1 - (x0 - a);
if (offset_x === 0) {
// Zero offset means we're on a grid line, so we're a full cell away from the next grid line
offset_x = 1;
}
if (dx < 0) { if (dx < 0) {
dx = -dx; dx = -dx;
step_a = -step_a; step_a = -step_a;
offset_x = 1 - offset_x; offset_x = 1 - offset_x;
} }
else if (offset_x === 0) {
// Zero offset means we're on a grid line, so we're a full cell away from the next grid line
// (if we're moving forward; if we're moving backward, the next cell really is 0 away)
offset_x = 1;
}
let step_b = 1; let step_b = 1;
let offset_y = 1 - (y0 - b); let offset_y = 1 - (y0 - b);
if (offset_y === 0) {
offset_y = 1;
}
if (dy < 0) { if (dy < 0) {
dy = -dy; dy = -dy;
step_b = -step_b; step_b = -step_b;
offset_y = 1 - offset_y; offset_y = 1 - offset_y;
} }
else if (offset_y === 0) {
offset_y = 1;
}
let err = dy * offset_x - dx * offset_y; let err = dy * offset_x - dx * offset_y;
if (dx > dy) { if (dx > dy) {
// Main axis is x/a // Main axis is x/a
while (min_a <= a && a <= max_a && min_b <= b && b <= max_b) { while (min_a <= a && a <= max_a && min_b <= b && b <= max_b) {
if (a === goal_x && b === goal_y) {
yield [a, b]; yield [a, b];
if (a === goal_x && b === goal_y)
return; return;
}
// When we go exactly through a corner, we cross two grid lines, but between them we
// enter a cell the line doesn't actually pass through. That happens here, when err ===
// dx, because it was 0 last loop
if (err !== dy) {
yield [a, b];
}
if (err > 0) { if (err > 0) {
err -= dx; err -= dx;
@ -347,9 +351,13 @@ export function* walk_grid(x0, y0, x1, y1, min_a, min_b, max_a, max_b) {
err = -err; err = -err;
// Main axis is y/b // Main axis is y/b
while (min_a <= a && a <= max_a && min_b <= b && b <= max_b) { while (min_a <= a && a <= max_a && min_b <= b && b <= max_b) {
if (a === goal_x && b === goal_y) {
yield [a, b]; yield [a, b];
if (a === goal_x && b === goal_y)
return; return;
}
if (err !== dx) {
yield [a, b];
}
if (err > 0) { if (err > 0) {
err -= dy; err -= dy;