#tile #hexagonal #grid #spiral #hexgrid

hexgridspiral

Hexagonal Cube-Coordinate System with Spiralling Integer Tile Identifiers

6 releases

new 0.2.2 Dec 16, 2024
0.2.1 Dec 16, 2024
0.1.2 Dec 15, 2024

#108 in Math

Download history 492/week @ 2024-12-11

492 downloads per month

MIT/Apache

81KB
1.5K SLoC

HexGridSpiral

  • A Coordinate System for hexagonal maps with support for the usual coordinate-system operations

  • Each tile is identified by a single unique integer

  • Tile indices start at 0 and spiral outwards

  • Ideal for e.g. a Level Selection Screen because the level number maps directly to a hex tile without scattering them all over the place.

  • My main contribution is efficiently finding the ring-index in HexGridSpiral-Space (HGS).

However, most other use-cases benefit greatly from the Cube Coordinates outlined in this amazing redblobgames article by Amit Patel. This repo also implements

  • Conversion to Cube Coordinates for efficient Neighbour finding
  • A Distance between tiles in grid steps
  • Vector Calculus
    You can subtract two tiles to get a vector and apply it to a different tile. You can scale vectors by multiplication.
  • A Euclidean Norm (Xiangguo Li 2013)
  • Conversion to Cartesian Pixel Coordinates
  • Rotation by 60 degrees around the origin
  • Addition, Subtraction of tiles
  • Which Wedge of the world (pizza slice) a tile is in
  • Which Tiles are in Movement Range and Intersecting Movement Ranges.
  • Reflection along or across axes, vertically and horizontally, and diagonally.

Usage

crates.io documentation changelog

Ideally have a look at the code - many features are only implemented on either HGSTile or CCTile, between which you can convert with .into(). HGSTile is related to spirals and rings, CCTile has a solid coordinate system to perform arithmetic in.

Here are some ways to construct tiles:

use hexgridspiral::{CCTile, HGSTile, RingCornerIndex, Ring};
let tile1 = CCTile::unit(&RingCornerIndex::RIGHT);
let tile2 = CCTile::unit(&RingCornerIndex::TOPRIGHT);
assert_eq!(tile1.euclidean_distance_to(&tile1), 0.);

let tile7: CCTile = HGSTile::make(7).into();
let tileo : CCTile = CCTile::origin();
assert_eq!(tile7.euclidean_distance_sq(&tileo), 3);

let corner0_hgs = HGSTile::new(Ring::new(2.into()).corner(RingCornerIndex::BOTTOMLEFT));

Installation

# Cargo.toml:
[dependencies]
hexgridspiral = { version = "0.1", features = ["nightly"] }

The feature nightly gates some code that only works when compiled with the nightly rust toolchain. Omit it if you need to build in stable rust. (Currently the only thing you lose is ability to iterate over MovementRange or some ..= range between two TileIndex.)

For Development

git clone git@github.com:lucidBrot/hexgridspiral.git
cd ../your-dependent-project

and add to Cargo.toml :

[dependencies]
hexgridspiral = {path = "../hexgridspiral"}

Coordinate Systems

CCTile is a pointy-top hex in a system where there are three non-orthogonal axes that always sum up to 1. It is explained very well on redblobgames.com (Original image is from there).

On top of this, we add a spiral system of integers (HGSTile).

Cube Coordinates (CCTile) Hex Grid Spiral (HGSTile)

So for example:

let tile = CCTile::from_qrs(2, -1, -1);
assert_eq!(tile.spiral_index(), TileIndex(7));
let tile2 = tile.spiral_steps(2);
assert_eq!(tile2.spiral_index(), TileIndex(9));
assert_eq!(tile2, CCTile::from_qrs(1, -2, 1));

The spiralling numbers count from zero (at the origin) and count counter-clockwise. In each Ring, the maximum number is at the right.

These two coordinate systems use almost exclusively integers. But sometimes you may want to go back to pixel coordinates. For this we have the .to_pixel and .from_pixel functions. The pixel coordinate system x-Axis points to the right and the y-Axis points upwards. If you want the y-Axis to point downwards, you can simply flip the sign.

Documentation

There is the usual cargo doc documentation. Use cargo +nightly doc --feature nightly to see all available docs, including the ones that do not work in stable rust.

I really believe that the function names should already tell what everything does, so as a first step I'd advise you to skim the code for function names you might want to use. The tests are some tiny examples.

I intend to add examples later on, once I have actually used this crate myself.

Alternative Crates

I have not used them, but if this crate does not fulfill your needs, there are some others on crates.io. They are unlikely to feature the mapping from spiral indices to cube coordinates, but might have other features that make them stand out. For your convenience, a few links:

  • hexagonal_pathfinding_astar
    • A-Star Pathfinding
  • hexing
    • Hexagonal Bounding Boxes
    • Serialization and Deserialization
  • hexx
    • Conversion from Axial Coordinates to cubic, doubled, hexmod, and offset coordinates.
  • beehive
    • 3D Hexagonal Tiles

Citing

If you'd like to cite this repo, you can copy-paste this:

@misc{Mink_2024, 
  title={Hexgridspiral}, 
  url={https://github.com/lucidBrot/hexgridspiral},
  journal={GitHub}, 
  author={Mink, Eric Lucidbrot}, 
  year={2024}} 

You'll very likely also want to reference the Red Blob Games Article on Hexagonal Grids that informed everything except the spiral part of this implementation.

@misc{Patel_2013,
 title={Hexagonal Grids},
 url={https://www.redblobgames.com/grids/hexagons}, 
 journal={Red Blob Games},
 author={Patel, Amit}, 
 year={2013}, month={Mar}} 

Dependencies

~1.4–2.1MB
~38K SLoC