Hector is a configurable vector library for working with hexagonal grids written in Lua.
hector = require 'hector'
-- Initialize the library
-- hector.init() -- uses all default values
hector.init{x_axis = P_AXIS, y_axis = -Q_AXIS, major = 64}
-- Create and manipulate vectors
u = vector(1, 2)
v = vector(3, 4)
w = u + v
w = w:rotate(1, vector(1, 1))
The hector.init
method takes a single keyed table as input. Any key not
provided will be initialized to a default value as in the table below:
keyword | meaning | allowed values | default |
---|---|---|---|
style | top of hex is flat | FLAT | FLAT |
or pointed. | POINTY | ||
x_axis | direction to be considered the | [-]P_AXIS | P_AXIS |
hex grid’s ‘x’ direction. | [-]Q_AXIS | ||
[-]R_AXIS | |||
y_axis | direction to be considered the | [-]P_AXIS | -R_AXIS |
hex grid’s ‘y’ direction. | [-]Q_AXIS | ||
[-]R_AXIS | |||
major | length of hex major axis | number | 32 |
(corner to opposite corner) | (pixels) | ||
minor | length of hex minor axis | number | calculated based on |
(side to opposite side) | (pixels) | value of major | |
point_height | height of point along the major | number | calculated based on |
axis | (pixels) | value of major |
The chosen style FLAT
or POINTY
affect how the constants P_AXIS
,
Q_AXIS
and R_AXIS
are interpreted as in the diagram below. The
axis constants can be negated (-P_AXIS
etc.) so any of the six
directions can be used for either axis. Each hex in the grid will then
have a unique representation in terms of the chosen x_axis
and y_axis
.
Figure 2: Different coordinates for POINTY hex grids resulting from {x_axis=P_AXIS, y_axis=Q_AXIS}
on the left and {x_axis=P_AXIS, y_axis=-Q_AXIS}
on the right.
The major
, minor
and point_height
options allow you to specify
the dimensions of the hexagons. If only major
is specified the other
values will be initialized such that the grid consists of regular
hexagons.
Importantly, major
, minor
and point_height
only affect the
on-screen proportions and location of the hexagons. hector
treats
all hex grids as regular hex grids until converted to screen
coordinates. That means rotations are always in multiples of 60
degrees even though the angle on screen may more or less than 60
degrees. Similarly the hex_cross
and hex_dot
methods (see below)
always return values based on multiples of 60 degrees.
The vector
function takes either 2 or 3 arguments:
u = vector(1, 2)
v = vector(1, 2, 3)
Typically you’ll only use 2 arguments (
Vectors support the usual vector operations via Lua metamethods:
u == v
- comparing two vectors for equality
-v
- negation/reversing the direction of a vector
u + v, u - v
- vector addition and subtraction
s * v
- scalar multiplication of a vector. The scalar must be the first operand.
v / s
- scalar division of a vector. The scalar must be the second operand.
u..v
- dot product. This is the usual dot product defined for
euclidean 3D vectors and should not be used directly with vectors
representing hex coordinates or directions on the hex grid. Use the
vector:hex_dot()
method instead. u^v
- a “cross-like” product. This is not a 3D cross
product—though it is related to it—but returns a scalar value
similar to the dot product. For hex coordinates use the
vector:hex_cross()
method.
[Implementation Note:] The multiplication operator is actually more
complicated than this. I didn’t want to rely on an external
vector/matrix library. The vectors in hector
are implemented as
multivectors because it makes for a fairly compact implementation
which can do much of what can be accomplished with matrices. Which
basically just means that it’s possible to multiply two vectors u
and v
together. You almost certainly don’t want to do this unless
you know what you’re doing but vector hector
handles rotations (including converting
to/from screen coordinates) and reflections.
Other methods available on vectors:
v:abs(), v:floor()
- These functions both perform the
associated mathematical function to each component of the vector.
vector(-1, 2):abs() -- returns vector(1, 2) vector(1.1, 2.3):floor() -- returns vector(1, 2)
v:hex_cross(u, v)
- The hex specific version of the
^
(__pow
) operator. It operates on ‘standard’ cube coordinates and therefore always behaves as if operating on regular hexagons. It returns the scalar value sin(θ) where θ, the angle betweenu
andv
, will always be a multiple of 60 degrees.
[Implementation Note:] In
hex_cross
andhex_dot
the hex coordinatesu
andv
are converted to a standard coordinates and normalized before performing the product which is why they return$sin(θ)$ and$cos(θ)$ instead of$|u||v|sin(θ)$ and$|u||v|cos(θ)$ respectively.
v:hex_dot(u, v)
- The hex specific version of the
..
(__concat
) operator. It operates on ‘standard’ cube coordinates and therefore always behaves as if operating on regular hexagons. It returns the scalar value cos(θ) where θ, the angle betweenu
andv
, will always be a multiple of 60 degrees.As with
hex_cross
,u
andv
are normalized internally. See the implementation note above. v:hex_len()
- The length of the vector in terms of number of
hexes travelled through. To calculate the distance between two
arbitrary hexes
u
andv
:dist = (v - u):hex_len()
v:magnitude()
- Calculates the magnitude of euclidean vector
v
. If you want the length in terms of number of hexes usev:hex_len()
instead. v:magnitude2()
- The squared magnitude of euclidean vector
v
.
v:neighbours()
- Returns an array of the hex’s 6
neighbouring hexes. (American spelling
vector:neighbors
also works.) v:reflect(w)
- Reflects the hex across a given axis. The vector
w
should be one of the axis constantsP_AXIS
,Q_AXIS
, orR_AXIS
. v:rotate(n, center)
- Rotates a hex vector around
center
byn
“places.” Bothn
andcenter
are optional.n
defaults to 1. Positiven
rotates counter-clockwise, negativen
rotates clockwise. Technicallyn
can be any interger value but rotating by$n = ± 3$ is equivalent to simply negating the vector:v:rotate(3) == v:rotate(-3) == -v
.center
defaults to the origin:vector(0, 0)
v:round()
- Implements the algorithm needed to convert screen coordinates to hex coordinates. You probably won’t ever need to use this yourself.
v:screen_len()
- Calculate the length of the vector in
pixels. The vector
v
is assumed to either represent a hex coordinate or a direction on the hex grid. Ifv
already represents screen coordinates usev:magnitude()
to find its length instead. v:show()
- The
__tostring
method displays the vector as a hex or screen coordinate vector with integerx
andy
components. This method will return a string representation of the full underlying multivector. Possibly useful for debugging. v:taxi_len()
- The “taxi-cab” metric on 3D vectors:
abs(x) + abs(y) + abs(z)
. Used to calculatehex_len
. You probably won’t need to use this directly. v:to_hex()
- Converts
v
, a coordinate in screen space, to a corresponding coordinate on the hex grid. v:to_screen()
- Converts
v
, a coordinate on the hex grid, to the screen position of the center of the hex.