it-swarm.com.de

Was ist ein effizienter Algorithmus, um überlappende Rechtecke zu finden?

Meine Situation

  • Eingabe: eine Reihe von Rechtecken 
  • jedes Rect besteht aus 4 Doubles wie folgt: (x0, y0, x1, y1)
  • sie sind in keinem Winkel "gedreht", es handelt sich lediglich um "normale" Rechtecke, die in Bezug auf den Bildschirm "nach oben/unten" und "nach links/rechts" gehen
  • sie werden zufällig platziert - sie berühren sich möglicherweise an den Rändern, überlappen sich oder haben keinen Kontakt
  • Ich werde mehrere hundert Rechtecke haben
  • dies ist in C # implementiert.

Ich muss finden

  • Der Bereich, der durch ihre Überlappung gebildet wird - der gesamte Bereich auf der Leinwand, den mehr als ein Rechteck "überdeckt" (z. B. mit zwei Rechtecken wäre dies die Schnittmenge).
  • Ich brauche nicht die Geometrie der Überlappung - nur die Fläche (Beispiel: 4 Quadratzoll)
  • Überlappungen sollten nicht mehrmals gezählt werden. Stellen Sie sich zum Beispiel 3 Rects vor, die die gleiche Größe und Position haben. Sie liegen direkt aufeinander.

Beispiel

  • Das Bild unten enthält drei Rechtecke: A, B, C
  • A und B überlappen sich (wie durch Striche angegeben)
  • B und C überlappen sich (wie durch Striche angedeutet)
  • Was ich suche, ist der Bereich, in dem die Striche angezeigt werden

-

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
44
namenlos

Eine effiziente Methode zur Berechnung dieses Bereichs besteht in der Verwendung eines Sweep-Algorithmus. Nehmen wir an, wir streichen eine vertikale Linie L(x) durch die Vereinigung der Rechtecke U: 

  • zunächst müssen Sie eine Ereigniswarteschlange Q erstellen, die in diesem Fall die geordnete Liste aller X-Koordinaten (links und rechts) der Rechtecke ist. 
  • während des Sweeps sollten Sie eine 1D-Datenstruktur aufrechterhalten, die Ihnen die Gesamtlänge des Schnittpunkts von L(x) und U angibt. Wichtig ist, dass diese Länge zwischen zwei aufeinander folgenden Ereignissen q und q 'konstant ist. Wenn also l(q) die Gesamtlänge von L (q +) (dh L gerade auf der rechten Seite von q), die mit U geschnitten ist, bezeichnet, ist der Bereich, der mit L zwischen den Ereignissen q und q 'überstrichen ist, genau l (q) * (q '- q).
  • sie müssen nur alle diese überstrichenen Bereiche zusammenfassen, um die Gesamtzahl zu erhalten.

Wir müssen das 1D-Problem noch lösen. Sie möchten eine 1D-Struktur, die dynamisch eine Vereinigung von (vertikalen) Segmenten berechnet. Mit dynamisch meine ich, dass Sie manchmal ein neues Segment hinzufügen und manchmal ein Segment entfernen. 

Ich habe bereits ausführlich in meiner Antwort auf diese einklappende Bereichsfrage beschrieben, wie man dies statisch macht (was tatsächlich ein 1D-Sweep ist). Wenn Sie also etwas einfaches wollen, können Sie dies direkt anwenden (indem Sie die Vereinigung für jedes Ereignis neu berechnen). Wenn Sie etwas effizienteres wollen, müssen Sie es nur ein wenig anpassen:

  • vorausgesetzt, Sie kennen die Vereinigung der Segmente S1... Sn besteht aus disjoints segmenten D1... dk. Hinzufügen von Sn + 1 ist sehr einfach, Sie müssen nur beide Enden von S findenn + 1 unter den Enden von D1... dk.
  • vorausgesetzt, Sie kennen die Vereinigung der Segmente S1... Sn besteht aus disjoints segmenten D1... dk, Entfernen des Segments Sich (vorausgesetzt, dass Sich war in D enthaltenj) bedeutet, die Vereinigung der Segmente neu zu berechnen, die Dj bestand aus, außer Sich (unter Verwendung des statischen Algorithmus).

Dies ist Ihr dynamischer Algorithmus. Angenommen, Sie verwenden sortierte Sätze mit Standortabfragen für Protokollzeiten, um D darzustellen1... dkDies ist wahrscheinlich die effizienteste, nicht spezialisierte Methode, die Sie erhalten können.

51
Camille

Ein Ausweg besteht darin, es auf einer Leinwand zu plotten! Zeichnen Sie jedes Rechteck mit einer halbtransparenten Farbe. Die .NET-Laufzeitumgebung zeichnet das Zeichnen mit optimiertem, nativem Code - oder sogar mit einem Hardwarebeschleuniger.

Dann müssen Sie die Pixel zurücklesen. Ist jedes Pixel die Hintergrundfarbe, die Rechteckfarbe oder eine andere Farbe? Es kann nur eine andere Farbe sein, wenn sich zwei oder mehr Rechtecke überlappen ...

Wenn dies zu viel ist, würde ich den Quad-Tree wie einen anderen Antworter oder den r-Tree empfehlen.

13
Will

Dies ist ein schneller und schmutziger Code, den ich im TopCoder SRM 160 Div 2 verwendet habe.

t = oben
b = unten
l = übrig
r = richtig

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;
    }
}
9
LeppyR64

