Skip to content

Commit

Permalink
✨ update grid-related code to use Hex class
Browse files Browse the repository at this point in the history
Grid now accepts a Hex constructor function as its first argument.
  • Loading branch information
flauwekeul committed Aug 8, 2022
1 parent 6b29a12 commit 5825ff0
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 36 deletions.
12 changes: 4 additions & 8 deletions src/grid/functions/distance.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { assertCubeCoordinates, HexCoordinates, HexPrototype } from '../../hex'
import { assertCubeCoordinates, Hex, HexCoordinates } from '../../hex'

export function distance(
hexPrototype: Pick<HexPrototype, 'offset' | 'isPointy'>,
from: HexCoordinates,
to: HexCoordinates,
) {
const { q: fromQ, r: fromR, s: fromS } = assertCubeCoordinates(hexPrototype, from)
const { q: toQ, r: toR, s: toS } = assertCubeCoordinates(hexPrototype, to)
export function distance(hex: Pick<Hex, 'offset' | 'isPointy'>, from: HexCoordinates, to: HexCoordinates) {
const { q: fromQ, r: fromR, s: fromS } = assertCubeCoordinates(hex, from)
const { q: toQ, r: toR, s: toS } = assertCubeCoordinates(hex, to)
return Math.max(Math.abs(fromQ - toQ), Math.abs(fromR - toR), Math.abs(fromS - toS))
}
54 changes: 32 additions & 22 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CompassDirection } from '../compass'
import { createHex, createHexPrototype, Hex, HexCoordinates, Point, pointToCube } from '../hex'
import { defineHex, Hex, HexConstructor, HexCoordinates, Point, pointToCube } from '../hex'
import { isFunction } from '../utils'
import { distance, neighborOf } from './functions'
import { concat } from './traversers'
Expand All @@ -13,18 +13,24 @@ export class Grid<T extends Hex> implements Iterable<T> {
throw new Error(`Can't create grid from empty iterable: ${JSON.stringify(hexes)}`)
}

return new Grid(Object.getPrototypeOf(firstHex) as T, hexes)
return new Grid(firstHex.constructor as HexConstructor<T>, hexes)
}

