2016-05-18 12 views
0

Ich war erstaunt zu erfahren, dass die closePath Methode von Java Path2D.Float nicht wirklich den Pfad korrekt schließt. Der folgende Code:Java Path2D.closePath funktioniert nicht richtig?

Path2D.Float p = new Path2D.Float(); 
p.moveTo(3, 3); 
p.lineTo(10, 3); 
p.lineTo(8, 5); 
p.closePath(); 

PathIterator it = p.getPathIterator(null); 

float lastx = 0; 
float lasty = 0; 
boolean first = true; 
while (it.isDone() == false) { 
    float[] coordinates = new float[2]; 
    int type = it.currentSegment(coordinates); 
    if (first) { 
     first = false; 
    } else { 
     System.out.println("Segment from "+lastx+", "+lasty+" to "+coordinates[0]+", "+coordinates[1]); 
    } 
    lastx = coordinates[0]; 
    lasty = coordinates[1]; 
    it.next(); 
} 

erzeugt diese Ausgabe:

Segment from 3.0, 3.0 to 10.0, 3.0 
Segment from 10.0, 3.0 to 8.0, 5.0 
Segment from 8.0, 5.0 to 0.0, 0.0 

Allerdings würde man closePath erwarten den Pfad zu schließen 3, 3 Koordinaten, wie es in der Dokumentation heißt es:

