28package com.jogamp.graph.curve;
30import java.io.PrintStream;
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.Comparator;
35import com.jogamp.graph.curve.tess.Triangulation;
36import com.jogamp.graph.curve.tess.Triangulator;
37import com.jogamp.graph.geom.Outline;
38import com.jogamp.graph.geom.Triangle;
39import com.jogamp.graph.geom.Vertex;
40import com.jogamp.math.FloatUtil;
41import com.jogamp.math.Vec3f;
42import com.jogamp.math.VectorUtil;
43import com.jogamp.math.Vert2fImmutable;
44import com.jogamp.math.geom.AABBox;
45import com.jogamp.math.geom.plane.AffineTransform;
46import com.jogamp.math.geom.plane.Path2F;
47import com.jogamp.math.geom.plane.Winding;
49import jogamp.opengl.Debug;
124 private static final boolean FORCE_COMPLEXSHAPE = Debug.debug(
"graph.curve.triangulation.force.complexshape");
125 private static final boolean FORCE_SIMPLESHAPE = Debug.debug(
"graph.curve.triangulation.force.simpleshape");
143 private static final int DIRTY_BOUNDS = 1 << 0;
147 private static final int DIRTY_VERTICES = 1 << 1;
151 private static final int DIRTY_TRIANGLES = 1 << 2;
155 private static final int DIRTY_CONVEX = 1 << 3;
156 private static final int OVERRIDE_CONVEX = 1 << 4;
161 final ArrayList<Outline> outlines;
163 private final AABBox bbox;
164 private final ArrayList<Triangle> triangles;
165 private final ArrayList<Vertex> vertices;
166 private int addedVerticeCount;
167 private boolean complexShape;
172 private int dirtyBits;
174 private float sharpness;
188 this.outlines =
new ArrayList<Outline>(3);
189 this.outlines.add(
new Outline());
192 this.triangles =
new ArrayList<Triangle>();
193 this.vertices =
new ArrayList<Vertex>();
194 this.addedVerticeCount = 0;
195 if( FORCE_COMPLEXSHAPE ) {
198 complexShape =
false;
210 return addedVerticeCount;
218 if( this.sharpness != s ) {
232 addedVerticeCount = 0;
233 if( FORCE_COMPLEXSHAPE ) {
236 complexShape =
false;
245 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
250 return outlines.size();
256 for(
final Outline o : outlines) {
257 res += o.getVertexCount();
289 if( !FORCE_COMPLEXSHAPE && !FORCE_SIMPLESHAPE &&
290 0 == ( OVERRIDE_CONVEX & dirtyBits ) &&
291 0 != ( DIRTY_CONVEX & dirtyBits ) )
293 complexShape =
false;
295 for(
int i=0; i<sz && !complexShape; ++i) {
298 dirtyBits &= ~DIRTY_CONVEX;
308 if( !FORCE_COMPLEXSHAPE && !FORCE_SIMPLESHAPE ) {
309 dirtyBits |= OVERRIDE_CONVEX;
310 complexShape = convex;
319 dirtyBits &= ~OVERRIDE_CONVEX;
320 dirtyBits |= DIRTY_CONVEX;
363 public final void addOutline(
final int position,
final Outline outline)
throws NullPointerException, IndexOutOfBoundsException {
364 if (
null == outline) {
365 throw new NullPointerException(
"outline is null");
367 if( outlines.size() == position ) {
369 if( outline.isEmpty() && lastOutline.
isEmpty() ) {
373 outlines.set(position-1, outline);
374 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
375 bbox.
resize(outline.getBounds());
378 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
382 outlines.add(position, outline);
383 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
384 bbox.
resize(outline.getBounds());
386 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
398 if (
null == outlineShape) {
399 throw new NullPointerException(
"OutlineShape is null");
402 for(
int i=0; i<outlineShape.getOutlineCount(); i++) {
416 public final void setOutline(
final int position,
final Outline outline)
throws NullPointerException, IndexOutOfBoundsException {
417 if (
null == outline) {
418 throw new NullPointerException(
"outline is null");
420 outlines.set(position, outline);
421 dirtyBits |= DIRTY_BOUNDS | DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
432 dirtyBits |= DIRTY_BOUNDS | DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
433 return outlines.remove(position);
442 return outlines.get(outlines.size()-1);
450 return outlines.get(position);
462 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
466 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
479 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
482 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
494 public final void addVertex(
final float x,
final float y,
final boolean onCurve) {
508 public final void addVertex(
final int position,
final float x,
final float y,
final boolean onCurve) {
521 public final void addVertex(
final float x,
final float y,
final float z,
final boolean onCurve) {
535 public final void addVertex(
final int position,
final float x,
final float y,
final float z,
final boolean onCurve) {
552 public final void addVertex(
final float[] coordsBuffer,
final int offset,
final int length,
final boolean onCurve) {
570 public final void addVertex(
final int position,
final float[] coordsBuffer,
final int offset,
final int length,
final boolean onCurve) {
571 addVertex(position,
new Vertex(coordsBuffer, offset, length, onCurve));
586 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
617 final float[] points = pathI.points();
618 while ( pathI.hasNext() ) {
619 final int idx = pathI.index();
620 final Path2F.SegmentType type = pathI.next();
628 }
else if ( !connect ) {
637 if( llc.
x() == points[idx+0] &&
638 llc.
y() == points[idx+1] ) {
660 throw new IllegalArgumentException(
"Unhandled Segment Type: "+type);
691 final float[] points = pathI.points();
692 while ( pathI.hasNext() ) {
693 final int idx = pathI.index();
694 final Path2F.SegmentType type = pathI.next();
702 }
else if ( !connect ) {
711 if( llc.
x() == points[idx+0] &&
712 llc.
y() == points[idx+1] ) {
734 throw new IllegalArgumentException(
"Unhandled Segment Type: "+type);
750 public final void moveTo(
final float x,
final float y,
final float z) {
770 public final void lineTo(
final float x,
final float y,
final float z) {
787 public final void quadTo(
final float x1,
final float y1,
final float z1,
final float x2,
final float y2,
final float z2) {
808 public final void cubicTo(
final float x1,
final float y1,
final float z1,
final float x2,
final float y2,
final float z2,
final float x3,
final float y3,
final float z3) {
859 addedVerticeCount += 2;
871 private void checkOverlaps() {
872 final ArrayList<Vertex> overlaps =
new ArrayList<Vertex>(3);
874 boolean firstpass =
true;
876 for (
int cc = 0; cc < count; cc++) {
878 int vertexCount = outline.getVertexCount();
879 for(
int i=0; i < outline.getVertexCount(); i++) {
880 final Vertex currentVertex = outline.getVertex(i);
881 if ( !currentVertex.isOnCurve()) {
882 final Vertex nextV = outline.getVertex((i+1)%vertexCount);
883 final Vertex prevV = outline.getVertex((i+vertexCount-1)%vertexCount);
884 final Vertex overlap;
891 overlap = checkTriOverlaps0(prevV, currentVertex, nextV);
895 if(
null != overlap || overlaps.contains(currentVertex) ) {
896 overlaps.remove(currentVertex);
898 subdivideTriangle(outline, prevV, currentVertex, nextV, i);
901 addedVerticeCount+=2;
903 if(overlap !=
null && !overlap.isOnCurve()) {
904 if(!overlaps.contains(overlap)) {
905 overlaps.add(overlap);
913 }
while( !overlaps.isEmpty() );
916 private Vertex checkTriOverlaps0(
final Vertex a,
final Vertex b,
final Vertex c) {
918 for (
int cc = 0; cc < count; cc++) {
920 final int vertexCount = outline.getVertexCount();
921 for(
int i=0; i < vertexCount; i++) {
922 final Vertex currV = outline.getVertex(i);
923 if( !currV.isOnCurve() && currV != a && currV != b && currV != c) {
924 final Vertex nextV = outline.getVertex((i+1)%vertexCount);
925 final Vertex prevV = outline.getVertex((i+vertexCount-1)%vertexCount);
928 if(prevV != c && nextV != a) {
929 if( VectorUtil.isInTriangle3(a.getCoord(), b.getCoord(), c.getCoord(),
930 currV.getCoord(), nextV.getCoord(), prevV.getCoord(),
931 tmpV1, tmpV2, tmpV3) ) {
934 if(VectorUtil.testTri2SegIntersection(a, b, c, prevV, currV) ||
935 VectorUtil.testTri2SegIntersection(a, b, c, currV, nextV) ||
936 VectorUtil.testTri2SegIntersection(a, b, c, prevV, nextV) ) {
946 private void cleanupOutlines() {
947 final boolean transformOutlines2Quadratic = VerticesState.QUADRATIC_NURBS != outlineState;
949 for (
int cc = 0; cc < count; cc++) {
951 int vertexCount = outline.getVertexCount();
953 if( transformOutlines2Quadratic ) {
954 for(
int i=0; i < vertexCount; i++) {
955 final Vertex currentVertex = outline.getVertex(i);
956 final int j = (i+1)%vertexCount;
957 final Vertex nextVertex = outline.getVertex(j);
958 if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) {
959 VectorUtil.midpoint(tmpV1, currentVertex.getCoord(), nextVertex.getCoord());
960 System.err.println(
"XXX: Cubic: "+i+
": "+currentVertex+
", "+j+
": "+nextVertex);
961 final Vertex v =
new Vertex(tmpV1,
true);
966 outline.addVertex(i, v);
970 if( 0 >= vertexCount ) {
971 outlines.remove(outline);
974 }
else if( 0 < vertexCount &&
975 outline.getVertex(0).getCoord().isEqual( outline.getLastVertex().getCoord() ) ) {
976 outline.removeVertex(vertexCount-1);
983 private int generateVertexIds() {
985 for(
int i=0; i<outlines.size(); i++) {
986 final ArrayList<Vertex> vertices = outlines.get(i).getVertices();
987 for(
int pos=0; pos<vertices.size(); pos++) {
988 vertices.get(pos).setId(maxVertexId++);
1007 if( 0 != ( DIRTY_VERTICES & dirtyBits ) ) {
1009 for(
int i=0; i<outlines.size(); i++) {
1010 vertices.addAll(outlines.get(i).getVertices());
1012 dirtyBits &= ~DIRTY_VERTICES;
1033 private void triangulateImpl() {
1034 if( 0 < outlines.size() ) {
1036 generateVertexIds();
1041 for(
int index = 0; index<outlines.size(); index++) {
1042 triangulator2d.
addCurve(triangles, outlines.get(index), sharpness);
1044 triangulator2d.
generate(triangles);
1046 triangulator2d.
reset();
1060 final boolean updated;
1062 throw new IllegalStateException(
"destinationType "+destinationType.name()+
" not supported (currently "+outlineState.name()+
")");
1064 if( 0 != ( DIRTY_TRIANGLES & dirtyBits ) ) {
1068 dirtyBits |= DIRTY_VERTICES;
1069 dirtyBits &= ~DIRTY_TRIANGLES;
1074 System.err.println(
"OutlineShape.getTriangles().X: "+triangles.size()+
", updated "+updated);
1077 for(
final Triangle t : triangles) {
1078 System.err.printf(
"- [%d]: %s%n", i++, t);
1093 final int osize = outlines.size();
1094 for(
int i=0; i<osize; i++) {
1095 newOutlineShape.
addOutline( outlines.get(i).transform(t) );
1097 return newOutlineShape;
1104 private void sortOutlines() {
1105 Collections.sort(outlines, reversSizeComparator);
1108 private static Comparator<Outline> reversSizeComparator =
new Comparator<Outline>() {
1110 public int compare(
final Outline o1,
final Outline o2) {
1125 }
else if( thisSize < otherSize ){
1132 private void validateBoundingBox() {
1133 dirtyBits &= ~DIRTY_BOUNDS;
1135 for (
int i=0; i<outlines.size(); i++) {
1136 bbox.
resize(outlines.get(i).getBounds());
1141 if( 0 != ( dirtyBits & DIRTY_BOUNDS ) ) {
1142 validateBoundingBox();
1153 public final boolean equals(
final Object obj) {
1180 throw new InternalError(
"hashCode not designed");
1186 return getClass().getName() +
"@" + Integer.toHexString(super.hashCode());
1189 public void print(
final PrintStream out) {
1191 for (
int oi = 0; oi < oc; oi++) {
1194 out.printf(
"- OL[%d]: %s%n", vc, outline.
getWinding());
1195 for(
int vi=0; vi < vc; vi++) {
1197 out.printf(
"-- OS[%d][%d]: %s%n", oi, vi, v);
A Generic shape objects which is defined by a list of Outlines.
boolean isComplex()
Returns cached or computed result if at least one polyline of getOutline(int) is a complex shape,...
void addPath(final Path2F path, final boolean connect)
Append the given path geometry to this outline shape.
static final float DEFAULT_SHARPNESS
Initial getSharpness() value, which can be modified via setSharpness(float).
final void addVertex(final int position, final float x, final float y, final boolean onCurve)
Add a 2D Vertex to the last open outline to the shape at position.
final void clear()
Clears all data and reset all states as if this instance was newly created.
final boolean equals(final Object obj)
final void addVertex(final int position, final float x, final float y, final float z, final boolean onCurve)
Add a 3D Vertex to the last open outline to the shape at position.
final Winding getWindingOfLastOutline()
Compute the Winding of the getLastOutline() using the VectorUtil#area(ArrayList) function over all of...
final void addVertex(final float x, final float y, final boolean onCurve)
Add a 2D Vertex to the last open outline to the shape's tail.
final void addVertex(final Vertex v)
Adds a vertex to the last open outline to the shape's tail.
final Outline removeOutline(final int position)
Removes the Outline element at the given position.
final void addPathRev(final Path2F.Iterator pathI, boolean connect)
Add the given Path2F.Iterator to this outline shape in reverse order.
OutlineShape()
Create a new Outline based Shape.
final int getOutlineCount()
Returns the number of Outlines.
final int getAddedVerticeCount()
Return the number of newly added vertices during getTriangles(VerticesState) while transforming the o...
final void addOutlineShape(final OutlineShape outlineShape)
Insert the OutlineShape elements of type Outline, .
final void addVertex(final float x, final float y, final float z, final boolean onCurve)
Add a 3D Vertex to the last open outline to the shape's tail.
void clearOverrideConvex()
Clears the isComplex() override done by setOverrideConvex(boolean).
final Outline getOutline(final int position)
Returns the Outline at position @endiliteral.
final void clearCache()
Clears cached triangulated data, i.e.
final ArrayList< Vertex > getVertices()
Return list of concatenated vertices associated with all Outlines of this object.
final void addVertex(final int position, final float[] coordsBuffer, final int offset, final int length, final boolean onCurve)
Add a vertex to the last open outline to the shape at position.
final void addVertex(final int position, final Vertex v)
Adds a vertex to the last open outline to the shape at position @endiliteral.
final void setIsQuadraticNurbs()
Claim this outline's vertices are all OutlineShape.VerticesState#QUADRATIC_NURBS, hence no cubic tran...
final void moveTo(final float x, final float y, final float z)
Start a new position for the next line segment at given point x/y (P1).
final void quadTo(final float x1, final float y1, final float z1, final float x2, final float y2, final float z2)
Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2).
final void addOutline(final Outline outline)
Appends the Outline element to the end, ensuring a clean tail.
final VerticesState getOutlineState()
Return the outline's vertices state, OutlineShape.VerticesState.
final int getVertexCount()
Returns the total vertex number of all Outlines.
void setOverrideConvex(final boolean convex)
Overrides isComplex() using the given value instead of computing via Outline#isComplex().
final Outline getLastOutline()
Get the last added outline to the list of outlines that define the shape.
final void lineTo(final float x, final float y, final float z)
Add a line segment, intersecting the last point and the given point x/y (P1).
final void setSharpness(final float s)
Sets sharpness, defaults to DEFAULT_SHARPNESS.
final void addPath(final Path2F.Iterator pathI, boolean connect)
Add the given Path2F.Iterator to this outline shape.
void addPathRev(final Path2F path, final boolean connect)
Append the given path geometry to this outline shape in reverse order.
final void closeLastOutline(final boolean closeTail)
Closes the last outline in the shape.
static void printPerf(final PrintStream out)
final void setOutline(final int position, final Outline outline)
Replaces the Outline element at the given position.
final void cubicTo(final float x1, final float y1, final float z1, final float x2, final float y2, final float z2, final float x3, final float y3, final float z3)
Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3).
final void addVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve)
Add a vertex to the last open outline to the shape's tail.
final void closePath()
Closes the current sub-path segment by drawing a straight line back to the coordinates of the last mo...
final int compareTo(final OutlineShape other)
Compare two outline shape's Bounding Box size.
final float getSharpness()
Sharpness value, defaults to DEFAULT_SHARPNESS.
void print(final PrintStream out)
final OutlineShape transform(final AffineTransform t)
Return a transformed instance with all Outlines are copied and transformed.
final void addEmptyOutline()
Add a new empty Outline to the end of this shape's outline list.
final void addOutline(final int position, final Outline outline)
Insert the Outline element at the given position.
final void setWindingOfLastOutline(final Winding enforced)
Sets the enforced Winding of the getLastOutline().
final ArrayList< Triangle > getTriangles(final VerticesState destinationType)
Triangulate the OutlineShape generating a list of triangles, while transformOutlines(VerticesState) b...
Abstract Outline shape representation define the method an OutlineShape(s) is bound and rendered.
static final boolean DEBUG_INSTANCE
static Triangulator create()
Create a new instance of a triangulation.
Define a single continuous stroke by control vertices.
final Winding getWinding()
Returns the cached or computed winding of this Outlines polyline using VectorUtil#area(ArrayList).
boolean equals(final Object obj)
final int getVertexCount()
final void addVertex(final Vertex vertex)
Appends a vertex to the outline loop/strip.
final Vertex getVertex(final int index)
final void setWinding(final Winding enforce)
Sets Winding to this outline.
boolean isComplex()
Returns cached or computed result if whether this Outlines polyline is a complex shape.
A Vertex exposing Vec3f vertex- and texture-coordinates.
final void setCoord(final Vec3f coord)
final void setOnCurve(final boolean onCurve)
Basic Float math utility functions.
static boolean isEqual2(final float a, final float b)
Returns true if both values are equal, i.e.
3D Vector based upon three float components.
static Vec3f midpoint(final Vec3f result, final Vec3f p1, final Vec3f p2)
Calculate the midpoint of two points.
Axis Aligned Bounding Box.
final boolean equals(final Object obj)
final AABBox reset()
Resets this box to the inverse low/high, allowing the next resize(float, float, float) command to hit...
final float getSize()
Get the size of this AABBox where the size is represented by the length of the vector between low and...
final AABBox resize(final AABBox newBox)
Resize the AABBox to encapsulate another AABox.
Path2F represents and provides construction method for a 2D shape using float[2] points.
Outline's vertices have undefined state until transformed.
VerticesState(final int state)
Winding direction, either clockwise (CW) or counter-clockwise (CCW).
Interface to the triangulation algorithms provided A triangulation of 2D outlines where you can provi...
int getAddedVerticeCount()
Return the number of newly added vertices during addCurve(List, Outline, float).
void setComplexShape(boolean complex)
Mark the to be triangulated shape complex or non-complex.
void reset()
Reset the triangulation to initial state Clearing cached data.
void generate(List< Triangle > sink)
Generate the triangulation of the provided List of Outlines.
void addCurve(List< Triangle > sink, Outline outline, float sharpness)
Add a curve to the list of Outlines describing the shape.