Skip to content

Hex grid for usage in Node.js with separate view for usage in the browser. Written in javascript.

Notifications You must be signed in to change notification settings

timelyportfolio/honeycomb

 
 

Repository files navigation

Honeycomb

Another hex grid library made in JavaScript, heavily inspired by Red Blob Games' 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.

Installation

npm i honeycomb-grid

Documentation

Origins

Hexes and views have an origin property that define the entity's point:

A hex's origin is its center by default (with a value if Point(0, 0)). If a hex's origin is set to Point(5, 10) it means its origin is 5 units right and 10 units down of the hex's center. When a hex is converted to a point, its origin is returned (relative to whatever it's converted from).

A view's origin is its container's top left corner by default (with a value of Point(0, 0)). This corresponds to how HTML elements are positioned in the DOM. If you want the center of the container as the view's origin, set it to Point(containerWidth / 2, containerHeight / 2).

Converting hexes to points and points to hexes

Mapping points to hexes and vice versa can be confusing (I thought it was confusing making this library 😕). This table might clear things up:

Method Input Output
Hex#toPoint N/A the hex's origin point relative to the start hex (Hex(0, 0, 0))
Grid#hexToPoint a hex same as Hex#toPoint
Grid#pointToHex a point the hex that contains the point
View#hexToPixel a hex the hex's top left point relative to its origin, relative to the view's origin
View#pixelToHex a point the hex that contains the point relative to the view's origin

API

Grid

Factory function for creating grids. It accepts optional hex settings that apply to all hexes in the grid. Several "shape" methods are exposed that return an array of hexes in a certain shape.

A grid is viewless, i.e.: it's a virtual grid with undefined dimensions. If you want to render a tangible grid, use View.

Parameters

  • hexSettings Object? Optional settings that apply to all hexes in the grid.
    • hexSettings.size number Size of all hexes. (optional, default 1)
    • hexSettings.orientation (FLAT | POINTY) All hexes are either POINTY ⬢ or FLAT ⬣. (optional, default POINTY)
    • hexSettings.origin Point Used to convert a hex to a point. Defaults to the hex's center at Point(0, 0). (optional, default Point(0,0))

Examples

import { Grid, HEX_ORIENTATIONS } from 'Honeycomb'

const grid = Grid({
    size: 50,
    orientation: HEX_ORIENTATIONS.FLAT,
    customProperty: `I'm custom 😃`
})

const singleHex = grid.Hex(5, -1, -4)
singleHex.coordinates()      // { x: 5, y: -1, z: -4 }
singleHex.size               // 50
singleHex.customProperty     // I'm custom 😃

grid.triangle(3)             // [ { x: 0, y: 0, z: 0 },
                             //   { x: 0, y: 1, z: -1 },
                             //   { x: 0, y: 2, z: -2 },
                             //   { x: 1, y: 0, z: -1 },
                             //   { x: 1, y: 1, z: -2 },
                             //   { x: 2, y: 0, z: -2 } ]

Returns Grid A grid instance containing a Hex factory and several methods. Use the Hex factory for creating individual hexes or using any of the Hex's methods.

Grid#colSize

Returns number The width of a (vertical) column of hexes in the grid.

Grid#hexagon

Creates a grid in the shape of a hexagon.

Parameters

  • options Object An options object.
    • options.radius number The radius (in hexes).
    • options.center Hex The center hex. (optional, default Hex(0,0,0))

Returns Array<Hex> Array of hexes in a hexagon arrangement (very meta 😎).

Grid#hexToPoint

Translates a hex to a point.

Parameters

  • hex Hex The hex to translate from.

Examples

import { Grid } from 'Honeycomb'

const grid = Grid({ size: 50 })
const hex = grid.Hex(-1, 4, -3)
grid.hexToPoint(hex) // { x: 86.60254037844386, y: 300 }

// a different origin...
const grid = Grid({ size: 50, origin: [50, 50] })
const hex = grid.Hex(-1, 4, -3)
// ...corresponds to a different point:
grid.hexToPoint(hex) // { x: 36.60254037844386, y: 250 }

Returns Point The point to translate to.

Grid#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,0,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)

Returns Array<Hex> Array of hexes in a parallelogram arrangement.

