C++ 六边形网格上两个六边形之间的距离
我有一个六边形网格: 使用模板类型坐标T。如何计算两个六边形之间的距离 例如: 距离((3,3)、(5,5))=3C++ 六边形网格上两个六边形之间的距离,c++,algorithm,distance,hexagonal-tiles,C++,Algorithm,Distance,Hexagonal Tiles,我有一个六边形网格: 使用模板类型坐标T。如何计算两个六边形之间的距离 例如: 距离((3,3)、(5,5))=3 dist((1,2),(1,4))=2我相信你寻求的答案是: d((x1,y1),(x2,y2))=max(abs(x1-x2),abs(y1-y2)); 您可以在此处找到关于六边形栅格坐标系/距离的良好解释: 正确的明确距离公式(使用坐标系)如下所示: d((x1,y1),(x2,y2)) = max( abs(x1 - x2),
dist((1,2),(1,4))=2我相信你寻求的答案是:
d((x1,y1),(x2,y2))=max(abs(x1-x2),abs(y1-y2));
您可以在此处找到关于六边形栅格坐标系/距离的良好解释:
正确的明确距离公式(使用坐标系)如下所示:
d((x1,y1),(x2,y2)) = max( abs(x1 - x2),
abs((y1 + floor(x1/2)) - (y2 + floor(x2/2)))
)
下面是一位女士所做的:
以一个单元格为中心(如果选择
0,0
,很容易看到),距离dY
的单元格形成一个大六边形(带有“半径”dY
)。此六边形的一个顶点是(dY2,dY)。
如果dX首先,需要将坐标转换为“数学”坐标系。每两列在y方向上移动坐标1个单位。“数学”坐标(s,t)可根据坐标(u,v)计算如下:
s=u+地板(v/2)
t=v
如果将六边形的一侧称为a,坐标系的基向量是(0,-sqrt(3)a)和(3a/2,sqrt(3)a/2)。要找到点之间的最小距离,需要计算坐标系中的曼哈顿距离,该距离由| s1-s2 |+| t1-t2 |给出,其中s和t是坐标系中的坐标。曼哈顿距离只包括沿基向量方向行走,所以它只包括像这样行走:|/但不包括像那样行走:|\。您需要将向量转换为另一个具有基向量(0,-sqrt(3)a)和(3a/2,-sqrt(3)a/2)的坐标系。该坐标系中的坐标由s'=s-t和t'=t给出,因此该坐标系中的曼哈顿距离由| s1'-s2'|+| t1'-t2'|给出。您要查找的距离是两个计算的曼哈顿距离中的最小值。您的代码如下所示:
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);
}
首先应用变换(y,x)|->(u,v)=(x,y+floor(x/2))
现在,面部邻接看起来像
0 1 2 3
0*-*-*-*
|\|\|\|
1*-*-*-*
|\|\|\|
2*-*-*-*
设点为(u1,v1)和(u2,v2)。设du=u2-u1,dv=v2-v1。距离是
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
在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)
def dist(p1、p2):
y1,x1=p1
y2,x2=p2
du=x2-x1
dv=(y2+x2//2)-(y1+x1//2)
如果((du>=0和dv>=0)或(du<0和dv<0))其他abs(du)+abs(dv),则返回max(abs(du),abs(dv)
在我看到我的一篇博文后,从另一个答案中获得了推荐流量。它被否决了,这是正确的,因为它是不正确的;但这是对我在帖子中提出的解决方案的错误描述
你的“蠕动”轴——就你的x坐标每隔一行被移动一次而言——将给你带来各种各样的头痛,如果这是为了某种游戏的话,你将在以后尝试确定距离或进行寻路。六边形网格自然适用于三个轴,六边形的“平方”网格最好有一些负坐标,这使得距离的数学计算更简单
这是一个绘制了(x,y)的网格,x增加到右下角,y向上增加
通过理顺问题,第三个轴变得显而易见
关于这一点,最巧妙的是三个坐标相互关联——所有三个坐标的总和总是0
在这种一致的坐标系中,任意两个六角之间的原子距离是三个坐标之间的最大变化,或者:
d = max( abs(x1 - x2), abs(y1 -y2), abs( (-x1 + -y1) - (-x2 + -y2) )
很简单。但你必须先修复你的网格 (奇数-r)(不带z,仅x,y)
我在上面的实现中看到了一些问题。对不起,我没有全部检查。但也许我的解决方案会对某些人有所帮助,也许这是一个糟糕的、未经优化的解决方案
主要思路是先按对角线,然后按水平线。但为此,我们需要注意:
1) 例如,我们有0;3(x1=0;y1=3)并且要转到y2=6,我们可以在6个步骤内处理每个点(0-6;6)
所以:0-左边界,6-右边界
2) 计算一些偏移量
#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;
}
#包括
#包括
int main()
{
//while(true){
int-x1,y1,x2,y2;
标准:cin>>x1>>y1;
标准:cin>>x2>>y2;
int diff_y=y2-y1;//仅向上->底部无需abs
int left_x,right_x;
int路径;
如果(y1>y2){//如果向下->向上,则交换
内部温度y=y1;
y1=y2;
y2=温度;
//
int temp_x=x1;
x1=x2;
x2=温度x;
}//现在我们有了向上->向下
//请注意,它是奇数-r水平布局
//OF-偏移线(y%2==1)
//NOF-未偏移行(y%2==0)
如果(y1%2==1&&y2%2==0){//OF->NOF
左_x=x1-((y2-y1+1)/2-1);//向上->向下无需abs
右_x=x1+(y2-y1+1)/2;//向上->向下无需abs
}
如果(y1%2==0&&y2%2==1){//OF->NOF
左x=x1-(y2-y1+1)/2;//向上->向下无需abs
右_x=x1+((y2-y1+1)/2-1);//向上->向下无需abs
}
否则{
左x=x1-(y2-y1+1)/2;//向上->向下无需abs
右_x=x1+(y2-y1+1)/2;//向上->向下无需abs
}
/////////////////////////////////////////////////////////////
如果(x2>=left_x&&x2您的网格有多大?为什么不使用距离公式?@OGH六边形网格中没有这样的步长公式,或者至少不像您所说的那样广为人知。网格的大小是m行和n列。不。它不适用于dist((2,0),(5,2))=4!=max(3,2)=3此方法无法确定给定示例之一的距离,dist((3,3),(5,5))=3此方法失败,因为它们使用不同的坐标系。@Serdalis是一个拼写错误,我的意思是“-”而不是“,”它似乎返回的距离比实际答案小1。(armin在对我答案的评论中给出的示例)。@Serdalis:我尝试过它:dist((0,0),(5,4))=max(5,abs((0+0/2)-(5+4/2))=max(5,7)=7;dist((3,3),(5,4))=max(2,abs((3+3/2)-(5+4/2))=ma
d = max( abs(x1 - x2), abs(y1 -y2), abs( (-x1 + -y1) - (-x2 + -y2) )
#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;
}