Create hex grids easily, in node or the browser.
Clone or download
Latest commit 7d4db28 Nov 29, 2018

README.md

Honeycomb

Gitter NPM version dependencies devDependencies GitHub license

Another hex grid library made in JavaScript, heavily inspired by Red Blob Games' blog posts and code samples.

All existing JS hex grid libraries I could find are coupled with some form of view. Most often a <canvas> element or the browser DOM. I want more separation of concerns…and a new hobby project to spend countless hours on.

Features

  • 🙌 Works in (modern) browsers and in Node.js.
  • 📐 Create hex grids in different shapes: ▭ rectangles, △ triangles, ⬡ hexagons and ▱ parallelograms.
  • 🌐 2 coordinate systems: cartesian (x and y) and cube (q, r and s).
  • Create your own hexes by extending the built-in hex factory.
  • 🗺 Convert points to hexes and vice versa.
  • ⬢ Pointy and ⬣ flat hexes.
  • 🖥 Lets you decide if and how hexes are rendered.

Installation

NPM:

npm i --save honeycomb-grid

Yarn:

yarn add honeycomb-grid

Or download the minified file from unpkg.com.

Getting started

Browser

Honeycomb works at least in recent versions of Chrome, Firefox, Edge and Safari.

<script src="honeycomb.js"></script>

<script>
    const Grid = Honeycomb.defineGrid()
    Grid.rectangle({ width: 4, height: 4 })
</script>

Node.js

const Honeycomb = require('honeycomb-grid')

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

With ES2015 modules

import { defineGrid } from 'honeycomb-grid'

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

Examples

Basic usage

Create a hex grid in 3 steps:

// 1.  (optionally) create a Hex factory by extending the default:
const Hex = Honeycomb.extendHex({
    size: 30,           // default: 1
    orientation: 'flat' // default: 'pointy'
})

// 2.  create a Grid factory that uses the Hex factory:
const Grid = Honeycomb.defineGrid(Hex)

// 3a. create a grid with a "shape" method:
const grid1 = Grid.rectangle({ width: 4, height: 4 })
// [
//    { x: 0, y: 0 },
//    { x: 0, y: 1 },
//    { x: 0, y: 2 },
//
// ]

// 3b. or create a grid from individual hexes:
const grid2 = Grid(Hex(1, 2), Hex(3, 4))
// [
//    { x: 1, y: 2 },
//    { x: 3, y: 4 }
// ]

Rendering

Honeycomb comes without the ability to render hexes to screen. Fortunately, it isn't very hard. Especially if you use a dedicated rendering library.

With PixiJS

const app = new PIXI.Application({ transparent: true })
const graphics = new PIXI.Graphics()

const Hex = Honeycomb.extendHex({ size: 5 })
const Grid = Honeycomb.defineGrid(Hex)

document.body.appendChild(app.view)
// set a line style of 1px wide and color #999
graphics.lineStyle(1, 0x999999)

// render 10,000 hexes
Grid.rectangle({ width: 100, height: 100 }).forEach(hex => {
    const point = hex.toPoint()
    // add the hex's position to each of its corner points
    const corners = hex.corners().map(corner => corner.add(point))
    // separate the first from the other corners
    const [firstCorner, ...otherCorners] = corners

    // move the "pen" to the first corner
    graphics.moveTo(firstCorner.x, firstCorner.y)
    // draw lines to the other corners
    otherCorners.forEach(({ x, y }) => graphics.lineTo(x, y))
    // finish at the first corner
    graphics.lineTo(firstCorner.x, firstCorner.y)

    app.stage.addChild(graphics)
})

Try it in JSFiddle.

With SVG.js

const draw = SVG(document.body)

const Hex = Honeycomb.extendHex({ size: 5 })
const Grid = Honeycomb.defineGrid(Hex)
// get the corners of a hex (they're the same for all hexes created with the same Hex factory)
const corners = Hex().corners()
// an SVG symbol can be reused
const hexSymbol = draw.symbol()
    // map the corners' positions to a string and create a polygon
    .polygon(corners.map(({ x, y }) => `${x},${y}`))
    .fill('none')
    .stroke({ width: 1, color: '#999' })

// render 10,000 hexes
Grid.rectangle({ width: 100, height: 100 }).forEach(hex => {
    const { x, y } = hex.toPoint()
    // use hexSymbol and set its position for each hex
    draw.use(hexSymbol).translate(x, y)
})

Try it in JSFiddle.

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 screen point (pixel) to the corresponding hex in a grid is possible with Grid.pointToHex().

const Hex = Honeycomb.extendHex({ size: 30 })
const Grid = Honeycomb.defineGrid(Hex)
const grid = Grid.rectangle({ width: 10, height: 10 })

