From e4b49663f6c6f138a8718847b68d1d78fba8fe73 Mon Sep 17 00:00:00 2001 From: Sven Göthel Date: Wed, 14 Feb 2024 20:23:24 +0100 Subject: Bug 1501: Refine convex == !complex: Use 'complex' term, have env-property toggle OutlineShape's isComplex() for visibility We may use complex for other criteria than !convex, i.e. self-intersecting etc. --- .../jogamp/opengl/demos/graph/ui/FontView01.java | 20 +++---- .../com/jogamp/graph/curve/OutlineShape.java | 61 ++++++++++++++-------- .../com/jogamp/graph/curve/tess/Triangulator.java | 12 ++++- .../classes/com/jogamp/graph/geom/Outline.java | 55 ++++++++++--------- .../jogamp/graph/curve/tess/CDTriangulator2D.java | 18 ++----- src/jogl/classes/jogamp/graph/curve/tess/Loop.java | 14 ++--- 6 files changed, 101 insertions(+), 79 deletions(-) diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java index 8409ac252..770b1b724 100644 --- a/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java +++ b/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java @@ -99,8 +99,8 @@ import com.jogamp.opengl.util.Animator; public class FontView01 { private static final float GlyphGridWidth = 3/4f; // FBO AA: 3/4f = 0.75f dropped fine grid lines @ 0.2f thickness; 0.70f OK private static final float GlyphGridBorderThickness = 0.02f; // thickness 0.2f dropping - private static final Vec4f GlyphGridBorderColorConvex = new Vec4f(0.2f, 0.2f, 0.7f, 1); private static final Vec4f GlyphGridBorderColorComplex = new Vec4f(0.2f, 0.2f, 0.2f, 1); + private static final Vec4f GlyphGridBorderColorSimple = new Vec4f(0.2f, 0.2f, 0.7f, 1); // static CommandlineOptions options = new CommandlineOptions(1280, 720, Region.MSAA_RENDERING_BIT, Region.DEFAULT_AA_QUALITY, 4); // static CommandlineOptions options = new CommandlineOptions(1280, 720, Region.VBAA_RENDERING_BIT); @@ -437,7 +437,7 @@ public class FontView01 { final int rows; final int rowsPerPage; final int elemCount; - int convexGlyphCount; + int complexGlyphCount; int maxNameLen; public GridDim(final Font font, final int columns, final int rowsPerPage, final int xReserved) { @@ -456,14 +456,14 @@ public class FontView01 { private int scanContourGlyphs(final Font font) { final long t0 = Clock.currentNanos(); contourChars.clear(); - convexGlyphCount = 0; + complexGlyphCount = 0; maxNameLen = 1; final int[] max = { max_glyph_count }; font.forAllGlyphs((final Glyph fg) -> { if( !fg.isNonContour() && max[0]-- > 0 ) { contourChars.add( fg.getCodepoint() ); - if( null != fg.getShape() && fg.getShape().isConvex() ) { - ++convexGlyphCount; + if( null != fg.getShape() && fg.getShape().isComplex() ) { + ++complexGlyphCount; } maxNameLen = Math.max(maxNameLen, fg.getName().length()); } @@ -475,7 +475,7 @@ public class FontView01 { return contourChars.size(); } @Override - public String toString() { return "GridDim[contours "+glyphCount+", convex "+convexGlyphCount+" ("+((float)convexGlyphCount/(float)glyphCount)*100+"%), "+columns+"x"+rows+"="+(columns*rows)+">="+elemCount+", rows/pg "+rowsPerPage+"]"; } + public String toString() { return "GridDim[contours "+glyphCount+", complex "+complexGlyphCount+" ("+((float)complexGlyphCount/(float)glyphCount)*100+"%), "+columns+"x"+rows+"="+(columns*rows)+">="+elemCount+", rows/pg "+rowsPerPage+"]"; } } static Group getGlyphShapeHolder(final Shape shape0) { @@ -505,7 +505,7 @@ public class FontView01 { for(int idx = 0; idx < gridDim.glyphCount; ++idx) { final char codepoint = gridDim.contourChars.get(idx); final Font.Glyph fg = font.getGlyph(codepoint); - final boolean isConvex = null != fg.getShape() ? fg.getShape().isConvex() : true; + final boolean isComplex = null != fg.getShape() ? fg.getShape().isComplex() : false; final GlyphShape g = new GlyphShape(options.renderModes, fg, 0, 0); g.setColor(0.1f, 0.1f, 0.1f, 1).setName("GlyphShape"); @@ -519,7 +519,7 @@ public class FontView01 { final AABBox gbox = fg.getBounds(tmpBox); // g.getBounds(glp); final boolean addUnderline = showUnderline && gbox.getMinY() < 0f; final Group c1 = new Group( new BoxLayout( 1f, 1f, addUnderline ? Alignment.None : Alignment.Center) ); - c1.setBorder(GlyphGridBorderThickness).setBorderColor(isConvex ? GlyphGridBorderColorConvex : GlyphGridBorderColorComplex) + c1.setBorder(GlyphGridBorderThickness).setBorderColor(isComplex ? GlyphGridBorderColorComplex : GlyphGridBorderColorSimple) .setInteractive(true).setDragAndResizable(false).setName("GlyphHolder2"); if( addUnderline ) { final Shape underline = new Rectangle(options.renderModes, 1f, gbox.getMinY(), 0.01f).setInteractive(false).setColor(0f, 0f, 1f, 0.25f); @@ -574,7 +574,7 @@ public class FontView01 { static String getGlyphInfo(final Font.Glyph g) { final OutlineShape os = g.getShape(); - final boolean isConvex = null != os ? os.isConvex() : true; + final boolean isComplex = null != os ? os.isComplex() : false; final int osVertices = null != os ? os.getVertexCount() : 0; final String name_s = null != g.getName() ? g.getName() : ""; final AABBox bounds = g.getBounds(); @@ -587,6 +587,6 @@ public class FontView01 { bounds.getWidth(), bounds.getHeight(), box_s, g.getAdvanceWidth(), g.getLeftSideBearings(), - osVertices, isConvex?"Convex":"Non-Convex"); + osVertices, isComplex?"Complex Shape":"Simple Shape"); } } diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index 25791e17d..0c42e791c 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -46,6 +46,8 @@ import com.jogamp.math.geom.plane.AffineTransform; import com.jogamp.math.geom.plane.Path2F; import com.jogamp.math.geom.plane.Winding; +import jogamp.opengl.Debug; + /** * A Generic shape objects which is defined by a list of Outlines. * This Shape can be transformed to triangulations. @@ -119,6 +121,9 @@ import com.jogamp.math.geom.plane.Winding; * @see Region */ public final class OutlineShape implements Comparable { + private static final boolean FORCE_COMPLEXSHAPE = Debug.debug("graph.curve.triangulation.force.complexshape"); + private static final boolean FORCE_SIMPLESHAPE = Debug.debug("graph.curve.triangulation.force.simpleshape"); + /** * Outline's vertices have undefined state until transformed. */ @@ -159,7 +164,7 @@ public final class OutlineShape implements Comparable { private final ArrayList triangles; private final ArrayList vertices; private int addedVerticeCount; - private boolean convexFlag; + private boolean complexShape; private VerticesState outlineState; @@ -187,7 +192,11 @@ public final class OutlineShape implements Comparable { this.triangles = new ArrayList(); this.vertices = new ArrayList(); this.addedVerticeCount = 0; - this.convexFlag = true; + if( FORCE_COMPLEXSHAPE ) { + complexShape = true; + } else { + complexShape = false; + } this.dirtyBits = 0; this.sharpness = DEFAULT_SHARPNESS; } @@ -221,7 +230,11 @@ public final class OutlineShape implements Comparable { vertices.clear(); triangles.clear(); addedVerticeCount = 0; - convexFlag = true; + if( FORCE_COMPLEXSHAPE ) { + complexShape = true; + } else { + complexShape = false; + } dirtyBits = 0; } @@ -262,10 +275,9 @@ public final class OutlineShape implements Comparable { } /** - * Returns cached or computed result whether all {@code polyline}s of {@link #getOutline(int)} are of convex shape, see {@link Outline#isConvex()}. + * Returns cached or computed result if at least one {@code polyline} of {@link #getOutline(int)} is a complex shape, see {@link Outline#isComplex()}. *

- * A polyline with less than 3 elements is marked convex for simplicity, - * since a non-convex complex shape may need to pass intersection testing within triangulation. + * A polyline with less than 3 elements is marked a simple shape for simplicity. *

*

* The result is cached. @@ -273,32 +285,35 @@ public final class OutlineShape implements Comparable { * @see #setOverrideConvex(boolean) * @see #clearOverrideConvex() */ - public boolean isConvex() { - if( 0 == ( OVERRIDE_CONVEX & dirtyBits ) ) { - if( 0 != ( DIRTY_CONVEX & dirtyBits ) ) { - convexFlag = true; - final int sz = this.getOutlineCount(); - for(int i=0; i { triangles.clear(); final Triangulator triangulator2d = Triangulation.create(); - triangulator2d.setConvexShape( isConvex() ); + triangulator2d.setComplexShape( isComplex() ); for(int index = 0; index + * Complex shapes may cause further processing costs, e.g. intersection tests to overcome self-intersecting shapes. + *

+ *

+ * Default is assuming a simple non-complex shape. + *

+ */ + public void setComplexShape(boolean complex); /** * Add a curve to the list of Outlines diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 8c39004a0..1525483c5 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -57,11 +57,11 @@ public class Outline implements Comparable { private final AABBox bbox; private boolean dirtyBBox; private Winding winding; - private boolean convexFlag; + private boolean complexShape; private int dirtyBits; private static final int DIRTY_WINDING = 1 << 0; - private static final int DIRTY_CONVEX = 1 << 0; + private static final int DIRTY_COMPLEXSHAPE = 1 << 0; /**Create an outline defined by control vertices. * An outline can contain off Curve vertices which define curved @@ -73,7 +73,7 @@ public class Outline implements Comparable { bbox = new AABBox(); dirtyBBox = false; winding = Winding.CCW; - convexFlag = false; + complexShape = false; dirtyBits = 0; } @@ -84,8 +84,16 @@ public class Outline implements Comparable { final int count = src.vertices.size(); vertices = new ArrayList(count); winding = Winding.CCW; - convexFlag = false; - dirtyBits = DIRTY_WINDING | DIRTY_CONVEX; + complexShape = false; + dirtyBits = DIRTY_WINDING | DIRTY_COMPLEXSHAPE; + if( 0 == ( src.dirtyBits & DIRTY_WINDING ) ) { + winding = src.winding; + dirtyBits &= ~DIRTY_WINDING; + } + if( 0 == ( src.dirtyBits & DIRTY_COMPLEXSHAPE ) ) { + complexShape = src.complexShape; + dirtyBits &= ~DIRTY_COMPLEXSHAPE; + } for(int i=0; i { public Outline(final Outline src, final Winding enforce) { final int count = src.vertices.size(); vertices = new ArrayList(count); + complexShape = false; + dirtyBits = DIRTY_COMPLEXSHAPE; final Winding had_winding = src.getWinding(); winding = had_winding; - if( 0 == ( src.dirtyBits & DIRTY_CONVEX ) ) { - convexFlag = src.convexFlag; - dirtyBits = 0; - } else { - convexFlag = false; - dirtyBits = DIRTY_CONVEX; - } if( enforce != had_winding ) { for(int i=count-1; i>=0; --i) { vertices.add( src.vertices.get(i).copy() ); @@ -124,6 +127,10 @@ public class Outline implements Comparable { vertices.add( src.vertices.get(i).copy() ); } } + if( 0 == ( src.dirtyBits & DIRTY_COMPLEXSHAPE ) ) { + complexShape = src.complexShape; + dirtyBits &= ~DIRTY_COMPLEXSHAPE; + } closed = src.closed; bbox = new AABBox(src.bbox); dirtyBBox = src.dirtyBBox; @@ -171,22 +178,22 @@ public class Outline implements Comparable { } /** - * Returns the cached or computed result whether this {@link Outline}s {@code polyline} has a convex shape using {@link VectorUtil#isConvex(ArrayList, boolean)}. + * Returns cached or computed result if whether this {@link Outline}s {@code polyline} is a complex shape. *

- * A polyline with less than 3 elements is marked convex for simplicity, - * since a non-convex complex shape may need to pass intersection testing within triangulation. + * A polyline with less than 3 elements is marked a simple shape for simplicity. *

*

* The result is cached. *

*/ - public boolean isConvex() { - if( 0 == ( dirtyBits & DIRTY_CONVEX ) ) { - return convexFlag; + public boolean isComplex() { + if( 0 == ( dirtyBits & DIRTY_COMPLEXSHAPE ) ) { + return complexShape; } - convexFlag = VectorUtil.isConvex1( getVertices(), true ); - dirtyBits &= ~DIRTY_CONVEX; - return convexFlag; + complexShape = !VectorUtil.isConvex1( getVertices(), true ); + // complexShape = VectorUtil.isSelfIntersecting1( getVertices() ); + dirtyBits &= ~DIRTY_COMPLEXSHAPE; + return complexShape; } public final int getVertexCount() { @@ -217,7 +224,7 @@ public class Outline implements Comparable { if(!dirtyBBox) { bbox.resize(vertex.getCoord()); } - dirtyBits |= DIRTY_WINDING | DIRTY_CONVEX; + dirtyBits |= DIRTY_WINDING | DIRTY_COMPLEXSHAPE; } /** Replaces the {@link Vertex} element at the given {@code position}. @@ -234,7 +241,7 @@ public class Outline implements Comparable { } vertices.set(position, vertex); dirtyBBox = true; - dirtyBits |= DIRTY_WINDING | DIRTY_CONVEX; + dirtyBits |= DIRTY_WINDING | DIRTY_COMPLEXSHAPE; } public final Vertex getVertex(final int index){ @@ -253,7 +260,7 @@ public class Outline implements Comparable { */ public final Vertex removeVertex(final int position) throws IndexOutOfBoundsException { dirtyBBox = true; - dirtyBits |= DIRTY_WINDING | DIRTY_CONVEX; + dirtyBits |= DIRTY_WINDING | DIRTY_COMPLEXSHAPE; return vertices.remove(position); } diff --git a/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java b/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java index 08b75478d..d69b33f1f 100644 --- a/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java +++ b/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java @@ -50,15 +50,13 @@ public class CDTriangulator2D implements Triangulator { protected static final boolean DEBUG = Debug.debug("graph.curve.Triangulation"); - private static final boolean FORCE_NONCONVEX = Debug.debug("graph.curve.triangulation.force.non-convex"); - private static final boolean FORCE_CONVEX = Debug.debug("graph.curve.triangulation.force.convex"); private static final boolean TEST_LINE_AA = Debug.debug("graph.curve.triangulation.LINE_AA"); private static final boolean TEST_MARK_LINE = Debug.debug("graph.curve.triangulation.MARK_AA"); private static final boolean TEST_ENABLED = TEST_LINE_AA || TEST_MARK_LINE; private final ArrayList loops = new ArrayList(); - private boolean convexFlag; + private boolean complexShape; private int addedVerticeCount; private int maxTriID; @@ -66,19 +64,13 @@ public class CDTriangulator2D implements Triangulator { /** Constructor for a new Delaunay triangulator */ public CDTriangulator2D() { - if( FORCE_NONCONVEX ) { - convexFlag = false; - } else { - convexFlag = true; - } + complexShape = false; reset(); } @Override - public void setConvexShape(final boolean convex) { - if( !FORCE_NONCONVEX && !FORCE_CONVEX ) { - convexFlag = convex; - } + public void setComplexShape(final boolean complex) { + complexShape = complex; } @Override @@ -114,7 +106,7 @@ public class CDTriangulator2D implements Triangulator { final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, false /* hole */, sharpness); // vertices.addAll(polyline.getVertices()); if( innerPoly.getGraphPoint().size() >= 3 ) { - loop = Loop.createBoundary(innerPoly, convexFlag); + loop = Loop.createBoundary(innerPoly, complexShape); if( null != loop ) { loops.add(loop); } diff --git a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java index a69fbd6b5..838cf8b5f 100644 --- a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java +++ b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java @@ -41,13 +41,13 @@ import com.jogamp.graph.geom.Triangle; public class Loop { private final AABBox box = new AABBox(); private final GraphOutline initialOutline; - private final boolean isConvex; + private final boolean complexShape; private HEdge root; private final List outlines = new ArrayList(); - private Loop(final GraphOutline polyline, final int edgeType, final boolean isConvex){ + private Loop(final GraphOutline polyline, final int edgeType, final boolean complexShape){ this.initialOutline = polyline; - this.isConvex = isConvex; + this.complexShape = complexShape; this.root = initFromPolyline(initialOutline, edgeType); } @@ -249,7 +249,7 @@ public class Loop { final Vertex rootPoint = root.getGraphPoint().getPoint(); final Vertex nextPoint = next1.getGraphPoint().getPoint(); final Vertex candPoint = next1.getNext().getGraphPoint().getPoint(); - if( !isConvex && intersectsOutline(rootPoint, nextPoint, candPoint) ) { + if( complexShape && intersectsOutline(rootPoint, nextPoint, candPoint) ) { return null; } return new Triangle(rootPoint, nextPoint, candPoint, checkVerticesBoundary(root)); @@ -290,7 +290,7 @@ public class Loop { final Vertex rootPoint = root.getGraphPoint().getPoint(); final Vertex nextPoint = next1.getGraphPoint().getPoint(); final Vertex candPoint = next1.getNext().getGraphPoint().getPoint(); - if( !isConvex && intersectsOutlineDbg(rootPoint, nextPoint, candPoint) ) { + if( complexShape && intersectsOutlineDbg(rootPoint, nextPoint, candPoint) ) { System.err.printf("Loop.cut.X0: last-simplex intersects%n"); return null; } @@ -409,7 +409,7 @@ public class Loop { if( !VectorUtil.isCCW( rootPoint, nextPoint, candPoint) ) { return null; } - if( !isConvex && intersectsOutline(rootPoint, nextPoint, candPoint) ) { + if( complexShape && intersectsOutline(rootPoint, nextPoint, candPoint) ) { return null; } if( !delaunay ) { @@ -440,7 +440,7 @@ public class Loop { candPoint, rootPoint, nextPoint, candPoint); return null; } - if( !isConvex && intersectsOutlineDbg(rootPoint, nextPoint, candPoint) ) { + if( complexShape && intersectsOutlineDbg(rootPoint, nextPoint, candPoint) ) { return null; } if( !delaunay ) { -- cgit v1.2.3