Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a more efficient way to detect polygon overlap/intersection than PathGeometry.FillContainsWithDetail()?

I have a method that is gobbling up 25% of my cpu time. I call this method about 27,000 times per second. (Yup, lots of calls since it's updating frequently). I am wondering if anybody knows a faster way to detect if 2 polygons overlap. Basically, I have to check the moving objects on the screen against stationary objects on the screen. I am using PathGeometry and the two calls below are using up 25% of the cpu time used by my program. The PointCollection objects I am passing just contain 4 points representing 4 corners of a polygon. They may not create a rectangular area, but all the points are connected. I guess a trapazoid would be the shape.

These methods are short and were very easy to implement, but I think I might want to opt for a more complicated solution if I can have it run more quickly than the code below. Any ideas?

public static bool PointCollectionsOverlap(PointCollection area1, PointCollection area2)
{
    PathGeometry pathGeometry1 = GetPathGeometry(area1);
    PathGeometry pathGeometry2 = GetPathGeometry(area2);
    return pathGeometry1.FillContainsWithDetail(pathGeometry2) != IntersectionDetail.Empty;
}

public static PathGeometry GetPathGeometry(PointCollection polygonCorners)
{
    List<PathSegment> pathSegments = new List<PathSegment> 
                                         { new PolyLineSegment(polygonCorners, true) };
    PathGeometry pathGeometry = new PathGeometry();
    pathGeometry.Figures.Add(new PathFigure(polygonCorners[0], pathSegments, true));
    return pathGeometry;
}
like image 286
Curtis Avatar asked Jun 25 '12 17:06

Curtis


People also ask

How do you find where two polygons intersect?

Compute the center of mass for each polygon. Compute the min or max or average distance from each point of the polygon to the center of mass. If C1C2 (where C1/2 is the center of the first/second polygon) >= D1 + D2 (where D1/2 is the distance you computed for first/second polygon) then the two polygons "intersect".

How do you know if two polygons overlap?

To be able to decide whether two convex polygons are intersecting (touching each other) we can use the Separating Axis Theorem. Essentially: If two convex polygons are not intersecting, there exists a line that passes between them. Such a line only exists if one of the sides of one of the polygons forms such a line.

How do you check if two polygons overlap in Python?

You can use the GDAL/OGR Python bindings for that. It returns None if they don't intersect. If they intersect it returns the geometry were both intersect. Also you can find further infos in the GDAL/OGR Cookbook .


1 Answers

Ok, after lots of research and finding many partial answers, but none that fully answered the question, I have found a faster way and it is actually about 4.6 times faster than the old way.

I created a special test app to test the speed this. You can find the test app here. If you download it, you can see a checkbox at the top of the app. Check and uncheck it to switch back and forth between the old way and the new way. The app generates a bunch of random polygons and the borders of the polygons change to white when they intersect another polygon. The numbers to the left of the 'Redraw' button are to allow you to enter the Number of Polygons, Max Length of a side, and Max offset from square (to make them less square and more odd shaped). Push 'Refresh' to clear and regenerate new polygons with the settings you've entered.

Anyway, here is the code for the two different implementations. You pass in a collection of the points that make up each polygon. The old way uses less code, but is 4.6 times slower than the new way.

Oh, one quick note. The new way has a couple calls to 'PointIsInsidePolygon'. These were necessary because without it, the method returned false when one polygon was entirely contained within a different polygon. But the PointIsInsidePolygon method fixes that problem.

Hope this all helps somebody else out with polygon intercepts and overlaps.

Old Way (4.6 times slower. YES REALLY 4.6 TIMES slower):

public static bool PointCollectionsOverlap_Slow(PointCollection area1, PointCollection area2)
{
    PathGeometry pathGeometry1 = GetPathGeometry(area1);
    PathGeometry pathGeometry2 = GetPathGeometry(area2);
    bool result = pathGeometry1.FillContainsWithDetail(pathGeometry2) != IntersectionDetail.Empty;
    return result;
}

public static PathGeometry GetPathGeometry(PointCollection polygonCorners)
{
    List<PathSegment> pathSegments = new List<PathSegment> { new PolyLineSegment(polygonCorners, true) };
    PathGeometry pathGeometry = new PathGeometry();
    pathGeometry.Figures.Add(new PathFigure(polygonCorners[0], pathSegments, true));
    return pathGeometry;
}

New Way (4.6 times faster. YES REALLY 4.6 TIMES faster):

public static bool PointCollectionsOverlap_Fast(PointCollection area1, PointCollection area2)
{
    for (int i = 0; i < area1.Count; i++)
    {
        for (int j = 0; j < area2.Count; j++)
        {
            if (lineSegmentsIntersect(area1[i], area1[(i + 1) % area1.Count], area2[j], area2[(j + 1) % area2.Count]))
            {
                return true;
            }
        }
    }

    if (PointCollectionContainsPoint(area1, area2[0]) ||
        PointCollectionContainsPoint(area2, area1[0]))
    {
        return true;
    }

    return false;
}

public static bool PointCollectionContainsPoint(PointCollection area, Point point)
{
    Point start = new Point(-100, -100);
    int intersections = 0;

    for (int i = 0; i < area.Count; i++)
    {
        if (lineSegmentsIntersect(area[i], area[(i + 1) % area.Count], start, point))
        {
            intersections++;
        }
    }

    return (intersections % 2) == 1;
}

private static double determinant(Vector vector1, Vector vector2)
{
    return vector1.X * vector2.Y - vector1.Y * vector2.X;
}

private static bool lineSegmentsIntersect(Point _segment1_Start, Point _segment1_End, Point _segment2_Start, Point _segment2_End)
{
    double det = determinant(_segment1_End - _segment1_Start, _segment2_Start - _segment2_End);
    double t = determinant(_segment2_Start - _segment1_Start, _segment2_Start - _segment2_End) / det;
    double u = determinant(_segment1_End - _segment1_Start, _segment2_Start - _segment1_Start) / det;
    return (t >= 0) && (u >= 0) && (t <= 1) && (u <= 1);
}
like image 129
Curtis Avatar answered Sep 30 '22 11:09

Curtis