2010-02-14 13 views
12

Ich bin auf der Suche nach einer Möglichkeit, die Fläche einer beliebigen Instanz von java.awt.geom.Area in Pixel zu berechnen.Wie berechnet man die Fläche einer java.awt.geom.Area?

Der Hintergrund: Ich habe Shape s in meinen Anwendungen, die sich überschneiden können. Ich möchte wissen, wie viel eine Shape eine andere überlappt. Die Shape s verzerrt werden kann, gedreht usw. Wenn ich eine Funktion hatte area(Shape) (oder Area), ich die Kreuzung zweier Shape s wie so verwenden:

double fractionObscured(Shape bottom, Shape top) { 
    Area intersection = new Area(bottom); 
    intersection.intersect(new Area(top)); 
    return area(intersection)/area(bottom); 
} 
+0

warum double [] coords = new double [6]; und nicht ein anderer Index wird verwendet? –

+0

Sie sollten in der Lage sein, die Polygonschnittpunktformel auf Beizer-Kurven zu erweitern. Dann können Sie Pfad-Iteratoren verwenden, um nahezu perfekte Bereiche für alle Formen/Bereiche zu erhalten. –

Antwort

3

Ein Ansatz wäre fill() jeder skaliert und transformedShape mit einer anderen Farbe mit einem geeigneten AlphaComposite und zählen Sie die überlappenden Pixel in der zugrunde liegenden Raster.

Addendum 1: Unter Verwendung dieses calculator, um den Effekt von AlphaComposite.Xor zu sehen, zeigt, dass der Intersetion von zwei undurchsichtigen Farben Null ist.

Addendum 2: Das Zählen von Pixeln kann Leistungsprobleme haben; Probenahme kann helfen. Wenn jeder Shape einigermaßen konvex ist, kann es möglich sein, die Überlappung aus dem Verhältnis des Bereichs zu der Summe der Bereiche der Shape s 'getBounds2D() zu schätzen. Beispiel:

Shape s1, s2 ... 
Rectangle2D r1 = s1.getBounds2D(); 
Rectangle2D r2 = s2.getBounds2D(); 
Rectangle2D r3 = new Rectangle2D.Double(); 
Rectangle2D.intersect(r1, r2, r3); 
double overlap = area(r3)/(area(r1) + area(r2)); 
... 
private double area(Rectangle2D r) { 
    return r.getWidth() * r.getHeight(); 
} 

Möglicherweise müssen Sie die Ergebnisse empirisch validieren.

+0

Vielen Dank, dass Sie auf die Möglichkeiten hingewiesen haben, einen Teil des Bildes zu rastern und die tatsächlichen Werte zu betrachten. – iter

+0

Ich denke, es ist genauer, aber ich schlug auch eine potenziell schnellere Alternative vor, die ausreichend sein könnte. – trashgod

11

Um die Fläche eines Polygons mit dem folgenden Schnipsel zu finden:

int sum = 0; 
for (int i = 0; i < n -1; i++) 
{ 
    sum = sum + x[i]*y[i+1] - y[i]*x[i+1]; 
} 
// (sum/2) is your area. 
System.out.println("The area is : " + (sum/2)); 

Hier ist N die Gesamtzahl von Vertices und x [i] und y [i] die x- und y-Koordinaten eines Scheitels sind ich. Beachten Sie, dass das Polygon geschlossen sein muss, damit dieser Algorithmus funktioniert. Es funktioniert an offenen Polygonen.

Sie können mathematische Algorithmen zu Polygonen here finden. Sie müssen es umwandeln, um selbst zu codieren :)

+0

Danke für den Link. Dies ist ein gültiger Ansatz, aber keine Richtung, in die ich gehen möchte. 'Shape's können Kurvensegmente enthalten und können Kompositionen anderer Formen sein.Die Mathematik wird zu haarig für mich zu folgen. – iter

+3

@iter, können Sie einen 'getPathIterator (AffineTransform at, doppelte Ebenheit)' verwenden, um die Kurve als Polygon zu approximieren. Außerdem werden die 'Area'-Konstruktoren die Form in nicht selbstschneidende Komponenten zerlegen, so dass dieser Algorithmus funktioniert, wenn Sie ihn so anpassen, dass er einen' PathIterator' verwendet. – finnw

+0

Siehe http://stackoverflow.com/a/22910330/798223 – GKFX

6

Ich habe diese Klasse verwendet, um den Bereich einer Form in einem meiner Projekte anzunähern. Es ist langsam, aber mit hohen Auflösung kann es immer noch schneller als Pixel zu zählen (weil die Kosten der Pixel zu zählen mit einer Auflösung wachsen quadratisch, aber die Anzahl der Liniensegmente auf dem Umfang wächst linear.)

import static java.lang.Double.NaN; 

import java.awt.geom.AffineTransform; 
import java.awt.geom.Area; 
import java.awt.geom.FlatteningPathIterator; 
import java.awt.geom.Line2D; 
import java.awt.geom.PathIterator; 

public abstract class Areas { 
    public static double approxArea(Area area, double flatness, int limit) { 
     PathIterator i = 
      new FlatteningPathIterator(area.getPathIterator(identity), 
             flatness, 
             limit); 
     return approxArea(i); 
    } 

    public static double approxArea(Area area, double flatness) { 
     PathIterator i = area.getPathIterator(identity, flatness); 
     return approxArea(i); 
    } 

    public static double approxArea(PathIterator i) { 
     double a = 0.0; 
     double[] coords = new double[6]; 
     double startX = NaN, startY = NaN; 
     Line2D segment = new Line2D.Double(NaN, NaN, NaN, NaN); 
     while (! i.isDone()) { 
      int segType = i.currentSegment(coords); 
      double x = coords[0], y = coords[1]; 
      switch (segType) { 
      case PathIterator.SEG_CLOSE: 
       segment.setLine(segment.getX2(), segment.getY2(), startX, startY); 
       a += hexArea(segment); 
       startX = startY = NaN; 
       segment.setLine(NaN, NaN, NaN, NaN); 
       break; 
      case PathIterator.SEG_LINETO: 
       segment.setLine(segment.getX2(), segment.getY2(), x, y); 
       a += hexArea(segment); 
       break; 
      case PathIterator.SEG_MOVETO: 
       startX = x; 
       startY = y; 
       segment.setLine(NaN, NaN, x, y); 
       break; 
      default: 
       throw new IllegalArgumentException("PathIterator contains curved segments"); 
      } 
      i.next(); 
     } 
     if (Double.isNaN(a)) { 
      throw new IllegalArgumentException("PathIterator contains an open path"); 
     } else { 
      return 0.5 * Math.abs(a); 
     } 
    } 

    private static double hexArea(Line2D seg) { 
     return seg.getX1() * seg.getY2() - seg.getX2() * seg.getY1(); 
    } 

    private static final AffineTransform identity = 
     AffineTransform.getQuadrantRotateInstance(0); 
} 
2

Ich würde kommentieren, wenn Ich könnte. Suraj, Ihr Algorithmus korrekt ist, aber der Code sollte

 int sum = 0; 
     for (int i = 0; i < npoints ; i++) 
     { 
      sum = sum + Xs[i]*Ys[(i+1)%npoints] - Ys[i]*Xs[(i+1)%npoints]; 
     } 

     return Math.abs(sum/2); 

In Ihrem Code letzte vertice Rechnung wird nicht übernommen werden. Nur ein kleiner Schnitt :)

Verwandte Themen