# Honeycomb: hexagon grids in JavaScript

There are plenty of JavaScript libraries offering the tools needed for rendering hexagon grids. But none (that I know of) where you can choose how the grid is rendered, or not render at all (on the server). Most libraries I could find came with canvas rendering built in or had mediocre APIs.

So, yearning for a new hobby project and some time to spare I started Honeycomb.

#### Basic usage

After installation (`npm i -S honeycomb-grid`

or `yarn add honeycomb-grid`

) you can have a grid in 2 steps:

// 1. create a Grid factory that uses the default Hex factory:

const Grid = Honeycomb.defineGrid()

// 2a. create a rectangular grid:

Grid.rectangle({ width: 4, height: 4 })

// [

// { x: 0, y: 0 },

// { x: 0, y: 1 },

// { x: 0, y: 2 },

// …

// ]

// 2b. or create a grid from individual hexes:

const Hex = Grid.Hex

Grid(Hex(1, 2), Hex(3, 4))

// [

// { x: 1, y: 2 },

// { x: 3, y: 4 }

// ]

There’s an optional (but very likely) step 0: creating a custom Hex factory:

// 0. create a Hex factory by extending the default:

const Hex = Honeycomb.extendHex({

size: 30, // default: 1

orientation: 'flat' // default: 'pointy'

})

// 1. create a Grid factory that uses the Hex factory:

const Grid = Honeycomb.defineGrid(Hex)

#### Rendering

Honeycomb doesn’t care how a grid is rendered. In fact, it’s so indifferent that it’s completely void of functionality to show hexes on a screen. Fortunately, it isn’t very hard. Especially if you use a dedicated rendering library.

Here’s an example of rendering 10,000 hexes with WebGL using PixiJS:

If you prefer SVGs (like I do), here’s how you can achieve the same with SVG.js:

#### Grids extend Array.prototype

Most properties/methods of grids are the same as their `Array`

counterpart:

const grid = Grid.rectangle({ width: 4, height: 4 })

grid.length // 16

grid.pop() // { x: 3, y: 3 }

grid.length // 15

grid[4] // { x: 1, y: 0 }

Some Grid methods are augmented. For example: `Array#includes`

always returns `false`

when passed an object literal because it uses strict equality internally. `Grid#includes`

*only* accepts object literals (in the form of points):

const array = [{ x: 1, y: 0 }]

array.includes({ x: 1, y: 0 }) // false

const grid = Grid(Hex(1, 0))

grid.includes({ x: 1, y: 0 }) // true

#### Grid methods that mutate

Methods that mutate the grid in-place (`Grid#push`

, `Grid#splice`

and `Grid#unshift`

) only accept valid hexes to prevent “grid corruption” 👮.

const grid = Grid() // []

// this silently fails:

grid.push('invalid hex') // 0 <- the grid's length, which remains 0

grid.includes('invalid hex') // false

Keep in mind that methods that return a new grid (e.g. `Grid#map`

) can create grids with invalid hexes:

const grid = Grid.rectangle({ width: 4, height: 4 })

const newGrid = grid.map(hex => 'invalid hex')

// [

// 'invalid hex',

// 'invalid hex',

// 'invalid hex',

// …

// ]

#### Be careful with bracket notation

It’s possible to add an invalid hex to a grid by using bracket notation:

const grid = Grid(Hex())

grid[0] // { x: 0, y: 0 }

grid[0] = 'invalid hex'

grid[0] // 'invalid hex' ⚠️

Use `Grid#get`

and `Grid#set`

instead:

const grid = Grid(Hex())

grid.get(0) // { x: 0, y: 0 }

grid.set(0, 'invalid hex')

grid.get(0) // { x: 0, y: 0 } <- invalid hex is ignored

// Grid#set() also accepts a point:

grid.set({ x: 0, y: 0 }, Hex(-1, 3))

// …as does Grid#get():

grid.get([-1, 3]) // { x: -1, y: 3 }

#### Point → Hex

Translating a point (pixel) in the grid to the corresponding hex is possible with `Grid.pointToHex`

:

#### Grid shapes

Honeycomb offers 4 shape methods: rectangle, triangle, hexagon and parallelogram:

#### Coordinate systems

The standard coordinate system is a cartesian one. It’s intuitive and easy to reason about. A lot of methods internally use a “cube” coordinate system. See this redblobgames.com blog post for an explanation between the two (he calls the cartesian system “offset coordinates”).

Hexes have getters for each of the cube coordinates `q`

, `r`

and `s`

:

const Hex = Honeycomb.extendHex()

const hex = Hex(3, 4)

hex.q // 1

hex.r // 4

hex.s // -5

hex.cartesian() // { x: 3, y: 4 }

hex.cube() // { q: 1, r: 4, s: -5 }

There are methods for converting between cartesian and cube:

const Hex = Honeycomb.extendHex()

const hex = Hex()

hex.toCube({ x: 3, y: 4 }) // { q: 1, r: 4, s: -5 }

// Hex#toCartesian doesn't require the s coordinate:

hex.toCartesian({ q: 1, r: 4 }) // { x: 3, y: 4 }

These methods always require coordinates to be passed and don’t work on a hex instance, even though they’re instance methods. This will be fixed in a future release 🙃

The full API documentation is available on Github.

While adding examples to the docs I’ve gained a lot of insights that I hope to apply in the coming months (a full-time job and a 1-year-old aren’t beneficial to a hobby project).

Let me know what you think about Honeycomb by leaving a comment or opening a Github issue if you have any questions or ideas.