document.addEventListener('click', ({ offsetX, offsetY }) => {
    // convert point to hex (coordinates)
    const hexCoordinates = Grid.pointToHex(offsetX, offsetY)
    // get the actual hex from the grid
    console.log(grid.get(hexCoordinates))
})

See a more elaborate example in JSFiddle.

Grid shapes

Honeycomb offers 4 shape methods: rectangle, triangle, hexagon and parallelogram. Try them out in JSFiddle.

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 🙃

Hexes can also be created from cube coordinates:

const Hex = Honeycomb.extendHex()
Hex({ q: 1, r: 4, s: -5 })  // { x: 3, y: 4 }

API

Table of Contents

Honeycomb

Type: Object

defineGrid

This function can be used to create Grid factories by passing it a Hex factory.

Parameters
  • Hex Hex A Hex factory. If nothing is passed, the default Hex factory is used by calling Honeycomb.extendHex() internally. (optional, default Honeycomb.extendHex())
Examples
// create a Grid factory that uses the default Hex Factory:
const Grid = Honeycomb.defineGrid()
const hex = Grid.Hex()
hex.size     // 1

// create your own Hex factory
const CustomHex = Honeycomb.extendHex({ size: 10, custom: '🤓' })
// …and pass it to defineGrid() to create a Grid factory that produces your custom hexes
const CustomGrid = Honeycomb.defineGrid(CustomHex)
const customHex = CustomGrid.Hex()
hex.size     // 10
hex.custom   // 🤓

Returns Grid A Grid factory.

extendHex

This function can be used to create custom hexes by extending the default Hex prototype.

All properties of the object passed to extendHex() will be added to the prototype of the resulting Hex factory. To add properties to individual hexes (instances), pass them to the Hex factory.

Parameters
  • prototype Object An object that's used as the prototype for all hexes in a grid. Warning: properties in this object will overwrite properties with the same name in the default prototype. (optional, default {})
Examples
const Hex = Honeycomb.extendHex({
    size: 50,
    orientation: 'flat',
    customProperty: `I'm custom 😃`,
    customMethod() {
        return `${this.customProperty} and called from a custom method 😎`
    }
})
const hex = Hex(5, -1)

hex.coordinates()    // { x: 5, y: -1 }
hex.size             // 50
hex.customProperty   // I'm custom 😃
hex.customMethod()   // I'm custom 😃 and called from a custom method 😎

// every hex created with Hex() shares these properties:
const hex2 = Hex(3, 0)
hex2.size            // 50
hex2.customProperty  // I'm custom 😃

// to set properties on individual hexes, pass them to Hex():
const hex3 = Hex(-2, -1, { instanceProperty: `I'm a unique snowflake 😌` })
hex3.instanceProperty    // I'm a unique snowflake 😌

Returns Hex A function to produce hexes that are all linked to the same prototype.

Point

See Point.

Grid

A function to create hex grids and perform various operations on them.

A Grid factory has several static methods that return grids of hexes in a certain shape. It can also be called with 1 or more hexes or an array of hexes to construct/clone a grid containing those hexes.

A grid extends Array.prototype, with some methods overwritten and some new methods added.

Parameters

  • arrayOrHex (Array<hex> | hex)? An array or a hex. Any invalid hexes are filtered out.
  • hexes ...hex? More hexes. Any invalid hexes are filtered out.

Examples

const Grid = Honeycomb.defineGrid()
// the Hex factory used by the Grid to produce hexes is available as a property
const Hex = Grid.Hex

Grid(Hex(3, -1), Hex(2, 0))      // [{ x: 3, y: -1 }, { x: 2, y: 0 }]
Grid([Hex(3, -1), Hex(2, 0)])    // [{ x: 3, y: -1 }, { x: 2, y: 0 }]

// invalid hexes are filtered out:
Grid('no hex', { x: 3, y: -1 })  // []
Grid(['no hex', Hex(1, -1)])     // [{ x: 1, y: -1 }]

// clone a grid:
const grid = Grid(Hex(), Hex(1), Hex(2))
const clonedGrid = Grid(grid)    // [{ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }]
grid === clonedGrid              // false

Returns grid A grid instance containing only valid hexes.

concat

Identical to Array#concat.

copyWithin

Identical to Array#copyWithin.

entries

Identical to Array#entries.

every

Identical to Array#every.

fill

  • Throws TypeError It makes no sense for a grid to fill it with arbitrary values, because it should only contain valid hexes.

Returns TypeError An error.

filter

Identical to Array#filter.

find

Identical to Array#find.

findIndex

Identical to Array#findIndex.

forEach

Identical to Array#forEach.

get

Get a hex from a grid.

Parameters
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid.rectangle({ width: 2, height: 2 })

grid.get(0)              // { x: 0, y: 0 }
grid.get(Hex(0, 1))      // { x: 0, y: 1 }
grid.get({ x: 0, y: 1 }) // { x: 0, y: 1 }
grid.get([0, 1])         // { x: 0, y: 1 }

grid.get(42)             // undefined
grid.get(Hex(6, -2))     // undefined

Returns hex The found hex or undefined.

hexesBetween

Parameters
  • firstHex hex The first hex.
  • lastHex hex The last hex.
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid.rectangle({ width: 4, height: 4 })

grid.hexesBetween(Hex(), Hex(3)) // [
                                 //    { x: 0, y: 0 },
                                 //    { x: 0, y: 1 },
                                 //    { x: 1, y: 1 },
                                 //    { x: 2, y: 2 },
                                 //    { x: 3, y: 2 },
                                 //    { x: 3, y: 3 },
                                 // ]

Returns Array<hex> Array (not a grid) of hexes in a straight line from firstHex to (and including) lastHex.

hexesInRange

Parameters
  • centerHex hex A hex to get surrounding hexes from.
  • range number The range (in hexes) surrounding the center hex. (optional, default 0)
  • includeCenterHex boolean Whether to include the center hex in the result (optional, default true)
Examples
const Hex = Honeycomb.extendHex({ orientation: 'pointy' })
const Grid = Honeycomb.defineGrid(Hex)
const grid = Grid.rectangle({ width: 5, height: 5 })

grid.hexesInRange(Hex(2, 2), 2)          // [
                                         //    { x: 0, y: 2 },
                                         //    { x: 0, y: 3 },
                                         //    { x: 1, y: 4 },
                                         //    ...
                                         //    { x: 3, y: 0 },
                                         //    { x: 3, y: 1 },
                                         //    { x: 4, y: 2 }
                                         // ]

// only returns hexes that exist in the grid:
grid.hexesInRange(Hex(0, 0), 1)          // [
                                         //    { x: 0, y: 0 },
                                         //    { x: 0, y: 1 },
                                         //    { x: 1, y: 0 }
                                         // ]

// exclude center hex:
grid.hexesInRange(Hex(2, 2), 1, false)   // [
                                         //    { x: 1, y: 2 },
                                         //    { x: 1, y: 3 },
                                         //    { x: 1, y: 1 },
                                         //    { x: 2, y: 3 },
                                         //    { x: 3, y: 2 }
                                         // ]
  • Throws Error When no valid hex is passed.

Returns Array<hex> An array with all hexes surrounding the passed center hex. Only hexes that are present in the grid are returned.

includes

Identical to Array#includes, but searches the passed hex (which can also be a point.

Parameters
  • point point The coordinates to search for.
  • fromIndex number Optional index to start searching. (optional, default 0)
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid(Hex(0))    // [ { x: 0, y: 0 } ]

grid.includes(Hex(0))        // true
grid.includes([0, 0])        // true
grid.includes(Hex(0), 1)     // false
grid.includes(Hex(5, 7))     // false

Returns boolean Whether the hex is included in the grid.

indexOf

Identical to Array#indexOf, but accepts a point and internally uses Hex#equals as a comparator.

Parameters
  • point point The coordinates to search for.
  • fromIndex number Optional index to start searching. If negative, it is taken as the offset from the end of the grid. (optional, default 0)
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid(Hex(0), Hex(1), Hex(0))
// [
//    { x: 0, y: 0 },
//    { x: 1, y: 1 },
//    { x: 0, y: 0 }
// ]

grid.indexOf(Hex(0))     // 0
grid.indexOf([0, 0])     // 0
grid.indexOf(Hex(0), 1)  // 2
grid.indexOf(Hex(5, 7))  // -1

Returns number The index of the found hex (first from the left) or -1 if the hex wasn't found.

join

Identical to Array#join.

keys

Identical to Array#keys.

lastIndexOf

Identical to Array#lastIndexOf, but accepts a point and internally uses Hex#equals as a comparator.

Because all hexes will have different coordinates in most grids, this method behaves the same as Grid#indexOf. This method might have a slightly better performance if you know the search hex is at the end of the grid.

Parameters
  • point point The coordinates to search for.
  • fromIndex number Optional index to start searching back from. If negative, it is taken as the offset from the end of the grid. (optional, default length-1)
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid(Hex(0), Hex(1), Hex(0))
// [
//    { x: 0, y: 0 },
//    { x: 1, y: 1 },
//    { x: 0, y: 0 }
// ]

grid.lastIndexOf(Hex(0))     // 2
grid.lastIndexOf([0, 0])     // 2
grid.lastIndexOf(Hex(0), 1)  // 0
grid.lastIndexOf(Hex(5, 7))  // -1

Returns number The last index of the found hex or -1 if the hex wasn't found.

map

Identical to Array#map.

neighborsOf

Parameters
Examples
const Hex = Honeycomb.extendHex({ orientation: 'pointy' })
const Grid = Honeycomb.defineGrid(Hex)
// conveniently creates a grid consisting of a hex surrounded by 6 hexes:
const grid = Grid.hexagon({ radius: 1 })

// all neighbors:
grid.neighborsOf(Hex())          // [
                                 //    { x: 1, y: 0 },
                                 //    { x: 0, y: 1 },
                                 //    { x: -1, y: 1 },
                                 //    { x: -1, y: 0 },
                                 //    { x: -1, y: -1 },
                                 //    { x: 0, y: -1 },
                                 // ]
// specific neighbor:
grid.neighborsOf(Hex(), 'NW')    // [{ x: -1, y: -1 }]
grid.neighborsOf(Hex(), 4)       // [{ x: -1, y: -1 }]

// multiple neighbors:
grid.neighborsOf(Hex(), ['SE', 'SW'])    // [
                                         //    { x: 0, y: 1 },
                                         //    { x: -1, y: 1 }
                                         // ]

grid.neighborsOf(Hex(), [1, 2])          // [
                                         //    { x: 0, y: 1 },
                                         //    { x: -1, y: 1 }
                                         // ]
// diagonal neighbor:
grid.neighborsOf(Hex(-1, 0), 'E', true)  // [{ x: 0, y: -1 }]

// only returns hexes that exist in the grid:
grid.neighborsOf(Hex(-1, -1), 'NW')      // []
  • Throws Error When no valid hex is passed.
  • Throws Error When the direction is invalid for the hex.

Returns Array<hex> An array of 0 up to 6 neighboring hexes. Only hexes that are present in the grid are returned.

pop

Identical to Array#pop.

push

Identical to Array#push, but filters out any passed invalid hexes.

Parameters
  • hexes ...hex? Hexes to add to the end of the grid. Invalid hexes are ignored.
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex

const grid = Grid(Hex(0))    // [{ x: 0, y: 0 }]
grid.push(Hex(1))            // 2
grid                         // [{ x: 0, y: 0 }, { x: 1, y: 1 }]

grid.push('invalid')         // 2
grid                         // [{ x: 0, y: 0 }, { x: 1, y: 1 }]

Returns number The new length of the grid.

reduce

Identical to Array#reduce.

reduceRight

Identical to Array#reduceRight.

reverse

Identical to Array#reverse.

set

Replace a hex with another hex. This is a safe alternative to using bracket notation (grid[0] = 'invalid').

If the target hex isn't present in the grid, the new hex is added (using Grid#push) to the grid. If the new hex is invalid, nothing changes.

Parameters
  • keyOrPoint (number | point) The coordinates of the hex that must be replaced.
  • newHex hex The replacing hex.
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid(Hex(0, 0)) // [ { x: 0, y: 0 } ]

// replace a hex:
grid.set(0, Hex(1, 1))
grid                         // [ { x: 1, y: 1 } ]
// the target hex can also be a point:
grid.set([1, 1], Hex(2, 2))
grid                         // [ { x: 2, y: 2 } ]

// invalid replace values are ignored:
grid.set(0, 'invalid')
grid                         // [ { x: 2, y: 2 } ]

// when the target hex isn't present in the grid, the replacing hex is added instead:
grid.set({ x: 9, y: 9 }, Hex(3, 3))
grid                         // [ { x: 2, y: 2 }, { x: 3, y: 3 } ]

Returns grid Itself.

shift

Identical to Array#shift.

some

Identical to Array#some.

sort

Identical to Array#sort.

splice

Identical to Array#splice, but filters out any passed invalid hexes.

Parameters
  • start number Index at which to start changing the grid.
  • deleteCount number Amount of hexes to delete. (optional, default length-start)
  • hexes ...hex The hexes to add to the grid, beginning at the start. (optional, default [])
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex
const grid = Grid.rectangle({ width: 2, height: 1 })
// [
//    { x: 0, y: 0 },
//    { x: 1, y: 0 },
//    { x: 0, y: 1 },
//    { x: 1, y: 1 }
// ]

grid.splice(2)               // [{ x: 0, y: 1 }, { x: 1, y: 1 }] <- deleted hexes
grid                         // [{ x: 0, y: 0 }, { x: 1, y: 0 }] <- leftover hexes

grid.splice(2, 1)            // [{ x: 0, y: 1 }]
grid                         // [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }]

grid.splice(2, 1, Hex(2))    // [{ x: 0, y: 1 }]
grid
// [
//    { x: 0, y: 0 },
//    { x: 1, y: 0 },
//    { x: 2, y: 2 },
//    { x: 1, y: 1 }
// ]

