@mousepox/math
Version:
Math-related objects and utilities
225 lines (224 loc) • 6.88 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Grid = void 0;
const core_1 = require("./core");
const BitFlags_1 = require("./BitFlags");
const Vector2_1 = require("./Vector2");
const ScratchBitFlags = new BitFlags_1.BitFlags();
const ScratchVector2 = new Vector2_1.Vector2();
const OrthogonalAdjacentOffsets = [
{ x: 0, y: -1 },
{ x: -1, y: 0 },
{ x: 1, y: 0 },
{ x: 0, y: 1 },
];
const AdjacentOffsets = [
{ x: -1, y: -1, mask: core_1.DirectionMask.NorthWest },
{ x: 0, y: -1, mask: core_1.DirectionMask.North },
{ x: 1, y: -1, mask: core_1.DirectionMask.NorthEast },
{ x: -1, y: 0, mask: core_1.DirectionMask.West },
{ x: 1, y: 0, mask: core_1.DirectionMask.East },
{ x: -1, y: 1, mask: core_1.DirectionMask.SouthWest },
{ x: 0, y: 1, mask: core_1.DirectionMask.South },
{ x: 1, y: 1, mask: core_1.DirectionMask.SouthEast },
];
class Grid {
constructor(width = 0, height = 0) {
this.width = 0;
this.height = 0;
this.cells = [];
this.center = { x: 0, y: 0 };
this.resize(width, height);
}
static fromStrings(rows, map) {
const grid = new Grid(rows[0].length, rows.length);
grid.forEach((_, x, y) => {
grid.set(x, y, map.indexOf(rows[y][x]));
});
return grid;
}
resize(width, height) {
this.width = width;
this.height = height;
this.center.x = Math.floor(width / 2);
this.center.y = Math.floor(height / 2);
this.cells.length = width * height;
this.fill(0);
}
fill(value) {
this.cells.fill(value);
}
valid(x, y) {
return x >= 0 && y >= 0 && x < this.width && y < this.height;
}
get(x, y) {
if (this.valid(x, y)) {
const index = this.xyToIndex(x, y);
return this.cells[index];
}
else {
throw new Error(`Invalid cell coordinates: ${x}, ${y}`);
}
}
getAdjacentFlags(x, y) {
const v = this.get(x, y);
ScratchBitFlags.clear();
for (const offset of AdjacentOffsets) {
const ox = x + offset.x;
const oy = y + offset.y;
if (!this.valid(ox, oy) || this.get(ox, oy) === v) {
ScratchBitFlags.set(offset.mask);
}
}
return ScratchBitFlags.value;
}
set(x, y, value) {
if (this.valid(x, y)) {
const index = this.xyToIndex(x, y);
this.setIndex(index, value);
}
else {
throw new Error(`Invalid cell coordinates: ${x}, ${y}`);
}
}
setIndex(index, value) {
if (index >= 0 && index < this.cells.length) {
this.cells[index] = value;
}
else {
throw new Error(`Invalid cell index: ${index}`);
}
}
copy(grid) {
grid.forEach((value, x, y) => this.set(x, y, value));
}
forEach(handler) {
const len = this.cells.length;
for (let index = 0; index < len; ++index) {
const x = this.indexToX(index);
const y = this.indexToY(index);
handler(this.get(x, y), x, y);
}
}
forEachInArea(x, y, width, height, handler) {
for (let iy = y; iy < y + height; ++iy) {
for (let ix = x; ix < x + width; ++ix) {
if (!this.valid(ix, iy)) {
continue;
}
handler(this.get(ix, iy), ix, iy);
}
}
}
forEachAdjacent(x, y, handler) {
for (const offset of OrthogonalAdjacentOffsets) {
const ax = x + offset.x;
const ay = y + offset.y;
if (!this.valid(ax, ay)) {
continue;
}
handler(this.get(ax, ay), ax, ay);
}
}
rotate(rotations = 1) {
Scratch.resize(this.height, this.width);
for (let i = 0; i < rotations; ++i) {
for (let y = 0; y < this.height; ++y) {
for (let x = 0; x < this.width; ++x) {
Scratch.set(Scratch.width - y - 1, x, this.get(x, y));
}
}
this.copy(Scratch);
}
}
rotatePoint(p, angle) {
ScratchVector2.set(p.x, p.y);
ScratchVector2.rotate(angle, this.center);
p.x = Math.round(ScratchVector2.x);
p.y = Math.round(ScratchVector2.y);
}
paste(grid, x, y) {
grid.forEach((value, sx, sy) => {
this.set(x + sx, y + sy, value);
});
}
raycast(origin, direction, result) {
if (result === undefined) {
result = {
distance: 0,
side: 0,
value: undefined,
};
}
else {
result.distance = 0;
result.side = 0;
result.value = undefined;
}
let x = Math.floor(origin.x);
let y = Math.floor(origin.y);
const deltaDistX = Math.sqrt(1 + (direction.y * direction.y) / (direction.x * direction.x));
const deltaDistY = Math.sqrt(1 + (direction.x * direction.x) / (direction.y * direction.y));
let sideDistX = 0;
let sideDistY = 0;
let stepX = 0;
let stepY = 0;
let hit = false;
if (direction.x < 0) {
stepX = -1;
sideDistX = (origin.x - x) * deltaDistX;
}
else {
stepX = 1;
sideDistX = (x + 1.0 - origin.x) * deltaDistX;
}
if (direction.y < 0) {
stepY = -1;
sideDistY = (origin.y - y) * deltaDistY;
}
else {
stepY = 1;
sideDistY = (y + 1.0 - origin.y) * deltaDistY;
}
while (!hit) {
if (sideDistX < sideDistY) {
sideDistX += deltaDistX;
x += stepX;
result.side = 0;
}
else {
sideDistY += deltaDistY;
y += stepY;
result.side = 1;
}
if (this.valid(x, y)) {
const value = this.get(x, y);
if (value !== 0) {
result.value = value;
hit = true;
}
}
else {
break;
}
}
if (result.side === 0) {
result.distance = (x - origin.x + (1 - stepX) / 2) / direction.x;
}
else {
result.distance = (y - origin.y + (1 - stepY) / 2) / direction.y;
}
return result;
}
xyToIndex(x, y) {
return y * this.width + x;
}
indexToX(index) {
return index % this.width;
}
indexToY(index) {
return Math.floor(index / this.width);
}
}
exports.Grid = Grid;
const Scratch = new Grid();