Reputation: 9784
I'm using the .NET API for Autocad, I have an algorithm (which I did not write) for determining if a point lies within a polygon (straight lines only).
I have been testing my command on the same 51 polygons repeatedly. 99% it will work perfectly. Every once in a while it will fail on 1 or more of the polygons, returning false for over 2000 points I am creating inside the bounding box of the polyline. I have seen it fail when the polyline isa simple rectangle and all of the points lie distributed in a grid within the polyline. It should have returned true over 2000 times in that case. It will never fail for just 1 of the points, it will fail all of them. I have confirmed that the points are being correctly created where I expect them to be and that the vertices of the polygon are where I expect them to be. When it fails, the last angle variable for the last point is at exactly double PI.
I am not doing any multi-threading. The only possibly 'funny' thing I am doing is COM Interop with Excel. This is happening after the transaction has been committed for the part with this algorithm, and I am sure I am cleaning up all my COM objects. I have not been able to reproduce the failure without the COM Interop part but I don't think I've tested it enough yet to have enough absence of evidence.
Any ideas what could be wrong?
bool IsInsidePolygon(Polyline polygon, Point3d pt)
{
int n = polygon.NumberOfVertices;
double angle = 0;
Point pt1, pt2;
for (int i = 0; i < n; i++)
{
pt1.X = polygon.GetPoint2dAt(i).X - pt.X;
pt1.Y = polygon.GetPoint2dAt(i).Y - pt.Y;
pt2.X = polygon.GetPoint2dAt((i + 1) % n).X - pt.X;
pt2.Y = polygon.GetPoint2dAt((i + 1) % n).Y - pt.Y;
angle += Angle2D(pt1.X, pt1.Y, pt2.X, pt2.Y);
}
if (Math.Abs(angle) < Math.PI)
return false;
else
return true;
}
public struct Point
{
public double X, Y;
};
public static double Angle2D(double x1, double y1, double x2, double y2)
{
double dtheta, theta1, theta2;
theta1 = Math.Atan2(y1, x1);
theta2 = Math.Atan2(y2, x2);
dtheta = theta2 - theta1;
while (dtheta > Math.PI)
dtheta -= (Math.PI * 2);
while (dtheta < -Math.PI)
dtheta += (Math.PI * 2);
return (dtheta);
}
Upvotes: 0
Views: 2855
Reputation: 1
I used some code from Kean Walmsley to convert 3d lines into 2d lines. But be aware that the following is not (always) true:
Point2d pt = lwp.GetPoint2dAt(i);
Point2d npt = new Point2d(lwp.GetPoint3dAt(i).X, lwp.GetPoint3dAt(i).Y);
pt == npt;
I encountered it using it on a polylines, with 3d vertices. I ended up using the npt.
http://through-the-interface.typepad.com/through_the_interface/2007/04/iterating_throu.html
Upvotes: 0
Reputation: 11
another method. Make one "temporary" point outside the polygon (find the min X and Y and make a point with X-1 and Y-1). Then make a line between your point and the new "temporary" point. Check if this line crosses the polygon - use polyline.IntersectWith. If number of cross points is odd - then your point is inside, if the number of crosses is even - the your point is not inside. This works for me, hope it will helps you also. If you find trouble with implementing this, i can send you an example code. Regards, Dobriyan Benov
Upvotes: 1
Reputation: 185
Some ideas:
floating point comparison have to be done using a tolerence, this might cause kind of arbitrary results especially in case where the point lies on the polyline (same remark for point3d, they must be compared using a tolerence)
maybe the last point of your polyline is at the same location as the first one, in that case, the angle cannot be computed (perhaps this is why you get a double pi value for the last point). You should then test is first and last points are equals.
I'm not sure your algorithm works regardless if the polyline is clockwise or counterclockwise (I think yes)
you may convert your polyline into a region and rely on region point containment method
Upvotes: 1