51

我的情况

  • 输入:一组矩形
  • 每个矩形由 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
4

16 回答 16

62

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

  • 首先,您需要构建一个事件队列 Q,在这种情况下,它是矩形的所有 x 坐标(左和右)的有序列表。
  • 在扫描期间,您应该维护一个一维数据结构,它应该给您 L(x) 和 U 的交集的总长度。重要的是这个长度在 Q 的两个连续事件 q 和 q' 之间是恒定的。所以, 如果 l(q) 表示 L(q+) 的总长度(即 L 恰好在 q 的右侧)与 U 相交,则 L 在事件 q 和 q' 之间扫过的区域正好是 l(q)*(q' -q)。
  • 您只需将所有这些扫过的区域加起来即可得到总和。

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

我已经在我对这个折叠范围问题的回答中详细说明了如何以静态方式进行操作(实际上是一维扫描)。所以如果你想要一些简单的东西,你可以直接应用它(通过重新计算每个事件的联合)。如果你想要更高效的东西,你只需要稍微调整一下:

  • 假设您知道线段 S 1 ...S n的并集由不相交的线段 D 1 ...D k组成。添加 S n+1非常简单,您只需在D 1 ...D k的两端中找到 S n+1的两端。
  • 假设您知道段的并集 S 1 ...S n由不相交的段 D 1 ...D k组成,删除段 S i(假设 S i包含在 D j中)意味着重新计算 D 的段的并集j包括,除了 S i(使用静态算法)。

这是您的动态算法。假设您将使用带有对数时间位置查询的排序集来表示 D 1 ...D k,这可能是您可以获得的最有效的非专业方法。

于 2008-10-28T23:20:01.027 回答
14

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

然后,您必须回读像素。每个像素是背景颜色、矩形颜色还是其他颜色?它可以是另一种颜色的唯一方法是两个或多个矩形重叠......

如果这太过分了,我会像另一个回答者那样推荐四叉树,或者r-tree

于 2008-10-28T19:49:17.287 回答
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)的区域,是重叠的区域。这个例子可以很容易地推广到多个矩形。

于 2014-08-18T01:44:07.153 回答
10

这是我在 TopCoder SRM 160 Div 2 中使用的一些快速而肮脏的代码。

t = 上
b = 下
l = 左
r = 右

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;
    }
}
于 2008-10-28T20:14:48.920 回答
6

这是我头顶上听起来可能有用的东西:

  1. 创建一个带有双键的字典和一个矩形+布尔值列表,如下所示:

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

  2. 对于集合中的每个矩形,找到 x0 和 x1 值的对应列表,并将矩形添加到该列表中,布尔值 true 表示 x0,false 表示 x1。这样,您现在拥有每个矩形进入(真)或离开(假)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

我想对于上下检查的排序+进入/离开算法的变体也是可行的:

  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,并将这个差值相加。

希望这是可以理解的,正如我所说,这不是我的想法,没有经过任何测试。

于 2008-10-28T22:14:59.990 回答
3

使用示例:

   1 2 3 4 5 6

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

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

1 3 4 5 6

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

1 2 3 4 6

3) 通过唯一 x 坐标之间的间隙数 * 唯一 y 坐标之间的间隙数创建一个 2D 数组。

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 在我的其他答案中使用硬件加速器并不是一个破旧的想法。它也很容易用比我上面概述的方法少得多的代码来实现。课程用马。

于 2008-11-18T09:41:51.817 回答
3

这是我为区域扫描算法编写的代码:

#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;
}
于 2010-04-13T04:44:52.520 回答
2

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

于 2008-10-28T22:15:33.920 回答
2

链接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();
  }     
于 2016-01-06T01:55:50.677 回答
1

以下答案应该只给出一次总面积。它来自以前的答案,但现在在 C# 中实现。它也适用于浮点数(或双精度,如果你需要[它不会遍历值)。

学分: 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
于 2017-10-16T14:06:07.193 回答
0

您可以找到 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);
}
于 2008-10-28T19:12:41.620 回答
0

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

这是计算机游戏中碰撞检测中的常见问题,因此不乏建议解决方法的资源。

是一篇总结 RCD 的不错的博客文章。

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

于 2008-10-28T21:49:44.097 回答
0

这种类型的碰撞检测通常称为 AABB(Axis Aligned Bounding Boxes),这是google 搜索的一个很好的起点。

于 2008-10-28T22:09:39.313 回答
0

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

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

我必须用 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 的类型相同;它将在同一点建造。

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

于 2013-12-27T12:37:42.077 回答
0

考虑到我们有两个矩形(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;
}
于 2016-10-03T06:52:42.320 回答
0

user3048546的帖子在第 12-17 行的逻辑中包含错误。这是一个有效的实现:

int rectoverlap (int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2)
{
    int width, height, 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;
    } else if (bx2>=ax2 && ax1>=bx1) {
        width=ax2-ax1;
    } else if (ax2>bx2) {
        width=bx2-ax1;
    } else {
        width=ax2-bx1;
    }

    if (ay2>=by2 && by1>=ay1){
        height=by2-by1;
    } else if (by2>=ay2 && ay1>=by1) {
        height=ay2-ay1;
    } else if (ay2>by2) {
        height=by2-ay1;
    } else {
        height=ay2-by1;
    }

    area = heigh*width;
    return (area);
}
于 2020-07-21T16:55:52.033 回答