Skip to content

Commit

Permalink
feat(grid): update grid rectangle method to only within previous grid
Browse files Browse the repository at this point in the history
  • Loading branch information
flauwekeul committed Apr 22, 2021
1 parent 20655d1 commit b242c94
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 35 deletions.
11 changes: 10 additions & 1 deletion src/grid/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AxialCoordinates } from '../hex'
import { AxialCoordinates, CubeCoordinates } from '../hex'

export const DIRECTION_COORDINATES: AxialCoordinates[] = [
{ q: 1, r: 0 },
Expand All @@ -8,3 +8,12 @@ export const DIRECTION_COORDINATES: AxialCoordinates[] = [
{ q: 0, r: -1 },
{ q: 1, r: -1 },
]

export const RECTANGLE_DIRECTIONS = [
['q', 'r', 's'],
['r', 'q', 's'],
['r', 's', 'q'],
['s', 'r', 'q'],
['s', 'q', 'r'],
['q', 's', 'r'],
] as [keyof CubeCoordinates, keyof CubeCoordinates, keyof CubeCoordinates][]
48 changes: 22 additions & 26 deletions src/grid/functions/rectangle.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,46 @@
import { createHex, CubeCoordinates, Hex, isPointy } from '../../hex'
import { offsetFromZero, signedModulo } from '../../utils'
import { CompassDirection, FlatCompassDirection, PointyCompassDirection } from '../types'
import { RECTANGLE_DIRECTIONS } from '../constants'
import { FlatCompassDirection, PointyCompassDirection, RectangleOptions } from '../types'

export interface RectangleOptions {
width: number
height: number
start?: CubeCoordinates
direction?: CompassDirection
}

const DIRECTIONS = [
['q', 'r', 's'],
['r', 'q', 's'],
['r', 's', 'q'],
['s', 'r', 'q'],
['s', 'q', 'r'],
['q', 's', 'r'],
] as [keyof CubeCoordinates, keyof CubeCoordinates, keyof CubeCoordinates][]

// todo: move this to Grid.rectangle()?
export function* rectangle<T extends Hex>(
export const rectangle = <T extends Hex>(
hexPrototype: T,
{
width,
height,
start = { q: 0, r: 0, s: 0 },
start = { q: 0, r: 0 },
direction = isPointy(hexPrototype) ? PointyCompassDirection.E : FlatCompassDirection.S,
}: RectangleOptions,
) {
) => {
const result: T[] = []
const _start: CubeCoordinates = { q: start.q, r: start.r, s: -start.q - start.r }
// const hasTraversedBefore = this.traverser !== infiniteTraverser
// const previousHexes = [...this.traverser()]
// let coordinates: CubeCoordinates = previousHexes[previousHexes.length - 1] || { q: 0, r: 0 }

if (direction < 0 || direction > 5) {
direction = signedModulo(direction, 6)
}

const [firstCoordinate, secondCoordinate, thirdCoordinate] = DIRECTIONS[direction]
const [firstCoordinate, secondCoordinate, thirdCoordinate] = RECTANGLE_DIRECTIONS[direction]
const [firstStop, secondStop] = isPointy(hexPrototype) ? [width, height] : [height, width]

for (let second = 0; second < secondStop; second++) {
const secondOffset = offsetFromZero(hexPrototype.offset, second)

for (let first = -secondOffset; first < firstStop - secondOffset; first++) {
const nextCoordinates = {
[firstCoordinate]: first + start[firstCoordinate],
[secondCoordinate]: second + start[secondCoordinate],
[thirdCoordinate]: -first - second + start[thirdCoordinate],
[firstCoordinate]: first + _start[firstCoordinate],
[secondCoordinate]: second + _start[secondCoordinate],
[thirdCoordinate]: -first - second + _start[thirdCoordinate],
} as unknown
yield createHex(hexPrototype, nextCoordinates as CubeCoordinates)
// coordinates = nextCoordinates as CubeCoordinates
// if (hasTraversedBefore && !previousHexes.some((prevCoords) => equals(prevCoords, coordinates))) {
// return result // todo: or continue? or make this configurable?
// }
result.push(createHex(hexPrototype, nextCoordinates as CubeCoordinates))
}
}

return result
}
57 changes: 49 additions & 8 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createHex, equals, Hex, HexCoordinates } from '../hex'
import { rectangle, RectangleOptions } from './functions'
import { GridGenerator, Traverser } from './types'
import { createHex, CubeCoordinates, equals, Hex, HexCoordinates, isPointy } from '../hex'
import { offsetFromZero, signedModulo } from '../utils'
import { RECTANGLE_DIRECTIONS } from './constants'
import { FlatCompassDirection, GridGenerator, PointyCompassDirection, RectangleOptions, Traverser } from './types'

interface InternalTraverser<T extends Hex> {
(this: Grid<T>): GridGenerator<T>
Expand All @@ -25,11 +26,6 @@ export class Grid<T extends Hex> {
return Grid.of(this.hexPrototype, traverser.bind(this))
}

rectangle(options: RectangleOptions) {
// todo: the generator is wrapped, should that be the same in other calls to clone()? Or should this not be wrapped? Doesn't seem consistent
return this.clone(() => rectangle(this.hexPrototype, options))
}

// fixme: use generic functions for these kinds of operations
// something like https://github.com/benji6/imlazy or https://github.com/lodash/lodash/wiki/FP-Guide
each(fn: (hex: T) => void) {
Expand Down Expand Up @@ -63,6 +59,51 @@ export class Grid<T extends Hex> {
return this // or clone()? todo: when to return clone and when not?
}

rectangle({
width,
height,
start = { q: 0, r: 0 },
direction = isPointy(this.hexPrototype) ? PointyCompassDirection.E : FlatCompassDirection.S,
}: RectangleOptions) {
const _start: CubeCoordinates = { q: start.q, r: start.r, s: -start.q - start.r }

// todo: remove this?
if (direction < 0 || direction > 5) {
direction = signedModulo(direction, 6)
}

const [firstCoordinate, secondCoordinate, thirdCoordinate] = RECTANGLE_DIRECTIONS[direction]
const [firstStop, secondStop] = isPointy(this.hexPrototype) ? [width, height] : [height, width]

// todo: duplication in traverse()
const rectangle: InternalTraverser<T> = () => {
const result: T[] = []
const hasTraversedBefore = this.traverser !== infiniteTraverser
const previousHexes = [...this.traverser()]
let coordinates: CubeCoordinates = previousHexes[previousHexes.length - 1] || { q: 0, r: 0 }

for (let second = 0; second < secondStop; second++) {
const secondOffset = offsetFromZero(this.hexPrototype.offset, second)

for (let first = -secondOffset; first < firstStop - secondOffset; first++) {
const nextCoordinates: unknown = {
[firstCoordinate]: first + _start[firstCoordinate],
[secondCoordinate]: second + _start[secondCoordinate],
[thirdCoordinate]: -first - second + _start[thirdCoordinate],
}
coordinates = nextCoordinates as CubeCoordinates
if (hasTraversedBefore && !previousHexes.some((prevCoords) => equals(prevCoords, coordinates))) {
return result // todo: or continue? or make this configurable?
}
result.push(createHex(this.hexPrototype, coordinates))
}
}

return result
}
return this.clone(rectangle)
}

traverse(...commands: Traverser[]) {
if (commands.length === 0) {
return this // or clone()? todo: when to return clone and when not?
Expand Down
6 changes: 6 additions & 0 deletions src/grid/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ export type GridGenerator<T extends Hex> = Iterable<T>
export interface Traverser {
(currentCoordinates: HexCoordinates): Iterable<HexCoordinates>
}

export interface RectangleOptions {
width: number
height: number
start?: HexCoordinates
direction?: CompassDirection
}

0 comments on commit b242c94

Please sign in to comment.