Distance between 2 hexagons on hexagon grid - c++

I have a hexagon grid:
with template type coordinates T. How I can calculate distance between two hexagons?
For example:
dist((3,3), (5,5)) = 3
dist((1,2), (1,4)) = 2

First apply the transform (y, x) |-> (u, v) = (x, y + floor(x / 2)).
Now the facial adjacency looks like
0 1 2 3
0*-*-*-*
|\|\|\|
1*-*-*-*
|\|\|\|
2*-*-*-*
Let the points be (u1, v1) and (u2, v2). Let du = u2 - u1 and dv = v2 - v1. The distance is
if du and dv have the same sign: max(|du|, |dv|), by using the diagonals
if du and dv have different signs: |du| + |dv|, because the diagonals are unproductive
In Python:
def dist(p1, p2):
y1, x1 = p1
y2, x2 = p2
du = x2 - x1
dv = (y2 + x2 // 2) - (y1 + x1 // 2)
return max(abs(du), abs(dv)) if ((du >= 0 and dv >= 0) or (du < 0 and dv < 0)) else abs(du) + abs(dv)

Posting here after I saw a blog post of mine had gotten referral traffic from another answer here. It got voted down, rightly so, because it was incorrect; but it was a mischaracterization of the solution put forth in my post.
Your 'squiggly' axis - in terms of your x coordinate being displaced every other row - is going to cause you all sorts of headaches with trying to determine distances or doing pathfinding later on, if this is for a game of some sort. Hexagon grids lend themselves to three axes naturally, and a 'squared off' grid of hexagons will optimally have some negative coordinates, which allows for simpler math around distances.
Here's a grid with (x,y) mapped out, with x increasing to the lower right, and y increasing upwards.
By straightening things out, the third axis becomes obvious.
The neat thing about this, is that the three coordinates become interlinked - the sum of all three coordinates will always be 0.
With such a consistent coordinate system, the atomic distance between any two hexes is the largest change between the three coordinates, or:
d = max( abs(x1 - x2), abs(y1 -y2), abs( (-x1 + -y1) - (-x2 + -y2) )
Pretty straightforward. But you must fix your grid first!

The correct explicit formula for the distance, with your coordinate system, is given by:
d((x1,y1),(x2,y2)) = max( abs(x1 - x2),
abs((y1 + floor(x1/2)) - (y2 + floor(x2/2)))
)

Here is what a did:
Taking one cell as center (it is easy to see if you choose 0,0), cells at distance dY form a big hexagon (with “radius” dY). One vertices of this hexagon is (dY2,dY). If dX<=dY2 the path is a zig-zag to the ram of the big hexagon with a distance dY. If not, then the path is the “diagonal” to the vertices, plus an vertical path from the vertices to the second cell, with add dX-dY2 cells.
Maybe better to understand: led:
dX = abs(x1 - x2);
dY = abs(y1 - y2);
dY2= floor((abs(y1 - y2) + (y1+1)%2 ) / 2);
Then:
d = d((x1,y1),(x2,y2))
= dX < dY2 ? dY : dY + dX-dY2 + y1%2 * dY%2

First, you need to transform your coordinates to a "mathematical" coordinate system. Every two columns you shift your coordinates by 1 unit in the y-direction. The "mathamatical" coordinates (s, t) can be calculated from your coordinates (u,v) as follows:
s = u + floor(v/2)
t = v
If you call one side of your hexagons a, the basis vectors of your coordinate system are (0, -sqrt(3)a) and (3a/2, sqrt(3)a/2). To find the minimum distance between your points, you need to calculate the manhattan distance in your coordinate system, which is given by |s1-s2|+|t1-t2| where s and t are the coordinates in your system. The manhattan distance only covers walking in the direction of your basis vectors so it only covers walking like that: |/ but not walking like that: |\. You need to transform your vectors into another coordinate system with basis vectors (0, -sqrt(3)a) and (3a/2, -sqrt(3)a/2). The coordinates in this system are given by s'=s-t and t'=t so the manhattan distance in this coordinate system is given by |s1'-s2'|+|t1'-t2'|. The distance you are looking for is the minimum of the two calculated manhattan distances. Your code would look like this:
struct point
{
int u;
int v;
}
int dist(point const & p, point const & q)
{
int const ps = p.u + (p.v / 2); // integer division!
int const pt = p.v;
int const qs = q.u + (q.v / 2);
int const qt = q.v;
int const dist1 = abs(ps - qs) + abs(pt - qt);
int const dist2 = abs((ps - pt) - (qs - qt)) + abs(pt - qt);
return std::min(dist1, dist2);
}

(odd-r)(without z, only x,y)
I saw some problems with realizations above. Sorry, I didn't check it all but. But maybe my solution will be helpful for someone and maybe it's a bad and not optimized solution.
The main idea to go by diagonal and then by horizontal. But for that we need to note:
1) For example, we have 0;3 (x1=0;y1=3) and to go to the y2=6 we can handle within 6 steps to each point (0-6;6)
so: 0-left_border , 6-right_border
2)Calculate some offsets
#include <iostream>
#include <cmath>
int main()
{
//while(true){
int x1,y1,x2,y2;
std::cin>>x1>>y1;
std::cin>>x2>>y2;
int diff_y=y2-y1; //only up-> bottom no need abs
int left_x,right_x;
int path;
if( y1>y2 ) { // if Down->Up then swap
int temp_y=y1;
y1=y2;
y2=temp_y;
//
int temp_x=x1;
x1=x2;
x2=temp_x;
} // so now we have Up->Down
// Note that it's odd-r horizontal layout
//OF - Offset Line (y%2==1)
//NOF -Not Offset Line (y%2==0)
if( y1%2==1 && y2%2==0 ){ //OF ->NOF
left_x = x1 - ( (y2 - y1 + 1)/2 -1 ); //UP->DOWN no need abs
right_x = x1 + (y2 - y1 + 1)/2; //UP->DOWN no need abs
}
else if( y1%2==0 && y2%2==1 ){ // OF->NOF
left_x = x1 - (y2 - y1 + 1)/2; //UP->DOWN no need abs
right_x = x1 + ( (y2 - y1 + 1)/2 -1 ); //UP->DOWN no need abs
}
else{
left_x = x1 - (y2 - y1 + 1)/2; //UP->DOWN no need abs
right_x = x1 + (y2 - y1 + 1)/2; //UP->DOWN no need abs
}
/////////////////////////////////////////////////////////////
if( x2>=left_x && x2<=right_x ){
path = y2 - y1;
}
else {
int min_1 = std::abs( left_x - x2 );
int min_2 = std::abs( right_x - x2 );
path = y2 - y1 + std::min(min_1, min_2);
}
std::cout<<"Path: "<<path<<"\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n";
//}
return 0;
}

