I have a map made up of rows and columns of hexagons
This isn\'t an actual image of the hex-map I am using,
I started out by looking at @pi 's answer https://stackoverflow.com/a/23370350/5776618 and thought it would be interesting to try something similar in cube coordinates with UVW-space (rather than the 2D, axial, UV-space).
The following equations map (x,y) => (u,v,w)
u = (2/3)*x;
v = -(1/3)*x + (1/2)*y;
w = -(1/3)*x - (1/2)*y;
Then it's as simple as rounding u, v, and w to the nearest integer and converting back to x,y. However there is a major snag...
In the answer above, it's noted that rounding in UV-space will have a few areas that map incorrectly:
This still happens when using cube coordinates as well:
Any area in the orange triangles is >0.5 units from the center of the hexagon and when rounded will round AWAY from the center. This is shown above as anything in the red triangle (to the left of the u=1.5 line) will have u rounded incorrectly to u=1 rather than u=2.
Some key observations here though...
1. The orange/red problem areas are non-overlapping
2. In cube coordinates, valid hex centers have u + v + w = 0
In the below code, u, v, and w, are all rounded from the start as rounding in only an issue if the rounded coordinates do not sum to zero.
uR = Math.round(u);
vR = Math.round(v);
wR = Math.round(w);
If these do not sum to zero, because the problem areas are non-overlapping, there will be only 1 coordinate that is rounded incorrectly. This coordinate is also the coordinate that was rounded the most.
arr = [ Math.abs(u-uR), Math.abs(v-vR), Math.abs(w-wR) ];
var i = arr.indexOf(Math.max(...arr));
After the problem coordinate is found, it is rounded in the other direction. The final (x,y) are then calculated from rounded/corrected (u,v,w).
nearestHex = function(x,y){
u = (2/3)*x;
v = -(1/3)*x + (1/2)*y;
w = -(1/3)*x - (1/2)*y;
uR = Math.round(u);
vR = Math.round(v);
wR = Math.round(w);
if(uR+vR+wR !== 0){
arr = [ Math.abs(u-uR), Math.abs(v-vR), Math.abs(w-wR) ];
var i = arr.indexOf(Math.max(...arr));
switch(i){
case 0:
Math.round(u)===Math.floor(u) ? u = Math.ceil(u) : u = Math.floor(u);
v = vR; w = wR;
break;
case 1:
Math.round(v)===Math.floor(v) ? v = Math.ceil(v) : v = Math.floor(v);
u = uR; w = wR;
break;
case 2:
Math.round(w)===Math.floor(w) ? w = Math.ceil(w) : w = Math.floor(w);
u = uR; v = vR;
break;
}
}
return {x: (3/2)*u, y: v-w};
}