Grid#pointToHex

Converts the passed 2-dimensional point to a hex.

Parameters

Examples

import { Grid, Point } from 'Honeycomb'

const grid = Grid({ size: 50 })

grid.pointToHex(Point(120, 300))     // { x: -1, y: 4, z: -3 }
// also accepts a point-like:
grid.pointToHex({ x: 120, y: 300 })  // { x: -1, y: 4, z: -3 }
grid.pointToHex([ 120, 300 ])        // { x: -1, y: 4, z: -3 }

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

Grid#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,0,0))
    • options.direction (0 | 1 | 2 | 3 | 4 | 5) The direction (from the start hex) in which to create the shape. Each direction corresponds to a different arrangement of hexes. (optional, default 0)

Returns Array<Hex> Array of hexes in a rectengular arrangement.

Grid#rowSize

Returns number The height of a (horizontal) row of hexes in the grid.

Grid#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,0,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). (optional, default 1)

Returns Array<Hex> Array of hexes in a triangular arrangement.

Hex

Factory function for creating hexes. It can only be accessed by creating a Grid (see the example).

Coordinates not passed to the factory are inferred using the other coordinates:

Parameters

  • coordinates (number | Object) The x coordinate or an object containing any of the x, y and z coordinates. (optional, default 0)
    • coordinates.x number The x coordinate. (optional, default 0)
    • coordinates.y number The y coordinate. (optional, default 0)
    • coordinates.z number The z coordinate. (optional, default 0)
  • y number The y coordinate. (optional, default 0)
  • z number The z coordinate. (optional, default 0)

Examples

import { Grid } from 'Honeycomb'
// `Hex()` is not exposed on `Honeycomb`, but on a grid instance instead:
const Hex = Grid().Hex

Hex()            // returns hex( x: 0, y: 0, z: 0 )
Hex(1)           // returns hex( x: 1, y: 1, z: -2 )
Hex(1, 2)        // returns hex( x: 1, y: 2, z: -3 )
Hex(1, 2, -3)    // returns hex( x: 1, y: 2, z: -3 )
Hex(1, 2, 5)     // coordinates don't sum up to 0; throws an error

Hex({ x: 3 })    // returns hex( x: 3, y: 3, z: -3 )
Hex({ y: 3 })    // returns hex( x: 3, y: 3, z: -6 )
Hex({ z: 3 })    // returns hex( x: 3, y: -6, z: 3 )

Returns Hex A hex object. It has all three coordinates (x, y and z) as its own properties and various methods in its prototype.

Hex.add

Parameters

  • firstHex Hex A hex.
  • secondHex Hex The hex that will be added to the first.

Returns Hex The sum of the passed hexes coordinates.

Hex.distance

Returns the amount of hexes between the current and the given hex.

Parameters

  • startHex Hex The start hex.
  • endHex Hex The end hex.

Examples

import { Grid } from 'Honeycomb'
const Hex = Grid().Hex

Hex.distance(Hex(0, 0, 0), Hex(1, 0, -1))    // 1
Hex.distance(Hex(-3, -3, 6), Hex(-1, 4, -3)) // 9

Returns number The amount of hexes between the passed startHex and endHex.

Hex.hexesBetween

Parameters

  • startHex Hex The first hex.
  • endHex Hex The second hex.

Returns Array<Hex> Array of hexes starting at the passed startHex and ending with the passed endHex.

Hex.lerp

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

Parameters

  • firstHex Hex The first hex.
  • secondHex Hex The second hex.
  • t number A "parameter" between 0 and 1.

Returns Hex A new hex (with possibly fractional coordinates).

Hex.neighbor

Returns the neighboring hex in the given direction.

Parameters

  • hex Hex The hex to get the neighboring hex from.
  • direction (0 | 1 | 2 | 3 | 4 | 5) Any of the 6 directions. 0 is the Eastern direction (East-southeast when the hex is flat), 1 corresponds to 60° clockwise, 2 to 120° clockwise and so forth. (optional, default 0)
  • diagonal boolean Whether to look for a neighbor opposite the hex's corner instead of its side. A direction of 0 means the top corner of the hex's right side when the hex is pointy and the right corner when the hex is flat. (optional, default false)