I believe the answer you seek is:
d((x1,y1),(x2,y2))=max(abs(x1-x2),abs(y1-y2));
You can find a good explanation on hexagonal grid coordinate-system/distances here:
http://keekerdc.com/2011/03/hexagon-grids-coordinate-systems-and-distance-calculations/

Related

C++ Voronoi styled tile map

I am trying to display map with Voronoi styled map tiles.
It is on 2d array, I set some steps to achieve it:
Divide 2d array map into equal sized squares (tile).
int map_width = 100, map_height = 100,
tile_size = 10;
vector<vector<int>> tile_map; // size 10x10
Uniformly distribute site(or central) points in tiles
vector<pair<int,int>> sites
for (int y = 0; y < tile_map.size(); y++)
for (int x = 0; x < tile_map[y].size(); x++)
sites.push_back({x*tile_size+(rand()%tile_size(),y*tile_size+(rand()%tile_size()});
Link site points to other sites in adjacent tiles.
Draw perpendicular line of lines formed in step 3.
Intersecting point of perpendicular lines is the vertex of voronoi styled polygon.
Here I am stuck with step 4 and 5.
Is there a way to find perpendicular line with 2 given points?
Or is there a better way to design voronoi diagram in c++?enter image description here
Given 2 points (x1, y1) and (x2, y2) gives you a dx = x2 - x1 and dy = y2 - y1 and a parametric equation for the line line(t) = (x1 + t * dx, y1 + t * dy).
Constructing a perpendicular line from that is easy. Find the midpoint and rotate the slope by 90°:
cx = (x1 + x2) / 2;
cy = (y1 + y2) / 2;
p(t) = (cx + t * dy, cy + t * dx)
Finding the intersection of 2 such lines is easy too:
p1(t1) = (cx1 + t1 * dy1, cy1 + t1 * dx1)
p2(t2) = (cx2 + t2 * dy2, cy2 + t2 * dx2)
The 2 lines intersect when p1(t1) = p2(t2). That gives you 2 equations and 2 unknowns:
cx1 + t1 * dy1 == cx2 + t2 * dy2
cy1 + t1 * dx1 == cy2 + t2 * dx2
Solve for either t1 or t2 and calculate p1(t1) or p2(t2) at that point.

How to draw an ellipse using the ellipse equation?

(x - x0)^2/a^2 + (y - y0)^2/b^2 == 1
where (x0, y0) is the centre of the ellipse.
the centre point (x0, y0) is randomly located in the region [SIZE/4, 3*SIZE/4]x[SIZE/4, 3*SIZE/4],
and a, b in the interval [SIZE/4, SIZE/2] so that in most cases the complete curves can lie in figure.
I have to print out an ellipse on a X-Y axis of Size 15(0 to 14), using the given information.
I am using cout to print '.' on the entire graph, and have to print an ellipse using the given dimensions only on that portion of the graph using 'E'.
I have to use the equation to test which points are inside or outside of the curve.
I have to find the closest points to the curve.
For example, I can start from the center (x0, y0) and keep moving up until
I find a point (x0, y1), such as:
(x0 - x0)^2/a^2 + (y1 - y0)^2/b^2 >= 1
(x0 - x0)^2/a^2 + ((y1 - 1)- y0)^2/b^2 <= 1
Then (x0, y1 - 1) and (x0, y1) are candidates which are points closest to the curve on the line x = x0.
Not going to give you a full solution, but I think I see the general problem you are having. My comments were just some brainstorming. This is how I would actually approach it:
As you are using cout to print ., you should use a
std::array<std::string,N_ROWS> grid;
Initialize it properly with strings of correct size, and use a function std::pair<double,double> get_coordinates(int x,int y) to transform between discrete coordinates and "world coordinates".
To know if a grid point is part of the ellipsis or not, you have to realize that for a discrete grid, the equation
(x - x0)^2/a^2 + (y - y0)^2/b^2 == 1
will never be satisfied exactly for some grid coordinates x_i,y_i. However, this inequality:
(x - x0)^2/a^2 + (y - y0)^2/b^2 < 1
tells you if a point (x,y) is inside the area of the ellipsis. And if you consider the four neighboring points:
x_i,y_i
x_i+1,y_i
x_i,y_i+1
x_i+1,y_i+1
then eiter
all of them are inside -> grid[x_i][y_i] is inside of the ellipsis
none of them is inside -> grid[x_i][y_i] is not inside of the ellipsis
some are inside, some are outside -> grid[x_i][y_i] is part of the ellipsis, ie it gets a .
(In the last step i used grid[x_i][y_i] which should rather be grid[x_i + 1/2][y_i + 1/2], however this just shifts the whole grid by half a pixel and should not matter too much)
PS: in the meantime the question has been edited. It isnt perfeclty clear what is part of the requirements and what is part of your solution, so I don't know if this answer is of any help. I'll just leave it here...
First create a class to hold a Point:
struct Point
{
const int x;
const int y;
};
Next create a class to hold ellipse parameters and to check if a point is on that ellipse (or inside it):
struct Ellipse
{
const Point center;
const int a;
const int b;
bool is_on_ellipse(Point p) const
{
return std::pow(p.x - center.x, 2) / std::pow(a, 2) + std::pow(p.y - center.y, 2) / std::pow(b, 2) == 1.0;
}
bool is_inside_ellipse(Point p) const
{
return std::pow(p.x - center.x, 2) / std::pow(a, 2) + std::pow(p.y - center.y, 2) / std::pow(b, 2) < 1.0;
}
};
Then you can create an ellipse like this:
Ellipse e = {{0, 0}, 2, 3};
And check if the point is on the ellipse by calling: e.is_on_ellipse({px, py})
With C++20 it can be a little more verbose (but easier to verify correctness) :
Ellipse e = {.center = {.x = 0, .y = 0}, .a = 2, .b = 3};
//....
e.is_on_ellipse({.x = px, .y = py})

Given two points, find a third point on the line

I have two points A (x1,y1) and B (x2,y2) that are given as an input to the program. I have to find a third point C that lies on the line AB and is at a distance 10 away from the point A.
I can easily get the slope of the line but that doesn't give me the full equation for the line. Even if I get the full equation, I am not sure using this equation, how would I find out a point that is x distance away from A.
Any suggestions on how to approach this?
There are always two points on each line:
get the vector from A to B (subtract the coordinates)
normalize the vector (divide by its length; pythagorean theorem)
multiply the vector by 10 or -10
add the vector to A to get C
Note that if A==B, the line is not defined, and this algorithm causes a division by zero. You may want to add a test for equality at the beginning.
You can use the sine or the cosine (times 10) of the angle of the line to get the horizontal or vertical distance of the point that is a distance of 10 from a given point. A shortcut is to use the horizontal or vertical distance divided by the direct distance between the points to get the sine or cosine.
You can do it using vectors like this:
Let D = the difference between B and A (D = B - A)
Then any point on the line can be described by this formula:
point = A + Dt
where t is a real number.
So just plug in any value for t to get another point. For example if you let t == 1 then the equation above reduces to point = B. If you let t = 0 then it reduces to point = A. So you can see that you can use this to find a point between A and B simply by let t range from 0 to 1. Additionally if you let t > 1, you will find a point past B.
You can see from the image that your given points are x1,y1 and x2,y2. You need to find an intermediate point at a distance 'R' from point x1,y1.
All you need to do is to find θ using
Tan θ = (y2-y1)/(x2-x1)
Then you can get the intermediate point as (R * cos θ),(R * Sin θ)
I have drawn this assuming positive slope.
Going on similar lines you can seek a solution for other special cases lile:
i. Horizontal line
ii. Vertical line
iii. Negative slope
Hope it clarifies.
I have done the calculation in Andengine using a Sprite object. I have two Array List x coordinates and y coordinates. Here i am just calculating using the last two values from these two array list to calculate the third point 800 pixel distant from Your point B. you can modify it using different values other than 800. Hope it will work.The coordinate system here is a little different where (0,0) on the top left corner of the screen. Thanks
private void addExtraCoordinate(CarSprite s) {
int x0, y0, x1, y1;
float x = 0f, y = 0f;
x0 = Math.round(xCoordinates.get(xCoordinates.size() - 2));
x1 = Math.round(xCoordinates.get(xCoordinates.size() - 1));
y0 = Math.round(yCoordinates.get(yCoordinates.size() - 2)) * (-1);
y1 = Math.round(yCoordinates.get(yCoordinates.size() - 1)) * (-1);
if (x1 == x0 && y1 == y0) {
return;
} else if (y1 == y0 && x1 != x0) {
if (x1 > x0) {
x = (float) x1 + 800f;
} else
x = (float) x1 - 800f;
y = Math.round(yCoordinates.get(yCoordinates.size() - 1));
} else if (y1 != y0 && x1 == x0) {
if (y1 > y0) {
y = (float) Math.abs(y1) - 800f;
} else
y = (float) Math.abs(y1) + 800f;
x = Math.round(xCoordinates.get(xCoordinates.size() - 1));
} else {
float m = (float) (yCoordinates.get(yCoordinates.size() - 1) * (-1) - yCoordinates
.get(yCoordinates.size() - 2) * (-1))
/ (float) (xCoordinates.get(xCoordinates.size() - 1) - xCoordinates
.get(xCoordinates.size() - 2));
if (x1 > x0) {
x = (float) ((float) x1 + 800f / (float) Math
.sqrt((double) ((double) 1f + (double) (m * m))));
} else
x = (float) ((float) x1 - 800f / (float) Math
.sqrt((double) ((double) 1f + (double) (m * m))));
if (y0 > y1) {
y = (float) ((float) Math.abs(y1) + 800f / (float) Math
.sqrt((double) (((double) 1f / (double) (m * m)) + (double) 1f)));
} else
y = (float) ((float) Math.abs(y1) - 800f / (float) Math
.sqrt((double) (((double) 1f / (double) (m * m)) + (double) 1f)));
}
xCoordinates.add(x);
yCoordinates.add(y);
}

Speeding up self-similarity in an image

I'm writing a program that will generate images. One measurement that I want is the amount of "self-similarity" in the image. I wrote the following code that looks for the countBest-th best matches for each sizeWindow * sizeWindow window in the picture:
double Pattern::selfSimilar(int sizeWindow, int countBest) {
std::vector<int> *pvecount;
double similarity;
int match;
int x1;
int x2;
int xWindow;
int y1;
int y2;
int yWindow;
similarity = 0.0;
// (x1, y1) is the original that's looking for matches.
for (x1 = 0; x1 < k_maxX - sizeWindow; x1++) {
for (y1 = 0; y1 < k_maxY - sizeWindow; y1++) {
pvecount = new std::vector<int>();
// (x2, y2) is the possible match.
for (x2 = 0; x2 < k_maxX - sizeWindow; x2++) {
for (y2 = 0; y2 < k_maxY - sizeWindow; y2++) {
// Testing...
match = 0;
for (xWindow = 0; xWindow < sizeWindow; xWindow++) {
for (yWindow = 0; yWindow < sizeWindow; yWindow++) {
if (m_color[x1 + xWindow][y1 + yWindow] == m_color[x2 + xWindow][y2 + yWindow]) {
match++;
}
}
}
pvecount->push_back(match);
}
}
nth_element(pvecount->begin(), pvecount->end()-countBest, pvecount->end());
similarity += (1.0 / ((k_maxX - sizeWindow) * (k_maxY - sizeWindow))) *
(*(pvecount->end()-countBest) / (double) (sizeWindow * sizeWindow));
delete pvecount;
}
}
return similarity;
}
The good news is that the algorithm does what I want it to: it will return a value from 0.0 to 1.0 about how 'self-similar' a picture is.
The bad news -- as I'm sure that you've already noted -- is that the algorithm is extremely slow. It takes (k_maxX - sizeWindow) * (k_maxY - sizeWindow) * (k_maxX - sizeWindow) * (k_maxY - sizeWindow) * sizeWindow * sizeWindow steps for a run.
Some typical values for the variables:
k_maxX = 1280
k_maxY = 1024
sizeWindow = between 5 and 25
countBest = 3, 4, or 5
m_color[x][y] is defined as short m_color[k_maxX][k_maxY] with values between 0 and 3 (but may increase in the future.)
Right now, I'm not worried about the memory footprint taken by pvecount. Later, I can use a sorted data set that doesn't add another element when it's smaller than countBest. I am only worried about algorithm speed.
How can I speed this up?
Ok, first, this approach is not stable at all. If you add random noise to your image, it will greatly decrease the similarity between the two images. More importantly, from an image processing standpoint, it's not efficient or particularly good. I suggest another approach; for example, using a wavelet-based approach. If you performed a 2d DWT on your image for a few levels and compared the scaling coefficients, you would probably get better results. Plus, the discrete wavelet transform is O(n).
The downside is that wavelets are an advanced mathematical topic. There are some good OpenCourseWare notes on wavelets and filterbanks here.
Your problem strongly reminds me of the calculations that have to be done for motion compensation in video compression. Maybe you should take a closer look what's done in that area.
As rlbond already pointed out, counting the number of points in a window where the colors exactly match isn't what's normally done in comparing pictures. A conceptually simpler method than using discrete cosine or wavelet transformations is to add the squares of the differences
diff = (m_color[x1 + xWindow][y1 + yWindow] - m_color[x2 + xWindow][y2 + yWindow]);
sum += diff*diff;
and use sum instead of match as criterion for similarity (now smaller means better).
Back to what you really asked: I think it is possible to cut down the running time by the factor 2/sizeWindow (maybe squared?), but it is a little bit messy. It's based on the fact that certain pairs of squares you compare stay almost the same when incrementing y1 by 1. If the offsets xOff = x2-x1 and yOff = y2-y1 are the same, only the top (rsp. bottom) vertical stripes of the squares are no longer (rsp. now, but not before) matched. If you keep the values you calculate for match in a two-dimensional array indexed by the offsets xOff = x2-x1 and yOff = y2-y1, then can calculate the new value for match[xOff][yOff] for y1 increased by 1 and x1 staying the same by 2*sizeWindow comparisons:
for (int x = x1; x < x1 + sizeWindow; x++) {
if (m_color[x][y1] == m_color[x + xOff][y1 + yOff]) {
match[xOff][yOff]--; // top stripes no longer compared
}
if (m_color[x][y1+sizeWindow] == m_color[x + xOff][y1 + sizeWindow + yOff]) {
match[xOff][yOff]++; // bottom stripe compared not, but wasn't before
}
}
(as the possible values for yOff changed - by incrementing y1 - from the interval [y2 - y1, k_maxY - sizeWindow - y1 - 1] to the interval [y2 - y1 - 1, k_maxY - sizeWindow - y1 - 2] you can discard the matches with second index yOff = k_maxY - sizeWindow - y1 - 1 and have to calculate the matches with second index yOff = y2 - y1 - 1 differently). Maybe you can also keep the values by how much you increase/decrease match[][] during the loop in an array to get another 2/sizeWindow speed-up.

(C++) Need to figure out all points within a radius using reg. 2D windows coord. system

Sorry in advance, I'm struggling a bit with how to explain this... :)
Essentially, I've got a typical windows coordinate system (the Top, Left is 0,0). If anybody's familiar with the haversine query, like in SQL, it can get all points in a radius based on latitude and longitude coordinates.
I need something much simpler, but my math skills ain't all up to par! Basically, I've got random points scattered throughout about a 600x400 space. I have a need to, for any X,Y point on the map, run a query to determine how many other points are within a given radius of that one.
If that's not descriptive enough, just let me know!
Straightforward approach:
You can calculate the distance between to points using the Pythagorean theorem:
deltaX = x1 - x2
deltaY = y1 - y2
distance = square root of (deltaX * deltaX + deltaY * deltaY)
Given point x1,y1, do this for every other point (x2,y2) to see if the calculated distance is within (less than or equal to) your radius.
If you want to make it speedier, calculate and store the square of the radius and just compare against (deltaX * deltaX + deltaY * deltaY), avoiding the square root.
Before doing the Pythagoras, you could also quickly eliminate any point that falls outside of the square that can fully contain the target circle.
// Is (x1, y1) in the circle defined by center (x,y) and radius r
bool IsPointInCircle(x1, y1, x, y, r)
{
if (x1 < x-r || x1 > x+r)
return false;
if (y1 < y-r || y1 > y+r)
return false;
return (x1-x)*(x1-x) + (y1-y)*(y1-y) <= r*r
}
Use Pythagoras:
distance = sqrt(xDifference^2 + yDifference^2)
Note that '^' in this example means "to the power of" and not C's bitwise XOR operator. In other words the idea is to square both differences.
If you only care about relative distance you shouldn't use square root you can do something like:
rSquared = radius * radius #square the radius
foreach x, y in Points do
dX = (x - centerX) * (x - centerX) #delta X
dY = (y - centerY) * (y - centerY) #delta Y
if ( dX + dY <= rSquared ) then
#Point is within Circle
end
end
Using the equation for a circle:
radius ** 2 = (x - centerX) ** 2 + (y - centerY) ** 2
We want to find if a point (x, y) is inside of the circle. We perform the test using this equation:
radius ** 2 < (x - centerX) ** 2 + (y - centerY) ** 2
// (Or use <= if you want the circumference of the circle to be included as well)
Simply substitute your values into that equation. If it works (the inequality is true), the point is inside of the circle. Otherwise, it isn't.