Returns Array<hex> A grid with the deleted hexes (if any).

toLocaleString

Identical to Array#toLocaleString.

toString

Identical to Array#toString.

unshift

Identical to Array#unshift, but filters out any passed invalid hexes.

Parameters
  • hexes ...hex? Hexes to add to the start of the grid. Invalid hexes are ignored.
Examples
const Grid = Honeycomb.defineGrid()
const Hex = Grid.Hex

const grid = Grid(Hex(0))    // [{ x: 0, y: 0 }]
grid.unshift(Hex(1))         // 2
grid                         // [{ x: 1, y: 1 }, { x: 0, y: 0 }]

grid.unshift('invalid')      // 2
grid                         // [{ x: 1, y: 1 }, { x: 0, y: 0 }]

Returns number The new length of the grid.

values

Identical to Array#values.

Hex

The Hex factory the Grid factory was created with.

hexagon

Creates a grid in the shape of a hexagon ⬡.

Parameters
  • options Object An options object.
    • options.radius number The radius (in hexes) excluding the center hex.
    • options.center hex The center hex. Each direction corresponds to a different arrangement of hexes. (optional, default Hex(0))
    • options.onCreate onCreate Callback that's called for each hex. Defaults to a no-op. (optional, default no-op)

Returns grid Grid of hexes in a hexagon arrangement.

isValidHex

Parameters
  • value any Any value.

Returns boolean Whether the passed value is a valid hex.

parallelogram

Creates a grid in the shape of a parallelogram ▱.

Parameters
  • options Object An options object.
    • options.width number The width (in hexes).
    • options.height number The height (in hexes).
    • options.start hex The start hex. (optional, default Hex(0))
    • options.direction (1 | 3 | 5) The direction (from the start hex) in which to create the shape. Each direction corresponds to a different arrangement of hexes. (optional, default 1)
    • options.onCreate onCreate Callback that's called for each hex. Defaults to a no-op. (optional, default no-op)

Returns grid Grid of hexes in a parallelogram arrangement.

pointToHex

Converts the passed point to a hex. Internally calls Hex#fromPoint.

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.
Examples
const Hex = Honeycomb.extendHex({ size: 50 })
const Grid = Honeycomb.defineGrid(Hex)
const Point = Honeycomb.Point

Grid.pointToHex(Point(120, 280))     // { x: 0, y: 3 }
Grid.pointToHex(120, 280)            // { x: 0, y: 3 }
Grid.pointToHex({ x: 120, y: 280 })  // { x: 0, y: 3 }
Grid.pointToHex([ 120, 280 ])        // { x: 0, y: 3 }

Returns hex A hex (with rounded coordinates) that contains the passed point.

rectangle

Creates a grid in the shape of a rectangle ▭.

Parameters
  • options Object An options object.
    • options.width number The width (in hexes).
    • options.height number The height (in hexes).
    • options.start hex The start hex. (optional, default Hex(0))
    • options.direction (COMPASS_DIRECTION | number) The direction (from the start hex) in which to create the shape. Defaults to 0 (E) for pointy hexes and 1 (S) for flat hexes. Each direction corresponds to a different arrangement of hexes. (optional, default E|S)
    • options.onCreate onCreate Callback that's called for each hex. Defaults to a no-op. (optional, default no-op)

Returns grid Grid of hexes in a rectangular arrangement.

triangle

Creates a grid in the shape of a (equilateral) triangle △.

Parameters
  • options Object An options object.
    • options.size number The side length (in hexes).
    • options.start hex The start hex. Note: it's not the first hex, but rather a hex relative to the triangle. (optional, default Hex(0))
    • options.direction (1 | 5) The direction in which to create the shape. Each direction corresponds to a different arrangement of hexes. In this case a triangle pointing up (direction: 1) or down (direction: 5) (with pointy hexes) or right (direction: 1) or left (direction: 5) (with flat hexes). Each direction corresponds to a different arrangement of hexes. (optional, default 1)
    • options.onCreate onCreate Callback that's called for each hex. Defaults to a no-op. (optional, default no-op)

Returns grid Grid of hexes in a triangle arrangement.

Hex

Factory function to create hexes. Use Honeycomb.extendHex to create a Hex factory.

Parameters

  • xOrProps (number | Object | Array<number>)? The x coordinate, or an object containing any of the cartesian (x and y) coordinates and optional custom properties, or an object containing all of the cube (q, r, and s) coordinates and optional custom properties, or an array containing any of the cartesian (x and y) coordinates.
    • xOrProps.x number? The x coordinate.
    • xOrProps.y number? The y coordinate.
  • y number? The y coordinate.
  • customProps object Any custom properties. The coordinates are merged into this object, ignoring any coordinates present in customProps. (optional, default {})

Examples

