Created
September 21, 2025 23:48
-
-
Save hoverbird/9f115f3d7a8a99f9bfb3e4b262111a44 to your computer and use it in GitHub Desktop.
HexGrid.ts — classic set of utils for dealing wtih hexagonal grids by Red Blob Games, updated to use modern TypeScript standards and practices
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- | |
| // Based on the classic utilities released by Red Blob Games, detailed here: http://www.redblobgames.com/grids/hexagons/ | |
| // Updated from their generated .ts code to use modern TypeScript style/standards by @hoverbird | |
| // Original and updated code area released as CC0 -- No Rights Reserved: https://creativecommons.org/public-domain/cc0/ | |
| // Code provided as-is with no warranties or guarantees. | |
| The code now follows all Deno-friendly TypeScript standards: | |
| - ✅ **No `erasableSyntaxOnly` violations** - Explicit property declarations instead of constructor parameter properties | |
| - ✅ **No `var` keywords** - All replaced with appropriate `let`/`const` | |
| - ✅ **Proper type annotations** - `string` instead of `String`, no implicit `any` | |
| - ✅ **Modern TypeScript practices** - Block-scoped declarations, immutable where appropriate | |
| --> | |
| export class Point { | |
| public x: number; | |
| public y: number; | |
| constructor(x: number, y: number) { | |
| this.x = x; | |
| this.y = y; | |
| } | |
| } | |
| export class Hex { | |
| public q: number; | |
| public r: number; | |
| public s: number; | |
| constructor(q: number, r: number, s: number) { | |
| if (Math.round(q + r + s) !== 0) throw "q + r + s must be 0"; | |
| this.q = q; | |
| this.r = r; | |
| this.s = s; | |
| } | |
| public add(b: Hex): Hex { | |
| return new Hex(this.q + b.q, this.r + b.r, this.s + b.s); | |
| } | |
| public subtract(b: Hex): Hex { | |
| return new Hex(this.q - b.q, this.r - b.r, this.s - b.s); | |
| } | |
| public scale(k: number): Hex { | |
| return new Hex(this.q * k, this.r * k, this.s * k); | |
| } | |
| public rotateLeft(): Hex { | |
| return new Hex(-this.s, -this.q, -this.r); | |
| } | |
| public rotateRight(): Hex { | |
| return new Hex(-this.r, -this.s, -this.q); | |
| } | |
| public static directions: Hex[] = [new Hex(1, 0, -1), new Hex(1, -1, 0), new Hex(0, -1, 1), new Hex(-1, 0, 1), new Hex(-1, 1, 0), new Hex(0, 1, -1)]; | |
| public static direction(direction: number): Hex { | |
| return Hex.directions[direction]; | |
| } | |
| public neighbor(direction: number): Hex { | |
| return this.add(Hex.direction(direction)); | |
| } | |
| public static diagonals: Hex[] = [new Hex(2, -1, -1), new Hex(1, -2, 1), new Hex(-1, -1, 2), new Hex(-2, 1, 1), new Hex(-1, 2, -1), new Hex(1, 1, -2)]; | |
| public diagonalNeighbor(direction: number): Hex { | |
| return this.add(Hex.diagonals[direction]); | |
| } | |
| public len(): number { | |
| return (Math.abs(this.q) + Math.abs(this.r) + Math.abs(this.s)) / 2; | |
| } | |
| public distance(b: Hex): number { | |
| return this.subtract(b).len(); | |
| } | |
| public round(): Hex { | |
| let qi: number = Math.round(this.q); | |
| let ri: number = Math.round(this.r); | |
| let si: number = Math.round(this.s); | |
| const q_diff: number = Math.abs(qi - this.q); | |
| const r_diff: number = Math.abs(ri - this.r); | |
| const s_diff: number = Math.abs(si - this.s); | |
| if (q_diff > r_diff && q_diff > s_diff) { | |
| qi = -ri - si; | |
| } | |
| else | |
| if (r_diff > s_diff) { | |
| ri = -qi - si; | |
| } | |
| else { | |
| si = -qi - ri; | |
| } | |
| return new Hex(qi, ri, si); | |
| } | |
| public lerp(b: Hex, t: number): Hex { | |
| return new Hex(this.q * (1.0 - t) + b.q * t, this.r * (1.0 - t) + b.r * t, this.s * (1.0 - t) + b.s * t); | |
| } | |
| public linedraw(b: Hex): Hex[] { | |
| const N: number = this.distance(b); | |
| const a_nudge: Hex = new Hex(this.q + 1e-06, this.r + 1e-06, this.s - 2e-06); | |
| const b_nudge: Hex = new Hex(b.q + 1e-06, b.r + 1e-06, b.s - 2e-06); | |
| const results: Hex[] = []; | |
| const step: number = 1.0 / Math.max(N, 1); | |
| for (let i = 0; i <= N; i++) { | |
| results.push(a_nudge.lerp(b_nudge, step * i).round()); | |
| } | |
| return results; | |
| } | |
| } | |
| export class OffsetCoord { | |
| public col: number; | |
| public row: number; | |
| constructor(col: number, row: number) { | |
| this.col = col; | |
| this.row = row; | |
| } | |
| public static EVEN: number = 1; | |
| public static ODD: number = -1; | |
| public static qoffsetFromCube(offset: number, h: Hex): OffsetCoord { | |
| const parity: number = h.q & 1; | |
| const col: number = h.q; | |
| const row: number = h.r + (h.q + offset * parity) / 2; | |
| if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { | |
| throw "offset must be EVEN (+1) or ODD (-1)"; | |
| } | |
| return new OffsetCoord(col, row); | |
| } | |
| public static qoffsetToCube(offset: number, h: OffsetCoord): Hex { | |
| const parity: number = h.col & 1; | |
| const q: number = h.col; | |
| const r: number = h.row - (h.col + offset * parity) / 2; | |
| const s: number = -q - r; | |
| if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { | |
| throw "offset must be EVEN (+1) or ODD (-1)"; | |
| } | |
| return new Hex(q, r, s); | |
| } | |
| public static roffsetFromCube(offset: number, h: Hex): OffsetCoord { | |
| const parity: number = h.r & 1; | |
| const col: number = h.q + (h.r + offset * parity) / 2; | |
| const row: number = h.r; | |
| if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { | |
| throw "offset must be EVEN (+1) or ODD (-1)"; | |
| } | |
| return new OffsetCoord(col, row); | |
| } | |
| public static roffsetToCube(offset: number, h: OffsetCoord): Hex { | |
| const parity: number = h.row & 1; | |
| const q: number = h.col - (h.row + offset * parity) / 2; | |
| const r: number = h.row; | |
| const s: number = -q - r; | |
| if (offset !== OffsetCoord.EVEN && offset !== OffsetCoord.ODD) { | |
| throw "offset must be EVEN (+1) or ODD (-1)"; | |
| } | |
| return new Hex(q, r, s); | |
| } | |
| public static qoffsetFromQdoubled(offset: number, h: DoubledCoord): OffsetCoord { | |
| const parity: number = h.col & 1; | |
| return new OffsetCoord(h.col, (h.row + offset * parity) / 2); | |
| } | |
| public static qoffsetToQdoubled(offset: number, h: OffsetCoord): DoubledCoord { | |
| const parity: number = h.col & 1; | |
| return new DoubledCoord(h.col, 2 * h.row - offset * parity); | |
| } | |
| public static roffsetFromRdoubled(offset: number, h: DoubledCoord): OffsetCoord { | |
| const parity: number = h.row & 1; | |
| return new OffsetCoord((h.col + offset * parity) / 2, h.row); | |
| } | |
| public static roffsetToRdoubled(offset: number, h: OffsetCoord): DoubledCoord { | |
| const parity: number = h.row & 1; | |
| return new DoubledCoord(2 * h.col - offset * parity, h.row); | |
| } | |
| } | |
| export class DoubledCoord { | |
| public col: number; | |
| public row: number; | |
| constructor(col: number, row: number) { | |
| this.col = col; | |
| this.row = row; | |
| } | |
| public static qdoubledFromCube(h: Hex): DoubledCoord { | |
| const col: number = h.q; | |
| const row: number = 2 * h.r + h.q; | |
| return new DoubledCoord(col, row); | |
| } | |
| public qdoubledToCube(): Hex { | |
| const q: number = this.col; | |
| const r: number = (this.row - this.col) / 2; | |
| const s: number = -q - r; | |
| return new Hex(q, r, s); | |
| } | |
| public static rdoubledFromCube(h: Hex): DoubledCoord { | |
| const col: number = 2 * h.q + h.r; | |
| const row: number = h.r; | |
| return new DoubledCoord(col, row); | |
| } | |
| public rdoubledToCube(): Hex { | |
| const q: number = (this.col - this.row) / 2; | |
| const r: number = this.row; | |
| const s: number = -q - r; | |
| return new Hex(q, r, s); | |
| } | |
| } | |
| export class Orientation { | |
| public f0: number; | |
| public f1: number; | |
| public f2: number; | |
| public f3: number; | |
| public b0: number; | |
| public b1: number; | |
| public b2: number; | |
| public b3: number; | |
| public start_angle: number; | |
| constructor(f0: number, f1: number, f2: number, f3: number, b0: number, b1: number, b2: number, b3: number, start_angle: number) { | |
| this.f0 = f0; | |
| this.f1 = f1; | |
| this.f2 = f2; | |
| this.f3 = f3; | |
| this.b0 = b0; | |
| this.b1 = b1; | |
| this.b2 = b2; | |
| this.b3 = b3; | |
| this.start_angle = start_angle; | |
| } | |
| } | |
| export class Layout { | |
| public orientation: Orientation; | |
| public size: Point; | |
| public origin: Point; | |
| constructor(orientation: Orientation, size: Point, origin: Point) { | |
| this.orientation = orientation; | |
| this.size = size; | |
| this.origin = origin; | |
| } | |
| public static pointy: Orientation = new Orientation(Math.sqrt(3.0), Math.sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, Math.sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5); | |
| public static flat: Orientation = new Orientation(3.0 / 2.0, 0.0, Math.sqrt(3.0) / 2.0, Math.sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, Math.sqrt(3.0) / 3.0, 0.0); | |
| public hexToPixel(h: Hex): Point { | |
| const M: Orientation = this.orientation; | |
| const size: Point = this.size; | |
| const origin: Point = this.origin; | |
| const x: number = (M.f0 * h.q + M.f1 * h.r) * size.x; | |
| const y: number = (M.f2 * h.q + M.f3 * h.r) * size.y; | |
| return new Point(x + origin.x, y + origin.y); | |
| } | |
| public pixelToHexFractional(p: Point): Hex { | |
| const M: Orientation = this.orientation; | |
| const size: Point = this.size; | |
| const origin: Point = this.origin; | |
| const pt: Point = new Point((p.x - origin.x) / size.x, (p.y - origin.y) / size.y); | |
| const q: number = M.b0 * pt.x + M.b1 * pt.y; | |
| const r: number = M.b2 * pt.x + M.b3 * pt.y; | |
| return new Hex(q, r, -q - r); | |
| } | |
| public pixelToHexRounded(p: Point): Hex { | |
| return this.pixelToHexFractional(p).round(); | |
| } | |
| public hexCornerOffset(corner: number): Point { | |
| const M: Orientation = this.orientation; | |
| const size: Point = this.size; | |
| const angle: number = 2.0 * Math.PI * (M.start_angle - corner) / 6.0; | |
| return new Point(size.x * Math.cos(angle), size.y * Math.sin(angle)); | |
| } | |
| public polygonCorners(h: Hex): Point[] { | |
| const corners: Point[] = []; | |
| const center: Point = this.hexToPixel(h); | |
| for (let i = 0; i < 6; i++) { | |
| const offset: Point = this.hexCornerOffset(i); | |
| corners.push(new Point(center.x + offset.x, center.y + offset.y)); | |
| } | |
| return corners; | |
| } | |
| } | |
| class Tests { | |
| constructor() { } | |
| public static equalHex(name: string, a: Hex, b: Hex): void { | |
| if (!(a.q === b.q && a.s === b.s && a.r === b.r)) { | |
| complain(name); | |
| } | |
| } | |
| public static equalOffsetcoord(name: string, a: OffsetCoord, b: OffsetCoord): void { | |
| if (!(a.col === b.col && a.row === b.row)) { | |
| complain(name); | |
| } | |
| } | |
| public static equalDoubledcoord(name: string, a: DoubledCoord, b: DoubledCoord): void { | |
| if (!(a.col === b.col && a.row === b.row)) { | |
| complain(name); | |
| } | |
| } | |
| public static equalInt(name: string, a: number, b: number): void { | |
| if (!(a === b)) { | |
| complain(name); | |
| } | |
| } | |
| public static equalHexArray(name: string, a: Hex[], b: Hex[]): void { | |
| Tests.equalInt(name, a.length, b.length); | |
| for (let i = 0; i < a.length; i++) { | |
| Tests.equalHex(name, a[i], b[i]); | |
| } | |
| } | |
| public static testHexArithmetic(): void { | |
| Tests.equalHex("hex_add", new Hex(4, -10, 6), new Hex(1, -3, 2).add(new Hex(3, -7, 4))); | |
| Tests.equalHex("hex_subtract", new Hex(-2, 4, -2), new Hex(1, -3, 2).subtract(new Hex(3, -7, 4))); | |
| } | |
| public static testHexDirection(): void { | |
| Tests.equalHex("hex_direction", new Hex(0, -1, 1), Hex.direction(2)); | |
| } | |
| public static testHexNeighbor(): void { | |
| Tests.equalHex("hex_neighbor", new Hex(1, -3, 2), new Hex(1, -2, 1).neighbor(2)); | |
| } | |
| public static testHexDiagonal(): void { | |
| Tests.equalHex("hex_diagonal", new Hex(-1, -1, 2), new Hex(1, -2, 1).diagonalNeighbor(3)); | |
| } | |
| public static testHexDistance(): void { | |
| Tests.equalInt("hex_distance", 7, new Hex(3, -7, 4).distance(new Hex(0, 0, 0))); | |
| } | |
| public static testHexRotateRight(): void { | |
| Tests.equalHex("hex_rotate_right", new Hex(1, -3, 2).rotateRight(), new Hex(3, -2, -1)); | |
| } | |
| public static testHexRotateLeft(): void { | |
| Tests.equalHex("hex_rotate_left", new Hex(1, -3, 2).rotateLeft(), new Hex(-2, -1, 3)); | |
| } | |
| public static testHexRound(): void { | |
| const a: Hex = new Hex(0.0, 0.0, 0.0); | |
| const b: Hex = new Hex(1.0, -1.0, 0.0); | |
| const c: Hex = new Hex(0.0, -1.0, 1.0); | |
| Tests.equalHex("hex_round 1", new Hex(5, -10, 5), new Hex(0.0, 0.0, 0.0).lerp(new Hex(10.0, -20.0, 10.0), 0.5).round()); | |
| Tests.equalHex("hex_round 2", a.round(), a.lerp(b, 0.499).round()); | |
| Tests.equalHex("hex_round 3", b.round(), a.lerp(b, 0.501).round()); | |
| Tests.equalHex("hex_round 4", a.round(), new Hex(a.q * 0.4 + b.q * 0.3 + c.q * 0.3, a.r * 0.4 + b.r * 0.3 + c.r * 0.3, a.s * 0.4 + b.s * 0.3 + c.s * 0.3).round()); | |
| Tests.equalHex("hex_round 5", c.round(), new Hex(a.q * 0.3 + b.q * 0.3 + c.q * 0.4, a.r * 0.3 + b.r * 0.3 + c.r * 0.4, a.s * 0.3 + b.s * 0.3 + c.s * 0.4).round()); | |
| } | |
| public static testHexLinedraw(): void { | |
| Tests.equalHexArray("hex_linedraw", [new Hex(0, 0, 0), new Hex(0, -1, 1), new Hex(0, -2, 2), new Hex(1, -3, 2), new Hex(1, -4, 3), new Hex(1, -5, 4)], new Hex(0, 0, 0).linedraw(new Hex(1, -5, 4))); | |
| } | |
| public static testLayout(): void { | |
| const h: Hex = new Hex(3, 4, -7); | |
| const flat: Layout = new Layout(Layout.flat, new Point(10.0, 15.0), new Point(35.0, 71.0)); | |
| Tests.equalHex("layout", h, flat.pixelToHexRounded(flat.hexToPixel(h))); | |
| const pointy: Layout = new Layout(Layout.pointy, new Point(10.0, 15.0), new Point(35.0, 71.0)); | |
| Tests.equalHex("layout", h, pointy.pixelToHexRounded(pointy.hexToPixel(h))); | |
| } | |
| public static testOffsetRoundtrip(): void { | |
| for (let q = -2; q < 3; q++) { | |
| for (let r = -2; r < 3; r++) { | |
| const cube: Hex = new Hex(q, r, -q - r); | |
| Tests.equalHex("conversion_roundtrip odd-q", cube, OffsetCoord.qoffsetToCube(OffsetCoord.ODD, OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, cube))); | |
| Tests.equalHex("conversion_roundtrip odd-r", cube, OffsetCoord.roffsetToCube(OffsetCoord.ODD, OffsetCoord.roffsetFromCube(OffsetCoord.ODD, cube))); | |
| Tests.equalHex("conversion_roundtrip even-q", cube, OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, cube))); | |
| Tests.equalHex("conversion_roundtrip even-r", cube, OffsetCoord.roffsetToCube(OffsetCoord.EVEN, OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, cube))); | |
| } | |
| } | |
| for (let col = -2; col < 3; col++) { | |
| for (let row = -2; row < 3; row++) { | |
| const offset: OffsetCoord = new OffsetCoord(col, row); | |
| Tests.equalOffsetcoord("conversion_roundtrip odd-q", offset, OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, OffsetCoord.qoffsetToCube(OffsetCoord.ODD, offset))); | |
| Tests.equalOffsetcoord("conversion_roundtrip odd-r", offset, OffsetCoord.roffsetFromCube(OffsetCoord.ODD, OffsetCoord.roffsetToCube(OffsetCoord.ODD, offset))); | |
| Tests.equalOffsetcoord("conversion_roundtrip even-q", offset, OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, offset))); | |
| Tests.equalOffsetcoord("conversion_roundtrip even-r", offset, OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, OffsetCoord.roffsetToCube(OffsetCoord.EVEN, offset))); | |
| } | |
| } | |
| } | |
| public static testOffsetFromCube(): void { | |
| Tests.equalOffsetcoord("offset_from_cube odd-r", new OffsetCoord(-2, 2), OffsetCoord.roffsetFromCube(OffsetCoord.ODD, new Hex(-3, 2, 1))); | |
| Tests.equalOffsetcoord("offset_from_cube odd-r", new OffsetCoord(1, -1), OffsetCoord.roffsetFromCube(OffsetCoord.ODD, new Hex(2, -1, -1))); | |
| Tests.equalOffsetcoord("offset_from_cube even-r", new OffsetCoord(-2, 2), OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, new Hex(-3, 2, 1))); | |
| Tests.equalOffsetcoord("offset_from_cube even-r", new OffsetCoord(2, -1), OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, new Hex(2, -1, -1))); | |
| Tests.equalOffsetcoord("offset_from_cube odd-q", new OffsetCoord(-2, 2), OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, new Hex(-2, 3, -1))); | |
| Tests.equalOffsetcoord("offset_from_cube odd-q", new OffsetCoord(-1, -2), OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, new Hex(-1, -1, 2))); | |
| Tests.equalOffsetcoord("offset_from_cube even-q", new OffsetCoord(-2, 2), OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, new Hex(-2, 3, -1))); | |
| Tests.equalOffsetcoord("offset_from_cube even-q", new OffsetCoord(-1, -1), OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, new Hex(-1, -1, 2))); | |
| } | |
| public static testOffsetToCube(): void { | |
| Tests.equalHex("offset_to_cube odd-r", new Hex(-3, 2, 1), OffsetCoord.roffsetToCube(OffsetCoord.ODD, new OffsetCoord(-2, 2))); | |
| Tests.equalHex("offset_to_cube odd-r", new Hex(2, -1, -1), OffsetCoord.roffsetToCube(OffsetCoord.ODD, new OffsetCoord(1, -1))); | |
| Tests.equalHex("offset_to_cube even-r", new Hex(-3, 2, 1), OffsetCoord.roffsetToCube(OffsetCoord.EVEN, new OffsetCoord(-2, 2))); | |
| Tests.equalHex("offset_to_cube even-r", new Hex(2, -1, -1), OffsetCoord.roffsetToCube(OffsetCoord.EVEN, new OffsetCoord(2, -1))); | |
| Tests.equalHex("offset_to_cube odd-q", new Hex(-2, 3, -1), OffsetCoord.qoffsetToCube(OffsetCoord.ODD, new OffsetCoord(-2, 2))); | |
| Tests.equalHex("offset_to_cube odd-q", new Hex(-1, -1, 2), OffsetCoord.qoffsetToCube(OffsetCoord.ODD, new OffsetCoord(-1, -2))); | |
| Tests.equalHex("offset_to_cube even-q", new Hex(-2, 3, -1), OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, new OffsetCoord(-2, 2))); | |
| Tests.equalHex("offset_to_cube even-q", new Hex(-1, -1, 2), OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, new OffsetCoord(-1, -1))); | |
| } | |
| public static testOffsetToDoubled(): void { | |
| for (let col = -2; col < 3; col++) { | |
| for (let row = -2; row < 3; row++) { | |
| const offset: OffsetCoord = new OffsetCoord(col, row); | |
| Tests.equalDoubledcoord("offset_to_doubled loop odd-q", DoubledCoord.qdoubledFromCube(OffsetCoord.qoffsetToCube(OffsetCoord.ODD, offset)), OffsetCoord.qoffsetToQdoubled(OffsetCoord.ODD, offset)); | |
| Tests.equalDoubledcoord("offset_to_doubled loop even-q", DoubledCoord.qdoubledFromCube(OffsetCoord.qoffsetToCube(OffsetCoord.EVEN, offset)), OffsetCoord.qoffsetToQdoubled(OffsetCoord.EVEN, offset)); | |
| Tests.equalDoubledcoord("offset_to_doubled loop odd-r", DoubledCoord.rdoubledFromCube(OffsetCoord.roffsetToCube(OffsetCoord.ODD, offset)), OffsetCoord.roffsetToRdoubled(OffsetCoord.ODD, offset)); | |
| Tests.equalDoubledcoord("offset_to_doubled loop even-r", DoubledCoord.rdoubledFromCube(OffsetCoord.roffsetToCube(OffsetCoord.EVEN, offset)), OffsetCoord.roffsetToRdoubled(OffsetCoord.EVEN, offset)); | |
| const qdoubled: DoubledCoord = new DoubledCoord(col * 2 + (row & 1), row); | |
| Tests.equalOffsetcoord("offset_from_doubled loop odd-q", OffsetCoord.qoffsetFromCube(OffsetCoord.ODD, qdoubled.qdoubledToCube()), OffsetCoord.qoffsetFromQdoubled(OffsetCoord.ODD, qdoubled)); | |
| Tests.equalOffsetcoord("offset_from_doubled loop even-q", OffsetCoord.qoffsetFromCube(OffsetCoord.EVEN, qdoubled.qdoubledToCube()), OffsetCoord.qoffsetFromQdoubled(OffsetCoord.EVEN, qdoubled)); | |
| const rdoubled: DoubledCoord = new DoubledCoord(col, row * 2 + (col & 1)); | |
| Tests.equalOffsetcoord("offset_from_doubled loop odd-r", OffsetCoord.roffsetFromCube(OffsetCoord.ODD, rdoubled.rdoubledToCube()), OffsetCoord.roffsetFromRdoubled(OffsetCoord.ODD, rdoubled)); | |
| Tests.equalOffsetcoord("offset_from_doubled loop even-r", OffsetCoord.roffsetFromCube(OffsetCoord.EVEN, rdoubled.rdoubledToCube()), OffsetCoord.roffsetFromRdoubled(OffsetCoord.EVEN, rdoubled)); | |
| } | |
| } | |
| } | |
| public static testOffsetFromDoubled(): void { | |
| } | |
| public static testDoubledRoundtrip(): void { | |
| for (let q = -2; q < 3; q++) { | |
| for (let r = -2; r < 3; r++) { | |
| const cube: Hex = new Hex(q, r, -q - r); | |
| Tests.equalHex("conversion_roundtrip doubled-q", cube, DoubledCoord.qdoubledFromCube(cube).qdoubledToCube()); | |
| Tests.equalHex("conversion_roundtrip doubled-r", cube, DoubledCoord.rdoubledFromCube(cube).rdoubledToCube()); | |
| } | |
| } | |
| for (let col = -2; col < 3; col++) { | |
| for (let row = -2; row < 3; row++) { | |
| const qdoubled: DoubledCoord = new DoubledCoord(col * 2 + (row & 1), row); | |
| Tests.equalDoubledcoord("conversion_roundtrip doubled-q", qdoubled, DoubledCoord.qdoubledFromCube(qdoubled.qdoubledToCube())); | |
| const rdoubled: DoubledCoord = new DoubledCoord(col, row * 2 + (col & 1)); | |
| Tests.equalDoubledcoord("conversion_roundtrip doubled-r", rdoubled, DoubledCoord.rdoubledFromCube(rdoubled.rdoubledToCube())); | |
| } | |
| } | |
| } | |
| public static testDoubledFromCube(): void { | |
| Tests.equalDoubledcoord("doubled_from_cube doubled-q", new DoubledCoord(1, 5), DoubledCoord.qdoubledFromCube(new Hex(1, 2, -3))); | |
| Tests.equalDoubledcoord("doubled_from_cube doubled-r", new DoubledCoord(4, 2), DoubledCoord.rdoubledFromCube(new Hex(1, 2, -3))); | |
| } | |
| public static testDoubledToCube(): void { | |
| Tests.equalHex("doubled_to_cube doubled-q", new Hex(1, 2, -3), new DoubledCoord(1, 5).qdoubledToCube()); | |
| Tests.equalHex("doubled_to_cube doubled-r", new Hex(1, 2, -3), new DoubledCoord(4, 2).rdoubledToCube()); | |
| } | |
| public static testAll(): void { | |
| Tests.testHexArithmetic(); | |
| Tests.testHexDirection(); | |
| Tests.testHexNeighbor(); | |
| Tests.testHexDiagonal(); | |
| Tests.testHexDistance(); | |
| Tests.testHexRotateRight(); | |
| Tests.testHexRotateLeft(); | |
| Tests.testHexRound(); | |
| Tests.testHexLinedraw(); | |
| Tests.testLayout(); | |
| Tests.testOffsetRoundtrip(); | |
| Tests.testOffsetFromCube(); | |
| Tests.testOffsetToCube(); | |
| Tests.testOffsetToDoubled(); | |
| Tests.testOffsetFromDoubled(); | |
| Tests.testDoubledRoundtrip(); | |
| Tests.testDoubledFromCube(); | |
| Tests.testDoubledToCube(); | |
| } | |
| } | |
| // Tests | |
| function complain(name: string) { console.log("FAIL", name); } | |
| Tests.testAll(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment