重叠圆圈的组合区域

时间:2009-11-03 13:22:02

标签: algorithm geometry area

我最近遇到了一个问题,我有四个圆圈(中点和半径)并且必须计算这些圆圈的并集区域。

示例图片:

对于两个圈来说,这很容易,

我可以计算出不在三角形内的每个圆圈区域的分数,然后计算三角形的面积。

但是,如果有两个以上的圆圈,我可以使用一个聪明的算法吗?

15 个答案:

答案 0 :(得分:93)

查找外围的所有圆形交叉点(例如下图中的B,D,F,H)。将它们与相应圆的中心连接在一起以形成多边形。圆的并集区域是多边形的面积+由连续交叉点和它们之间的圆心定义的圆切片的面积。你还需要考虑任何漏洞。

circle overlap

答案 1 :(得分:31)

我确信有一个聪明的算法,但这是一个愚蠢的,以节省不得不寻找它;

  • 在圈子周围放置一个边界框;
  • 在边界框内生成随机点;
  • 弄清楚随机点是否在其中一个圆圈内;
  • 通过一些简单的加法和除法计算区域(proportion_of_points_inside * area_of_bounding_box)。

当然这是愚蠢的,但是:

  • 您可以根据需要获得准确答案,只需生成更多积分;
  • 它适用于任何可以计算内/外区别的形状;
  • 它将很好地并行化,因此您可以使用所有核心。

答案 2 :(得分:16)

对于与前一个解决方案不同的解决方案,您可以使用四叉树生成具有任意精度的估计。

如果您可以判断方形是在内部还是外部或与形状相交,这也适用于任何形状联合。

每个单元格都有以下状态之一:空,满,部分

该算法包括以低分辨率(例如标记为空的4个单元格)从四边形中“绘制”圆圈。每个单元格都是:

  • 在至少一个圆圈内,然后将单元格标记为已满,
  • 在所有圈子外面,将单元格标记为空,
  • else将单元格标记为部分。

完成后,您可以计算区域的估计值:完整单元格给出下限,空单元格给出上限,部分单元格给出最大区域误差。

如果错误对您来说太大,您可以细化部分单元格,直到获得正确的精度。

我认为这比执行许多特殊情况所需的几何方法更容易实现。

答案 3 :(得分:15)

Ants Aasma的回答给出了基本的想法,但我想让它更具体一点。看看下面的五个圆圈以及它们被分解的方式。

Example

  • 蓝点是圆心。
  • 红点是圆形边界交点。
  • 带有白色内部的红点未包含在任何其他圈子中的圆形边界交点。

识别这3种点很容易。现在构建一个图形数据结构,其中节点是蓝点,红点是白色内部。对于每个圆圈,在其边界的圆形中间(蓝点)和每个交叉点(带有白色内部的红点)之间放置一条边。

这将圆形并集分解为一组多边形(阴影蓝色)和圆形饼图(绿色阴影),它们成对不相交并覆盖原始联合(即分区)。由于这里的每一块都是易于计算区域的东西,你可以通过对碎片区域求和来计算联合区域。

答案 4 :(得分:12)

我喜欢2个交叉圆的情况 - 这里是我如何使用相同方法的略微变化来处理更复杂的例子。

它可以更好地洞察大量半重叠圆的算法。

这里的不同之处在于我从连接中心开始(因此在圆心之间有一个顶点,而不是在圆相交的地方之间)我认为这可以让它更好地概括。

(在实践中,也许monte-carlo方法是值得的)