Die einfachste Lösung

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)

In diesem Beispiel erstellen wir zwei Nullmatrizen, die der Größe der Leinwand entsprechen. Füllen Sie für jedes Rechteck eine dieser Matrizen mit denen, bei denen das Rechteck Platz beansprucht. Dann summieren Sie die Matrizen. Jetzt ist sum(A+B > 0) der Bereich der Vereinigung und sum(A+B > 1) der Bereich der Überlappung. Dieses Beispiel kann leicht auf mehrere Rechtecke verallgemeinert werden.

8
Rose Perrone

Hier ist etwas, das sich aus meinem Kopf anhört, als könnte es funktionieren:

  1. Erstellen Sie ein Wörterbuch mit einer Doppeltaste und einer Liste mit Rechteck + booleschen Werten wie folgt:

    Dictionary <Double, List <KeyValuePair <Rechteck, Boolean >>> Rechtecke;

  2. Suchen Sie für jedes Rechteck in Ihrem Set die entsprechende Liste für die Werte x0 und x1, und fügen Sie der Liste das Rechteck hinzu. Der boolesche Wert ist true für x0 und false für x1. Auf diese Weise haben Sie jetzt eine vollständige Liste aller x-Koordinaten, die jedes Rechteck entweder in die x-Richtung einführt (true) oder belässt (false)

  3. Schnappen Sie sich alle Schlüssel aus diesem Wörterbuch (alle eindeutigen X-Koordinaten), sortieren Sie sie und durchlaufen Sie sie in der richtigen Reihenfolge. Stellen Sie sicher, dass Sie sowohl den aktuellen X-Wert als auch den nächsten (auch beide) benötigen ). Dadurch erhalten Sie individuelle Rechteckstreifen

  4. Behalten Sie eine Reihe von Rechtecken bei, die Sie gerade betrachten, die leer sind. Fügen Sie für jeden x-Wert, den Sie in Punkt 3 durchlaufen, das Rechteck mit einem echten Wert hinzu, fügen Sie es der Gruppe hinzu, andernfalls entfernen Sie es.
  5. Für einen Streifen sortieren Sie die Rechtecke nach ihrer y-Koordinate
  6. Durchlaufen Sie die Rechtecke des Streifens und zählen Sie sich überlappende Entfernungen (für mich ist noch unklar, wie dies effizient gemacht werden kann).
  7. Berechnen Sie die Breite der Streifenzeiten, um sich überlappende Entfernungen zu berechnen, um Bereiche zu erhalten

Beispiel: 5 Rechtecke, übereinander zeichnen, von a bis e:

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

Hier ist die Liste der x-Koordinaten:

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

Die Liste wäre (wobei jedem v einfach eine Koordinate gegeben wird, die bei 0 beginnt und nach oben geht):

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

Jeder Streifen wäre also (Rechtecke von oben nach unten sortiert):

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

für jeden Streifen wäre die Überlappung:

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