const Hex = Honeycomb.extendHex()

// passing numbers:
Hex()                        // { x: 0, y: 0 }
Hex(1)                       // { x: 1, y: 1 }
Hex(1, 2)                    // { x: 1, y: 2 }

// passing an object with cartesian coordinates:
Hex({})                      // { x: 0, y: 0 }
Hex({ x: 1 })                // { x: 1, y: 1 }
Hex({ y: 2 })                // { x: 2, y: 2 }
Hex({ x: 1, y: 2 })          // { x: 1, y: 2 }

// passing an object with cube coordinates:
Hex({ q: 1, r: 2, s: -3 })   // { x: 2, y: 2 }
Hex({ q: 1 })                // throws an error because of missing cube coordinates

// passing an array:
Hex([])                      // { x: 0, y: 0 }
Hex([1])                     // { x: 1, y: 1 }
Hex([1, 2])                  // { x: 1, y: 2 }

// custom properties:
Hex(1, 2, { a: 3 })          // { a: 3, x: 1, y: 2 }
Hex({ x: 1, y: 2, a: 3 })    // { a: 3, x: 1, y: 2 }

// cloning a hex:
const someHex = Hex(4, -2)   // { x: 4, y: -2 }
const clone = Hex(someHex)   // { x: 4, y: -2 }
someHex === clone            // false

Returns hex A hex. It always contains only the cartesian (x and y) coordinates and any custom properties.

add

Parameters
  • point point The hex (or point) that will be added to the current.

Returns hex A new hex where the passed hex's coordinates are added to the current. Any custom properties are copied.

cartesian

Alias for Hex#coordinates.

cartesianToCube

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.
Examples
const Hex = Honeycomb.extendHex()
const Point = Honeycomb.Point

Hex().cartesianToCube(Point(4, -2))      // { q: 5, r: -2, s: -3 }
Hex().cartesianToCube(4, -2)             // { q: 5, r: -2, s: -3 }
Hex().cartesianToCube({ x: 4, y: -2 })   // { q: 5, r: -2, s: -3 }
Hex().cartesianToCube([4, -2])           // { q: 5, r: -2, s: -3 }

Returns Object The hex's cube q, r and s coordinates.

center

Examples
const Hex1 = Honeycomb.extendHex({ size: 10 })
Hex1().center()  // { x: 8.660254037844386, y: 10 }

const Hex2 = Honeycomb.extendHex({ size: 10, origin: [5, 5] })
Hex2().center()  // { x: 3.6602540378443855, y: 5 }

Returns point Point relative to the hex's origin. Note that the default origin is the top left corner, so the default center is { x: hexWidth / 2, y: hexHeight / 2 }.

coordinates

Examples
const Hex = Honeycomb.extendHex()

Hex().coordinates()      // { x: 0, y: 0 }
Hex(1, 2).coordinates()  // { x: 1, y: 2 }

Returns Object The hex's cartesian x and y coordinates.

corners

Examples
// a hex's origin defaults to its top left corner (as if it's a rectangle)
const Hex1 = Honeycomb.extendHex({ size: 30 })
Hex1().corners() // [
                 //    { x: 51.96152422706631, y: 15 },
                 //    { x: 51.96152422706631, y: 45 },
                 //    { x: 25.980762113533157, y: 60 },
                 //    { x: 0, y: 45 },
                 //    { x: 0, y: 15 },
                 //    { x: 25.980762113533157, y: 0 }
                 // ]

// set the origin to a hex's center
const Hex2 = Honeycomb.extendHex({ size: 30, origin: [25.980762113533157, 30] })
Hex2().corners() // [
                 //    { x: 25.980762113533157, y: -15 },
                 //    { x: 25.980762113533157, y: 15 },
                 //    { x: 0, y: 30 },
                 //    { x: -25.980762113533157, y: 15 },
                 //    { x: -25.980762113533157, y: -15 },
                 //    { x: 0, y: -30 }
                 // ]

Returns Array<point> Array of corner points relative to the hex's origin. Starting at the top right corner for pointy hexes and the right corner for flat hexes.

cube

Examples
const Hex = Honeycomb.extendHex()

Hex().cube()     // { q: 0, r: 0, s: 0 }
Hex(1, 2).cube() // { q: 0, r: 2, s: -2 }

Returns Object The hex's cube q, r and s coordinates.

cubeToCartesian

Parameters
  • cubeCoordinates Object At least the q and r cube coordinates.
    • cubeCoordinates.q number The q cube coordinate.
    • cubeCoordinates.r number The r cube coordinate.
    • cubeCoordinates.s number? The optional s cube coordinate.
Examples
const Hex = Honeycomb.extendHex()

