代码之家  ›  专栏  ›  技术社区  ›  namenlos

求重叠矩形区域的有效算法是什么

  •  44
  • namenlos  · 技术社区  · 16 年前

    我的处境

    • 输入:一组矩形
    • 每个rect由4个这样的双精度组成:(x0,y0,x1,y1)
    • 它们不会以任何角度“旋转”,它们都是“正常”的矩形,相对于屏幕“向上/向下”和“左/右”。
    • 它们是随机放置的-它们可能接触到边缘、重叠或没有任何接触
    • 我要几百个长方形的
    • 这是在C中实现的#

    我需要找到

    • 由它们的重叠形成的区域-画布中有多个矩形“覆盖”的所有区域(例如,有两个矩形,则为交叉点)。
    • 我不需要重叠的几何图形-只需要面积(例如:4平方英寸)
    • 重叠不应计算多次-例如,假设3个矩形具有相同的大小和位置-它们彼此位于顶部-此区域应计算一次(不是三次)

    例子

    • 下图包含三个矩形:A、B、C
    • A和B重叠(如虚线所示)
    • B和C重叠(如虚线所示)
    • 我要找的是虚线显示的区域

    -

    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAA--------------BBB
    AAAAAAAAAAAAAAAA--------------BBB
    AAAAAAAAAAAAAAAA--------------BBB
    AAAAAAAAAAAAAAAA--------------BBB
                    BBBBBBBBBBBBBBBBB
                    BBBBBBBBBBBBBBBBB
                    BBBBBBBBBBBBBBBBB
                    BBBBBB-----------CCCCCCCC
                    BBBBBB-----------CCCCCCCC
                    BBBBBB-----------CCCCCCCC
                          CCCCCCCCCCCCCCCCCCC
                          CCCCCCCCCCCCCCCCCCC
                          CCCCCCCCCCCCCCCCCCC
                          CCCCCCCCCCCCCCCCCCC
    
    15 回复  |  直到 7 年前
        1
  •  51
  •   Community CDub    8 年前

    计算该区域的一种有效方法是使用扫描算法。假设我们将垂直线l(x)扫过矩形u的并集:

    • 首先,您需要构建一个事件队列Q,在本例中,它是所有x坐标(左和右)矩形的有序列表。
    • 在扫描过程中,您应该保持一个1d数据结构,它应该给出l(x)和u相交处的总长度。重要的是,该长度在两个连续事件q和q'之间是恒定的。因此,如果l(q)表示l(q+)的总长度(即l刚好在q的右侧)与u相交,则被扫描的区域事件q和q之间的l’正好是l(q)*(q’-q)。
    • 你只需要把所有这些扫描面积加起来就可以得到总面积。

    我们仍然需要解决一维问题。您需要一个1d结构,它动态计算(垂直)段的并集。动态地说,我的意思是有时添加一个新的段,有时删除一个。

    我已经在回答中详细说明了 collapsing ranges question 如何以静态方式进行(实际上是一维扫描)。因此,如果您想要一些简单的东西,您可以直接应用它(通过为每个事件重新计算联合)。如果你想要更有效率的东西,你只需要稍微调整一下:

    • 假设你知道分段的联合 …s n 由不相交的段D组成 …D K . 加S N+ 1 很容易,你只需要找到S的两端 N+ 1 A在D的末端 …D K .
    • 假设你知道分段的联合 …s n 由不相交的段D组成 …D K ,删除段S (假设是 包括在D中 J )表示重新计算数据段的并集。 J 包括,除了 (使用静态算法)。

    这是您的动态算法。假设您将使用带有日志时间位置查询的排序集来表示数据。 …D K ,这可能是最有效的非专门化方法。

        2
  •  13
  •   Will    16 年前

    一种方法是将其绘制到画布上!使用半透明颜色绘制每个矩形。NET运行时将使用优化的本机代码进行绘图,甚至使用硬件加速器。

    然后,你必须读回像素。每个像素是背景色、矩形色还是其他颜色?唯一能变成另一种颜色的方法是两个或多个矩形重叠…

    如果这是一个太多的欺骗,我会推荐四叉树作为另一个答案,或 r-tree .

        3
  •  9
  •   LeppyR64    16 年前

    这是我在Topcoder SRM 160第2部分中使用的一些快速而肮脏的代码。

    T =顶部
    ButtoTM
    左=左
    右=右

    public class Rect
    {
        public int t, b, l, r;
    
        public Rect(int _l, int _b, int _r, int _t)
        {
            t = _t;
            b = _b;
            l = _l;
            r = _r;
        }   
    
        public bool Intersects(Rect R)
        {
            return !(l > R.r || R.l > r || R.b > t || b > R.t);
        }
    
        public Rect Intersection(Rect R)
        {
            if(!this.Intersects(R))
                return new Rect(0,0,0,0);
            int [] horiz = {l, r, R.l, R.r};
            Array.Sort(horiz);
            int [] vert = {b, t, R.b, R.t};
            Array.Sort(vert);
    
            return new Rect(horiz[1], vert[1], horiz[2], vert[2]);
        } 
    
        public int Area()
        {
            return (t - b)*(r-l);
        }
    
        public override string ToString()
        {
            return l + " " + b + " " + r + " " + t;
        }
    }
    
        4
  •  8
  •   Rose Perrone    11 年前

    最简单的解决方案

    import numpy as np
    
    A = np.zeros((100, 100))
    B = np.zeros((100, 100))
    
    A[rect1.top : rect1.bottom,  rect1.left : rect1.right] = 1
    B[rect2.top : rect2.bottom,  rect2.left : rect2.right] = 1
    
    area_of_union     = np.sum((A + B) > 0)
    area_of_intersect = np.sum((A + B) > 1)
    

    在这个例子中,我们创建了两个零矩阵,它们是画布的大小。对于每个矩形,用其中一个矩阵填充矩形占用空间的矩阵。然后对矩阵求和。现在 sum(A+B > 0) 是工会所在的地区,以及 sum(A+B > 1) 是重叠区域。这个例子很容易归纳为多个矩形。

        5
  •  6
  •   Lasse V. Karlsen    16 年前

    下面是一些我觉得可行的方法:

    1. 使用双键和矩形+布尔值列表创建字典,如下所示:

      Dictionary<Double,List<KeyValuePair<Rectangle,Boolean>>>矩形;

    2. 对于集合中的每个矩形,找到X0和x1值的对应列表,并将矩形添加到该列表中,其中布尔值对于X0为true,对于x1为false。这样,您就有了一个完整的X坐标列表,每个矩形要么输入(真),要么离开(假)X方向。

    3. 从字典中获取所有键(所有不同的x坐标),对它们进行排序,并按顺序循环它们,确保可以同时获取当前x值和下一个x值(两者都需要)。这将为您提供单独的矩形条带

    4. 保持一组当前正在查看的矩形,开始时为空。对于在点3中迭代的每个x值,如果矩形注册为真值,则将其添加到集合中,否则将其移除。
    5. 对于条形图,请按其Y坐标对矩形进行排序。
    6. 在条带中的矩形中循环,计算重叠的距离(到目前为止我还不清楚如何有效地做到这一点)
    7. 计算条带宽度乘以重叠距离的高度得到面积

    示例:5个矩形,相互顶部绘制,从A到E:

    aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
    aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
    aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
    aaaaaaaaaaaaaaaa          bbbbbbbbbbbbbbbbb
    aaaaaaaadddddddddddddddddddddddddddddbbbbbb
    aaaaaaaadddddddddddddddddddddddddddddbbbbbb
            ddddddddddddddddddddddddddddd
            ddddddddddddddddddddddddddddd
            ddddddddddddddeeeeeeeeeeeeeeeeee
            ddddddddddddddeeeeeeeeeeeeeeeeee
            ddddddddddddddeeeeeeeeeeeeeeeeee
    ccccccccddddddddddddddeeeeeeeeeeeeeeeeee
    ccccccccddddddddddddddeeeeeeeeeeeeeeeeee
    cccccccccccc          eeeeeeeeeeeeeeeeee
    cccccccccccc          eeeeeeeeeeeeeeeeee
    cccccccccccc
    cccccccccccc
    

    这是X坐标列表:

    v       v  v   v      v   v         v  v  v   
    |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
    |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
    |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
    |aaaaaaa|aa|aaaa      |   bbbbbbbbbb|bb|bbb
    |aaaaaaaddd|dddddddddd|ddddddddddddddbb|bbb
    |aaaaaaaddd|dddddddddd|ddddddddddddddbb|bbb
    |       ddd|dddddddddd|dddddddddddddd  |
    |       ddd|dddddddddd|dddddddddddddd  |
    |       ddd|ddddddddddeeeeeeeeeeeeeeeeee
    |       ddd|ddddddddddeeeeeeeeeeeeeeeeee
    |       ddd|ddddddddddeeeeeeeeeeeeeeeeee
    ccccccccddd|ddddddddddeeeeeeeeeeeeeeeeee
    ccccccccddd|ddddddddddeeeeeeeeeeeeeeeeee
    cccccccccccc          eeeeeeeeeeeeeeeeee
    cccccccccccc          eeeeeeeeeeeeeeeeee
    cccccccccccc
    cccccccccccc
    

    列表将是(其中每个v的坐标从0开始向上):

    0: +a, +c
    1: +d
    2: -c
    3: -a
    4: +e
    5: +b
    6: -d
    7: -e
    8: -b
    

    因此,每个条带都是(从上到下排序的矩形):

    0-1: a, c
    1-2: a, d, c
    2-3: a, d
    3-4: d
    4-5: d, e
    5-6: b, d, e
    6-7: b, e
    7-8: b
    

    对于每个条带,重叠部分为:

    0-1: none
    1-2: a/d, d/c
    2-3: a/d
    3-4: none
    4-5: d/e
    5-6: b/d, d/e
    6-7: none
    7-8: none
    

    我可以想象,用于顶部底部检查的sort+enter/leave算法的变体也是可行的:

    1. 对条形图中当前分析的矩形进行排序,从上到下,对于具有相同顶部坐标的矩形,也按底部坐标对其进行排序。
    2. 迭代Y坐标,当您输入一个矩形时,将其添加到集合中,当您离开一个矩形时,将其从集合中移除。
    3. 每当集合有多个矩形时,都会有重叠(如果确保添加/删除所有具有当前查看的上/下坐标的矩形,则多个重叠的矩形不会是问题

    对于上面的1-2条,您可以这样迭代:

    0. empty set, zero sum
    1. enter a, add a to set (1 rectangle in set)
    2. enter d, add d to set (>1 rectangles in set = overlap, store this y-coordinate)
    3. leave a, remove a from set (now back from >1 rectangles in set, add to sum: y - stored_y
    4. enter c, add c to set (>1 rectangles in set = overlap, store this y-coordinate)
    5. leave d, remove d from set (now back from >1 rectangles in set, add to sum: y - stored_y)
    6. multiply sum with width of strip to get overlapping areas
    

    实际上也不需要在这里维护一个实际的集合,只需要计算你所在的矩形的数量,每当它从1到2,存储y,每当它从2到1,计算当前的y存储y,并求和这个差。

    希望这是可以理解的,正如我所说,这是我的头上,没有任何测试。

        6
  •  3
  •   Will    16 年前

    使用示例:

       1   2   3   4   5   6
    
    1  +---+---+
       |       |   
    2  +   A   +---+---+
       |       | B     |
    3  +       +   +---+---+
       |       |   |   |   |
    4  +---+---+---+---+   +
                   |       |
    5              +   C   +
                   |       |
    6              +---+---+
    

    1)将所有X坐标(左、右)收集到一个列表中,然后对其进行排序并删除重复项

    1 3 4 5 6

    2)将所有Y坐标(顶部和底部)收集到一个列表中,然后对其进行排序并删除重复项

    1 2 3 4 6

    3)通过唯一X坐标之间的间隙数*唯一Y坐标之间的间隙数创建二维阵列。

    4 * 4

    4)将所有矩形绘制到此网格中,增加每个单元格的计数:

       1   3   4   5   6
    
    1  +---+
       | 1 | 0   0   0
    2  +---+---+---+
       | 1 | 1 | 1 | 0
    3  +---+---+---+---+
       | 1 | 1 | 2 | 1 |
    4  +---+---+---+---+
         0   0 | 1 | 1 |
    6          +---+---+
    

    5)网格中计数大于1的单元格区域的总和是重叠区域。为了在稀疏用例中提高效率,每次将单元格从1移动到2时,实际上可以在绘制矩形时保持该区域的总运行时间。


    在这个问题中,矩形被描述为四个双精度。双精度通常包含舍入误差,误差可能会蔓延到计算的重叠区域。如果合法坐标位于有限点,请考虑使用整数表示。


    PS使用硬件加速器和我的另一个答案一样,如果分辨率可以接受的话,这不是一个糟糕的想法。它也很容易实现,代码比我上面概述的方法要少得多。马为课程。

        7
  •  3
  •   extraeee    15 年前

    这是我为面积扫描算法编写的代码:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    
    class Rectangle {
    public:
        int x[2], y[2];
    
        Rectangle(int x1, int y1, int x2, int y2) {
            x[0] = x1;
            y[0] = y1;
            x[1] = x2;
            y[1] = y2; 
        };
        void print(void) {
            cout << "Rect: " << x[0] << " " << y[0] << " " << x[1] << " " << y[1] << " " <<endl;
        };
    };
    
    // return the iterator of rec in list
    vector<Rectangle *>::iterator bin_search(vector<Rectangle *> &list, int begin, int end, Rectangle *rec) {
        cout << begin << " " <<end <<endl;
        int mid = (begin+end)/2;
        if (list[mid]->y[0] == rec->y[0]) {
            if (list[mid]->y[1] == rec->y[1])
                return list.begin() + mid;
            else if (list[mid]->y[1] < rec->y[1]) {
                if (mid == end)
                    return list.begin() + mid+1;
                return bin_search(list,mid+1,mid,rec);
            }
            else {
                if (mid == begin)
                    return list.begin()+mid;
                return bin_search(list,begin,mid-1,rec);
            }
        }
        else if (list[mid]->y[0] < rec->y[0]) {
            if (mid == end) {
                return list.begin() + mid+1;
            }
            return bin_search(list, mid+1, end, rec);
        }
        else {
            if (mid == begin) {
                return list.begin() + mid;
            }
            return bin_search(list, begin, mid-1, rec);
        }
    }
    
    // add rect to rects
    void add_rec(Rectangle *rect, vector<Rectangle *> &rects) {
        if (rects.size() == 0) {
            rects.push_back(rect);
        }
        else {
            vector<Rectangle *>::iterator it = bin_search(rects, 0, rects.size()-1, rect);
            rects.insert(it, rect);
        }
    }
    
    // remove rec from rets
    void remove_rec(Rectangle *rect, vector<Rectangle *> &rects) {
        vector<Rectangle *>::iterator it = bin_search(rects, 0, rects.size()-1, rect);
        rects.erase(it);
    }
    
    // calculate the total vertical length covered by rectangles in the active set
    int vert_dist(vector<Rectangle *> as) {
        int n = as.size();
    
        int totallength = 0;
        int start, end;
    
        int i = 0;
        while (i < n) {
            start = as[i]->y[0];
            end = as[i]->y[1];
            while (i < n && as[i]->y[0] <= end) {
                if (as[i]->y[1] > end) {
                    end = as[i]->y[1];
                }
                i++;
            }
            totallength += end-start;
        }
        return totallength;
    }
    
    bool mycomp1(Rectangle* a, Rectangle* b) {
        return (a->x[0] < b->x[0]);
    }
    
    bool mycomp2(Rectangle* a, Rectangle* b) {
        return (a->x[1] < b->x[1]);
    }
    
    int findarea(vector<Rectangle *> rects) {
        vector<Rectangle *> start = rects;
        vector<Rectangle *> end = rects;
        sort(start.begin(), start.end(), mycomp1);
        sort(end.begin(), end.end(), mycomp2);
    
        // active set
        vector<Rectangle *> as;
    
        int n = rects.size();
    
        int totalarea = 0;
        int current = start[0]->x[0];
        int next;
        int i = 0, j = 0;
        // big loop
        while (j < n) {
            cout << "loop---------------"<<endl;
            // add all recs that start at current
            while (i < n && start[i]->x[0] == current) {
                cout << "add" <<endl;
                // add start[i] to AS
                add_rec(start[i], as);
                cout << "after" <<endl;
                i++;
            }
            // remove all recs that end at current
            while (j < n && end[j]->x[1] == current) {
                cout << "remove" <<endl;
                // remove end[j] from AS
                remove_rec(end[j], as);
                cout << "after" <<endl;
                j++;
            }
    
            // find next event x
            if (i < n && j < n) {
                if (start[i]->x[0] <= end[j]->x[1]) {
                    next = start[i]->x[0];
                }
                else {
                    next = end[j]->x[1];
                }
            }
            else if (j < n) {
                next = end[j]->x[1];
            }
    
            // distance to next event
            int horiz = next - current;
            cout << "horiz: " << horiz <<endl;
    
            // figure out vertical dist
            int vert = vert_dist(as);
            cout << "vert: " << vert <<endl;
    
            totalarea += vert * horiz;
    
            current = next;
        }
        return totalarea;
    }
    
    int main() {
        vector<Rectangle *> rects;
        rects.push_back(new Rectangle(0,0,1,1));
    
        rects.push_back(new Rectangle(1,0,2,3));
    
        rects.push_back(new Rectangle(0,0,3,3));
    
        rects.push_back(new Rectangle(1,0,5,1));
    
        cout << findarea(rects) <<endl;
    }
    
        8
  •  2
  •   Mark Ransom    16 年前

    如果将每个矩形拆分为较小的矩形,可以将此问题简化很多。收集所有矩形的所有X和Y坐标,这些坐标就成为分割点——如果一个矩形横过这条线,将其分割为两个。完成后,会有一个重叠0%或100%的矩形列表,如果对它们进行排序,就很容易找到相同的矩形。

        9
  •  2
  •   tick_tack_techie    9 年前

    链接上列出了一个解决方案 http://codercareer.blogspot.com/2011/12/no-27-area-of-rectangles.html 求多个矩形的总面积,使重叠区域只计算一次。

    上面的解决方案可以扩展到只计算重叠区域(即使重叠区域被多个矩形覆盖,也只能计算一次),每对连续的垂直扫描线都有水平扫描线。

    如果目的仅仅是找出所有矩形所覆盖的总面积,那么不需要水平扫描线,只要将两条垂直扫描线之间的所有矩形合并就可以得到该面积。

    另一方面,如果只计算重叠区域,则需要水平扫描线来确定垂直(y1,y2)扫描线之间重叠的矩形数。

    下面是我在Java中实现的解决方案的工作代码。

    import java.io.*;
    import java.util.*;
    
    class Solution {
    
    static class Rectangle{
             int x;
             int y;
             int dx;
             int dy;
    
             Rectangle(int x, int y, int dx, int dy){
               this.x = x;
               this.y = y;
               this.dx = dx;
               this.dy = dy;
             }
    
             Range getBottomLeft(){
                return new Range(x, y);
             }
    
             Range getTopRight(){
                return new Range(x + dx, y + dy);
             }
    
             @Override
             public int hashCode(){
                return (x+y+dx+dy)/4;
             }
    
             @Override
             public boolean equals(Object other){
                Rectangle o = (Rectangle) other;
                return o.x == this.x && o.y == this.y && o.dx == this.dx && o.dy == this.dy;
             }
    
            @Override
            public String toString(){
                return String.format("X = %d, Y = %d, dx : %d, dy : %d", x, y, dx, dy);
            }
         }     
    
         static class RW{
             Rectangle r;
             boolean start;
    
             RW (Rectangle r, boolean start){
               this.r = r;
               this.start = start;
             }
    
             @Override
             public int hashCode(){
                 return r.hashCode() + (start ? 1 : 0);
             }
    
             @Override
             public boolean equals(Object other){
                  RW o = (RW)other;
                 return o.start == this.start && o.r.equals(this.r);
             }
    
            @Override
            public String toString(){
                return "Rectangle : " + r.toString() + ", start = " + this.start;
            }
         }
    
         static class Range{
             int l;
             int u;   
    
           public Range(int l, int u){
             this.l = l;
             this.u = u;
           }
    
             @Override
             public int hashCode(){
                return (l+u)/2;
             }
    
             @Override
             public boolean equals(Object other){
                Range o = (Range) other;
                return o.l == this.l && o.u == this.u;
             }
    
            @Override
            public String toString(){
                return String.format("L = %d, U = %d", l, u);
            }
         }
    
         static class XComp implements Comparator<RW>{
                 @Override
                 public int compare(RW rw1, RW rw2){
                     //TODO : revisit these values.
                     Integer x1 = -1;
                     Integer x2 = -1;
    
                     if(rw1.start){
                         x1 = rw1.r.x;
                     }else{
                         x1 = rw1.r.x + rw1.r.dx;
                     }   
    
                     if(rw2.start){
                         x2 = rw2.r.x;
                     }else{
                         x2 = rw2.r.x + rw2.r.dx;
                     }
    
                     return x1.compareTo(x2);
                 }
         }
    
         static class YComp implements Comparator<RW>{
                 @Override
                 public int compare(RW rw1, RW rw2){
                     //TODO : revisit these values.
                     Integer y1 = -1;
                     Integer y2 = -1;
    
                     if(rw1.start){
                         y1 = rw1.r.y;
                     }else{
                         y1 = rw1.r.y + rw1.r.dy;
                     }   
    
                     if(rw2.start){
                         y2 = rw2.r.y;
                     }else{
                         y2 = rw2.r.y + rw2.r.dy;
                     }
    
                     return y1.compareTo(y2);
                 }
         }
    
         public static void main(String []args){
             Rectangle [] rects = new Rectangle[4];
    
             rects[0] = new Rectangle(10, 10, 10, 10);
             rects[1] = new Rectangle(15, 10, 10, 10);
             rects[2] = new Rectangle(20, 10, 10, 10);
             rects[3] = new Rectangle(25, 10, 10, 10);
    
             int totalArea = getArea(rects, false);
             System.out.println("Total Area : " + totalArea);
    
             int overlapArea = getArea(rects, true);              
             System.out.println("Overlap Area : " + overlapArea);
         }
    
    
         static int getArea(Rectangle []rects, boolean overlapOrTotal){
             printArr(rects);
    
             // step 1: create two wrappers for every rectangle
             RW []rws = getWrappers(rects);       
    
             printArr(rws);        
    
             // steps 2 : sort rectangles by their x-coordinates
             Arrays.sort(rws, new XComp());   
    
             printArr(rws);        
    
             // step 3 : group the rectangles in every range.
             Map<Range, List<Rectangle>> rangeGroups = groupRects(rws, true);
    
             for(Range xrange : rangeGroups.keySet()){
                 List<Rectangle> xRangeRects = rangeGroups.get(xrange);
                 System.out.println("Range : " + xrange);
                 System.out.println("Rectangles : ");
                 for(Rectangle rectx : xRangeRects){
                    System.out.println("\t" + rectx);               
                 }
             }   
    
             // step 4 : iterate through each of the pairs and their rectangles
    
             int sum = 0;
             for(Range range : rangeGroups.keySet()){
                 List<Rectangle> rangeRects = rangeGroups.get(range);
                 sum += getOverlapOrTotalArea(rangeRects, range, overlapOrTotal);
             }
             return sum;         
         }    
    
         static Map<Range, List<Rectangle>> groupRects(RW []rws, boolean isX){
             //group the rws with either x or y coordinates.
    
             Map<Range, List<Rectangle>> rangeGroups = new HashMap<Range, List<Rectangle>>();
    
             List<Rectangle> rangeRects = new ArrayList<Rectangle>();            
    
             int i=0;
             int prev = Integer.MAX_VALUE;
    
             while(i < rws.length){
                 int curr = isX ? (rws[i].start ? rws[i].r.x : rws[i].r.x + rws[i].r.dx): (rws[i].start ? rws[i].r.y : rws[i].r.y + rws[i].r.dy);
    
                 if(prev < curr){
                    Range nRange = new Range(prev, curr);
                    rangeGroups.put(nRange, rangeRects);
                    rangeRects = new ArrayList<Rectangle>(rangeRects);
                 }
                 prev = curr;
    
                 if(rws[i].start){
                   rangeRects.add(rws[i].r);
                 }else{
                   rangeRects.remove(rws[i].r);
                 }
    
               i++;
             }
           return rangeGroups;
         }
    
         static int getOverlapOrTotalArea(List<Rectangle> rangeRects, Range range, boolean isOverlap){
             //create horizontal sweep lines similar to vertical ones created above
    
             // Step 1 : create wrappers again
             RW []rws = getWrappers(rangeRects);
    
             // steps 2 : sort rectangles by their y-coordinates
             Arrays.sort(rws, new YComp());
    
             // step 3 : group the rectangles in every range.
             Map<Range, List<Rectangle>> yRangeGroups = groupRects(rws, false);
    
             //step 4 : for every range if there are more than one rectangles then computer their area only once.
    
             int sum = 0;
             for(Range yRange : yRangeGroups.keySet()){
                 List<Rectangle> yRangeRects = yRangeGroups.get(yRange);
    
                 if(isOverlap){
                     if(yRangeRects.size() > 1){
                         sum += getArea(range, yRange);
                     }
                 }else{
                     if(yRangeRects.size() > 0){
                         sum += getArea(range, yRange);
                     }
                 }
             }         
             return sum;
         } 
    
        static int getArea(Range r1, Range r2){
          return (r2.u-r2.l)*(r1.u-r1.l);      
        }
    
        static RW[] getWrappers(Rectangle []rects){
             RW[] wrappers = new RW[rects.length * 2];
    
             for(int i=0,j=0;i<rects.length;i++, j+=2){
                 wrappers[j] = new RW(rects[i], true); 
                 wrappers[j+1] = new RW(rects[i], false); 
             }
             return wrappers;
         }
    
        static RW[] getWrappers(List<Rectangle> rects){
             RW[] wrappers = new RW[rects.size() * 2];
    
             for(int i=0,j=0;i<rects.size();i++, j+=2){
                 wrappers[j] = new RW(rects.get(i), true); 
                 wrappers[j+1] = new RW(rects.get(i), false); 
             }
             return wrappers;
         }
    
      static void printArr(Object []a){
        for(int i=0; i < a.length;i++){
          System.out.println(a[i]);
        }
        System.out.println();
      }     
    
        10
  •  0
  •   Oliver Hallam    16 年前

    如果您的矩形将是稀疏的(大部分不是相交的),那么它可能值得一看递归维度集群。否则,一棵四叉树似乎就是一条出路(正如其他海报所提到的那样)。

    这是计算机游戏中碰撞检测中的一个常见问题,因此提出解决方法的资源并不缺乏。

    Here 是一篇很好的总结RCD的博客文章。

    Here 是Dobbs博士的一篇文章,总结了各种适合的碰撞检测算法。

        11
  •  0
  •   grapefrukt    16 年前

    这种类型的碰撞检测通常称为AABB(轴对齐的边界框),这是 google search .

        12
  •  0
  •   community wiki 2 revs Toon Krijthe    16 年前

    你可以在x轴和y轴上找到重叠,然后将它们相乘。

    int LineOverlap(int line1a, line1b, line2a, line2b) 
    {
      // assume line1a <= line1b and line2a <= line2b
      if (line1a < line2a) 
      {
        if (line1b > line2b)
          return line2b-line2a;
        else if (line1b > line2a)
          return line1b-line2a;
        else 
          return 0;
      }
      else if (line2a < line1b)
        return line2b-line1a;
      else 
        return 0;
    }
    
    
    int RectangleOverlap(Rect rectA, rectB) 
    {
      return LineOverlap(rectA.x1, rectA.x2, rectB.x1, rectB.x2) *
        LineOverlap(rectA.y1, rectA.y2, rectB.y1, rectB.y2);
    }
    
        13
  •  0
  •   Torsten Fehre    11 年前

    我找到了一个与扫描算法不同的解决方案。

    由于矩形都是矩形放置的,因此矩形的水平线和垂直线将形成矩形不规则网格。您可以在网格上“绘制”矩形;这意味着,您可以确定将填充网格的哪些字段。由于网格线是由给定矩形的边界形成的,因此网格中的字段将始终完全为空或完全由矩形填充。

    我必须用Java解决这个问题,所以这里是我的解决方案: http://pastebin.com/03mss8yf

    此函数计算矩形所占的完整面积。如果您只对“重叠”部分感兴趣,则必须在第70行和第72行之间扩展代码块。也许您可以使用第二个集合来存储哪些网格字段被多次使用。第70行和第72行之间的代码应替换为如下块:

    GridLocation gl = new GridLocation(curX, curY);
    if(usedLocations.contains(gl) && usedLocations2.add(gl)) {
      ret += width*height;
    } else {
      usedLocations.add(gl);
    }
    

    这里的变量usedlocations2与usedlocations的类型相同;它将被构造 在同一点上。

    我不太熟悉复杂度计算;所以我不知道这两个解决方案(扫描或网格解决方案)中哪一个能更好地执行/扩展。

        14
  •  0
  •   user3048546    8 年前

    考虑到我们有两个矩形(A和B),我们有它们的左下(x1,y1)和右上(x2,y2)坐标。使用下面的代码可以计算C++中的重叠区域。

        #include <iostream>
    using namespace std;
    
    int rectoverlap (int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2)
    {
        int width, heigh, area;
    
        if (ax2<bx1 || ay2<by1 || ax1>bx2 || ay1>by2) {
            cout << "Rectangles are not overlapped" << endl;
            return 0;
        }
        if (ax2>=bx2 && bx1>=ax1){
            width=bx2-bx1;
            heigh=by2-by1;
        } else if (bx2>=ax2 && ax1>=bx1) {
            width=ax2-ax1;
            heigh=ay2-ay1;
        } else {
            if (ax2>bx2){
                width=bx2-ax1;
            } else {
                width=ax2-bx1;
            }
            if (ay2>by2){
                heigh=by2-ay1;
            } else {
                heigh=ay2-by1;
            }
        }
        area= heigh*width;
        return (area);
    }
    
    int main()
    {
        int ax1,ay1,ax2,ay2,bx1,by1,bx2,by2;
        cout << "Inter the x value for bottom left for rectangle A" << endl;
        cin >> ax1;
        cout << "Inter the y value for bottom left for rectangle A" << endl;
        cin >> ay1;
        cout << "Inter the x value for top right for rectangle A" << endl;
        cin >> ax2;
        cout << "Inter the y value for top right for rectangle A" << endl;
        cin >> ay2;
        cout << "Inter the x value for bottom left for rectangle B" << endl;
        cin >> bx1;
        cout << "Inter the y value for bottom left for rectangle B" << endl;
        cin >> by1;
        cout << "Inter the x value for top right for rectangle B" << endl;
        cin >> bx2;
        cout << "Inter the y value for top right for rectangle B" << endl;
        cin >> by2;
        cout << "The overlapped area is " <<  rectoverlap (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) << endl;
    }
    
        15
  •  0
  •   ephraim    7 年前

    下面的答案应该只给出一次总面积。 它有以前的答案,但现在在C中实现。 它也适用于浮点数(如果需要,也可以是double,如果不超过值的话)。

    信用: http://codercareer.blogspot.co.il/2011/12/no-27-area-of-rectangles.html

    编辑: OP要求重叠区域-这显然非常简单:

    var totArea = rects.Sum(x => x.Width * x.Height);
    

    答案是:

    var overlappingArea =totArea-GetArea(rects)
    

    代码如下:

       #region rectangle overlapping
            /// <summary>
            /// see algorithm for detecting overlapping areas here: https://stackoverflow.com/a/245245/3225391
            /// or easier here:
            /// http://codercareer.blogspot.co.il/2011/12/no-27-area-of-rectangles.html
            /// </summary>
            /// <param name="dim"></param>
            /// <returns></returns>
            public static float GetArea(RectangleF[] rects)
            {
                List<float> xs = new List<float>();
                foreach (var item in rects)
                {
                    xs.Add(item.X);
                    xs.Add(item.Right);
                }
                xs = xs.OrderBy(x => x).Cast<float>().ToList();
                rects = rects.OrderBy(rec => rec.X).Cast<RectangleF>().ToArray();
                float area = 0f;
                for (int i = 0; i < xs.Count - 1; i++)
                {
                    if (xs[i] == xs[i + 1])//not duplicate
                        continue;
                    int j = 0;
                    while (rects[j].Right < xs[i])
                        j++;
                    List<Range> rangesOfY = new List<Range>();
                    var rangeX = new Range(xs[i], xs[i + 1]);
                    GetRangesOfY(rects, j, rangeX, out rangesOfY);
                    area += GetRectArea(rangeX, rangesOfY);
                }
                return area;
            }
    
            private static void GetRangesOfY(RectangleF[] rects, int rectIdx, Range rangeX, out List<Range> rangesOfY)
            {
                rangesOfY = new List<Range>();
                for (int j = rectIdx; j < rects.Length; j++)
                {
                    if (rangeX.less < rects[j].Right && rangeX.greater > rects[j].Left)
                    {
                        rangesOfY = Range.AddRange(rangesOfY, new Range(rects[j].Top, rects[j].Bottom));
    #if DEBUG
                        Range rectXRange = new Range(rects[j].Left, rects[j].Right);
    #endif
                    }
                }
            }
    
            static float GetRectArea(Range rangeX, List<Range> rangesOfY)
            {
                float width = rangeX.greater - rangeX.less,
                    area = 0;
    
                foreach (var item in rangesOfY)
                {
                    float height = item.greater - item.less;
                    area += width * height;
                }
                return area;
            }
    
            internal class Range
            {
                internal static List<Range> AddRange(List<Range> lst, Range rng2add)
                {
                    if (lst.isNullOrEmpty())
                    {
                        return new List<Range>() { rng2add };
                    }
    
                    for (int i = lst.Count - 1; i >= 0; i--)
                    {
                        var item = lst[i];
                        if (item.IsOverlapping(rng2add))
                        {
                            rng2add.Merge(item);
                            lst.Remove(item);
                        }
                    }
                    lst.Add(rng2add);
                    return lst;
                }
                internal float greater, less;
                public override string ToString()
                {
                    return $"ln{less} gtn{greater}";
                }
    
                internal Range(float less, float greater)
                {
                    this.less = less;
                    this.greater = greater;
                }
    
                private void Merge(Range rng2add)
                {
                    this.less = Math.Min(rng2add.less, this.less);
                    this.greater = Math.Max(rng2add.greater, this.greater);
                }
                private bool IsOverlapping(Range rng2add)
                {
                    return !(less > rng2add.greater || rng2add.less > greater);
                    //return
                    //    this.greater < rng2add.greater && this.greater > rng2add.less
                    //    || this.less > rng2add.less && this.less < rng2add.greater
    
                    //    || rng2add.greater < this.greater && rng2add.greater > this.less
                    //    || rng2add.less > this.less && rng2add.less < this.greater;
                }
            }
            #endregion rectangle overlapping