Thanks for the responses.
I have a working solution now in C#.
public static readonly float SQRT_3 = Mathf.Sqrt(3);
public delegate bool Callback(Vector2Int hex, Vector2 intersection, float t);
public static void Traversal(Vector2 p0, Vector2 p1, float scale, Callback callback, int iterations = 1000)
{
Vector2 HexToCart(Vector2 hex)
{
float cY = hex.y * scale / (2f / 3f * SQRT_3);
float cX = hex.x * scale + cY * SQRT_3 / 3f;
return new Vector2(cX, cY);
}
Vector2 CartToHex(Vector2 cart)
{
float vX = (cart.x - cart.y * SQRT_3 / 3f) / scale;
float vY = (2f / 3f * cart.y * SQRT_3) / scale;
return new Vector2(vX, vY);
}
Vector2Int HexRound(Vector2 hex)
{
Vector3 cube = new Vector3(hex.x, hex.y, -hex.x - hex.y);
int rx = Mathf.RoundToInt(cube.x);
int ry = Mathf.RoundToInt(cube.y);
int rz = Mathf.RoundToInt(cube.z);
float x_diff = Mathf.Abs(rx - cube.x);
float y_diff = Mathf.Abs(ry - cube.y);
float z_diff = Mathf.Abs(rz - cube.z);
if (x_diff > y_diff && x_diff > z_diff)
rx = -ry - rz;
else if (y_diff > z_diff)
ry = -rx - rz;
return new Vector2Int(rx, ry);
}
//Delta of the line p0, p1
Vector2 delta = (p1 - p0);
//Unit vectors of three hexagonal directions
Vector2 n0 = new Vector2(1, 0);
Vector2 n1 = new Vector2(+.5f, SQRT_3 * .5f);
Vector2 n2 = new Vector2(-.5f, SQRT_3 * .5f);
//The sign of each of the three directions
int s0 = (int)Mathf.Sign(Vector2.Dot(n0, delta));
int s1 = (int)Mathf.Sign(Vector2.Dot(n1, delta));
int s2 = (int)Mathf.Sign(Vector2.Dot(n2, delta));
//Orient the directions so they are the three normals nearest to the line
n0 *= s0;
n1 *= s1;
n2 *= s2;
//Scale the directions to the size of the grid
n0 /= scale;
n1 /= scale;
n2 /= scale;
//The steps in integer hex coordinates for each of the three directions
Vector2Int step0 = new Vector2Int( 1, 0) * s0;
Vector2Int step1 = new Vector2Int( 0, 1) * s1;
Vector2Int step2 = new Vector2Int(-1, 1) * s2;
//Calculate the current hex that the ray origin is contained within
Vector2Int current_hex = HexRound(CartToHex(p0));
for (int i = 0; i < iterations; i++)
{
//Get the difference between the center of the current hex and the start of the ray
Vector2 rdelta = p0 - HexToCart(current_hex);
//Get the distances to each edge
float d0 = (.5f - Vector2.Dot(n0, rdelta)) / Vector2.Dot(n0, delta);
float d1 = (.5f - Vector2.Dot(n1, rdelta)) / Vector2.Dot(n1, delta);
float d2 = (.5f - Vector2.Dot(n2, rdelta)) / Vector2.Dot(n2, delta);
//Find the nearest edge
float t = d0;
Vector2Int step = step0;
if (d1 < t)
{
t = d1;
step = step1;
}
if (d2 < t)
{
t = d2;
step = step2;
}
//Break at the end of the line
if (t > 1)
break;
//Calculate where the line intersects with the edge
Vector2 intersect = p0 + delta * t;
//Increment the current hex tile across the nearest edge
current_hex += step;
//Do callback and break on return true
if (callback(current_hex, intersect, t))
break;
}
}