# Converting hexagon grid coordinate system

I have been building a RTS game based on a hexagon grid in JavaScript and I stumbled across a problem regarding the coordinate system. I have been trying to implement an A-star system to find paths, but it seems that a horizontal hex grid could cause some problems so far when it comes to odd and even.

It was made horizontal as it is the way it is rendered to make all graphics not overlapping improperly.

So now to my question after some digging on the web. How do you convert between horizontal and 60 degrees coords?

I have tried this code, but it's not good enough, and I'm stuck at what I'm missing for my two functions GetWorldXpos and GetWorldYpos.

function DrawHex(context, hexagonData, isEven, x, y)
{
var heightValue;

heightValue =  GetIntNoDecimals(hexagonData.heightValue); //parseInt(hexagonData.heightValue, 10);

//sets up so if the height of sea-levels is achived it should be equal level
heightValue = CheckForLakeLevel(heightValue);

//Gets color code depengin on height
var colorCode = GetColorCode(heightValue, hexagonData.heightValue);

if(isEven){
//creates and draws object
var hex = new HexagonObj(GetLargeMapXpos(x), GetLargeMapYpos(y), GetWorldXpos(x + 1, y), GetWorldYpos(x, y));
hex.Draw(context, 0.99, lmapData.spaceing, colorCode, (heightValue), 0);

//Adds data for usage how to draw other worl dependent objects
lmapHeightData.push(new LargeGridData(GetLargeMapXpos(x), GetLargeMapYpos(y), GetWorldXpos(x, y), GetWorldYpos(x, y), heightValue));
}
else
{
//creates and draws object
var hex = new HexagonObj(GetLargeMapXpos(x), GetLargeMapYpos(y) + lmapData.spaceing, GetWorldXpos(x, y), GetWorldYpos(x, y));
hex.Draw(context, 0.99, lmapData.spaceing, colorCode, (heightValue), 0);

lmapHeightData.push(new LargeGridData(GetLargeMapXpos(x), GetLargeMapYpos(y) + lmapData.spaceing, GetWorldXpos(x, y), GetWorldYpos(x, y), heightValue));
}
}

//Gets what is the coordinate to be drawn on screen
function GetLargeMapXpos(x)
{
var lgX = ((lmapData.posX + lmapData.spaceing) * x) + lmapData.areaPosX + lmapData.correctionX;
return lgX;
}

//Gets what is the coordinate to be drawn on screen
function GetLargeMapYpos(y)
{
return (lmapData.posY * y) + lmapData.areaPosY + lmapData.correctionY;
}

//Gets what is the coordinate for current hexagon
function GetWorldXpos(x, y)
{
return (x + lmapData.cameraPosX) - ((y + lmapData.cameraPosY) * 2);
}

//Gets what is the coordinate for current hexagon
function GetWorldYpos(x, y)
{
return y + lmapData.cameraPosY + (x + lmapData.cameraPosX);
}


Any suggestions?

• I havn't put much thought into it but can't you just use the 60 degrees to an amount of radius as offset? – Sidar May 18 '13 at 21:47

You can use A* with the original grid system you are using. The key things you need are neighbors and distance (for the heuristic). For neighbors with your grid system, you need to do something different for even and odd columns (as you mention); here's how:

neighbors =  [
[  [+1, +1],  [+1,  0],  [ 0, -1],
[-1,  0],  [-1, +1],  [ 0, +1]  ],
[  [+1,  0],  [+1, -1],  [ 0, -1],
[-1, -1],  [-1,  0],  [ 0, +1]  ]
];
parity =  x & 1;
d = neighbors[parity][direction];
return Hex(x + d[0], y + d[1]);


For distances, the simplest thing to do is to convert from your coordinate system to a 3-axis form and then get distances with that. So that brings us to coordinate conversion.

In my guide to hexes, I call your coordinate system “even-q” because the even columns(q) are pushed down. (Note: in my guide I call hex coordinates q,r to distinguish them from 3-axis coordinates x,y,z but since you use x,y in your code I'll use xx,yy,zz for 3-axis in this answer.) You can convert your x,y to 3-axis xx,yy,zz with this:

xx = x
zz = y - (x + x&1) / 2
yy = -xx-zz


Once you've converted both the source and destination to the xx,yy,zz form, you can get distances easily:

distance = (abs(xx1 - xx2) + abs(yy1 - yy2) + abs(zz1 - zz2)) / 2


To answer your actual question, once you have the xx,yy,zz form, take a look at the 3-axis system below; I think -zz,-yy will give you what you want:

• Thanks for the answer! A bit late right now for me, see if I can give it a shot tomorrow. – Jonas Lindahl May 18 '13 at 23:05
• +1; I came into this question with the intention of posting a link to your guide. You've saved me the trouble! (Except that I've done it anyway, now.) – Trevor Powell May 20 '13 at 3:02

Consider this:

public sealed partial class HexCoords {
static HexCoords() {
MatrixUserToCanon  = new IntMatrix2D(2, 1,  0,2,  0,0,  2);
MatrixCanonToUser  = new IntMatrix2D(2,-1,  0,2,  0,1,  2);
}
protected HexCoords(CoordsType coordsType, IntVector2D vector) {
switch(coordsType) {
default:
case CoordsType.Canon: _vectorCanon = vector;  isUserNull  = true;  break;
case CoordsType.User:  _vectorUser  = vector;  isCanonNull = true;  break;
}
}
protected static IntMatrix2D MatrixUserToCanon;
protected IntVector2D VectorCanon {
get { return !isCanonNull ? _vectorCanon : VectorUser * MatrixUserToCanon; }
set { _vectorCanon = value;  isUserNull = true; }
} IntVector2D _vectorCanon;
bool isCanonNull;

protected static IntMatrix2D MatrixCanonToUser;
protected IntVector2D VectorUser {
get { return !isUserNull  ? _vectorUser : VectorCanon  * MatrixCanonToUser; }
set { _vectorUser  = value;  isCanonNull = true; }
} IntVector2D _vectorUser;
bool isUserNull;
}


with these utility classes:

public struct IntMatrix2D : IEquatable<IntMatrix2D> {
public static IntVector2D operator * (IntVector2D v, IntMatrix2D m) {
return new IntVector2D (
v.X*m.M11 + v.Y*m.M21 + m.M31,  v.X*m.M12 + v.Y*m.M22 + m.M32,  v.W*m.M33
).Normalize();
}
}

public struct IntVector2D : IEquatable<IntVector2D> {
public int X { get; private set; }
public int Y { get; private set; }
public int W { get; private set; }
public IntVector2D(int x, int y, int w) : this() {
X = x;      Y = y;      W = w;
}
public IntVector2D Normalize() {
var x = (X >= 0) ? X : X - W;
var y = (Y >= 0) ? Y : Y - W;
return new IntVector2D(x/W, y/W);
}
}


For this purpose canonical coordinates have origin in the upper-left, with y-axis running vertically downwards and x-axis running at 120 degrees up and to the right. User coordinates are from the same origin, with y-axis running vertically downwards and x-axis running horizontally to the right, zigging down and then up from the origin.

I have implemented this and assorted other utilities for Hex-Grid manipulation in an open source library (MIT Licence) here on CodePlex.

• OP specifically stated in JavaScript. This appears to be Java or C#. – JDSweetBeat May 12 '15 at 13:00