Sign in to follow this  
Talin

Mouse to hex coordinates - again.

Recommended Posts

Right, I know this has been asked a dozen of times, but none of the threads or articles I dug out actually helped me get it right, so this thread is pretty much my last hope. :) So, the hexes are lined up thusly: 0,0 ------- 0,1 ------- 0,2 ----- 1,0 ------- 1,1 ----- 2,0 ------- 2,1 ------- 2,2 The numbers are hex x,y coordinates. What complicates things a little is that they are lined up vertically, and that the tiles are 20 x 16, like this: __ /..\ 16 \__/ 20 (4+12+4) So, typically, I have the mouse coordinates of where the user clicked, and using that I need to find the actual hex x,y the user clicked on. If it helps, this is the best article I found (that is, closest to what I actually need), but I still couldn't implement that properly. It not only gives me the wrong coordinates, it also doesn't have consistent values for a single hex. What I'd like is some specific formula for my own tile size and dimensions, without any scrolling offset considerations (consider the map to be unscrollable).

Share this post


Link to post
Share on other sites
I do it with just three quick constants; HEX_SLOPE is the rise/run ratio of your side ascent (in your case, 8/4), TILE_WIDTH is one slope plus the flat section (in your case, 16), and TILE_HEIGHT is the full tile height (again, 16).

This works by breaking columns up into squares of the following...

_____ _____
| / |\ |
|/ | \___|
|\ | / |
|_\___|/____|
You can see that evenly tiles to cover a hex grid; so then I just run the math to find which of the three sectors in a given tile the point is in.

Some fairly easy-to-translate Java.

public static void getHexAtPoint (double aX, double aY, Point2D aReturn)
{
final long gX = (long) Math.floor (aX / TILE_WIDTH);
final long gY = (long) Math.floor (aY / TILE_HEIGHT);
final double fX = (aX / TILE_WIDTH) - gX;
final double fY = (aY / TILE_HEIGHT) - gY;

final long retX;
final long retY;

if( (Math.abs (gX) & 1) == 1 )
{
if( fY > 0.5 )
{
if( 1.0 - fX * HEX_SLOPE < fY )
{
retX = gX;
retY = gY;
}
else
{
retX = gX - 1;
retY = gY;
}
}
else
{
if( fX * HEX_SLOPE > fY )
{
retX = gX;
retY = gY - 1;
}
else
{
retX = gX - 1;
retY = gY;
}
}
}
else
{
if( fY > 0.5 )
{
if( fY - 0.5 < fX * HEX_SLOPE )
{
retX = gX;
retY = gY;
}
else
{
retX = gX - 1;
retY = gY;
}
}
else
{
if( fY - 0.5 > fX * -HEX_SLOPE )
{
retX = gX;
retY = gY;
}
else
{
retX = gX - 1;
retY = gY - 1;
}
}
}

aReturn.setLocation (retX, retY);
}




BTW: use [ code ][/ code ] tags for fixed-width but unadorned text, and [ source lang="java|cpp"][/ source ] tags for scrolling syntax-highlighted text.

EDIT: crap, wait. You use side-offset rows instead of down-offset columns. You'll have to change the +/- constants (ie retX = gX + 1), but otherwise it should still work for you.

Share this post


Link to post
Share on other sites
Thanks a lot for the help, the code seems pretty clean. I'll try it out today, had a busy week. :)

Share this post


Link to post
Share on other sites
If you are programing in javascript and just want to use integer math, you can alter the slope comparison statement by mutiplying by the denominators.

So, to see if the point slope is less than the hexagon slope

is ( y / x ) less than ( rise / run ) check now becomes

is ( y * run ) less than ( rise * x ) check

this avoids dividing integers and having to deal with fractions.

I also calculate a mapgrid on the hexagon map for placing counters on the hexagon map and calculating ranges, bearing and line of sight. Sample code in javascript and C# at my http://www.webwargaming.org

Share this post


Link to post
Share on other sites
Here is what the mapgrid would look like


Movement is easily calculated with simple math.

North x + 0, y - 4
Northeast x + 2, y - 2
Southeast x + 2, y + 2
South x + 0, y + 4
Southwest x - 2, y + 2
Northwest x - 2, y - 2

Share this post


Link to post
Share on other sites
Range is now easy

if ( absX > absY ) {
range = absX / 2;
} else {
range = ( absX + absY ) / 4;
}

for example, from 0101 ( 4, 8 ) to 0404 ( 10, 22 )

absX = Math.abs( 10 - 4 ) = 6
absY = Math.abs( 22 - 8 ) = 14

range = ( 6 + 14 ) / 4 = 20/4 = 5 !!


range demo

[Edited by - Mark Butler on June 26, 2009 9:59:40 AM]

Share this post


Link to post
Share on other sites
Couldn't you just calculate which hex field's middle coordinate is closest to the point of mouse click via Phytagoras' theorem?

Share this post


Link to post
Share on other sites
c-Row; you still need a reliable approximation of which hexes your cursor is actually nearest to, unless you want to test every hex. Two float-to-int truncations, two integer multiplies, and two integer additions is much cheaper than (best case scenario) twelve floating-point subtractions, and six floating-point multiplications; that's for three range compares, one for each of the theoretically nearest hexes.

Determining which four hexes are nearest to the cursor costs at least two multiplies and two subtractions... and the natural reaction to that is "hey, wait a minute..."

Share this post


Link to post
Share on other sites
My approaches might not be elegant, but they are easier to understand. ;) I don't know what absX is for a start.

Given that a hex is, say 50 pixels high and 50 pixels wide, you just need to find those whose coordinates are equal or less than 25 pixels away from the mouse. If you are more than 25 pixels away from the center, you are not within the hex anyway. Just from the top of my head, actual pixel measurements might vary, but the general idea is the same.

Share this post


Link to post
Share on other sites
Right, I know, I follow you. The thing is; how do you determine the position of those nearest hex's centers? By dividing by the hex height, to get the row, and dividing by most of the hex width, to get the column.

At that point, instead of calcuating the center position of three hexes and then comparing the distances to them, you can instead just test being above or below a slope, a calculation only taking a multiply and an add or two.

Share this post


Link to post
Share on other sites
I chop up the map into rectangles, just like the example above.
Then, use the slope to figure out if the point is left/right, up/down of the hexagon.
Except I chop them up into smaller pieces, so the cursor can also identify hexsides.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this