如何判断两个多边形是否相交?

10

假设我有4个点的坐标,它们组成了一个多边形。这些点用C#中的PointF表示。如果我有两个多边形(使用8个点),如何判断它们是否相交?

Rectangle类有一个名为IntersectsWith的方法,但我找不到类似于GraphicsPath或Region的东西。

非常感谢任何建议。

Mosh

4个回答

6

正如Charlie所指出的那样,您可以使用分离轴定理。

查看此文章,了解C#实现和多边形碰撞检测示例。

我还在这里回答了关于C#中的2D碰撞问题。


4

严格来说,其他回答提供的算法可能是你最好的选择。但是除了性能之外,你提到无法找到GraphicsPath或Region的IntersectsWith类似方法。然而,有一个Intersect方法,可以更新区域为它自己和另一个区域或路径的交集。您可以创建两个区域,使用Intersect()方法将其相交,然后测试Region.IsEmpty()。

但我想这可能是一种相当缓慢的方法,如果在循环中执行,可能会导致大量分配。


1
令人惊讶的是,如果这两个区域没有相交,我并没有注意到减速。只有当这些区域相交时才会有明显的减速。(尽管我测试的区域并不特别复杂。) - user2428118

2
这是一个老问题,但我想分享我的解决方案。Region.IsEmpty()需要一个Graphics上下文,并且据我所知,仅设计用于执行像素精度的命中测试。这对许多情况并不理想。一个更好的解决方案是使用Angus Johnson的Clipper库。在我的经验中,这是一个快速、经过充分测试的库。您可以提供自己的精度,它处理非常复杂的多边形。

http://www.angusj.com/delphi/clipper.php

有一个C#实现。你需要做的就是执行一个交集操作,就像System.Drawing.Region方法一样。然后检查操作结果。如果它为空,则不存在交集。如果包含数据,则数据就是交点。

http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/ClipType.htm

一些你会发现有用的方法。
private static int scale = 1000; //your desired precision

        public static List<List<IntPoint>> ConvertToClipperPolygons(GraphicsPath path)
    {
        var Polygon = new List<IntPoint>();
        var Polygons = new List<List<IntPoint>>();

        var it = new GraphicsPathIterator(path);
        it.Rewind();
        bool isClosed;
        int startIndex;
        int endIndex;
        for (int i = 0; i < it.SubpathCount; i++)
        {
            var PointCount = it.NextSubpath(out startIndex, out endIndex, out isClosed);

            var Points = new PointF[PointCount];
            var Types = new byte[PointCount];
            it.CopyData(ref Points, ref Types, startIndex, endIndex);
            Polygons.Add(new List<IntPoint>(Points.Select(x => new IntPoint(Convert.ToInt64(x.X * scale), Convert.ToInt64(x.Y * scale)))));

        }
        it.Dispose();
        return Polygons;
    }

进行一个交集操作
        public static GraphicsPath intersect(ref GraphicsPath p1, ref GraphicsPath p2)
    {
        List<List<IntPoint>> polygonB = ConvertToClipperPolygons(p1);
        List<List<IntPoint>> polygons = new List<List<IntPoint>>();
        List<List<IntPoint>> polygonA = ConvertToClipperPolygons(p2);

        Clipper c = new Clipper();
        c.AddPolygons(polygonB, PolyType.ptSubject);
        c.AddPolygons(polygonA, PolyType.ptClip);
        c.Execute(ClipType.ctIntersection, polygons, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);

        return ConvertClipperToGraphicsPath(polygons);
    }
        public static GraphicsPath ConvertClipperToGraphicsPath(List<List<IntPoint>> path)
    {
        GraphicsPath returnPath = new GraphicsPath();

        for (int i = 0; i < path.Count; i++)
        {
            returnPath.AddPolygon(ToPointList(path[i]).ToArray());
        }
        return returnPath;
    }
        private static List<PointF> ToPointList(List<IntPoint> pointList)
    {

        List<PointF> newList = new List<PointF>();
        foreach (IntPoint pt in pointList)
        {
            newList.Add(new PointF(((float)pt.X / (float)scale), ((float)pt.Y / (float)scale)));
        }

        return newList;
    }

1
如果你的多边形是凸多边形,那么你应该能够使用分离轴定理。这里有一个演示链接(它是用ActionScript编写的,但代码应该很容易移植到C#)。
这真的不是我的专业领域,但我希望它能帮到你。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接