-
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
Un moyen efficace de calculer cette zone consiste à utiliser un algorithme de balayage. Supposons que nous balayons une ligne verticale L(x) à travers l'union des rectangles U:
Nous devons encore résoudre le problème 1D. Vous voulez une structure 1D, qui calcule dynamiquement une union de segments (verticaux). Par dynamique, je veux dire que vous ajoutez parfois un nouveau segment et en supprimez parfois un.
J'ai déjà détaillé dans ma réponse à cette question collapsing range comment le faire de manière statique (qui est en fait un balayage 1D). Donc, si vous voulez quelque chose de simple, vous pouvez l'appliquer directement (en recalculant l'union pour chaque événement). Si vous voulez quelque chose de plus efficace, il vous suffit de l'adapter un peu:
Ceci est votre algorithme dynamique. En supposant que vous utilisiez des ensembles triés avec des requêtes d'emplacement de journal pour représenter D1...RÉk, c’est probablement la méthode non spécialisée la plus efficace que vous puissiez obtenir.
Une approche possible consiste à le tracer sur une toile! Dessinez chaque rectangle en utilisant une couleur semi-transparente. Le runtime .NET dessinera dans un code natif optimisé - ou même à l'aide d'un accélérateur matériel.
Ensuite, vous devez relire les pixels. Chaque pixel est-il la couleur de fond, la couleur du rectangle ou une autre couleur? La seule façon de choisir une autre couleur est si deux ou plusieurs rectangles se chevauchent ...
Si c'est trop compliqué, je recommanderais le quad-tree comme un autre répondeur, ou le r-tree .
C'est un code rapide et sale que j'ai utilisé dans le TopCoder SRM 160 Div 2.
t = top
b = botttom
l = left
r = right
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;
}
}
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)
Dans cet exemple, nous créons deux matrices nulles qui correspondent à la taille du canevas. Pour chaque rectangle, remplissez l'une de ces matrices avec des matrices où le rectangle occupe de la place. Puis additionnez les matrices. Maintenant sum(A+B > 0)
est la zone du syndicat et sum(A+B > 1)
est la zone de chevauchement. Cet exemple peut facilement généraliser à plusieurs rectangles.
Voici quelque chose qui me dit que cela pourrait marcher:
Créez un dictionnaire avec une double clé et une liste de valeurs rectangle + booléen, comme ceci:
Dictionary <Double, List <KeyValuePair <Rectangle, Boolean >>> rectangles;
Pour chaque rectangle de votre ensemble, recherchez la liste correspondante pour les valeurs x0 et x1 et ajoutez le rectangle à cette liste, avec une valeur booléenne true pour x0 et false pour x1. De cette façon, vous avez maintenant une liste complète de toutes les coordonnées x que chaque rectangle entre (true) ou laisse (false) la direction x
Prenez toutes les clés de ce dictionnaire (toutes les coordonnées x distinctes), triez-les et passez-les dans l'ordre, assurez-vous que vous pouvez obtenir à la fois la valeur x actuelle et la suivante (vous en aurez besoin à la fois ). Cela vous donne des bandes individuelles de rectangles
Exemple, 5 rectangles, se superposent, de 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
Voici la liste des coordonnées 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
La liste serait (où chaque v reçoit simplement une coordonnée commençant à 0 et allant vers le haut):
0: +a, +c
1: +d
2: -c
3: -a
4: +e
5: +b
6: -d
7: -e
8: -b
Chaque bande serait donc (des rectangles triés de haut en bas):
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
pour chaque bande, le chevauchement serait:
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
J'imagine qu'une variante de l'algorithme sort + entrée/sortie pour la vérification de haut en bas serait également réalisable:
Pour la bande 1-2 ci-dessus, vous voudriez itérer comme ceci:
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
Vous n’auriez pas besoin de conserver un ensemble réel ici non plus, mais seulement le nombre de rectangles dans lesquels vous vous trouvez, chaque fois que cela passe de 1 à 2, enregistrez le y, et chaque fois qu’il passe de 2 à 1, calculez le y actuel - y stocké, et additionnez cette différence.
J'espère que cela était compréhensible, et comme je l'ai dit, cela ne vient pas de moi, cela n'a pas été testé.
En utilisant l'exemple:
1 2 3 4 5 6 1 + --- + --- + | | 2 + A + --- + --- + | | B | 3 + + + --- + --- + | | | | | 4 + --- + --- + --- + --- + + | | 5 + C + | | 6 + --- + --- +
1) rassemblez toutes les coordonnées x (à gauche et à droite) dans une liste, puis triez-la et supprimez les doublons
1 3 4 5 6
2) rassemblez toutes les coordonnées y (haut et bas) dans une liste, puis triez-la et éliminez les doublons
1 2 3 4 6
3) créer un tableau 2D par nombre d'espaces entre les coordonnées x uniques * nombre d'espaces entre les coordonnées y uniques.
4 * 4
4) Peignez tous les rectangles dans cette grille, en incrémentant le nombre de cellules sur lesquelles elle apparaît.
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) la somme des zones des cellules de la grille dont le nombre est supérieur à un est la zone de chevauchement. Pour une meilleure efficacité dans les cas d'utilisation peu nombreux, vous pouvez réellement conserver un total cumulé de la surface lorsque vous peignez les rectangles, chaque fois que vous déplacez une cellule de 1 à 2.
Dans la question, les rectangles sont décrits comme étant quatre doubles. Les doublons contiennent généralement des erreurs d'arrondi et des erreurs peuvent se glisser dans la zone de chevauchement calculée. Si les coordonnées légales sont en points finis, envisagez d'utiliser une représentation entière.
PS en utilisant l'accélérateur matériel comme dans mon autre réponse n'est pas une si mauvaise idée, si la résolution est acceptable. Il est également facile à implémenter dans beaucoup moins de code que l’approche ci-dessus. Chevaux de course.
Voici le code que j'ai écrit pour l'algorithme de balayage de zone:
#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;
}
Vous pouvez simplifier un peu ce problème en scindant chaque rectangle en rectangles plus petits. Rassemblez toutes les coordonnées X et Y de tous les rectangles et ceux-ci deviennent vos points de scission - si un rectangle traverse la ligne, scindez-les en deux. Lorsque vous avez terminé, vous avez une liste de rectangles qui se chevauchent soit à 0%, soit à 100%. Si vous les triez, il devrait être facile de trouver ceux qui sont identiques.
Il existe une solution répertoriée au lien http://codercareer.blogspot.com/2011/12/no-27-area-of-rectangles.html pour rechercher la surface totale de plusieurs rectangles telle que la zone superposée est compté qu'une seule fois.
La solution ci-dessus peut être étendue pour ne calculer que la zone superposée (et cela aussi une seule fois, même si la zone superposée est recouverte de plusieurs rectangles) avec des lignes horizontales pour chaque paire de lignes verticales consécutives.
Si l'objectif est simplement de connaître la surface totale couverte par tous les rectangles, les lignes horizontales de balayage ne sont pas nécessaires et une simple fusion de tous les rectangles entre deux lignes de balayage verticales donnerait l'aire.
D'autre part, si vous souhaitez calculer uniquement la zone superposée, les lignes de balayage horizontales sont nécessaires pour déterminer le nombre de rectangles qui se chevauchent entre les lignes de balayage verticales (y1, y2).
Voici le code de travail pour la solution que j'ai implémentée en 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();
}
Si vos rectangles vont être clairsemés (la plupart du temps ne se croisent pas), alors il vaut peut-être la peine de regarder la mise en grappe dimensionnelle récursive. Sinon, un quad-arbre semble être la voie à suivre (comme cela a été mentionné par d'autres affiches.
Il s’agit d’un problème courant dans la détection des collisions dans les jeux informatiques. Il ne manque donc pas de ressources suggérant des moyens de le résoudre.
Here est un billet de blog de Nice résumant le RCD.
Here est un article de Dr.Dobbs résumant divers algorithmes de détection de collision, qui conviendrait.
Considérant que nous avons deux rectangles (A et B) et que nous avons leur coordination en bas à gauche (x1, y1) et en haut à droite (x2, y2). Le morceau de code suivant vous permet de calculer la zone superposée en 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;
}
La réponse suivante devrait donner la surface totale une seule fois… .. elle vient avec les réponses précédentes, mais elle est maintenant mise en œuvre en C # . Elle fonctionne aussi avec des flottants (ou double si vous avez besoin [cela ne vaut pas la valeur] .
Crédits: http://codercareer.blogspot.co.il/2011/12/no-27-area-of-rectangles.html
MODIFIER: Le PO a demandé la zone de chevauchement - ce qui est évidemment très simple:
var totArea = rects.Sum(x => x.Width * x.Height);
et alors la réponse est:
var overlappingArea =totArea-GetArea(rects)
Voici le code:
#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
Ce type de détection de collision est souvent appelé AABB (Axis Aligned Bounding Boxes), ce qui constitue un bon point de départ pour une recherche google .
J'ai trouvé une solution différente de celle de l'algorithme de balayage.
Puisque vos rectangles sont tous rectangulaires, les lignes horizontales et verticales des rectangles formeront une grille rectangulaire irrégulière. Vous pouvez "peindre" les rectangles sur cette grille; ce qui signifie que vous pouvez déterminer quels champs de la grille seront remplis. Comme les lignes de la grille sont formées à partir des limites des rectangles donnés, un champ de cette grille sera toujours soit complètement vide, soit complètement rempli par un rectangle.
Je devais résoudre le problème en Java, voici donc ma solution: http://Pastebin.com/03mss8yf
Cette fonction calcule la surface complète occupée par les rectangles. Si vous ne vous intéressez qu'à la partie "chevauchement", vous devez étendre le bloc de code entre les lignes 70 et 72. Vous pouvez peut-être utiliser un deuxième jeu pour stocker les champs de grille utilisés plusieurs fois. Votre code entre les lignes 70 et 72 doit être remplacé par un bloc tel que:
GridLocation gl = new GridLocation(curX, curY);
if(usedLocations.contains(gl) && usedLocations2.add(gl)) {
ret += width*height;
} else {
usedLocations.add(gl);
}
La variable usedLocations2 est ici du même type que usedLocations; il sera construitau même point.
Je ne suis pas vraiment familier avec les calculs de complexité; donc je ne sais pas laquelle des deux solutions (balayage ou ma solution de grille) sera plus performante/évolutive.
Vous pouvez trouver le chevauchement sur les axes x et y et les multiplier.
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);
}