views:

695

answers:

7

Hi, I am trying to implement the Winding Number Algorithm to test if a point is within another polygon. Although the results from my algorithm are wrong and not consistent. I have been working on this for ages now and it has become a bit of a pain!

I have basically converted pseudo code from notes and websites, such as, softsurfer.com

I successfully detect if my player and building object bounding boxes overlap. I return the result to a struct, (BoxResult) which lets me know if there has been a collision and returns the box which it has collided with (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
     }
    }
}

I then test for a collision between the building and the player (Below). I have tried 5 different attempts and hours of debugging to understand where the error is occuring, however I am implementing the most ineffienct method which just uses maths (Below).

      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.

Also incase you need to know these, wy and wx are the world co-ordinates which are translated. Thus moving the player around the world e.g. to move the player forward everything is translated by a minus number for wy.

Further, a Building object would look something like the following struct below:

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

If anyone can help I would amazingly grateful! I just wish I could see where it is all going wrong! (Something I am sure all programmers have said in there time lol) Thanks for readings...

+1  A: 

The two lines calculating the modulus of PV and PV1 are incorrect. They should be

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

Does that fix the problem?

Troubadour
Hi there, that has sorted the problem concerning the incorrect modulus being calculated however I still have other problems occuring. I have made the edits to the code and ammended the copy of my code in the first post. Which I will explain in another comment, as I have run out of space!
Graham
Two major problems occur, when the Point p intersects with the bounding box of Building b in the x-axis the algorithm returns that the point is within b. Despite there being a large gap.Also it appears that in the y axis, it only detects if the orgin is within Building b. This does not make sense!
Graham
PV is the vector from P to V is it not? If so then you need to subtract (b.getXi(i) + wx) from p.x, etc.BTW I just noticed that you can save a sqrt in denom. Just multiply out the sum of the squares for PV and PV1 and then take the sqrt at the end.
Troubadour
I have tried, as Troubadour has suggested however this does not work and always returns 0.The winding num formula : 1/2PI * SUM OF acos(PVi . PVi+1/ |PVi| |PVi+1|) FOR ALL ISorry I am unsure how to write the summation symbol on here. Any other ideas?
Graham
Well the description on softsurfer does say it is the *signed* angles you need. Remember acos will only give you an angle between 0 and pi. Also I'm sceptical about checking for exact equality to 360. Should that not just be if ( abs(wn) >= 360 ) result = 1; since result is initialised to 0?
Troubadour
A: 

Hi again, I have given up with the winding number code, it really has got me! If anyone does find the solution I would still be amazingly grateful. I am now trying with point in poly detection using the crossing number algorithm. I kept the pesudo code in the comments, again from 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)
}

Again this always returns zero, I am unsure why!?! Have I converted the algorithm incorrectly? Does it matter which direction the points are tested (i.e. clockwise, anti-clockwise)?

Graham
All I can say is that you should really try to understand the algorithm. Once you do, you'll be able to debug the code. It's just basic geometry + common sense, after all.
+1  A: 

I probably don't understand your problem/question, but there's a simple and robust point in polygon test available here: PNPOLY.

Believe it or not, but I have already tried doing that point in poly test aswell!lol I will post the code in another answer to my question below...
Graham
A: 

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...

Original C code...

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

My code....

Where wx and wy are the global co-ordinates.

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

I am testing the player object, against a 2D square building. This also returns strange results, when I hit bottom line (xmin,ymin to xmax,ymin) it works fine. If I hit ethier of the sides (xmin,ymin to xmin,ymax or xmax,ymin to xmax,ymax) it returns 1 only if the player is so far in its past the orgin point. Also on side (xmin,ymin to xmin,ymax) where the player enters the bounding box the algorithm returns 2 despite to hitting the polygon. On the top side, (xmin,ymax to xmax,ymax) it returns 1 only if the player is totally in the polygon.

Also i pass two vectors x and y which are from the Building object, and the vector size as int nvert. Could any of this be to do with the heading of the player object? How is the accounted for within the algorithm?

Graham
+1  A: 

As regards your implementation of the crossing number algorithm the first obvious mistake is that you are not looping over all the sides. You are one short. You should loop up to i < n and then define i plus one as

int ip1 = ( i + 1 )  % n;

This applies to the code in your original question too of course to save you having to have two copies of the code.

The second one is that

rem = cn % 1;

has no effect. The code on softsurfer is fine i.e.

rem = (cn&1);

It is trying to detect if cn is odd or even by testing if the last bit is set. If you want to the same test using the modulo operator % then you should write it as

rem = cn % 2;

as that assigns the remainder on division by two of cn to rem.

I haven't looked beyond that to see if there are any more issues.

Troubadour
Ah, I see I made some silly errors. I have made the corrections that you have suggested, however the if statement never evaluates as true. I'll post my code up again in another answer, with the new changes. Thanks again for your spotting those errors, truly appreciated!
Graham
A: 

Hi have done as Troubadour has suggested concerning the crossing number algorithm and made several changes, however the if statement never returns true for some reason. I post of the new code is below. Btw thanks again for everyones replies :-)

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)
}
Graham
A: 

Below I corrected the code, I forgot to add the world co-ords into account. Yet another silly silly error...

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);
    ret

urn (rem); // 0 if even (out), and 1 if odd (in) }

Although this works to detect when a point is in a polygon, it does not take into account the current heading of the player.

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.

These are figured out within the draw function, however the collision detection function does not take the heading into account. To do this do I symply multiply the x and y co-ord given by the Building object by the heading? Unfortunately I am not very good at geometry.

Graham
For each frame, make a copy of the objects untransformed geometry and transform that copy into world space as you would for rendering. Use that transformed copy also for collision detection etc. Functions that deal with the geometry can then assume that it's already in world space i.e. remove wx/wy.
Functastic
Oh, are you asking for code to rotate a polygon?
Functastic
Ah I think I get it. So basically for every point of the Building object I need to transform it, by the same amount which I transformed the players heading. It should be ok, I have the code to transform the polygon further up in my code. I will post the finished solution (which hopefull will work!)
Graham
Ah I now totally get this! I have finished my 2D game and will later post up the solutions to both algorithms with an easier explanation of how they work and what to do when I have got the time.
Graham