Closes den aktuellen Unterpfad durch Zeichnen einer geraden Linie zurück zu den Koordinaten des letzten moveTo. Wenn der Pfad bereits geschlossen ist, hat diese Methode keine Wirkung. (https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Path2D.html#closePath--)

Ersetzen closePath durch lineTo auf die Startkoordinaten die gewünschten Segmente erzeugt jedoch der Segmenttyp des letzten Segments auf diese Weise nicht gleich SEG_CLOSE: (https://docs.oracle.com/javase/8/docs/api/java/awt/geom/PathIterator.html)

Type: 0 // SEG_MOVETO 
Type: 1 // SEG_LINETO 
Segment from 3.0, 3.0 to 10.0, 3.0 
Type: 1 
Segment from 10.0, 3.0 to 8.0, 5.0 
Type: 1 
Segment from 8.0, 5.0 to 3.0, 3.0 

Anfügen ein weiterer closePath Aufruf führt wieder zu falschen Ergebnissen:

Type: 0 
Type: 1 
Segment from 3.0, 3.0 to 10.0, 3.0 
Type: 1 
Segment from 10.0, 3.0 to 8.0, 5.0 
Type: 1 
Segment from 8.0, 5.0 to 3.0, 3.0 
Type: 4 // SEG_CLOSE 
Segment from 3.0, 3.0 to 0.0, 0.0 // <- not the correct coordinates! 

Kann jemand dies reproduzieren, oder anderweitig erklären, was ich vermisse, wenn das kein Fehler ist?

Zusätzliche Informationen: Betriebssystem: Mac OS X 10.10.5 JDK: jdk1.8.0_92

Mit freundlichen Grüßen

+0

Ich nehme an, dass es auch falsch zeichnet? – Ironcache

+0

Sehen Sie sich den 'PathIterator' Javadoc an und Sie erhalten Ihre Antwort.Wenn das zurückgegebene int gleich SEG_CLOSE ist, werden keine Punkte in das Array geschrieben (daher kommt [0.0, 0.0], die Standard-Float-Werte, zurück, wie Sie bemerken und wie erwartet). Um das Problem zu beheben, überprüfen Sie den int-Rückgabetyp von 'currentSegment()' https://docs.oracle.com/javase/8/docs/api/java/awt/geom/PathIterator.html – Ironcache

+0

ich sehe. Ich weiß nichts über die Zeichnung (aber @Boann schlägt vor, dass es in Ordnung sein wird), weil ich den Pfad nicht zum Zeichnen verwende. Ich verwende es einfach, um zu testen, ob Punkte in dem Bereich mit dem Pfad als Form enthalten sind oder nicht. – Warkst

Antwort

1

Wenn Sie definieren eine Path2D speichert, im Wesentlichen eine nicht interpretierte Liste der Methodenaufrufe Diese wurden verwendet, um den Pfad an erster Stelle zu definieren, so dass closePath() keine Geometrie Logik tut. Es zeichnet auf, dass closePath() aufgerufen wurde, speichert jedoch keine zusätzlichen Punkte im internen Punktarray des Pfads, da dies nicht erforderlich ist. Code, der die Pfadsegmente iteriert, kann sich daran erinnern, wo der Pfad gestartet wurde.

Ebenso sagt PathIterator.currentSegment "SEG_CLOSE gibt keine Punkte zurück" (seit closePath() wurde nicht mit irgendwelchen Punkten aufgerufen). Da keine Punkte zurückgegeben werden, drucken Sie die Standard-Nullen aus, mit denen Ihr coordinates Array initialisiert wird.

Wenn Sie den Pfad für einen bestimmten Zweck manuell durchlaufen, müssen Sie jeden Segmenttyp separat behandeln, da sie eine unterschiedliche Anzahl von verknüpften Punkten haben. Sie können auf diese Weise aus dem Weg drucken:

float moveX = 0, moveY = 0; 
for (PathIterator it = path.getPathIterator(null); !it.isDone(); it.next()) { 
    float[] c = new float[6]; 
    int type = it.currentSegment(c); 
    switch (type) { 
    case PathIterator.SEG_MOVETO: 
     System.out.println("moveTo(" + c[0] + ", " + c[1] + ")"); 
     moveX = c[0]; moveY = c[1]; 
     break; 
    case PathIterator.SEG_LINETO: 
     System.out.println("lineTo(" + c[0] + ", " + c[1] + ")"); 
     break; 
    case PathIterator.SEG_QUADTO: 
     System.out.println("quadTo(" + c[0] + ", " + c[1] + ", " + c[2] + ", " + c[3] + ")"); 
     break; 
    case PathIterator.SEG_CUBICTO: 
     System.out.println("cubicTo(" + c[0] + ", " + c[1] + ", " + c[2] + ", " + c[3] + ", " + c[4] + ", " + c[5] + ")"); 
     break; 
    case PathIterator.SEG_CLOSE: 
     System.out.println("closePath() (back to " + moveX + ", " + moveY + ")"); 
     break; 
    } 
} 

Wenn Sie den Pfad senden von einem Graphics2D gezogen werden Sie nicht brauchen, zu kümmern; Der Renderer wird damit richtig umgehen. Ebenso funktionieren alle Treffer-Testmethoden des Pfades, wie they iterate the path derselbe grundlegende Weg, wie Sie oben sehen.

+0

Ich verstehe. Ich habe verstanden, dass 'closePath' einfach ein' lineTo' zu den Koordinaten des letzten 'moveTo' hinzugefügt und mit einem speziellen Segmenttyp versehen hat, um anderen geom Bibliotheken anzuzeigen, dass der Pfad geschlossen ist (und somit eine Form mit einer Oberfläche von sortiert). Von dem, was ich verstehe, ist das, was es beim Rendern macht, aber nicht beim Iterieren über die Segmente. Also, um mein Problem zu lösen, kann ich entweder den Segmenttyp untersuchen und SEG_CLOSE manuell in eine Zeile zu den ersten Koordinaten übersetzen, oder einfach meinen Pfad mit einem lineTo schließen, anstatt closePath, oder? Vielen Dank. – Warkst

+0

@Warkst Wenn Sie die 'contains' Methoden des Pfades für Hit-Tests verwenden, gibt es kein Problem. Es behandelt dies bereits korrekt. – Boann

+0

Neben Hit-Tests interessiert mich auch der Abstand zwischen einem beliebigen Punkt und der Form des Pfades. Dafür dachte ich, ich würde über die Segmente iterieren, sie in line2d-Instanzen konvertieren und die ptLineDist-Methode verwenden, um den kürzesten Abstand zu einem beliebigen Punkt zu finden (als Minimum aller Entfernungen zu jedem der Segmente). In diesem Fall ist es erforderlich, dass das letzte Segment die korrekten Koordinaten zurückgibt, um es korrekt in eine Zeile2d umzuwandeln. – Warkst