diff options
author | Sven Göthel <[email protected]> | 2024-02-12 06:17:07 +0100 |
---|---|---|
committer | Sven Göthel <[email protected]> | 2024-02-12 06:17:07 +0100 |
commit | a77b487a44124a9e55fa9a53d1f9c3ae20b9c3ba (patch) | |
tree | bb9271e933b09bfaa92253bc76e8295f56174041 | |
parent | bf882af1675f390500cc36c5396f75c394372d52 (diff) |
Bug 1501: Graph Delaunay: Use default winding outer-boundary:=CCW and inner-hole:=CW w/o using winding determination (might be incorrect)
This simplifies our code further and it has been validated that our polygon shoelace-algo for area >= 0 ? CCW
doesn't produce correct results with all curves.
Hence rely on given winding depending on outer-boundary and inner-hole if CDTriangulator2D.FixedWindingRule == true (default and fixed).
This also removes the more costly winding shoelace calculus,
hence Outline ctor only sets dirtyWinding:=true w/o calculating the winding.
5 files changed, 77 insertions, 45 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index 805db522b..3e85ff3dc 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -61,15 +61,16 @@ import com.jogamp.math.geom.plane.Winding; * <a name="windingrules"> * Outline shape general {@link Winding} rules * <ul> - * <li>Outer boundary shapes are required as {@link Winding#CCW}, if unsure + * <li>Outer boundary-shapes are required as {@link Winding#CCW}</li> + * <li>Inner hole-shapes should be {@link Winding#CW}</li> + * <li>If unsure * <ul> - * <li>You may check {@link Winding} via {@link #getWindingOfLastOutline()} or {@link Outline#getWinding()} (optional)</li> + * <li>You may check {@link Winding} via {@link #getWindingOfLastOutline()} or {@link Outline#getWinding()} (optional, might be incorrect)</li> * <li>Use {@link #setWindingOfLastOutline(Winding)} before {@link #closeLastOutline(boolean)} or {@link #closePath()} } to enforce {@link Winding#CCW}, or</li> * <li>use {@link Outline#setWinding(Winding)} on a specific {@link Outline} to enforce {@link Winding#CCW}.</li> * <li>If e.g. the {@link Winding} has changed for an {@link Outline} by above operations, its vertices have been reversed.</li> * </ul></li> - * <li>Inner shapes or holes are adjusted to be {@link Winding#CW}, no user consideration is required here.</li> - * <li>Safe path: Simply create all shapes with {@link Winding#CCW} or apply {@link Outline#setWinding(Winding)}.</li> + * <li>Safe path: Simply create all outer boundary-shapes with {@link Winding#CCW} and inner hole-shapes with {@link Winding#CW}.</li> * </ul> * </p> * Example to creating an Outline Shape: @@ -238,7 +239,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { } /** - * Compute the {@link Winding} of the {@link #getLastOutline()} using the {@link #area(ArrayList)} function over all of its vertices. + * Compute the {@link Winding} of the {@link #getLastOutline()} using the {@link VectorUtil#area2d(ArrayList)} function over all of its vertices. * @return {@link Winding#CCW} or {@link Winding#CW} */ public final Winding getWindingOfLastOutline() { diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 522919cc6..a9ebd0346 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -78,8 +78,8 @@ public class Outline implements Comparable<Outline> { public Outline(final Outline src) { final int count = src.vertices.size(); vertices = new ArrayList<Vertex>(count); - winding = src.getWinding(); - dirtyWinding = false; + winding = Winding.CCW; + dirtyWinding = true; for(int i=0; i<count; i++) { vertices.add( src.vertices.get(i).copy() ); } @@ -138,7 +138,7 @@ public class Outline implements Comparable<Outline> { } /** - * Compute the winding of the {@link #getLastOutline()} using the {@link #area(ArrayList)} function over all of its vertices. + * Compute the winding of the {@link #getLastOutline()} using the {@link VectorUtil#area2d(ArrayList)} function over all of its vertices. * @return {@link Winding#CCW} or {@link Winding#CW} */ public final Winding getWinding() { @@ -149,7 +149,7 @@ public class Outline implements Comparable<Outline> { if( 3 > count ) { winding = Winding.CCW; } else { - winding = VectorUtil.getWinding( getVertices() ); + winding = VectorUtil.getWinding2d( getVertices() ); } dirtyWinding = false; return winding; diff --git a/src/jogl/classes/com/jogamp/math/VectorUtil.java b/src/jogl/classes/com/jogamp/math/VectorUtil.java index 716b63e47..7e66f0b4f 100644 --- a/src/jogl/classes/com/jogamp/math/VectorUtil.java +++ b/src/jogl/classes/com/jogamp/math/VectorUtil.java @@ -537,7 +537,7 @@ public final class VectorUtil { /** * Check if points are in ccw order * <p> - * Consider using {@link #getWinding(ArrayList)} using the {@link #area(ArrayList)} function over all points + * Consider using {@link #getWinding2f(ArrayList)} using the {@link #area2f(ArrayList)} function over all points * on complex shapes for a reliable result! * </p> * @param a first vertex @@ -552,29 +552,29 @@ public final class VectorUtil { /** * Compute the winding of the 3 given points * <p> - * Consider using {@link #getWinding(ArrayList)} using the {@link #area(ArrayList)} function over all points + * Consider using {@link #getWinding2f(ArrayList)} using the {@link #area2f(ArrayList)} function over all points * on complex shapes for a reliable result! * </p> * @param a first vertex * @param b second vertex * @param c third vertex * @return {@link Winding#CCW} or {@link Winding#CW} - * @see #getWinding(ArrayList) + * @see #getWinding2f(ArrayList) */ public static Winding getWinding(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c) { return triAreaVec2d(a,b,c) > InCircleDThreshold ? Winding.CCW : Winding.CW ; } /** - * Computes the area of a list of vertices. + * Computes the area of a list of vertices via shoelace formula. * <p> * This method is utilized e.g. to reliably compute the {@link Winding} of complex shapes. * </p> * @param vertices * @return positive area if ccw else negative area value - * @see #getWinding(ArrayList) + * @see #getWinding2f(ArrayList) */ - public static float area(final ArrayList<? extends Vert2fImmutable> vertices) { + public static float area2f(final ArrayList<? extends Vert2fImmutable> vertices) { final int n = vertices.size(); float area = 0.0f; for (int p = n - 1, q = 0; q < n; p = q++) { @@ -586,17 +586,51 @@ public final class VectorUtil { } /** - * Compute the winding using the {@link #area(ArrayList)} function over all vertices for complex shapes. + * Compute the winding using the {@link #area2f(ArrayList)} function over all vertices for complex shapes. * <p> - * Uses the {@link #area(ArrayList)} function over all points + * Uses the {@link #area2f(ArrayList)} function over all points * on complex shapes for a reliable result! * </p> * @param vertices array of Vertices * @return {@link Winding#CCW} or {@link Winding#CW} - * @see #area(ArrayList) + * @see #area2f(ArrayList) */ - public static Winding getWinding(final ArrayList<? extends Vert2fImmutable> vertices) { - return area(vertices) >= 0 ? Winding.CCW : Winding.CW ; + public static Winding getWinding2f(final ArrayList<? extends Vert2fImmutable> vertices) { + return area2f(vertices) >= 0 ? Winding.CCW : Winding.CW ; + } + + /** + * Computes the area of a list of vertices via shoelace formula. + * <p> + * This method is utilized e.g. to reliably compute the {@link Winding} of complex shapes. + * </p> + * @param vertices + * @return positive area if ccw else negative area value + * @see #getWinding2d(ArrayList) + */ + public static double area2d(final ArrayList<? extends Vert2fImmutable> vertices) { + final int n = vertices.size(); + double area = 0.0; + for (int p = n - 1, q = 0; q < n; p = q++) { + final Vert2fImmutable pCoord = vertices.get(p); + final Vert2fImmutable qCoord = vertices.get(q); + area += (double)pCoord.x() * (double)qCoord.y() - (double)qCoord.x() * (double)pCoord.y(); + } + return area; + } + + /** + * Compute the winding using the {@link #area2f(ArrayList)} function over all vertices for complex shapes. + * <p> + * Uses the {@link #area2f(ArrayList)} function over all points + * on complex shapes for a reliable result! + * </p> + * @param vertices array of Vertices + * @return {@link Winding#CCW} or {@link Winding#CW} + * @see #area2d(ArrayList) + */ + public static Winding getWinding2d(final ArrayList<? extends Vert2fImmutable> vertices) { + return area2d(vertices) >= 0 ? Winding.CCW : Winding.CW ; } /** diff --git a/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java b/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java index d27b7a584..1c2f0b323 100644 --- a/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java +++ b/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java @@ -79,31 +79,24 @@ public class CDTriangulator2D implements Triangulator { } + /* pp */ static final boolean FixedWindingRule = true; + @Override public final void addCurve(final List<Triangle> sink, final Outline polyline, final float sharpness) { Loop loop = getContainerLoop(polyline); - final Winding winding = polyline.getWinding(); if( null == loop ) { // HEdge.BOUNDARY -> Winding.CCW - int edgeType; - boolean hole; - if( Winding.CCW != winding ) { - System.err.println("CDT2.add.xx.BOUNDARY: !CCW but "+winding); - // polyline.print(System.err); - if( false ) { - edgeType = HEdge.HOLE; - hole = true; - } else { - edgeType = HEdge.BOUNDARY; - hole = false; - polyline.setWinding(Winding.CCW); + final int edgeType = HEdge.BOUNDARY; + final boolean hole = false; + if( !FixedWindingRule ) { + final Winding winding = polyline.getWinding(); + if( Winding.CCW != winding ) { + System.err.println("CDT2.add.xx.BOUNDARY: !CCW but "+winding); + // polyline.print(System.err); + polyline.setWinding(Winding.CCW); // FIXME: Too late? } - } else { - edgeType = HEdge.BOUNDARY; - hole = false; } - // Too late: polyline.setWinding(winding); final GraphOutline outline = new GraphOutline(polyline); final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, hole, sharpness); // vertices.addAll(polyline.getVertices()); @@ -140,12 +133,14 @@ public class CDTriangulator2D implements Triangulator { Thread.dumpStack(); } } else { + final int edgeType = HEdge.HOLE; + final boolean hole = true; // HEdge.HOLE -> Winding.CW, but Winding.CCW is also accepted! // Winding.CW not required, handled in Loop.initFromPolyline(): polyline.setWinding(winding); final GraphOutline outline = new GraphOutline(polyline); - final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, true, sharpness); + final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, hole, sharpness); // vertices.addAll(innerPoly.getVertices()); - loop.addConstraintCurve(innerPoly); + loop.addConstraintCurve(innerPoly, edgeType); } } diff --git a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java index d94ab775d..4961ce895 100644 --- a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java +++ b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java @@ -122,8 +122,8 @@ public class Loop { } return null; } - final Winding winding = outline.getOutline().getWinding(); final Winding edgeWinding = HEdge.BOUNDARY == edgeType ? Winding.CCW : Winding.CW; + final Winding winding = CDTriangulator2D.FixedWindingRule ? edgeWinding : outline.getOutline().getWinding(); if( HEdge.BOUNDARY == edgeType && Winding.CCW != winding ) { // XXXX @@ -135,7 +135,7 @@ public class Loop { HEdge lastEdge = null; if( winding == edgeWinding || HEdge.BOUNDARY == edgeType ) { - // Correct Winding or skipped CW -> CCW (no inversion possible here, too late ??) + // Correct Winding or skipped CW -> CCW (no inversion possible here, too late) final int max = vertices.size() - 1; for(int index = 0; index <= max; ++index) { final GraphVertex v1 = vertices.get(index); @@ -156,7 +156,7 @@ public class Loop { } lastEdge = edge; } - } else { // if( hasWinding == Winding.CW ) { + } else { // if( winding == Winding.CW ) { // CCW <-> CW for(int index = vertices.size() - 1; index >= 0; --index) { final GraphVertex v1 = vertices.get(index); @@ -182,13 +182,15 @@ public class Loop { return firstEdge; } - public void addConstraintCurve(final GraphOutline polyline) { + public void addConstraintCurve(final GraphOutline polyline, final int edgeType) { // GraphOutline outline = new GraphOutline(polyline); /**needed to generate vertex references.*/ - if( null == initFromPolyline(polyline, HEdge.HOLE) ) { + if( null == initFromPolyline(polyline, edgeType) ) { // 'usually' HEdge.HOLE return; } - addConstraintCurveImpl(polyline); + if( HEdge.HOLE == edgeType ) { + addConstraintCurveImpl(polyline); + } } private void addConstraintCurveImpl(final GraphOutline polyline) { final GraphVertex v3 = locateClosestVertex(polyline); |