English 中文(简体)
错误,实现绕数算法,(OpenGL,C++)
原标题:
  • 时间:2009-03-20 19:26:11
  •  标签:

我正尝试实现绕数算法来测试一个点是否在另一个多边形内。虽然我的算法结果是错误且不一致的。我已经研究这个已经很久了,而且它已经成为了一个烦人的问题!

我基本上是从笔记和网站上转换了伪代码,例如:softsurfer.com

我成功地检测到我的玩家对象和建筑物对象的边界框是否重叠。我将结果返回到一个结构体(BoxResult),它能让我知道是否发生了碰撞并返回它所发生碰撞的框(Below)。

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;
}

For reasons I do not understand acs = acos(angle) always returns 1.#IND000. Btw so you know, I am just testing the algorithm against another square, hence the two if statements if i == 3 and if i < 3.

而且,如果您需要知道,wy和wx是已经转换的世界坐标。因此,移动玩家在世界中的位置,例如向前移动玩家,所有坐标都需要被wy的负数所转换。

此外,一个楼房对象的结构将类似于以下的结构体:

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)
}

如果有人能帮忙,我会感激不尽!我只希望能看到哪里出了问题!(我相信所有的程序员都曾说过这句话,哈哈)谢谢您的阅读...

问题回答

计算 PV 和 PV1 的模数的两条线是不正确的。它们应该是:

modPV  = sqrt(PV.x  * PV.x  + PV.y  * PV.y );
modPV1 = sqrt(PV1.x * PV1.x + PV1.y * PV1.y);

那解决了问题吗?

我可能不明白你的问题/疑问,但是这里有一个简单而强大的点和多边形测试:PNPOLY

关于你对十字数算法的实现,第一个明显的错误是你没有循环所有的边,你缺了一个。你应该循环到 i < n,然后将 i 加一定义为

int ip1 = ( i + 1 )  % n;

这当然也适用于您原始问题中的代码,这样可以避免您必须拥有两份代码。

第二个是那个。

rem = cn % 1;

没有影响。Softsurfer上的代码很好,即

rem = (cn&1);

它尝试通过测试最后一位是否设置来检测cn是奇数还是偶数。如果您想使用取模运算符%进行相同的测试,则应将其写为

rem = cn % 2;

将cn÷2的余数分配给rem。

我还没有进一步查看是否有其他问题。

我已经放弃了环绕数编码,它真的让我束手无策!如果有人找到解决方案,我仍然会非常感激。我现在正在尝试使用交叉数算法来检测点在多边形内。我将伪代码保留在评论中,同样来自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)
}

再次运行后,结果仍然为零,我不确定为什么!?我把算法转换错误了吗?测试点的方向(即顺时针,逆时针)是否重要?

I have tried implementing PNPOLY as audris suggests. However this gives some funny results. Below is the orginal C code, then below that is my conversion of that for my app...

原始的 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),只有当玩家在其过去的原点时,才会返回1。此外,在侧面(xmin,ymin到xmin,ymax)上,玩家进入边界框时,算法返回2,尽管未击中多边形。在顶部(xmin,ymax到xmax,ymax)上,只有当玩家完全在多边形中时,它才返回1。

我还传递了两个向量x和y,它们来自Building对象,并将向量大小作为int nvert。这些是否与玩家对象的标题有关?该算法如何对其进行考虑?

嗨,我已经按照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)
}

以下是翻译:我已经纠正了代码,我忘记将世界坐标纳入考虑。又是一个愚蠢的错误...

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)
}

尽管这能够检测一个点是否在多边形内,但它没有考虑到玩家当前的朝向。

If this doesn t make sense, in the 2D game I move the world map around the player by translating all the polygons by the world co-ordinates. These are wx and wy in the game. Also I rotate the player about a heading varriable.

这些是在绘制函数中计算出来的,然而碰撞检测函数没有考虑方向。为了解决这个问题,我是否只要将 Building 对象给定的 x 和 y 坐标乘以方向即可?不幸的是,我对几何学不是很擅长。





相关问题
热门标签