Examples

import { Grid } from 'Honeycomb'
const Hex = Grid().Hex

const targetHex = Hex()
Hex.neighbor(targetHex)          // { x: 1, y: -1, z: 0 }, the hex across the 0th (right) side of targetHex
Hex.neighbor(targetHex, 2)       // { x: 0, y: 1, z: -1 }, the hex across the 3rd (South West) side of targetHex
Hex.neighbor(targetHex, 3, true) // { x: -2, y: 1, z: 1 }, the hex opposite the 4th corner of targetHex

Returns Hex The neighboring hex.

Hex.neighbors

Returns all neighboring hexes of the given hex.

Parameters

  • hex Hex The hex to get all neighbors from.

Returns Array<Hex> An array of the 6 neighboring hexes.

Hex.nudge

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

Parameters

  • hex Hex The hex to nudge.

Returns Hex A new hex with a minute offset.

Hex.round

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

Parameters

  • hex Hex The hex to be rounded.

Returns Hex A new hex with rounded coordinates.

Hex.subtract

Parameters

  • firstHex Hex A hex.
  • secondHex Hex The hex that will be subtracted from the first.

Returns Hex The difference between the passed hexes coordinates.

Hex.thirdCoordinate

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

Parameters

  • firstCoordinate number The first other coordinate.
  • secondCoordinate number The second other coordinate.

Returns number The third coordinate.

Hex#coordinates

Returns Object The hex's x, y and z coordinates.

Hex#corners

Returns Array<Point> Array of corner points. Starting at the top right corner for pointy hexes and the right corner for flat hexes.

Hex#height

Returns number The (vertical) height of any hex.

Hex#isFlat

Returns boolean Whether hexes have a flat ⬣ orientation.

Hex#isPointy

Returns boolean Whether hexes have a pointy ⬢ orientation.

Hex#oppositeCornerDistance

Returns number The distance between opposite corners of a hex.

Hex#oppositeSideDistance

Returns number The distance between opposite sides of a hex.

Hex#topLeft

A hex's origin is relative to its center point, but a browser positions a DOM node relative to its top left point. This method can be used to translate the center-originating hex to a top-left-originating hex.

Returns Point The vector relative to the hex's center.

Hex#toPoint

Converts the current hex to its origin point relative to the start hex.

Returns Point The 2D point the hex corresponds to.

Hex#width

Returns number The (horizontal) width of any hex.

ORIENTATIONS

The different orientations hexes can have.

POINTY

FLAT

Point

Factory function for creating 2-dimensional points. Accepts a point-like and returns a point instance. A point-like can be an object with an x and y property (e.g. { x: 0, y: 0 }) or an array with 2 items (e.g. [0, 0]) that correspond to x and y respectively.

Parameters

  • coordinatesOrX (number | Array<number> | Object) The x coordinate or a point-like. (optional, default 0)
    • coordinatesOrX.x number The x coordinate. (optional, default 0)
    • coordinatesOrX.y number The y coordinate. (optional, default 0)
  • y number The y coordinate. (optional, default 0)

Examples

import { Point } from 'Honeycomb'

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

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

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

Returns Point A point object.

Point#add

Parameters

  • point Point The point to add to the current point.

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

Point#divide

Parameters

  • point Point The point where the current point is divided by.

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

Point#multiply

Parameters

  • point Point The point to multiply with the current point.

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

Point#subtract

Parameters

  • point Point The point to subtract from the current point.

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

View

Factory function for creating views. A view instance can be used to render (a grid of) hexes. This function expects raw DOM elements, i.e. it lacks helpers to convert strings to DOM elements. You can use other libraries (e.g. jQuery, svg.js) to help create these DOM elements.

