tags:

views:

113

answers:

4

I have an algorithm which can find if a point is in a given polygon:

 int CGlEngineFunctions::PointInPoly(int npts, float *xp, float *yp, float x, float y)
 {
     int i, j, c = 0;
     for (i = 0, j = npts-1; i < npts; j = i++) {
         if ((((yp[i] <= y) && (y < yp[j])) ||
             ((yp[j] <= y) && (y < yp[i]))) &&
             (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
             c = !c;
     }
     return c;
 }

given this, how could I make it check if its within a rectangle defind by Ptopleft and Pbottomright instead of a single point?

Thanks

Basically you know how in Adobe Illustrator you can drag to select all objects that fall within the selection rectangle? well I mean that. –

+3  A: 

Can't you just find the minimum and maximum x and y values among the points of the polygon and check to see if any of the values are outside the rectangle's dimensions?

Jeffrey
Okay, I already compute min and max so i'll just do this.
Milo
does this work for a circle too?
Milo
@Jex: Yes, you can use this for any shape around which you can construct a bounding rectangle. That includes circles, polygons, triangles, lines, curves, hexagons and...why, yes, in fact all shapes :) (This is assuming that you want to see if a shape is inside a rectangle, the question isn't entirely clear)
Bart van Heukelom
@Bart van Heukelom: Er, not quite. That would only tell you if the polygon was within the bounding rectangle. It could be within the bounding square for a circle but not completely within the circle.
Troubadour
@Jex: To modify it for a circle you would have to go round each polygon vertex and check its distance from the circle's centre. If any of them exceed the circle radius then the polygon is not within the circle.
Troubadour
@Jeff: The OP asked for an algorithm that checks whether a rectangle lies inside a polygon. You for some reason respond with the algorithm that checks whether the polygon lies inside the rectangle (i.e. vise-versa)... A separate question, of course, is why the OP marks it as an accepted answer...
AndreyT
@Andrey: Yes, that's why I said the question is unclear :P@Troubadour: My comment says "This is assuming that you want to see if a shape is inside a rectangle"
Bart van Heukelom
A: 
int isPointInRect( Point point, Point ptopleft, Point pbottomright) {

   float xp[2] ;
   xp[0] = ptopleft.x, 
   xp[1] = pbottomright.x;

   float yp[2] ;
   yp[0] = ptopleft.y ;
   yp[1] = pbottomright.y ;

   return CGlEngineFunctions::PointInPoly(2, xp, yp, point.x, point.y);
}
tpdi
This was my first reading of the question too, but thankfully I think wrong -- this would be taking reuse too far, since the infrastructure to do so is basically more complicated than solving the simpler problem from scratch.
walkytalky
The given function walks through the vertices of the polygon and checks whether the point is at the same side relative to all of the sides. For this, it requires an array of all the vertices coordinates. In your solution, there is only the two opposite corners of the rectangle.
ysap
@ysap: No, it doesn't do that. The given function checks how many times an infinite ray extended from the test point `(x, y)` to the right intersects the edges of the polygon. If the number is odd, the point is inside. If the number is even, the point is outside. This is a classic algorithm. You have totally misinterpreted it.
AndreyT
A: 

EDIT: duh, I misinterpreted the question. If you want to ensure that the polygon is encosed by a rectangle, do a check for each polygon point. You can do that more cheaply with the minimum/maximum x and y coordinates and checking if that rectangle is within the query rectangle.

EDIT2: Oops, meant horizontal, not vertical edges.

EDIT3: Oops #2, it does handle horizontal edges by avoiding checking edges that are horizontal. If you cross multiply however, you can avoid the special casing as well.

MSN
What do you mean regarding the vertical edges?
Milo
He wanted it implemented in terms of his existing function.
tpdi
@Jex, I meant horizontal edges. If two adjacent points have the same Y value, you will get a divide by zero.
MSN
@MSN well it does infact work with convex polygons or any other, i'v never gotten a div by zero error with it.
Milo
A: 

As mentioned before, for that specific problem, this function is an overkill. However, if you are required to use it, note that:
1. It works only for convex polygons,
2. The arrays holding the polygon's vertices must be sorted such that consecutive points in the array relate to adjacent vertices of your polygon.
3. To work properly, the vertices must be ordered in the "right hand rule" order. That means that when you start "walking" along the edges, you only make left turns.

That said, I think there is an error in the implementation. Instead of:

// c initialized to 0 (false), then...
c = !c;  

you should have something like:

// c initialized to 1 (true), then...
// negate your condition:
if ( ! (....))
    c = 0;
ysap
Absolutely incorrect. The code in the original post implements the classic parity-based algorithm. It works for *any* polygon, regardless of whether it is convex or not. And the whole point of it is to do a parity check, which is why it does `c = !c` inside. I haven't checked this specific implementation for bugs, but in any case the above is true. Of course, it is assumed that array elements describe the consecutive points of the polygon. And no, there's no requirement for the points to be ordered in right-hand rule order. They can be ordered in any direction (out of the two possible).
AndreyT
@AndreyT - thanks for the correction. You reminded me things I forgot a long ago.
ysap