Hex().cubeToCartesian({ q: 1, r: 2, s: -3 }) // { x: 2, y: 2 }
// the `s` coordinate isn't required:
Hex().cubeToCartesian({ q: -3, r: 5 })       // { x: -1, y: 5 }

Returns Object The hex's cartesian x and y coordinates.

distance

Parameters
  • hex hex The last hex (cannot be a point).
Examples
const Hex = Honeycomb.extendHex()

Hex().distance(Hex(1, 0))        // 1
Hex(-2, -2).distance(Hex(4, 1))  // 8

Returns number The amount of hexes from the current to (and excluding) the last hex.

equals

Parameters
  • point point The hex (or point) whose coordinates will be compared against the current hex.

Returns boolean Whether the coordinates of the current and the passed point are equal.

fromPoint

Returns a hex from the passed point.

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.
Examples
const Hex = Honeycomb.extendHex({ size: 50 })
const Point = Honeycomb.Point
const hex = Hex()

hex.fromPoint(Point(120, 280))     // { x: 0, y: 3 }
hex.fromPoint(120, 280)            // { x: 0, y: 3 }
hex.fromPoint({ x: 120, y: 280 })  // { x: 0, y: 3 }
hex.fromPoint([ 120, 280 ])        // { x: 0, y: 3 }

Returns hex A hex (with rounded coordinates) that contains the passed point.

height

Returns number The (vertical) height of a hex.

isFlat

Returns boolean Whether hexes have a flat ⬣ orientation.

isPointy

Returns boolean Whether hexes have a pointy ⬢ orientation.

lerp

Returns an interpolation between the current hex and the passed hex for a t between 0 and 1. More info on wikipedia.

Parameters
  • hex hex The other hex (cannot be a point).
  • t number A "parameter" between 0 and 1.

Returns hex A new hex (likely with floating point coordinates). Any custom properties are copied.

nudge

Returns hex A new hex with a tiny offset from the current hex. Useful for interpolating in a consistent direction.

offset

  • See: OFFSET

Used to calculate the coordinates of rows for pointy hexes and columns for flat hexes. Defaults to -1 (odd offset). See OFFSET for details. See redblobgames.com why this is needed.

Type: number

oppositeCornerDistance

Returns number The distance between opposite corners of a hex.

oppositeSideDistance

Returns number The distance between opposite sides of a hex.

orientation

Either ⬢ pointy or ⬣ flat. Defaults to pointy.

Type: string

origin

Distance from a hex's top left corner (as if it were a rectange). Defaults to Point(0). Can be anything the Honeycomb.Point factory accepts. When a hex is converted to a point, it is converted to this origin.

Type: point

q

Getter for q cube coordinate. Calls Hex#cartesianToCube internally.

Type: number

r

Getter for r cube coordinate. Calls Hex#cartesianToCube internally.

Type: number

round

Rounds the current floating point hex coordinates to their nearest integer hex coordinates.

Examples
const Hex = Honeycomb.extendHex()
Hex(3.1415, 0.5).round() // { x: 3, y: 1 }

Returns hex A new hex with rounded coordinates. Any custom properties are copied.

s

Getter for s cube coordinate. Calls Hex#cartesianToCube internally.

Type: number

set

Parameters
  • coordinates any Same parameters as the Hex factory.
Examples
const Hex = Honeycomb.extendHex()

const hex = Hex({ x: 1, y: 2, a: 3, b: 4 })          // { a: 3, b: 4, x: 1, y: 2 }
const updatedHex = hex.set({ x: 0, y: -1, b: 5 })    // { a: 3, b: 5, x: 0, y: -1 }
hex === updatedHex                                   // true: hex is updated in-place

Returns hex Itself with the passed parameters merged into it.

size

A hex's radius or the length of any of its sides. Defaults to 1.

Type: number

subtract

Parameters
  • point point The hex (or point) that will be subtracted from the current.

Returns hex A new hex where the passed hex's coordinates are subtracted from the current. Any custom properties are copied.

toCartesian

Alias for Hex#cubeToCartesian.

toCube

Alias for Hex#cartesianToCube.

toPoint

Examples
const Hex = Honeycomb.extendHex({ size: 30 })
Hex().toPoint()          // { x: 0, y: 0 }
Hex(-2, -5).toPoint()    // { x: -77.94228634059947, y: -225 }

Returns point The hex's origin point.

toString

Returns string A string representation of the hex.

width

Returns number The (horizontal) width of a hex.

thirdCoordinate

Calculates the third cube coordinate from the other two. The sum of all three coordinates must be 0.

Parameters
  • firstCoordinate number The first other cube coordinate.
  • secondCoordinate number The second other cube coordinate.
Examples
const Hex = Honeycomb.extendHex()
Hex.thirdCoordinate(3, -2)   // -1

Returns number The third cube coordinate.

Point

Factory function for creating two-dimensional points.