Ich könnte mir vorstellen, dass eine Variation des Sortier-/Eingabe/Verlass-Algorithmus für die Prüfung von oben nach unten ebenfalls möglich ist:

  1. sortiere die Rechtecke, die wir gerade im Streifen analysieren, von oben nach unten für Rechtecke mit derselben oberen Koordinate, sortiere sie auch nach der unteren Koordinate
  2. iterieren Sie durch die y-Koordinaten und fügen Sie ein Rechteck bei der Eingabe zum Satz hinzu. Wenn Sie ein Rechteck verlassen, entfernen Sie es aus dem Satz
  3. wenn der Satz mehr als ein Rechteck hat, haben Sie Überlappungen (und wenn Sie sicherstellen, dass Sie alle Rechtecke mit der gleichen oberen/unteren Koordinate, die Sie gerade betrachten, hinzufügen/entfernen, sind mehrere überlappende Rechtecke kein Problem

Für den 1-2-Streifen oben würden Sie folgendermaßen iterieren:

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

Sie müssten auch keine tatsächliche Menge hier beibehalten, nur die Anzahl der darin enthaltenen Rechtecke, wann immer dies von 1 nach 2 geht, speichern Sie das y, und wann immer es von 2 nach unten geht, berechnen Sie das aktuelle y - y gespeichert und summiert diese Differenz.

Ich hoffe, das war verständlich, und wie ich sagte, ist dies aus meinem Kopf heraus und in keiner Weise getestet worden.

Verwenden Sie das Beispiel:

    1 2 3 4 5 6 

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

1) Sammeln Sie alle x-Koordinaten (links und rechts) in einer Liste, sortieren Sie sie und entfernen Sie Duplikate

1 3 4 5 6

2) Sammeln Sie alle y-Koordinaten (sowohl oben als auch unten) in einer Liste, sortieren Sie sie und entfernen Sie Duplikate

1 2 3 4 6

3) Erstellen Sie ein 2D-Array nach Anzahl der Lücken zwischen den eindeutigen x-Koordinaten * Anzahl der Lücken zwischen den eindeutigen y-Koordinaten.

4 * 4

4) Malen Sie alle Rechtecke in dieses Raster und erhöhen Sie die Anzahl der Zellen, über denen sie auftritt:

 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) die Summe der Bereiche der Zellen im Gitter, deren Zählwert größer als eins ist, ist der Überlappungsbereich. Um bei spärlichen Anwendungsfällen eine bessere Effizienz zu erzielen, können Sie während des Malens der Rechtecke eine laufende Gesamtsumme der Fläche beibehalten, jedes Mal, wenn Sie eine Zelle von 1 auf 2 verschieben.


In der Frage werden die Rechtecke als vier Doubles beschrieben. Doppelte enthalten in der Regel Rundungsfehler und können sich in den berechneten Überlappungsbereich einschleichen. Wenn die rechtlichen Koordinaten an endlichen Punkten liegen, sollten Sie eine Ganzzahldarstellung in Betracht ziehen.


PS mit dem Hardwarebeschleuniger wie in meiner anderen Antwort ist keine schäbige Idee, wenn die Auflösung akzeptabel ist. Es ist auch leicht in viel weniger Code als der oben beschriebene Ansatz zu implementieren. Pferde für Kurse.

3
Will

Hier ist der Code, den ich für den Gebiets-Sweep-Algorithmus geschrieben habe:

#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;
}
3
extraeee

Sie können dieses Problem erheblich vereinfachen, wenn Sie jedes Rechteck in kleinere Rechtecke aufteilen. Sammeln Sie alle X- und Y-Koordinaten aller Rechtecke, und diese werden zu Ihren Trennpunkten. Wenn ein Rechteck die Linie kreuzt, teilen Sie es in zwei Teile. Wenn Sie fertig sind, haben Sie eine Liste von Rechtecken, die entweder 0% oder 100% überlappen. Wenn Sie sie sortieren, sollte es leicht sein, die identischen zu finden.

2
Mark Ransom

Es gibt eine Lösung, die unter dem Link http://codercareer.blogspot.com/2011/12/no-27-area-of-rectangles.html aufgelistet ist, um die Gesamtfläche mehrerer Rechtecke zu ermitteln, die sich überlappen wird nur einmal gezählt. 

