多边形相交的简单算法

Posted

技术标签:

【中文标题】多边形相交的简单算法【英文标题】:A simple algorithm for polygon intersection 【发布时间】:2011-01-17 08:22:01 【问题描述】:

我正在寻找一种非常简单的算法来计算多边形交集/裁剪。 也就是说,给定多边形PQ,我希望找到包含在PQ 中的多边形T,并且我希望T 在所有可能的多边形中是最大的。

我不介意运行时间(我有一些非常小的多边形),我也可以得到多边形交点的近似值(即,一个点数较少的多边形,但仍包含在多边形的交点)。

但对我来说,算法简单(更便宜的测试)并且最好是简短(更少的代码)对我来说非常重要。

编辑:请注意,我希望获得一个代表交叉点的多边形。对于两个多边形是否相交的问题,我只需要一个布尔答案。

【问题讨论】:

多边形是否凸?因为如果不是,那么它们的交点将不需要一个多边形。 @DNNX,如果它们是凸的,那将很容易。它们不是凸的,我有兴趣找到代表交叉点的所有多边形。 你看这个问题了吗?你的不太一样,因为你问的是实现的简单性。但是提到的一些库可能会满足您的需求...***.com/questions/1526352/… 【参考方案1】:

我知道原发帖人正在寻找一个简单的解决方案,但不幸的是,真的没有简单的解决方案。

尽管如此,我最近创建了一个开源免费软件剪辑库(用 Delphi、C++ 和 C# 编写),它可以剪辑各种多边形(包括自相交的多边形)。这个库使用起来非常简单:http://sourceforge.net/projects/polyclipping/。

【讨论】:

我自己不久前得出了这个不幸的结论。每个解决方案都极其复杂。感谢图书馆! 也许我还应该提到,我的 Clipper 库与其他剪贴库相比也非常出色:angusj.com/delphi/clipper.php#features @angus johnson 如果一个多边形与另一个多边形相交或是否完全包含,您将使用什么来进行简单测试? @AngusJohnson,您的库是否支持计算两个开放路径的交点?谢谢 2018 年更新:Polyclipping 已重命名为 Clipper,可作为 NuGet 包使用。【参考方案2】:

您可以使用 多边形裁剪 算法来查找两个多边形之间的交点。然而,当考虑到所有边缘情况时,这些算法往往是复杂的。

Weiler-Atherton 是您可以使用自己喜欢的搜索引擎查找的一种多边形裁剪实现。 wikipedia article on Weiler-Atherton

Alan Murta 有一个完整的多边形剪裁器 GPC。

编辑:

另一种方法是先将每个多边形分成一组三角形,这样比较容易处理。 Gary H. Meisters 的双耳定理可以解决问题。这个page at McGill很好地解释了三角形的细分。

【讨论】:

我搜索了多边形裁剪,也发现了这些结果。但是,请注意,这些算法旨在高效精确和复杂。我的目标是一个缓慢的,可能是简单的近似算法。 我也希望有一个简单易用的方法。可以想象,如果通过 API 公开更原始的几何操作,那么 WPF 和 GDI+ 会进行通常有用的裁剪。当一个人一开始很简单时,随着时间的推移,程序会变得更加复杂,因为这些困难的边缘情况会被考虑在内。【参考方案3】:

如果你使用 C++,并且不想自己创建算法,你可以使用Boost.Geometry。它使用上述 Weiler-Atherton 算法的改编版本。

【讨论】:

【参考方案4】:

你没有给我们你的多边形表示。所以我正在为你选择(更像是建议)一个:)

将每个多边形表示为一个大凸多边形,以及需要从该大凸多边形中“减去”的较小凸多边形列表。

现在给定该表示中的两个多边形,您可以将交集计算为:

计算大凸多边形的交集以形成交集的大多边形。然后“减去”两者中所有较小的交点的交点,得到一个被分割的多边形列表。

您会得到一个遵循相同表示的新多边形。

既然凸多边形相交很容易,那么这个相交的查找也应该很容易。

这似乎应该可行,但我还没有对正确性/时间/空间复杂度进行更深入的思考。

【讨论】:

哇!这只是我的想法,但是:(1) 多边形表示为一系列 CW 段,将其转换为凸-凸是不平凡的。 (2) 在构造第一个凸后,我得到了一个需要处理的非凸形状,我不确定从多边形中构造一个凸比找到两个多边形之间的交点要容易得多... @Elazar:要将线段表示转换为凸 - 凸,您可以执行以下操作:1)形成凸包。 2)对于凸包的每一边,如果它不在里面,你可以找到一个你需要减去的非凸多边形。然后,您可以“三角剖分”这个非凸多边形以获得凸形状的联合。至于您的第 2 点):如果您使用该表示,您实际上不必做任何实际的减法。我想对于凸包+“三角剖分”,已经有包可以做到这一点。 此算法将在以下评论中的“干草叉刀片以直角相交”示例中失败。具体来说,它会错过应该在每个干草叉的横杆旁边添加的切口。 实际上,算法需要减去两个形状的所有较小的多边形,而不是它们的交点。不过,您可能希望将它们与新船体相交。【参考方案5】:

这是一种简单而愚蠢的方法:在输入时,将多边形离散化为位图。相交,将位图和在一起。要生成输出多边形,请绘制位图的锯齿边界并使用polygon-approximation algorithm 平滑锯齿。 (我不记得那个链接是否给出了最合适的算法,它只是谷歌的第一个命中。您可以查看其中一个将位图图像转换为矢量表示的工具。也许您可以在不重新实现算法的情况下调用它们?)

我认为最复杂的部分是tracing out the borders。

