diff options
Diffstat (limited to 'web/src/math')
| -rw-r--r-- | web/src/math/Coord.ts | 23 | ||||
| -rw-r--r-- | web/src/math/Ratio.ts | 61 | ||||
| -rw-r--r-- | web/src/math/Rect.ts | 97 |
3 files changed, 181 insertions, 0 deletions
diff --git a/web/src/math/Coord.ts b/web/src/math/Coord.ts new file mode 100644 index 0000000..db7ee6d --- /dev/null +++ b/web/src/math/Coord.ts @@ -0,0 +1,23 @@ +/** A coord on a grid whose origin is in the top left. */ +export default class Coord { + private readonly _x: number; + private readonly _y: number; + + constructor(x: number, y: number) { + this._x = x; + this._y = y; + } + + get x(): number { + return this._x; + } + + get y(): number { + return this._y; + } + + /** Get the squared distance of this point from the origin. */ + squaredDistanceFromOrigin(): number { + return this._x * this._x + this._y * this._y; + } +} diff --git a/web/src/math/Ratio.ts b/web/src/math/Ratio.ts new file mode 100644 index 0000000..4973ff4 --- /dev/null +++ b/web/src/math/Ratio.ts @@ -0,0 +1,61 @@ +/** Serializable representation of a ratio. */ +export type RatioData = [numerator: number, denominator: number]; + +/** Representation of a ratio for performing fractional artithmetic. */ +export default class Ratio { + private readonly _numerator: number; + private readonly _denominator: number; + + get numerator(): number { + return this._numerator; + } + + get denominator(): number { + return this._denominator; + } + + constructor(numerator: number, denominator: number) { + if (!Number.isInteger(numerator) || !Number.isInteger(denominator)) { + throw new TypeError( + `Ratio must have integer parts: ${numerator} / ${denominator}`, + ); + } + + if (denominator === 0) { + throw new RangeError("Ratio demnominator cannot be zero"); + } + + this._numerator = numerator; + this._denominator = denominator; + } + + multiplyRatio(other: Ratio): Ratio { + return new Ratio( + this.numerator * other.numerator, + this.denominator * other.denominator, + ); + } + + divideRatio(other: Ratio): Ratio { + return new Ratio( + this.numerator * other.denominator, + this.denominator * other.numerator, + ); + } + + toNumber(): number { + return this.numerator / this.denominator; + } + + static fromInteger(n: number): Ratio { + return new Ratio(n, 1); + } + + toData(): RatioData { + return [this.numerator, this.denominator]; + } + + static fromData(ratio: RatioData): Ratio { + return new Ratio(ratio[0], ratio[1]); + } +} diff --git a/web/src/math/Rect.ts b/web/src/math/Rect.ts new file mode 100644 index 0000000..e26fbae --- /dev/null +++ b/web/src/math/Rect.ts @@ -0,0 +1,97 @@ +import Coord from "./Coord"; + +/** A rectangle on a grid whose origin is in the top left. */ +export default class Rect { + private readonly _topLeft: Coord; + private readonly _width: number; + private readonly _height: number; + + constructor( + topLeftX: number, + topLeftY: number, + width: number, + height: number, + ) { + this._topLeft = new Coord(topLeftX, topLeftY); + this._width = width; + this._height = height; + } + + /** Width of this rectangle. */ + get width(): number { + return this._width; + } + + /** Height of this rectangle. */ + get height(): number { + return this._height; + } + + /** Coord of the top-left point of this rectangle. */ + get topLeft(): Coord { + return this._topLeft; + } + + /** Coord of the bottom-right point of this rectangle. */ + get bottomRight(): Coord { + return new Coord( + this._topLeft.x + this._width, + this._topLeft.y + this._height, + ); + } + + /** Determine if this rectangle contains the point at `coord`. */ + containsCoord(coord: Coord): boolean { + return ( + this.topLeft.x <= coord.x && + coord.x <= this.bottomRight.x && + this.topLeft.y <= coord.y && + coord.y <= this.bottomRight.y + ); + } + + verticallyContainsCoord(coord: Coord): boolean { + return this.topLeft.y <= coord.y && coord.y <= this.bottomRight.y; + } + + horizontallyContainsCoord(coord: Coord): boolean { + return this.topLeft.x <= coord.x && coord.x <= this.bottomRight.x; + } + + extend(other: Rect): Rect { + const topLeftX = Math.min( + this.topLeft.x, + this.bottomRight.x, + other.topLeft.x, + other.bottomRight.x, + ); + + const topLeftY = Math.min( + this.topLeft.y, + this.bottomRight.y, + other.topLeft.y, + other.bottomRight.y, + ); + + const bottomRightX = Math.max( + this.topLeft.x, + this.bottomRight.x, + other.topLeft.x, + other.bottomRight.x, + ); + + const bottomRightY = Math.max( + this.topLeft.y, + this.bottomRight.y, + other.topLeft.y, + other.bottomRight.y, + ); + + return new Rect( + topLeftX, + topLeftY, + bottomRightX - topLeftX, + bottomRightY - topLeftY, + ); + } +} |