Parameters

  • options Object Options to instantiate the view with. (optional, default {})
    • options.grid Grid A grid instance.
    • options.template Function Template function that should return a DOM element. When hexes are rendered (e.g. by calling View#renderGrid or View#renderHexes), the template function is called with each hex. In the template function this refers to the view instance, so any view method can be called.
    • options.container Node The container in which hexes are to be rendered. Should be an existing DOM element (e.g. a <div> or <svg>) in the document.
    • options.origin Point Pixel origin where the start hex (Hex(0, 0, 0)) is placed. Defaults to Point(0, 0), i.e.: the top left corner of the container. The origin is relative to the container (not to the document). (optional, default Point(0,0))

Examples

const { Grid, View } from 'Honeycomb'

const container = document.getElementById('my-container')
const rect = container.getBoundingClientRect()

const view = View({
    grid: Grid({ size: 20 }),
    template: hex => {
        // `this` refers to the view instance
        const position = this.hexToPixel(hex)
        const div = document.createElement('div')

        div.classList.add('hex')
        div.style.left = position.x + 'px'
        div.style.top = position.y + 'px'

        return div
    },
    container,
    // set the view's origin to the container's center
    origin: {
        x: rect.width / 2,
        y: rect.height / 2
    }
})

// The view instance can now be used to render hexes as divs with dimensions that
// would fit pointy hexes with a radius of 20px. Each div having the class 'hex'
// and a `style` attribute with a `left` and `top` property. The start hex
// (`Hex(0, 0, 0)`) would be placed in the center of the container.

Returns View A view instance to render hexes in.

View#height

Returns number The rounded amount of hexes that fit in the container vertically ↕.

View#hexToPixel

Converts the passed hex to a pixel position relative to the container's origin. The hex's top left corner is used (instead of its origin) to render the hex as a DOM node. DOM nodes have their top left corner defined as their origin.

Parameters

  • hex Hex The hex to convert to a pixel position.

Returns Point The pixel position.

View#pixelToHex

Converts the passed pixel position (relative to the container) to the corresponding hex.

Parameters

  • pixel Point The pixel position to convert to a hex.

Returns Hex The corresponding (rounded) hex.

View#renderGrid

Renders all hexes that fit in the container, plus the optional padding. This padding defaults to 3 (hexes), so that the container is completely covered in hexes.

Parameters

  • padding number Number of hexes to add to all 4 sides of the container. Can be used to guarantee the container is completely covered in hexes. (optional, default 3)

Examples

const { View } from 'Honeycomb'
const view = View() // view creation truncated for brevity

// This renders all hexes that fit in the container, plus an extra layer of 3 hexes:
view.renderGrid()

Returns View The view instance, for chaining.

View#renderHexes

Renders the passed array of hexes in the container.

Parameters

Examples

const { Grid, View } from 'Honeycomb'

const grid = Grid({ size: 20 })
const view = View() // view creation truncated for brevity

// This renders hexes in the shape of a hexagon with a radius of 3 hexes
// and the start hex (`Hex(0, 0, 0)`) as its center:
view.renderHexes(grid.hexagon(3))

Returns View The view instance, for chaining.

View#width

Returns number The rounded amount of hexes that fit in the container horizontally ↔.

Backlog

Bugs

  1. Get babel-polyfill (or babel-runtime?) to work with babel-preset-env, preferably without including a bazillion unused polyfills in the dist...
  2. Docs: find a way to link modules together. Currently, methods of the factory functions doesn't seem to belong to their factory functions (in the context of jsdoc). This bug is nasty, tried lots of things already...

Features

  1. Expose Hex on Honeycomb, adding the possibility to create (individual) hexes and use Hex's static methods without having to create a grid first?
  2. Add possibility to stretch hexes; they needn't be regularly shaped. This is an actual request as well.
  3. Make it possible to filter overlapping hexes when multiple shapes are rendered.
  4. Use JSFiddle for better examples.
  5. Explain (hex) directions.
  6. Shiny github.io pages 😎
  7. View should be hex-orientation-agnostic (always pointy) and just use transform to toggle orientations.
  8. Maybe add instance methods for Grid and Views.DOM to get/set options. Then it's optional to pass the options to the Grid and Views.DOM factories and makes it possible to get/set those options later.
  9. Add helper to easily fall back to a hex's prototype?

Refactorings

  1. Don't transpile to ES5. Who needs IE anyway?
  2. Replace Webpack by Rollup, because it's supposed to be more suitable for libraries.
  3. Update code (and tests) of Point to be more consice with other modules.
  4. Grid shape methods should return Sets instead of arrays?

About

Hex grid for usage in Node.js with separate view for usage in the browser. Written in javascript.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%