alt text
(来源:secretGeek.net

答案 5 :(得分:4)

如果你想要一个离散的(而不是连续的)答案,你可以做类似于像素绘画算法的事情。

在网格上绘制圆圈,然后为网格的每个单元格着色,如果它主要包含在圆圈内(即,其中至少50%的区域位于其中一个圆圈内)。对整个网格执行此操作(网格覆盖圆圈覆盖的所有区域),然后计算网格中的彩色单元格数。

答案 6 :(得分:3)

嗯,非常有趣的问题。我的方法可能与以下内容类似:

  • 找出一种方法来计算任意数量的圆圈之间的交叉区域,即如果我有3个圆圈,我需要能够计算出这些圆圈之间的交点。 “Monte-Carlo”方法是一种近似这种方法的好方法(http://local.wasp.uwa.edu.au/~pbourke/geometry/circlearea/)。
  • 消除完全包含在另一个较大圆圈中的任何圆圈(查看半径和两个圆心之间距离的模数)我认为不是强制性的。
  • 选择2个圆圈(称为A和B)并使用以下公式计算总面积:

(对于任何形状都是如此,无论是圆形还是其他形状)

area(A∪B) = area(A) + area(B) - area(A∩B)

其中A ∪ B表示联合B而A ∩ B表示A相交B(您可以从第一步开始解决此问题。

  • 现在继续添加圆圈并继续计算添加的区域,作为圆圈区域和圆圈之间交叉区域的和/减。例如,对于3个圆圈(称为额外圆圈C),我们使用以下公式计算出该区域:

(与A取代A∪B的情况相同)

area((A∪B)∪C) = area(A∪B) + area(C) - area((A∪B)∩C)

我们刚刚制定了area(A∪B)area((A∪B)∩C)可以找到:

area((A∪B)nC) = area((A∩C)∪(B∩C)) = area(A∩C) + area(A∩B) - area((A∩C)∩(B∩C)) = area(A∩C) + area(A∩B) - area(A∩B∩C)

你可以从上面找到区域(A∩B∩C)。

棘手的一点是最后一步 - 添加的圈越多,它变得越复杂。我相信有一个扩展来计算一个有限联合的交叉区域,或者你也可以递归地解决它。

另外,关于使用蒙特卡罗来近似它的面积,我相信它可以减少任意数量的圆与这些圆中的4个圆的交点,这可以精确计算(不知道如何然而,这样做。)

有可能有更好的方法来做到这一点 - 对于每增加一个额外的圆圈,复杂性会显着增加(可能是指数级的,但我不确定)。

答案 7 :(得分:3)

我一直在研究模拟重叠星场的问题,试图从密集场中的实际磁盘区域估计真实的星数,其中较大的明亮星可以掩盖较暗的星。我也曾希望通过严格的正式分析能够做到这一点,但无法找到任务的算法。我通过在蓝色背景上生成星形字段作为绿色圆盘来解决它,其直径由概率算法确定。一个简单的程序可以配对它们,看看是否有重叠(将星形对变成黄色);然后,颜色的像素数产生观察区域以与理论区域进行比较。然后,这会生成真实计数的概率曲线。蛮力可能,但它似乎工作正常。

(来源:2from.com

答案 8 :(得分:2)

使用所谓的电源图,可以有效地解决这个问题。这是非常重的数学,而不是我想要随便解决的问题。要获得“简单”的解决方案,请查找线扫描算法。这里的基本原则是你将图形分成条状,计算每个条带的面积相对容易。

因此,在包含所有没有擦掉的圆圈的图形上,在每个位置绘制一条水平线,该位置是圆的顶部,圆的底部或2个圆的交点。请注意,在这些条带内,您需要计算的所有区域看起来都是相同的:“梯形”,两侧由圆形段代替。因此,如果您可以计算出如何计算这样的形状,您只需对所有单个形状进行计算并将它们添加到一起。这种天真方法的复杂性是O(N ^ 3),其中N是图中圆圈的数量。使用一些聪明的数据结构,你可以将这种线扫描方法改进为O(N ^ 2 * log(N)),但除非你真的需要,否则它可能不值得麻烦。

答案 9 :(得分:1)

我发现这个链接可能很有用。但似乎没有明确的答案。 Google answers。三个圆圈的另一个参考是Haruki's theorem。那里也有一篇论文。

答案 10 :(得分:1)

根据您尝试解决的问题,可能足以获得上限和下限。上限很容易,只是所有圆的总和。对于下限,您可以选择单个半径,使得没有圆圈重叠。为了更好地找到每个圆的最大半径(直到实际半径),使其不重叠。去除任何完全重叠的圆也应该是微不足道的(所有这些圆都满足| P_a - P_b |< = r_a)其中P_a是圆A的中心,P_b是圆B的中心,r_a是半径A)并且这更好地改善了上限和下限。如果你在任意对上使用你的配对公式而不仅仅是所有圆的总和,你也可以获得更好的上限。可能有一种很好的方法来选择“最佳”对(导致最小总面积的对。

考虑到上限和下限,你可以更好地调整蒙特卡罗方法,但没有具体的想法。另一个选项(同样取决于您的应用)是光栅化圆圈和计算像素。它基本上是具有固定分布的蒙特卡罗方法。

答案 11 :(得分:1)

这是一种应该在实践中易于实现的算法,可以调整以产生任意小的错误:

  1. 通过以同一点为中心的正多边形来近似每个圆
  2. 计算多边形,它是近似圆的并集
  3. 计算合并多边形的面积
  4. 步骤2和3可以使用标准的,易于查找的计算几何算法来执行。

    显然,每个近似多边形使用的边越多,答案就越准确。您可以使用内切和外接多边形进行近似,以获得确切答案的界限。

答案 12 :(得分:1)

我有办法得到一个近似答案如果你知道你所有的圆圈都在一个特定的区域内,即圆圈中的每个点都在一个你知道尺寸的盒子里.例如,如果所有圆圈都在已知大小的图像中,则该假设将是有效的。如果您可以做出这个假设,请将包含您的图像的区域划分为“像素”。对于每个像素,计算它是否在至少一个圆圈内。如果是,则将运行总计加 1。完成后,您知道至少一个圆内有多少像素,并且您还知道每个像素的面积,因此您可以计算所有重叠圆的总面积。

通过增加区域的“分辨率”(像素数),您可以改进近似值。

此外,如果包含圆圈的区域的大小是有界的,并且您保持分辨率(像素数)不变,则算法在 O(n) 时间内运行(n 是圆圈的数量)。这是因为对于每个像素,您必须检查它是否在您的 n 个圆圈中的每个圆圈内,并且像素总数是有界的。

答案 13 :(得分:0)

这可以使用格林定理来解决,其复杂度为n ^ 2log(n)。 如果您不熟悉格林定理,并且想了解更多,这里是可汗学院的videonotes。但是出于我们的问题,我认为我的描述就足够了。

  

很抱歉,无法链接到照片,因为我无法发布图像。(信誉度不足)

General Equation of Green's Theorem

如果我这样放置 L M

Condition

那么RHS就是区域 R 的面积,可以通过求解闭合积分或LHS来获得,这正是我们要做的。

All unions can be broken into such disjoint sets of circles which intersect

因此,沿逆时针方向的路径积分将为该区域的 Area ,而沿顺时针方向的积分将为该区域的 Area 。所以

AreaOfUnion =((逆时针方向沿红色弧积分+顺时针方向沿蓝色弧积分)

但是很酷的窍门是,如果对于每个圆,如果我们整合不在任何其他圆内的弧,则得到所需的面积,即,我们沿所有红色弧在逆时针方向上进行积分,并沿顺时针沿所有蓝弧进行积分。方向。 完成作业!!!

  

即使是圆不与其他圆相交的情况   照顾。

这是指向我的C++ Code

的GitHub链接

答案 14 :(得分:-1)

像素绘画方法(由@Loadmaster建议)以各种方式优于数学解决方案:

  1. 实施很多更简单。上述问题可以在不到100行代码as this JSFiddle solution demonstrates中解决(主要是因为它在概念上更简单,并且没有边缘情况或例外情况需要处理)。
  2. 它可以轻松适应更普遍的问题。它适用于任何形状,无论形态如何,只要它可以用2D绘图库(即“所有它们!”)渲染 - 圆形,椭圆形,样条线,多边形,你可以命名它。哎呀,甚至是位图图片。
  3. 与数学解的〜O [n * n]相比,像素绘制解决方案的复杂度为~O [n]。这意味着随着形状数量的增加它会表现得更好。
  4. 谈到性能,你经常会免费获得硬件加速,因为大多数现代2D库(比如HTML5的画布,我相信)会将渲染工作卸载到图形加速器上。
  5. 像素绘画的一个缺点是解决方案的有限精度。但是,这可以根据情况需要简单地渲染到更大或更小的画布来调整。另请注意,2D渲染代码中的anti-aliasing(默认情况下经常打开)将产生优于像素级的精度。因此,举例来说,将100x100的数字渲染到相同尺寸的画布中,我认为,应该产生1 /(100 x 100 x 255)= .000039%......的准确度......这可能是“足够好”除了最苛刻的问题之外的所有问题。

    <p>Area computation of arbitrary figures as done thru pixel-painting, in which a complex shape is drawn into an HTML5 canvas and the area determined by comparing the number of white pixels found in the resulting bitmap.  See javascript source for details.</p>
    
    <canvas id="canvas" width="80" height="100"></canvas>
    
    <p>Area = <span id="result"></span></p>
    
    // Get HTML canvas element (and context) to draw into
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    
    // Lil' circle drawing utility
    function circle(x,y,r) {
      ctx.beginPath();
      ctx.arc(x, y, r, 0, Math.PI*2);
      ctx.fill();
    }
    
    // Clear canvas (to black)
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    // Fill shape (in white)
    ctx.fillStyle = 'white';
    circle(40, 50, 40);
    circle(40, 10, 10);
    circle(25, 15, 12);
    circle(35, 90, 10);
    
    // Get bitmap data
    var id = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var pixels = id.data; // Flat array of RGBA bytes
    
    // Determine area by counting the white pixels
    for (var i = 0, area = 0; i < pixels.length; i += 4) {
      area += pixels[i]; // Red channel (same as green and blue channels)
    }
    
    // Normalize by the max white value of 255
    area /= 255;
    
    // Output result
    document.getElementById('result').innerHTML = area.toFixed(2);