Skip to content

Commit

Permalink
✨ Hex is now a class
Browse files Browse the repository at this point in the history
Also:
- Added `defineHex()` and other helpers to make it easier to extend `Hex`.
- Remove obsolete `createHex()` (replaced by `new Hex()`), `createHexPrototype()` (replaced by extending `Hex` and/or using `defineHex()`) and `isHex()` (replaced by `instanceof`) functions.
- Remove standalone hex functions `center()`, `cloneHex()`, `corners()`, `height()`, `isFlat()`, `isPointy()` and `width()` and move them to Hex.
- Add `translate()` method to Hex.
- Change the type of Hex's `offset` property from a number to a union of `-1` and `1`.
  • Loading branch information
flauwekeul committed Aug 7, 2022
1 parent 677953d commit 6b29a12
Show file tree
Hide file tree
Showing 38 changed files with 292 additions and 896 deletions.
2 changes: 1 addition & 1 deletion docs/guide/defining-hexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@

In a grid with pointy hexes, each row is offsetted half a hex relative to the previous row. In grids with flat hexes, this applies to the columns. Redblobgames has a [visual example](https://www.redblobgames.com/grids/hexagons/#coordinates-offset).

Set the offset property to `1` or `-1` (default) to control whether the even or odd rows/columns are offsetted. Or go loco 🚂 and use other values (at your own risk).
Set the offset property to `1` or `-1` (default) to control whether the even or odd rows/columns are offsetted.

## Customization
37 changes: 21 additions & 16 deletions playground/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { createHexPrototype, Grid, Hex, rectangle } from '../src'
import { defineHex, Grid, rectangle } from '../src'
import { render } from './render'

interface CustomHex extends Hex {
custom: string
}
// interface CustomHex extends Hex {
// custom: string
// }

const hexPrototype = createHexPrototype<CustomHex>({
dimensions: 30,
orientation: 'pointy',
origin: 'topLeft',
})
const grid = new Grid(hexPrototype, rectangle({ width: 10, height: 10 }))
// this creates this prototype chain:
// CustomHex -> class extends Hex -> Hex -> Object
// while creating a class that extends Hex has this:
// CustomHex -> Hex -> Object
class CustomHex extends defineHex({ dimensions: 30, origin: 'topLeft' }) {
custom = 'test'
}
// class CustomHex extends Hex {
// get dimensions(): Ellipse {
// return createHexDimensions(30)
// }
// get origin(): Point {
// return createHexOrigin('topLeft', this)
// }

/**
* todo: change how directions work: with degrees by default? Compass direction converts to degrees, or maybe drop compass direction altogether?
* todo: add integration tests for concatenating traversers
* todo: traverser should adhere to rules:
* - when a cursor is passed, but no start: skip the first hex (doesn't apply to all traversers)
*/
// custom = 'test'
// }
const grid = new Grid(CustomHex, rectangle({ width: 10, height: 10 }))

let i = 0
for (const hex of grid) {
Expand Down
2 changes: 1 addition & 1 deletion playground/render.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SVG } from '@svgdotjs/svg.js'
import { Hex } from '../dist'
import { Hex } from '../src'

const draw = SVG().addTo('body').size('100%', '100%')

Expand Down
7 changes: 4 additions & 3 deletions src/hex/functions/assertCubeCoordinates.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isOffset, isTuple, tupleToCube } from '../../utils'
import { CubeCoordinates, HexCoordinates, HexPrototype } from '../types'
import { Hex } from '../hex'
import { CubeCoordinates, HexCoordinates } from '../types'
import { completeCubeCoordinates } from './completeCubeCoordinates'
import { offsetToCube } from './offsetToCube'

Expand All @@ -9,11 +10,11 @@ import { offsetToCube } from './offsetToCube'
* @privateRemarks It's not placed in /src/utils because that causes circular dependencies.
*/
export function assertCubeCoordinates(
hexPrototype: Pick<HexPrototype, 'offset' | 'isPointy'>,
hex: Pick<Hex, 'offset' | 'isPointy'>,
coordinates: HexCoordinates,
): CubeCoordinates {
return isOffset(coordinates)
? offsetToCube(hexPrototype, coordinates)
? offsetToCube(hex, coordinates)
: isTuple(coordinates)
? tupleToCube(coordinates)
: completeCubeCoordinates(coordinates)
Expand Down
16 changes: 0 additions & 16 deletions src/hex/functions/center.test.ts

This file was deleted.

15 changes: 0 additions & 15 deletions src/hex/functions/center.ts

This file was deleted.

37 changes: 0 additions & 37 deletions src/hex/functions/cloneHex.test.ts

This file was deleted.

17 changes: 0 additions & 17 deletions src/hex/functions/cloneHex.ts

This file was deleted.

59 changes: 0 additions & 59 deletions src/hex/functions/corners.test.ts

This file was deleted.

43 changes: 0 additions & 43 deletions src/hex/functions/corners.ts

This file was deleted.

47 changes: 0 additions & 47 deletions src/hex/functions/createHex.test.ts

This file was deleted.

22 changes: 0 additions & 22 deletions src/hex/functions/createHex.ts

This file was deleted.

28 changes: 28 additions & 0 deletions src/hex/functions/createHexDimensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { isObject } from '../../utils'
import { BoundingBox, Ellipse, Orientation } from '../types'

export function createHexDimensions(radius: number): Ellipse
export function createHexDimensions(boundingBox: BoundingBox, orientation: Orientation): Ellipse
export function createHexDimensions(ellipse: Ellipse): Ellipse
export function createHexDimensions(input: number | BoundingBox | Ellipse, orientation?: Orientation): Ellipse {
if (isObject<Ellipse>(input) && input.xRadius > 0 && input.yRadius > 0) {
return input
}

if (isObject<BoundingBox>(input) && input.width > 0 && input.height > 0) {
const { width, height } = input
return orientation === Orientation.POINTY
? { xRadius: width / Math.sqrt(3), yRadius: height / 2 }
: { xRadius: width / 2, yRadius: height / Math.sqrt(3) }
}

if (input > 0) {
return { xRadius: input, yRadius: input } as Ellipse
}

throw new TypeError(
`Invalid dimensions: ${JSON.stringify(
input,
)}. Dimensions must be expressed as an Ellipse ({ xRadius: number, yRadius: number }), a Rectangle ({ width: number, height: number }) or a number.`,
)
}
21 changes: 21 additions & 0 deletions src/hex/functions/createHexOrigin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { isPoint } from '../../utils'
import { BoundingBox, Point } from '../types'

export function createHexOrigin(input: 'topLeft', boundingBox: BoundingBox): Point
export function createHexOrigin(input: Point): Point
export function createHexOrigin(input: Point | 'topLeft', boundingBox?: BoundingBox): Point {
if (isPoint(input)) return input

if (!boundingBox)
throw new TypeError(
`Supply a bounding box ({ width: number, height: number }). Received: ${JSON.stringify(boundingBox)}`,
)

if (input === 'topLeft') return { x: boundingBox.width * -0.5, y: boundingBox.height * -0.5 }

throw new TypeError(
`Invalid origin: ${JSON.stringify(
input,
)}. Origin must be expressed as a Point ({ x: number, y: number }) or the string 'topLeft'.`,
)
}

0 comments on commit 6b29a12

Please sign in to comment.