UNPKG

@logic-pad/core

Version:
233 lines (232 loc) 7.17 kB
import { Direction, Orientation } from './primitives.js'; /** * Offset the given position by a given step in the given direction. * @param position The position to offset. * @param direction The direction to offset in. * @param step The distance to offset by. * @returns The offset position. */ export function move(position, direction, step = 1) { switch (direction) { case Direction.Up: case Orientation.Up: return { x: position.x, y: position.y - step }; case Direction.Down: case Orientation.Down: return { x: position.x, y: position.y + step }; case Direction.Left: case Orientation.Left: return { x: position.x - step, y: position.y }; case Direction.Right: case Orientation.Right: return { x: position.x + step, y: position.y }; case Orientation.UpLeft: return { x: position.x - step, y: position.y - step }; case Orientation.UpRight: return { x: position.x + step, y: position.y - step }; case Orientation.DownLeft: return { x: position.x - step, y: position.y + step }; case Orientation.DownRight: return { x: position.x + step, y: position.y + step }; } } /** * Check if two edges are the same, regardless of direction. * @param a The first edge. * @param b The second edge. * @returns Whether the edges are the same. */ export function isSameEdge(a, b) { return ((a.x1 === b.x1 && a.y1 === b.y1 && a.x2 === b.x2 && a.y2 === b.y2) || (a.x1 === b.x2 && a.y1 === b.y2 && a.x2 === b.x1 && a.y2 === b.y1)); } /** * Convert the given direction to a rotation in degrees. * @param direction The direction to convert. * @returns The rotation in degrees. */ export function directionToRotation(direction) { switch (direction) { case Direction.Up: return 0; case Direction.Left: return 270; case Direction.Right: return 90; case Direction.Down: return 180; } } /** * Convert the given orientation to a rotation in degrees. * @param orientation The orientation to convert. * @returns The rotation in degrees. */ export function orientationToRotation(orientation) { switch (orientation) { case Orientation.Up: return 0; case Orientation.Left: return 270; case Orientation.Right: return 90; case Orientation.Down: return 180; case Orientation.DownLeft: return 225; case Orientation.DownRight: return 125; case Orientation.UpLeft: return 315; case Orientation.UpRight: return 45; } } /** * Create a new 2D array with the given dimensions and values. * @param width The width of the array. * @param height The height of the array. * @param value A function that returns the value for each x,y coordinate. * @returns The 2D array. */ export function array(width, height, value) { const result = []; for (let y = 0; y < height; y++) { result[y] = []; for (let x = 0; x < width; x++) { result[y][x] = value(x, y); } } return result; } export function resize(array, newSize, defaultValue) { if (array.length < newSize) { return [ ...array, ...Array.from({ length: newSize - array.length }, defaultValue), ]; } else if (array.length > newSize) { return array.slice(0, newSize); } return array; } /** * Check if all the given values are equal. * @param values The values to compare. * @returns Whether all the values are equal. */ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters export function allEqual(...values) { return values.every(value => value === values[0]); } /** * Return the first element of the array which has the minimum mapped value. * * @param values The array of values. * @param mapper The function to map each value to a number. * @returns The first element with the minimum mapped value. */ export function minBy(values, mapper) { let min = Number.POSITIVE_INFINITY; let result; for (const value of values) { const mapped = mapper(value); if (mapped < min) { min = mapped; result = value; } } return result; } /** * Return the first element of the array which has the maximum mapped value. * * @param values The array of values. * @param mapper The function to map each value to a number. * @returns The first element with the maximum mapped value. */ export function maxBy(values, mapper) { let max = Number.NEGATIVE_INFINITY; let result; for (const value of values) { const mapped = mapper(value); if (mapped > max) { max = mapped; result = value; } } return result; } /** * Escape the given text by replacing the specified characters with HTML escape sequences. * @param text The text to escape. * @param escapeCharacters The characters to escape. * @returns The escaped text. */ export function escape(text, escapeCharacters = '=,:|') { let result = ''; for (const char of text) { if (escapeCharacters.includes(char) || char === '&') { result += `&#${char.charCodeAt(0)};`; } else { result += char; } } return result; } /** * Unescape the given text by replacing HTML escape sequences with the corresponding characters. * @param text The text to unescape. * @param escapeCharacters The characters to unescape. This should match the characters escaped by the `escape` function. * @returns The unescaped text. */ export function unescape(text, escapeCharacters = '=,:|') { let result = ''; const matches = text.matchAll(/&#([0-9]+);/g); let index = 0; for (const match of matches) { result += text.substring(index, match.index); const char = String.fromCharCode(parseInt(match[1], 10)); if (escapeCharacters.includes(char) || char === '&') { result += char; } else { result += match[0]; } index = match.index + match[0].length; } return result + text.substring(index); } export class CachedAccess { constructor(getter) { Object.defineProperty(this, "getter", { enumerable: true, configurable: true, writable: true, value: getter }); Object.defineProperty(this, "cache", { enumerable: true, configurable: true, writable: true, value: CachedAccess.UNCACHED }); } static of(getter) { return new CachedAccess(getter); } get value() { if (this.cache === CachedAccess.UNCACHED) { this.cache = this.getter(); } return this.cache; } } Object.defineProperty(CachedAccess, "UNCACHED", { enumerable: true, configurable: true, writable: true, value: Symbol('uncached') });