将圆连接到多边形的算法

时间:2016-01-23 17:30:31

标签: algorithm geometry polygon

将重叠圆组合成多边形的最佳方法是什么?

我获得了一个固定直径的圆心点列表。

Rendering of 5 random circles

我需要将任何重叠的圆圈连接在一起,并在结果多边形中输出一个点列表。 Rendering of desired output

这似乎是一个相当普遍的问题(GIS系统,载体等)。这可以通过Google Maps API完成,但我正在寻找实际的算法。

我试图通过计算每个圆圈周围的点来解决这个问题。 Rendering of 16 points on the circumference of each circle

然后移除位于任何圆圈内的任何点。 enter image description here

这为我提供了所需多边形中正确的点列表。 Rendering of points on the desired polygon

然而,点的顺序是该解决方案的问题。每个圆的点都存储在一个数组中。要将它们正确地与2个重叠的圆圈合并,这是相对简单的。但是,当处理多个重叠的圆圈时,它变得复杂。 enter image description here

希望您有一些想法可以使这个解决方案工作,或者是另一个能够达到预期效果的算法。

提前致谢!

2 个答案:

答案 0 :(得分:3)

您应该能够使用如下的appraoch:

  1. 首先,确定属于同一组重叠圆圈的圆圈。显然,如果它们的中心的绝对距离小于它们的组合半径,则两个圆重叠。对于每个圆圈,将itersecs中的圆圈存储在hashmap / dictionary中。 (您可以使用union-find algorithm, or disjoint sets将其扩展到整个圆圈组,但这不是真正需要的。)
  2. 创建属于一个圆的点时,记住每个点的左右邻居。只需将它们存储在有序数组中即可免费获得。
  3. 删除“内部”点,即距离一个半径较近的点到与第一个圆相交的圆的任何中心。
  4. 将邻居标记为那些未被移除圆圈的“松散末端”的内部点。
  5. 将未移除的点(包括松散的末端)连接到原来的左右邻居。
  6. 对于每个松散的终点,找到同一组中不同圆圈的最近的松散末端并连接它们。
  7. 这是一张图片来说明这种方法。

    enter image description here

    • 红点是被删除的“内部”点
    • 蓝点是与“内在”点相邻的“松散的目的”;每个“松散的一端”都有一个不同圆圈的另一个“松散的一端”,距离不到两个“点距离”
    • 绿点可以很容易地连接到它们的邻居(包括“松散的末端”),按照它们围绕圆圈排列的顺序。

    您可以通过区分松散的末端,并使用每个左侧的松散末端必须连接的事实来进一步减少可能组合的数量到另一个圆圈的右边松散的一端。对于群组中的n个圈子,只留下(n-1)!种方式来连接这些圈子,无论每个圈子的点数是多少。

    如果你只考虑组中实际与圆圈相交的那些圆圈和松散端(只是将它们存储在一个hashmap / dictionary中),即使这可以进一步减少。因此,如果您的n个圈子平均与k个其他圈子相交,则只有n*k个可能的组合。

    你也可以使用这样一个事实:另一个松散的一端不能远离圆圈上两点之间距离的两倍。让我们调用这个距离d,然后你可以创建一个分辨率为d的空间地图(例如一个散列图,或者只是一个二维数组),并将每个松散端分配给该地图中的单元格,然后另一个松散end必须与第一个松散末端的周围单元格相同。

    因此,点可以相互连接的方式的数量大大减少(特别是,不允许在最终图像中连接它们的方式):除非您在同一组中有批次重叠的圆圈,否则即使使用暴力也应该这样做,并且您可以使用更智能的nearest-neighbor search算法。

    更新:在一段时间后回顾这个答案,似乎你可以很容易地确定匹配的“松散结束”点:假设你在圆圈A中有一个“正确”的松散端,那么匹配的“左”松散end必须属于圆,其半径与下一个“内”点重叠到第一个“松散端”。因此,如果将该关系存储在另一个地图中,则可以立即确定匹配的松散端。 (如果内部点与多个其他圆重叠,则地图应包含与其重叠的圆。)

答案 1 :(得分:3)

这是O(n log n)时间算法的草图。

  1. 使用您喜欢的算法/库来计算圈子中心的Delaunay triangulation

  2. 删除不重叠的圆圈之间的边缘。

  3. 绕着每个连接组件的无限面走动。使用doubly connected edge list表示很容易做到这一点,其中边列表的排序用于指示平面图的拓扑。该边界上的每个半边都变成一个顶点,其中属于尾点的弧段结束,属于其头点的弧段开始。

  4. (可选)用折线近似每个弧段。

  5. 如果谷歌的任何人正在阅读此内容,请注意我没有查看相关代码。