Parameters

  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.

Examples

const Point = Honeycomb.Point

Point()                  // { x: 0, y: 0 }
Point(1)                 // { x: 1, y: 1 }
Point(1, 2)              // { x: 1, y: 2 }

Point([])                // { x: 0, y: 0 }
Point([1])               // { x: 1, y: 1 }
Point([1, 2])            // { x: 1, y: 2 }

Point({})                // { x: 0, y: 0 }
Point({ x: 1 })          // { x: 1, y: 1 }
Point({ y: 2 })          // { x: 2, y: 2 }
Point({ x: 1, y: 2 })    // { x: 1, y: 2 }

Returns point A point.

add

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.

Returns point The sum of the passed point's coordinates to the current point's.

divide

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.

Returns point The division of the current point's coordinates and the passed point's.

multiply

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.

Returns point The multiplication of the passed point's coordinates and the current point's.

subtract

Parameters
  • pointOrX (number | Array<number> | point)? The x coordinate or an array with 2 numbers or an object with an x and y coordinate.
    • pointOrX.x number? The x coordinate.
    • pointOrX.y number? The y coordinate.
  • y number? The y coordinate.

Returns point The difference between the passed point's coordinates and the current point's.

Instances

grid

Extends Array

Type: Object

Properties
  • length number Amount of hexes in the grid.

hex

An object with x and y properties and several methods in its prototype chain, created by a Hex factory.

Type: Object

Properties
  • x number Cartesian x coordinate.
  • y number Cartesian y coordinate.

point

An object with just an x and a y property.

Create your own:

const point = { x: 1, y: 2 }

Or use the included Point factory:

const point = Honeycomb.Point(1, 2)

Type: Object

Properties
  • x number (horizontal) x coordinate
  • y number (vertical) y coordinate

Constants

OFFSET

How rows/columns of hexes are placed relative to each other.

An even offset:

  • places even rows of pointy hexes half a hex right of the odd rows;
  • places even columns of flat hexes half a hex down of the odd rows;

An odd offset:

  • places odd rows of pointy hexes half a hex right of the even rows;
  • places odd columns of flat hexes half a hex down of the even rows;
Properties

COMPASS_DIRECTION

There's an (approximate) compass direction for each side of a hex. The right side of a pointy hex has the east ('E') compass direction. The bottom right side the southeast ('SE') direction, etc. This also means that pointy hexes don't have a north and south compass direction and flat hexes don't have a west and east compass direction.

Number directions map to a side of a hex. A pointy hex's right side is 0, its bottom right side 1, its bottom left side 2, etc. Number directions of flat hexes start at their bottom right side (0), their bottom side is 1, etc.

Type: string

Properties

Other

onCreate

Callback of a Grid shape method. Gets called for each hex that's about to be added to the grid.

Type: Function

Parameters
  • hex hex The freshly created hex, just before it's added to the grid.
  • grid grid The grid (for as far as it's created).

Returns void Nothing.

Backlog

🐛 Bugs

🚀 Features

  1. Add src to package and point module to it? Also point main to unminified version by default? Also: add browser property to package.json? See jimp for an example of a library that's used on the server and the browser.
  2. Hex methods that do nothing with a hex's coordinates should be static (e.g. cubeToCartesian, isPointy, width)?
  3. Hex.corners should return points relative to Hex.origin (like Hex#corners does now) and Hex#corners should return points relative to the hex (so it's not needed to add the hex's point to them).
  4. Make some Grid instance methods also Grid static methods and vice versa?
  5. Instead of filtering invalid hexes, attempt to convert values to hexes (by passing them to Hex())?
  6. Make some methods getters (e.g. Hex#width)?
  7. Maybe make entities immutable?
  8. Add possibility to stretch hexes; they needn't be regularly shaped. This is an actual request as well. Maybe this should be solved during rendering (and not in Honeycomb at all)?
  9. Add logger that "renders" a grid using console.log.
  10. Overwrite Grid#sort so it can sort by 1 or more dimensions, ascending/descending (and also accepts a custom comparator)?
  11. Add Grid.union, Grid.subtract, Grid.intersect and Grid.difference (or maybe as prototype methods?). More info.
  12. Shiny github.io pages 😎
  13. Maybe Honeycomb.defineGrid should accept a prototype too (as a second parameter).
  14. Maybe Honeycomb should (also) be a function that accepts a hex prototype and returns a Grid factory?
  15. Investigate how instance properties are set vs prototype properties. When creating a custom hex it should be possible to set properties that are copied when creating new hexes and properties that only exist in the prototype. Similar to how stampit solves this.

🛠 Refactorings

  1. Rewrite in typescript and use classes internally?
  2. Don't use this at all and just inject a context. Functional programming yo 🤓.