我正在尝试实现绕组数算法来测试一个点是否在另一个多边形内。虽然我的算法的结果是错误的并且不一致。我已经做了很多年了,现在已经变得有点痛苦了!
我基本上已经从笔记和网站转换了伪代码,例如softsurfer.com
我成功检测到我的播放器和构建对象边界框是否重叠。我将结果返回给一个结构体(BoxResult),它让我知道是否有碰撞并返回它碰撞的框(下图)
struct BoxResult{
bool collide;
Building returned;
};
void buildingCollision(){
int wn = 0; //winding number count
BoxResult detect = boxDetection(); //detect if any bounding boxes intersect
if(detect.collide){ //If a bounding box has collided, excute Winding Number Algorithm.
for(int i = 0; i < player.getXSize(); i++){
Point p;
p.x = player.getXi(i);
p.y = player.getYi(i);
wn = windingNum(detect.returned,p);
cout << wn << endl;
//Continue code to figure out rebound reaction
}
}
}
然后我测试建筑物和玩家之间的碰撞(下图)。我已经尝试了5次不同的尝试和数小时的调试来了解错误发生的位置,但是我实现了最无效的方法,只使用数学(下图)。
int windingNum(Building & b, Point & p){
int result = 0; //Winding number is one, if point is in poly
float total; //Counts the total angle between different vertexs
double wn;
for(int i = 0; i <= b.getXSize()-1;i++){
float acs, nom, modPV, modPV1, denom, angle;
if(i == 3){
//Create the different points PVi . PVi+1
Point PV, PV1;
PV.x = (b.getXi(i) + wx) * p.x;
PV.y = (b.getYi(i) + wy) * p.y;
PV1.x = (b.getXi(0) + wx) * p.x;
PV1.y = (b.getYi(0) + wy) * p.y;
modPV = sqrt( (PV.x * PV.x) + (PV.y * PV.y)); //Get the modulus of PV
modPV1 = sqrt( (PV1.x * PV1.x) + (PV1.y * PV1.y)); //Get modulus of PV1
nom = (PV1.x * PV.x) + (PV1.y * PV.y); //Dot product of PV and PV1
denom = modPV * modPV1; //denomintor of winding number equation
angle = nom / denom;
acs = acos(angle) * 180/PI; //find the angle between the different points
total = total + acs; //add this angle, to the total angle count
}
if(i < 3){
//Create the different points PVi . PVi+1
Point PV, PV1;
PV.x = (b.getXi(i) + wx) * p.x;
PV.y = (b.getYi(i) + wy) * p.y;
PV1.x = (b.getXi(i+1) +wx) * p.x;
PV1.y = (b.getYi(i+1) +wy) * p.y;
modPV = sqrt((PV.x * PV.x) + (PV.y * PV.y)); //Get the modulus of PV
modPV1 = sqrt((PV1.x * PV1.x) + (PV1.y * PV1.y)); //Get modulus of PV1
nom = (PV1.x * PV.x) + (PV1.y * PV.y); //Dot product of PV and PV1
denom = modPV * modPV1; //denomintor of winding number equation
angle = nom / denom;
acs = acos(angle) * 180/PI; //find the angle between the different points
total = total + acs; //add this angle, to the total angle count
}
}
wn = total;
if(wn < 360){
result = 0;}
if(wn == 360){
result = 1;}
return result;
}
由于原因我不明白acs = acos(angle)总是返回1.#IND000。 顺便说一句,所以你知道,我只是针对另一个方格测试算法,因此如果i == 3并且如果i&lt; 3.
此外,你需要知道这些,wy和wx是被翻译的世界坐标。从而将玩家移动到世界各地,例如为了向前移动玩家,所有内容都会被一个减号转换为wy。
此外,Building对象看起来类似于以下结构:
struct Building {
vector<float> x; //vector storing x co-ords
vector<float> y; //vector storing y co-ords
float ymax, ymin, xmax, xmin //values for bounding box
vector<int> polygons; //stores the number points per polygon (not relevant to the problem)
}
如果有人可以提供帮助,我会非常感激!我只是希望我能看到它出错的地方! (我确信所有程序员都在那里说过一些事情lol)感谢阅读...
答案 0 :(得分:1)
计算PV和PV1模数的两条线是不正确的。他们应该是
modPV = sqrt(PV.x * PV.x + PV.y * PV.y );
modPV1 = sqrt(PV1.x * PV1.x + PV1.y * PV1.y);
这样可以解决问题吗?
答案 1 :(得分:1)
我可能不理解你的问题/问题,但这里有一个简单而强大的多边形测试点:PNPOLY。
答案 2 :(得分:1)
关于你的交叉数算法的实现,第一个明显的错误是你没有遍历所有方面。你是一个简短的。你应该循环到i&lt; n然后将i加1定义为
int ip1 = ( i + 1 ) % n;
这当然也适用于原始问题中的代码,以节省您必须拥有两份代码的副本。
第二个是那个
rem = cn % 1;
无效。 softsurfer上的代码很好,即
rem = (cn&1);
它试图通过测试最后一位是否设置来检测cn是奇数还是偶数。如果你想使用模运算符%进行相同的测试,那么你应该把它写成
rem = cn % 2;
因为它将余数除以cn中的两个除去。
我没有超越它,看看是否还有其他问题。
答案 3 :(得分:0)
我已经放弃了蜿蜒的数字代码,它真的有我!如果有人确实找到了解决方案,我仍然会非常感激。我现在尝试使用交叉数算法进行多边形检测。我在评论中保留了pesudo代码,再次来自softsurfer ....
int cn_PnPoly( Point P, Building & b, int n )
{
int cn = 0; // the crossing number counter
int rem = 0;
vector<float>x;
vector<float>y;
x.swap(b.getX());
y.swap(b.getY());
//// loop through all edges of the polygon
//for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
// if (((V[i].y <= P.y) && (V[i+1].y > P.y)) // an upward crossing
// || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
// // compute the actual edge-ray intersect x-coordinate
// float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
// if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
// ++cn; // a valid crossing of y=P.y right of P.x
// }
//}
//return (cn&1); // 0 if even (out), and 1 if odd (in)
// loop through all edges of the polygon
for (int i=0; i<n-1; i++) { // edge from V[i] to V[i+1]
if (((y.at(i) <= P.y) && (y.at(i+1) > P.y)) // an upward crossing
|| ((y.at(i) > P.y) && (y.at(i+1) <= P.y))) { // a downward crossing
// compute the actual edge-ray intersect x-coordinate
float vt = (float)(P.y - y.at(i)) / (y.at(i+1) - y.at(i));
if (P.x < x.at(i) + vt * (x.at(i+1) - x.at(i))) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
rem = cn % 1;
return (rem); // 0 if even (out), and 1 if odd (in)
}
再次这总是归零,我不确定为什么!?!我是否错误地转换了算法?测试点的方向(即顺时针,逆时针)是否重要?
答案 4 :(得分:0)
我已尝试按照audris的建议实施PNPOLY。然而,这给了一些有趣的结果。 下面是原始的C代码,然后是我的应用程序的转换...
原始C代码......
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
我的代码......
wx和wy是全球坐标。
int pnpoly(int nvert, vector<float> vertx, vector<float> verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( (( (verty.at(i)+wy) > testy) != ( (verty.at(j)+wy) >testy)) &&
(testx < ((vertx.at(j)+wx) - (vertx.at(i)+wx) ) * (testy- (verty.at(i)+wy) ) / ( (verty.at(j)+wy) - (verty.at(i)+wy)) + (vertx.at(i)+wx)) )
c++;
}
return c;
}
我正在测试玩家对象,对着2D方形建筑物。这也会返回奇怪的结果,当我达到底线(xmin,ymin到xmax,ymin)时,它可以正常工作。如果我击中两侧的边界(xmin,ymin到xmin,ymax或xmax,ymin到xmax,ymax),只有当玩家距离orgin点过去时才返回1。此外,在玩家进入边界框的一侧(xmin,ymin到xmin,ymax),算法返回2,尽管击中了多边形。在顶部,(xmin,ymax到xmax,ymax)只有当玩家完全在多边形中时才会返回1。
此外,我传递两个向量x和y,它们来自Building对象,向量大小为int nvert。这可能与玩家对象的标题有关吗?如何在算法中加以考虑?
答案 5 :(得分:0)
你已经完成了Troubadour关于交叉数算法的建议并做了几处修改,但if语句由于某种原因永远不会返回true。我发布的新代码如下。顺便回答每个人的回复: - )
int cn_PnPoly( Point P, Building & b, int n )
{
int cn = 0; // the crossing number counter
int rem = 0;
vector<float>x;
vector<float>y;
x.swap(b.getX());
y.swap(b.getY());
//// loop through all edges of the polygon
//for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
// if (((V[i].y <= P.y) && (V[i+1].y > P.y)) // an upward crossing
// || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
// // compute the actual edge-ray intersect x-coordinate
// float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
// if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
// ++cn; // a valid crossing of y=P.y right of P.x
// }
//}
//return (cn&1); // 0 if even (out), and 1 if odd (in)
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
int ip1 = (i +1) %n;
if (((y.at(i) <= P.y) && (y.at(ip1) > P.y)) // an upward crossing
|| ((y.at(i) > P.y) && (y.at(ip1) <= P.y))) { // a downward crossing
// compute the actual edge-ray intersect x-coordinate
float vt = (float)(P.y - y.at(i)) / (y.at(ip1) - y.at(i));
if (P.x < x.at(i) + vt * (x.at(ip1) - x.at(i))) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
rem = (cn&1);
return (rem); // 0 if even (out), and 1 if odd (in)
}
答案 6 :(得分:0)
下面我更正了代码,我忘了添加世界合作伙伴。又一个愚蠢的愚蠢错误......
int cn_PnPoly( Point P, Building & b, int n )
{
int cn = 0; // the crossing number counter
int rem = 0;
vector<float>x;
vector<float>y;
x.swap(b.getX());
y.swap(b.getY());
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
int ip1 = (i +1) %n;
if ((( (y.at(i)+wy) <= P.y) && ( (y.at(ip1)+wy) > P.y)) // an upward crossing
|| (( (y.at(i)+wy) > P.y) && ( (y.at(ip1)+wy) <= P.y))) { // a downward crossing
// compute the actual edge-ray intersect x-coordinate
float vt = (float)(P.y - (y.at(i)+wy) ) / ( (y.at(ip1)+wy) - (y.at(i)+wy) );
if (P.x < (x.at(i)+wx) + vt * ( (x.at(ip1)+wx) - (x.at(i)+wx) )) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
rem = (cn&1);
return (rem); // 0 if even (out), and 1 if odd (in)
}
虽然这可以检测点何时在多边形中,但它不会考虑播放器的当前标题。
如果这没有意义,在2D游戏中,我通过按世界坐标翻译所有多边形来移动玩家周围的世界地图。这些在游戏中是wx和wy。 此外,我还会围绕一个可变的标题旋转玩家。
这些是在绘制函数中计算出来的,但是碰撞检测功能不考虑标题。要做到这一点,我是否将由Building对象给出的x和y co-ord乘以标题?不幸的是,我不太擅长几何。