顺便说一句,早在 90 年代初,我就在工作中遇到过类似的问题。我闷闷不乐:我想出了一个(完全不同的)算法,它可以在实数坐标上工作,但面对浮点(和嘈杂的输入)的现实,似乎遇到了完全无法修复的大量退化情况.也许在互联网的帮助下我会做得更好!

【讨论】:

如果您意识到每个顶点要么是其中一个多边形的顶点,要么是每个多边形的线段的交点,那么追踪边界可能会更容易。【参考方案6】:

我没有非常简单的解决方案,但这里是 real 算法的主要步骤:

    为多边形顶点做一个自定义双链表 边缘。使用 std::list 是不行的,因为你必须交换 next 和 以前的指针/偏移量自己进行特殊操作 节点。这是拥有简单代码的唯一方法,这将给出 表现不错。 通过比较每对边找到交点。笔记 比较每对边缘将给出 O(N²) 时间,但改进 之后 O(N·logN) 的算法将很容易。对于一些 边(比如 a→b 和 c→d),通过使用找到交点 边 a→b 上的参数(从 0 到 1),由下式给出 tₐ=d₀/(d₀-d₁),其中d₀是(c-a)×(b-a),d₁是(d-a)×(b-a)。 × 是 二维叉积,例如 p×q=pₓ·qᵧ-pᵧ·qₓ。在找到 tₐ 之后, 找到交点将其用作线性插值 段 a→b 上的参数:P=a+tₐ(b-a) 分割每条边添加顶点(和链表中的节点) 线段相交的地方。 那么你必须交叉交点处的节点。这是 您需要执行自定义双链接的操作 列表。您必须交换一对 next 指针(并更新 previous 相应的指针)。

然后你就有了多边形相交解析算法的原始结果。通常,您会希望根据每个区域的缠绕数选择一些区域。搜索 多边形缠绕数 以获取相关说明。

如果你想从这个 O(N²) 算法中创建一个 O(N·logN) 算法,你必须做完全相同的事情,除了你在行扫描算法内部做。查找 Bentley Ottman 算法。内部算法将是相同的,唯一的区别是在循环内部,要比较的边数会减少。

【讨论】:

【参考方案7】:

我解决同样问题的方式

    将多边形分成线段 使用IntervalTreesLineSweepAlgo查找相交线 使用GrahamScanAlgo查找闭合路径以查找具有相邻顶点的闭合路径 交叉引用 3. 使用 DinicAlgo 解决它们

注意:我的场景不同,因为多边形有一个共同的顶点。但希望这能有所帮助

【讨论】:

【参考方案8】:

如果您不关心可预测的运行时间,您可以尝试先将多边形拆分为凸多边形的并集,然后成对计算子多边形之间的交集。

这会给你一个凸多边形的集合,这样它们的并集就是你的起始多边形的交集。

【讨论】:

【参考方案9】:

如果多边形未对齐,则它们必须对齐。我会通过找到多边形的中心(X 中的平均值,Y 中的平均值)然后通过矩阵变换增量旋转多边形,将点投影到轴之一并使用最小 stdev 的角度来对齐形状(你也可以使用主成分)。为了找到交点,一个简单的算法将定义一个点网格。对于每个点,维护一个多边形或另一个多边形或两者(联合)内的点数(有简单快速的算法,例如http://wiki.unity3d.com/index.php?title=PolyContainsPoint)。计算多边形 1 和多边形 2 的点数,除以多边形 1 或多边形 2 中的点数,就可以粗略估计多边形重叠的比例(取决于网格采样)。相交区域将由对应于 AND 操作的点给出。

例如。

function get_polygon_intersection($arr, $user_array)

    $maxx = -999;  // choose sensible limits for your application
    $maxy = -999;
    $minx = 999;
    $miny = 999;
    $intersection_count = 0;
    $not_intersected = 0;
    $sampling = 20;
    
    // find min, max values of polygon (min/max variables passed as reference)
    get_array_extent($arr, $maxx, $maxy, $minx, $miny);
    get_array_extent($user_array, $maxx, $maxy, $minx, $miny);
    
    $inc_x = $maxx-$minx/$sampling;
    $inc_y = $maxy-$miny/$sampling;
    
    // see if x,y is within poly1 and poly2 and count
    for($i=$minx; $i<=$maxx; $i+= $inc_x)
    
        for($j=$miny; $j<=$maxy; $j+= $inc_y)
        
            $in_arr = pt_in_poly_array($arr, $i, $j);
            $in_user_arr = pt_in_poly_array($user_array, $i, $j);
            
            if($in_arr && $in_user_arr)
            
                $intersection_count++;
            
            else
            
                $not_intersected++;
            
        
    
    
    // return score as percentage intersection
    return 100.0 * $intersection_count/($not_intersected+$intersection_count);

【讨论】:

【参考方案10】:

这可能是一个巨大的近似值,具体取决于您的多边形,但这里有一个:

计算每个的质心 多边形。 计算最小值或最大值或平均值 到每个点的距离 多边形到质心。 如果 C1C2(其中 C1/2 是第一个/第二个多边形的中心)>= D1 + D2(其中 D1/2 是您为第一个/第二个多边形计算的距离),则两个多边形“相交”。

不过,这应该非常有效,因为对多边形的任何变换都以相同的方式应用于质心,并且中心节点距离只能计算一次。

【讨论】:

我如何得到一个代表交叉区域的多边形?

以上是关于多边形相交的简单算法的主要内容,如果未能解决你的问题,请参考以下文章

是否有用于查找复杂多边形的凸包的线性时间算法?

如何重绘一个完全自相交的多边形?

如何使用一些多边形裁剪算法找到多边形的总面积和质心?

无孔多边形联合

如何将自相交多边形划分为简单多边形?

闭合多边形的算法