Die obige Lösung kann erweitert werden, um nur den überlappten Bereich (und auch diesen nur einmal zu berechnen, selbst wenn der überlappte Bereich von mehreren Rechtecken bedeckt ist) mit horizontalen Sweep-Linien für jedes Paar aufeinanderfolgender vertikaler Sweep-Zeilen. 

Wenn nur die Gesamtfläche ermittelt werden soll, die von allen Rechtecken abgedeckt wird, sind keine horizontalen Sweep-Linien erforderlich, und ein Zusammenfügen aller Rechtecke zwischen zwei vertikalen Sweep-Linien würde die Fläche ergeben. 

Wenn Sie dagegen nur den überlappten Bereich berechnen möchten, werden die horizontalen Sweep-Linien benötigt, um herauszufinden, wie viele Rechtecke zwischen den vertikalen (y1, y2) Sweep-Linien überlappen.

Hier ist der Arbeitscode für die Lösung, die ich in Java implementiert habe.

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();
  }     
2

Wenn Ihre Rechtecke spärlich sein werden (meistens nicht überlappend), ist es möglicherweise einen Blick auf das rekursive dimensionale Clustering wert. Ansonsten scheint ein Quad-Baum der richtige Weg zu sein (wie auf anderen Postern bereits erwähnt wurde).

Dies ist ein häufiges Problem bei der Kollisionserkennung in Computerspielen, daher gibt es keinen Mangel an Ressourcen, die Lösungsansätze vorschlagen.

Hier ist ein schöner Blogbeitrag, der RCD zusammenfasst.

Hier ist ein Artikel von Dr.Dobbs, der verschiedene Kollisionserkennungsalgorithmen zusammenfasst, die geeignet wären.

0
Oliver Hallam

In Anbetracht dessen haben wir zwei Rechtecke (A und B) und deren untere linke (x1, y1) und obere rechte (x2, y2) Koordination. Mit dem folgenden Code können Sie den überlappten Bereich in C++ berechnen.

    #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;
}
0
user3048546

Die folgende Antwort sollte die Gesamtfläche nur einmal angeben. Sie kommt aus den vorherigen Antworten, wurde aber jetzt in C # implementiert. Sie funktioniert auch mit Floats (oder doppelt, falls Sie dies benötigen). .

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

BEARBEITEN: Das OP hat nach dem überlappenden Bereich gefragt - das ist natürlich sehr einfach:

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

und dann lautet die Antwort:

var overlappingArea =totArea-GetArea(rects)

Hier ist der 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
0
ephraim

Diese Art der Kollisionserkennung wird häufig als AABB (Axis Aligned Bounding Boxes) bezeichnet. Dies ist ein guter Ausgangspunkt für eine google-Suche

0
grapefrukt

Ich habe eine andere Lösung als den Sweep-Algorithmus gefunden.

Da Ihre Rechtecke alle rechteckig platziert sind, bilden die horizontalen und vertikalen Linien der Rechtecke ein rechteckiges unregelmäßiges Gitter. Sie können die Rechtecke in diesem Raster "zeichnen"; Sie können also festlegen, welche Felder des Rasters ausgefüllt werden. Da die Gitterlinien aus den Grenzen der angegebenen Rechtecke gebildet werden, ist ein Feld in diesem Raster immer entweder vollständig leer oder vollständig mit einem Rechteck gefüllt.

Ich musste das Problem in Java lösen, daher hier meine Lösung: http://Pastebin.com/03mss8yf

Diese Funktion berechnet den gesamten von den Rechtecken belegten Bereich. Wenn Sie nur an dem überlappenden Teil interessiert sind, müssen Sie den Codeblock zwischen den Zeilen 70 und 72 erweitern. Möglicherweise können Sie einen zweiten Satz verwenden, um zu speichern, welche Gitterfelder mehr als einmal verwendet werden. Ihr Code zwischen Zeile 70 und 72 sollte durch einen Block wie folgt ersetzt werden:

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

Die Variable usedLocations2 ist hier vom selben Typ wie usedLocations; es wird der gleiche Punkt konstruiert.

Komplexitätsberechnungen sind mir nicht wirklich vertraut. Ich weiß also nicht, welche der beiden Lösungen (Sweep oder Grid-Lösung) besser funktioniert.

0
Torsten Fehre

Sie finden die Überlappung auf der X- und Y-Achse und multiplizieren diese. 

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);
}
0
Toon Krijthe