NAME

Math::Geometry::HexGrid - Fast, XS-accelerated hexagonal grid mathematics

SYNOPSIS

use Math::Geometry::HexGrid qw(:all);

# 1. High-Performance Functional Interface (Pure C Speed)
# Convert odd-r offset coordinates to 3D cube coordinates:
my ($q, $r, $s) = oddr_to_cube(15, 12); # (9, 12, -21)
my ($col, $row) = cube_to_oddr($q, $r, $s);

# Calculate exact Manhattan hex distance:
my $dist = hex_distance(0, 0, 15, 12);

# Check if two coordinates lie along a common cardinal hex axis:
if (hex_is_collinear($c1, $r1, $c2, $r2)) {
    my @ray = hex_collinear_ray($c1, $r1, $c2, $r2);
    for my $step (@ray) {
        say "Step: $step->[0], $step->[1]";
    }
}

# Fetch 6 immediate neighbors (respects odd-r row parity):
my @neighbors = hex_neighbors(4, 4);

# 2. Object-Oriented Interface (Ergonomic Value Objects)
my $h1 = Math::Geometry::HexGrid->hex(4, 4);
my $h2 = Math::Geometry::HexGrid->hex(10, 4);

say $h1->col;              # 4
say $h1->row;              # 4
say $h1->distance_to($h2); # 6
say "$h1";                 # Overloaded stringification: "4,4"

if ($h1 == $h2) {
    # Overloaded equality in C
}

my @nbs = $h1->neighbors; # Returns Math::Geometry::HexGrid::Hex objects

DESCRIPTION

Math::Geometry::HexGrid provides high-performance, exact hexagonal grid mathematics implemented in ANSI C with Perl XS bindings.

Working with hexagonal grids in standard Cartesian ($col, $row) offset coordinates is notoriously error-prone because movement vectors change depending on row or column parity. By mapping offset coordinates to symmetric 3-axis Cube Coordinates ($q, $r, s) where $q + $r + $s == 0, all hexagonal operations (distance, direction, adjacency, and line-drawing) become simple, branch-free linear algebra.

All mathematical operations use pure integer arithmetic, completely avoiding floating-point roundoff errors and precision drift.

MATHEMATICAL FOUNDATIONS & ALGORITHMS

This module implements pointy-topped hexagonal grids with odd-r offset layout (where odd-numbered rows are shifted to the right by half a hex width).

1. Coordinate Systems

  • Odd-R Offset Coordinates ($col, $row):

    Standard 2D array coordinates convenient for map storage and display.

  • Cube Coordinates ($q, $r, $s):

    A 3D coordinate system where each axis corresponds to one of the three primary hexagonal diagonal directions, constrained to the diagonal plane:

    q + r + s = 0

2. Conversion Formulas

Conversion between odd-r offset and cube coordinates is an exact, reversible integer bijection:

Offset to Cube

q = col - floor((row - (row & 1)) / 2)
r = row
s = -q - r

Because row - (row & 1) is always an even integer, dividing by 2 yields an exact integer without fractional truncation.

Cube to Offset

col = q + floor((r - (r & 1)) / 2)
row = r

3. Hexagonal Distance

The shortest path distance between two hexes in a regular grid is defined by the Manhattan metric in cube coordinates:

distance = max(|q1 - q2|, |r1 - r2|, |s1 - s2|)

Equivalently:

distance = (|q1 - q2| + |r1 - r2| + |s1 - s2|) / 2

4. Adjacency & Direction Offsets

Every hexagon has exactly 6 neighbors. In cube coordinates, neighbor directions are unit permutations of (+1, -1, 0):

Direction 0 (East):      (+1,  0, -1)
Direction 1 (Northeast): (+1, -1,  0)  [or (+1, -1) in odd rows]
Direction 2 (Northwest): ( 0, -1, +1)
Direction 3 (West):      (-1,  0, +1)
Direction 4 (Southwest): (-1, +1,  0)
Direction 5 (Southeast): ( 0, +1, -1)

hex_neighbors maps these offsets directly to odd-r coordinates taking row parity into account.

