【问题标题】:Calculating the area(s) of a rectangle that aren't being overlapped计算不重叠的矩形面积
【发布时间】:2009-06-02 21:55:47
【问题描述】:

我有两个由包含 x1、y1、x2、y2 坐标的结构表示的矩形。一个矩形可以被认为是父级,另一个是子级。

我已经知道如何检测子矩形是否在父矩形内;我现在想弄清楚的是最简单、最快的方法来确定父级中未被子矩形重叠的矩形区域。

例如,假设一个 100x100 的父矩形和一个 50x50 的子矩形正好位于父矩形的中心。这意味着将有四个矩形代表父矩形中未被子矩形重叠的四个区域。

当然,孩子可能在左上角、右上角、左下角、右下角,或者向左一点、向右一点等等……可能是一、二、三,或表示非重叠区域的四个矩形。

我有一些想法来解决这个问题,但似乎都过于复杂。有没有一种简单、快速的方法来解决这个问题?

【问题讨论】:

  • 那么,您到底需要计算什么? 100^2 和 50^2 的区别?这些矩形是如何发挥作用的?

标签: math geometry overlap


【解决方案1】:

因此最多可以有 4 个矩形的非重叠区域。让我们列出它们。

leftrect = rightrect = toprect = bottomrect = None
trimmedparent = duplicate(parent)

if parent.x1 < child.x1:
    leftrect = duplicate(parent)
    leftrect.x2 = child.x1
    trimmedparent.x1 = child.x1

if parent.x2 > child.x2:
    rightrect = duplicate(parent)
    rightrect.x1 = child.x2
    trimmedparent.x2 = child.x2

if parent.y1 < child.y1:
    toprect = duplicate(trimmedparent)
    toprect.y2 = child.y1

if parent.y2 > child.y2:
    bottomrect = duplicate(trimmedparent)
    bottomrect.y1 = child.y2

唯一棘手的部分是消除 leftrect 和 toprect 可能相交的部分。我使用 'trimmedparent' 作为中间步骤从 toprect 修剪该部分。

【讨论】:

  • 酷...看起来就可以了。谢谢!
【解决方案2】:
parent = Rectangle.new(x1,y1,mx1,my1)
child = Rectangle.new(x2,y2,mx2,my2)

rects = []
if (parent.contains(child))
  rects.push Rectangle.new(parent.x, parent.y, parent.mx, child.y) if child.y>parent.y
  rects.push Rectangle.new(parent.x, child.my, parent.mx, parent.my) if child.my<parent.my
  rects.push Rectangle.new(parent.x, parent.y, child.x, pareny.my) if child.x>parent.x
  rects.push Rectangle.new(child.mx, parent.y, parent.mx, parent.my) if child.mx<parent.mx
end

【讨论】:

    【解决方案3】:

    这是基本算法:

    对于子节点中的每个点,如果它在父节点内部,则对应的子节点和父节点组成对角线一个矩形。现在,对于子节点的每一侧,如果两个点在父节点中,则这两个点和父节点边缘上的匹配点形成一个矩形。如果子节点的边上只有一个点在父节点中,则该点和子节点边缘点对应的父节点不在父节点中,形成一个矩形的对角线。返回这些矩形。

    您最多可以获得八个矩形(每个角一个,每个边一个)。如果您想要尽可能少的矩形,请查看它们是否共享边缘,如果共享,请将它们组合起来。

    【讨论】:

      【解决方案4】:

      这是另一种计算父级非重叠区域的方法:

      Function max(ByVal v1 As Double, ByVal v2 As Double) As Double
          If v1 > v2 Then
              Return v1
          Else
              Return v2
          End If
      End Function
      
      Function min(ByVal v1 As Double, ByVal v2 As Double) As Double
          If v1 > v2 Then
              Return v2
          Else
              Return v1
          End If
      End Function
      
      Function IntervalOverLap(ByVal p1 As Double, ByVal p2 As Double, ByVal c1 As Double, ByVal c2 As Double) As Double
          'determine how much of the parent(p1 to p2) segment '
          ' is overlapped by the child(c1 to c2) segment      '
      
          'sort to standard direction  '
          Dim ph As Double = max(p1, p2)
          Dim pl As Double = min(p1, p2)
          Dim ch As Double = max(c1, c2)
          Dim cl As Double = min(c1, c2)
      
          'restrict the child to within the parent '
          ch = min(ph, max(pl, ch))
          cl = max(pl, min(cl, ph))
      
          'return the overlapped length  '
          Return (ch - cl)
      End Function
      
      Function NonOverLappedArea(ByVal parent As Rectangle, ByVal child As Rectangle) As Double
          'return the area of the parent        '
          ' that is not overlapped by the child '
          Return IntervalOverLap(parent.X1, parent.X2, child.X1, child.X2) _
              * IntervalOverLap(parent.Y1, parent.Y2, child.Y1, child.Y2)
      End Function
      

      【讨论】:

        【解决方案5】:

        根据您的描述,孩子总是完全被父母所包含。因此,非重叠区域总是一个矩形甜甜圈,尽管它可能在 4 条边中的任何一条边上退化,因为子边可能与父边相邻,完全退化的情况是子边与父边相同.

        甜甜圈可以分解为 4 个矩形。分解可能不是唯一的,这意味着您可以根据执行分解的方式获得不同的矩形。 在 4 个矩形中,丢弃退化的(面积为 0 的)就完成了。

        这是一个垂直偏向分解

        // assume the child is known to be in the parent bounds at this point
        // assume parent and child are normalized
        std::vector<CRect> rects;
        CRect rect( parent.x1(), parent.y1(), child.x1(), parent.y2() ); // left
        if ( rect.area() > 0.0 ) rects.push_back(rect);
        rect.set( child.x1(), parent.y1(), child.x2(), child.y1() ); // bottom
        if ( rect.area() > 0.0 ) rects.push_back(rect);
        rect.set( child.x1(), child.y2(), child.x2(), parent.y2() ) ); // top
        if ( rect.area() > 0.0 ) rects.push_back(rect);
        rect.set( child.x2(), parent.y1(), parent.x2(), parent.y2() ) ); // right
        if ( rect.area() > 0.0 ) rects.push_back(rect);
        
        // yes, this could be written without all the code replication... :)
        

        【讨论】:

          猜你喜欢
          • 2015-01-25
          • 2020-08-23
          • 2021-03-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多