static fromJSON<T extends Hex>({ hexSettings, coordinates }: GridAsJSON<T>) {
const hexPrototype = createHexPrototype(hexSettings)
static fromJSON({ hexSettings, coordinates }: GridAsJSON) {
const HexClass = defineHex(hexSettings)
return new Grid(
hexPrototype,
coordinates.map((_coordinates) => createHex(hexPrototype, _coordinates)),
HexClass,
coordinates.map((_coordinates) => new HexClass(_coordinates)),
)
}

readonly [Symbol.toStringTag] = 'Grid'
get [Symbol.toStringTag]() {
return `Grid(${this.size})`
}

get hexPrototype() {
return this.#hexClass.prototype as T
}

get size() {
return this.#hexes.size
Expand Down Expand Up @@ -64,27 +70,27 @@ export class Grid<T extends Hex> implements Iterable<T> {
return this.#hexes.values()
}

readonly hexPrototype: T
readonly #hexClass: HexConstructor<T>

#hexes = new Map<string, T>()

constructor(hexPrototype: T)
constructor(hexPrototype: T, traversers: Traverser<T> | Traverser<T>[])
constructor(hexPrototype: T, hexes: Iterable<T>)
constructor(hexClass: HexConstructor<T>)
constructor(hexClass: HexConstructor<T>, traversers: Traverser<T> | Traverser<T>[])
constructor(hexClass: HexConstructor<T>, hexes: Iterable<T>)
constructor(grid: Grid<T>)
constructor(hexPrototypeOrGrid: T | Grid<T>, input: Traverser<T> | Traverser<T>[] | Iterable<T> = []) {
if (hexPrototypeOrGrid instanceof Grid<T>) {
this.hexPrototype = hexPrototypeOrGrid.hexPrototype
this.setHexes(hexPrototypeOrGrid)
constructor(hexClassOrGrid: HexConstructor<T> | Grid<T>, input: Traverser<T> | Traverser<T>[] | Iterable<T> = []) {
if (hexClassOrGrid instanceof Grid<T>) {
this.#hexClass = hexClassOrGrid.#hexClass
this.setHexes(hexClassOrGrid)
return
}

this.hexPrototype = hexPrototypeOrGrid
this.#hexClass = hexClassOrGrid
this.setHexes(this.#createHexesFromIterableOrTraversers(input))
}

createHex(coordinates?: HexCoordinates): T {
return createHex<T>(this.hexPrototype, coordinates)
return new this.#hexClass(coordinates)
}

getHex(coordinates: HexCoordinates): T | undefined {
Expand All @@ -104,7 +110,7 @@ export class Grid<T extends Hex> implements Iterable<T> {
}

filter(predicate: (hex: T) => boolean): Grid<T> {
const result = new Grid(this.hexPrototype)
const result = new Grid(this.#hexClass)

for (const hex of this) {
if (predicate(hex)) result.#setHex(hex)
Expand All @@ -114,7 +120,7 @@ export class Grid<T extends Hex> implements Iterable<T> {
}

map(fn: (hex: T) => T): Grid<T> {
const result = new Grid(this.hexPrototype)
const result = new Grid(this.#hexClass)

for (const hex of this) {
result.#setHex(fn(hex))
Expand All @@ -128,7 +134,7 @@ export class Grid<T extends Hex> implements Iterable<T> {
traverse(grid: Grid<T>, options?: { bail?: boolean }): Grid<T>
traverse(input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T>, options?: { bail?: boolean }): Grid<T>
traverse(input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T>, { bail = false } = {}): Grid<T> {
const result = new Grid(this.hexPrototype)
const result = new Grid(this.#hexClass)

for (const hex of this.#createHexesFromIterableOrTraversers(input)) {
const foundHex = this.getHex(hex)
Expand Down Expand Up @@ -175,8 +181,12 @@ export class Grid<T extends Hex> implements Iterable<T> {
return Array.from(this)
}

toJSON(): GridAsJSON<T> {
return { hexSettings: this.hexPrototype, coordinates: this.toArray() }
// todo: add to docs that hexSettings don't include any custom properties
toJSON(): GridAsJSON {
// these four properties are getters that may be present further up the prototype chain
// JSON.stringify() ignores properties in the prototype chain
const { dimensions, orientation, origin, offset } = this.hexPrototype
return { hexSettings: { dimensions, orientation, origin, offset }, coordinates: this.toArray() }
}

toString(): string {
Expand Down
4 changes: 2 additions & 2 deletions src/grid/traversers/rectangle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Compass, CompassDirection } from '../../compass'
import { completeCubeCoordinates, Hex, HexCoordinates, hexToOffset, OffsetCoordinates } from '../../hex'
import { completeCubeCoordinates, Hex, HexCoordinates, HexOffset, hexToOffset, OffsetCoordinates } from '../../hex'
import { isOffset, isTuple, tupleToCube } from '../../utils'
import { Traverser } from '../types'
import { line } from './line'
Expand Down Expand Up @@ -71,7 +71,7 @@ function optionsFromOpposingCorners(
}

// todo: move to util?
function assertOffsetCoordinates(coordinates: HexCoordinates, isPointy: boolean, offset: number): OffsetCoordinates {
function assertOffsetCoordinates(coordinates: HexCoordinates, isPointy: boolean, offset: HexOffset): OffsetCoordinates {
if (isOffset(coordinates)) return coordinates

const { q, r } = isTuple(coordinates) ? tupleToCube(coordinates) : completeCubeCoordinates(coordinates)
Expand Down
3 changes: 1 addition & 2 deletions src/grid/traversers/ring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export function ring<T extends Hex>(options: RingOptions | RingFromRadiusOptions
let firstHex: T

if (Number.isFinite(radius)) {
firstHex = createHex(center)
firstHex.q += radius
firstHex = createHex(center).translate({ q: radius, s: -radius })
} else {
firstHex = createHex((options as RingOptions).start ?? cursor)
radius = distance(firstHex, center, firstHex)
Expand Down
10 changes: 8 additions & 2 deletions src/grid/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ export type Traverser<T extends Hex, R extends Iterable<T> = T[]> = (
cursor?: HexCoordinates,
) => R

export interface GridAsJSON<T extends Hex> {
hexSettings: HexSettings & Omit<T, keyof Hex>
export interface GridAsJSON {
hexSettings: HexSettings
coordinates: AxialCoordinates[]
}

/**
* @category Traverser
*/
export enum Rotation {
CLOCKWISE = 'CLOCKWISE',
COUNTERCLOCKWISE = 'COUNTERCLOCKWISE',
}

/**
* @category Traverser
*/
export type RotationLike = Rotation | 'CLOCKWISE' | 'clockwise' | 'COUNTERCLOCKWISE' | 'counterclockwise'

0 comments on commit 5825ff0

Please sign in to comment.