一种简单的多边形交叉算法

时间:2010-02-16 10:38:43

标签: math geometry 2d polygon

我正在寻找一种计算多边形交叉/剪裁的非常简单的算法。 也就是说,给定多边形PQ,我希望找到包含在TP中的多边形Q,我希望{{1在所有可能的多边形中最大化。

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

但对我来说,算法很简单(测试更便宜),最好是短(代码少)。

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

11 个答案:

答案 0 :(得分:50)

我理解原始海报正在寻找一个简单的解决方案,但不幸的是,确实没有简单的解决方案。

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

答案 1 :(得分:17)

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

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

Alan Murta完全实现了多边形裁剪器GPC

编辑:

另一种方法是首先将每个多边形划分为一组三角形,这些三角形更容易处理。 Gary H. Meisters的 Two-Ears Theorem 可以解决问题。这个page at McGill在解释三角形细分方面做得很好。

答案 2 :(得分:12)

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

答案 3 :(得分:6)

您尚未向我们提供多边形的表示。所以我选择(更像是建议)一个给你:)

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

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

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

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

由于凸多边形交叉很容易,这个交点发现也应该很容易。

这似乎应该有效,但我没有在正确性/时间/空间复杂性方面给予更深入的思考。

答案 4 :(得分:5)

这是一种基于三角测量的方法,该方法非常简单,可以在O(N 2 )中运行。

BTW,O(N 2 )对于这个问题是最佳的。想象一下两个形状像干草叉叶片的多边形,它们以直角相交。每个都有一些与尖齿数量成比例的部分;交叉点中多边形的数量与尖齿数的平方成正比。

  1. 首先,对每个多边形进行三角测量。

  2. 将P中的所有三角形与Q中的所有三角形进行比较,以检测交叉点。可以将任何一对交叉三角形分成较小的三角形,每个三角形在P,Q或交叉点中。 (无论你在步骤1中使用什么,都可以重复使用来帮助解决这个问题。)只保留交叉点中的三角形。

  3. 通过成对比较每个三角形的邻居,并构建邻接图。该图将包含P和Q交叉点中每个多边形的一个连通子图。

  4. 对于每个这样的子图,选择一个三角形,走到边缘,然后在边缘处走动,产生限定相应输出多边形的线段。

答案 5 :(得分:4)

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

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

早在90年代初,我在工作中遇到了类似这个问题的问题。我把它搞砸了:我提出了一个(完全不同的)算法,它可以处理实数坐标,但是面对浮点(和嘈杂的输入)的现实,似乎遇到了一个完全无法修复的过多退化情况。也许在互联网的帮助下我会做得更好!

答案 6 :(得分:0)

我没有非常简单的解决方案,但以下是真实算法的主要步骤:

  1. 为多边形顶点做一个自定义双链表 边缘。使用std::list不会,因为你必须交换下一个和 以前的指针/偏移你自己的特殊操作 节点。这是获得简单代码的唯一方法,这将给出 良好的表现。
  2. 通过比较每对边来找到交点。注意 比较每对边缘将给出O(N²)时间,但改善 之后,对O(N·logN)的算法将很容易。对于一些 边(比如a→b和c→d),通过使用找到交点 边缘a→b上的参数(从0到1),由下式给出 t + = d 0 /(d 0 -d 1),其中d 0是(c-a)×(b-a),d 1是(d-a)×(b-a)。 ×是 二维交叉乘积,如p×q =pₓ·qᵧ-pᵧ·qₓ。找到tₐ后, 找到交点正在将其用作线性插值 段a→b上的参数:P = a +tₐ(b-a)
  3. 拆分每个边添加顶点(以及链表中的节点) 段相交的地方。
  4. 然后你必须交叉交叉点的节点。这是 您需要执行自定义双链接的操作 名单。你必须交换一对 next 指针(并更新 之前的指针)。
  5. 然后你得到了多边形交集解算算法的原始结果。通常,您需要根据每个区域的匝数选择一些区域。搜索多边形缠绕数以获得对此的解释。

    如果你想用这个O(N²)算法制作O(N·logN)算法,你必须做同样的事情,除非你在行扫描算法中做。寻找 Bentley Ottman算法。内部算法将是相同的,唯一的区别是你将在循环内部减少要比较的边数。

答案 7 :(得分:0)

我处理同样问题的方式

  1. 将多边形分成线段
  2. 使用IntervalTreesLineSweepAlgo
  3. 查找相交线
  4. 使用GrahamScanAlgo查找封闭路径以查找具有相邻顶点的闭合路径
  5. 交叉参考3.用DinicAlgo解散它们
  6. 注意:鉴于多边形有一个共同的顶点,我的场景是不同的。但希望这可以帮助

答案 8 :(得分:0)

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

这将为您提供凸多边形的集合,以使它们的并集恰好是起始多边形的交集。

答案 9 :(得分:0)

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

例如

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 :(得分:-2)

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

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

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