5. Collinear Line-Drawing & Raycasting

Two hexes are collinear if and only if they lie along a common straight line parallel to one of the 3 cube axes:

q1 == q2  OR  r1 == r2  OR  s1 == s2

hex_collinear_ray traces intermediate hexes by interpolating along the shared axis, rounding each step to integer coordinates.

FUNCTIONAL API

The following subroutines are available for export. Use the tag :all to import all of them:

use Math::Geometry::HexGrid qw(:all);

oddr_to_cube($col, $row)

my ($q, $r, $s) = oddr_to_cube($col, $row);

Converts 2D odd-r offset coordinates ($col, $row) to 3D cube coordinates ($q, $r, $s). Returns a 3-element list of integers.

cube_to_oddr($q, $r, $s)

my ($col, $row) = cube_to_oddr($q, $r, $s);

Converts 3D cube coordinates ($q, $r, $s) back to 2D odd-r offset coordinates. Returns a 2-element list of integers ($col, $row).

hex_distance($col1, $row1, $col2, $row2)

my $dist = hex_distance($col1, $row1, $col2, $row2);

Calculates the exact integer Manhattan hex distance between two hexes.

hex_is_collinear($col1, $row1, $col2, $row2)

if (hex_is_collinear($c1, $r1, $c2, $r2)) { ... }

Returns a boolean indicating whether the two hex coordinates share one of the 6 cardinal hexagonal axes. Returns false if the coordinates are identical.

hex_neighbors($col, $row)

my @nbs = hex_neighbors($col, $row);

Returns a 6-element list of array references ([$c, $r], ...) representing the immediate neighboring coordinates in clockwise order starting from East (direction 0).

hex_collinear_ray($col1, $row1, $col2, $row2, [$max_length = 100])

my @ray = hex_collinear_ray($col1, $row1, $col2, $row2, $max_len);

Traces a straight line from ($col1, $row1) toward ($col2, $row2). Returns an empty list if the two points are not collinear. Otherwise, returns a list of array references ([$c, $r], ...) beginning with the first step past the origin, up to $max_length steps or reaching the destination.

OBJECT-ORIENTED API

For higher-level application logic, Math::Geometry::HexGrid provides a lightweight value object class Math::Geometry::HexGrid::Hex.

All OO methods are implemented in native C/XS with zero Perl method-call overhead.

Math::Geometry::HexGrid->hex($col, $row)

Math::Geometry::HexGrid::Hex->new($col, $row)

my $hex = Math::Geometry::HexGrid->hex($col, $row);

Constructs and returns a new hex coordinate value object.

$hex->col

Returns the integer column coordinate.

$hex->row

Returns the integer row coordinate.

$hex->to_cube

my ($q, $r, $s) = $hex->to_cube;

Returns the 3D cube coordinates ($q, $r, $s) as a list.

$hex->distance_to($other)

my $d = $hex->distance_to($other_hex);

Returns the integer hex distance to another Math::Geometry::HexGrid::Hex object.

$hex->is_collinear_with($other)

if ($hex->is_collinear_with($other)) { ... }

Returns true if both hex objects are collinear.

$hex->neighbors

my @neighbors = $hex->neighbors;

Returns a list of 6 new Math::Geometry::HexGrid::Hex objects representing the adjacent neighbors.

$hex->ray_to($other, [$max_length = 100])

my @ray = $hex->ray_to($other, 5);

Returns a list of Math::Geometry::HexGrid::Hex objects representing the collinear ray toward $other.

$hex->equals($other)

Returns true if both coordinates have identical column and row values.

Overloaded Operators

The Math::Geometry::HexGrid::Hex class overloads the following operators:

  • Stringification (""): Formats as "col,row" (e.g. "4,7"), making hex objects natural hash keys.

  • Equality (==): Checks exact integer equality in native C.

  • Inequality (!=): Inverted equality check.

SEE ALSO

AUTHOR

Steffen Mueller <cpan@steffen-mueller.net>

COPYRIGHT AND LICENSE

Copyright (C) 2026 Steffen Mueller

This software is released under the MIT License. See the LICENSE file included with this distribution for full license details.