The simplest way to find position and offset in a matrix where the rows lengths are staggered given x and y offsets.

I am building a software program which has a matrix as in the picture below. Unfortunately, my math skills are lacking so I am reaching out to this community.

The matrix has a dynamic number of rows and columns so I can't fix those numbers but the numbers of columns and rows are is always odd. Starting from the center where the star is shown I need to calculate the index or position starting from the top left.

// the order is like this
0  1  2  3  4 5 6 7 8 9 10 11 12 13
 14 15 16....

hex matrix

So, I have two tasks:

First, given an x,y offset from the center I need to calculate the position. I have put some offsets into the second image to show what I mean. The yellow numbers at the top are the ordinal positions.

Next, I need to find the x,y offset given an ordinal position. If someone could help me out with some formulae I would greatly appreciate.

Lastly, if the star were to move to a new position the formula for finding ordinal should still work from there.

enter image description here


The easiest way to handle this is to flatten all the horizontal boundaries - not graphically, but in the way you think about the array in the background. Then it is just a several rows of square cells where the odd numbered rows (top row is "$0$") are one cell shorter and pushed a half-cell to the right. We also handle that by pushing those rows back a half-cell left. Now we have a rectangular grid:

The black cells are verboten - they are not actually part of the array and you are not allowed to move there. Around all cells, you can move up, down, right, left (except off the edge or into the verboten cells). In even-numbered rows, you can also move up-left and down-left, but not up-right or down-right. For odd numbered rows, up-right and down-right are allowed, but up-left and down-left are not. Cells in those directions are not considered neighbors.

The calculations you describe can all be handled in terms of the row and column address of each cell - except this will give a conservative $x,y$ offset scheme instead of the non-conservative scheme you demonstrate in your question. The offset to cell $(r_2, c_2)$ from cell $(r_1, c_1)$ is just $$x = r_2 - r_1\\y = c_2 - c_1.$$

Translating back and forth between row/column and ordinal is a little more difficult because of the verboten cells that do not get counted. Let $W$ be the width of the array - that is, the number of columns of the long rows. If the verboten cells were included, the ordinal formula for cell $(r,c)$ would just be $n = r\times W + c$. But we need to adjust it for the verboten cells. Cells in rows $0$ and $1$ have no verboten cells occurring before them in ordinal order. Cells in rows $2$ and $3$ have one verboten cell before them, row $4$ and $5$ cells have two verboten cells before them, etc. This gives $$n = r\times W + c - \left\lfloor \frac r2\right\rfloor$$ Where $\left\lfloor \frac r2\right\rfloor$ means the greatest integer $\le \frac r2$. To go the other way, $$\begin{align}r &= \left\lfloor \frac {2n+1}{2W + 1}\right\rfloor\\c &= n - r\times W + \left\lfloor \frac r2\right\rfloor\end{align}$$

When moving from a cell to its neighbors:

  • Moving left is $(r,c) \to (r,c-1)$
  • Moving right is $(r,c) \to (r,c+1)$
  • Moving to the upper-left hexagon is $(r,c) \to (r-1, c - (r+1\bmod 2))$
  • Moving to the upper-right hexagon is $(r,c) \to (r-1, c + (r+1\bmod 2))$
  • Moving to the lower-left hexagon is $(r,c) \to (r+1, c - (r+1\bmod 2))$
  • Moving to the lower-right hexagon is $(r,c) \to (r+1, c + (r+1\bmod 2))$

Where $$r+1\bmod 2 = \begin{cases}1&r\text{ is even}\\0&r\text{ is odd}\end{cases}$$