JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
OutlineShape.java
Go to the documentation of this file.
1/**
2 * Copyright 2010-2024 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.graph.curve;
29
30import java.io.PrintStream;
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.Comparator;
34
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;
48
49import jogamp.opengl.Debug;
50
51/**
52 * A Generic shape objects which is defined by a list of Outlines.
53 * This Shape can be transformed to triangulations.
54 * The list of triangles generated are render-able by a Region object.
55 * The triangulation produced by this Shape will define the
56 * closed region defined by the outlines.
57 * <p>
58 * One or more OutlineShape Object can be associated to a region
59 * this is left as a high-level representation of the Objects. For
60 * optimizations, flexibility requirements for future features.
61 * </p>
62 * <p>
63 * <a name="windingrules">
64 * Outline shape general {@link Winding} rules
65 * <ul>
66 * <li>Outer boundary-shapes are required as {@link Winding#CCW}</li>
67 * <li>Inner hole-shapes should be {@link Winding#CW}</li>
68 * <li>If unsure
69 * <ul>
70 * <li>You may check {@link Winding} via {@link #getWindingOfLastOutline()} or {@link Outline#getWinding()} (optional, might be incorrect)</li>
71 * <li>Use {@link #setWindingOfLastOutline(Winding)} before {@link #closeLastOutline(boolean)} or {@link #closePath()} } to enforce {@link Winding#CCW}, or</li>
72 * <li>use {@link Outline#setWinding(Winding)} on a specific {@link Outline} to enforce {@link Winding#CCW}.</li>
73 * <li>If e.g. the {@link Winding} has changed for an {@link Outline} by above operations, its vertices have been reversed.</li>
74 * </ul></li>
75 * <li>Safe path: Simply create all outer boundary-shapes with {@link Winding#CCW} and inner hole-shapes with {@link Winding#CW}.</li>
76 * </ul>
77 * </p>
78 * Example to creating an Outline Shape:
79 * <pre>
80 addVertex(...)
81 addVertex(...)
82 addVertex(...)
83 addEmptyOutline()
84 addVertex(...)
85 addVertex(...)
86 addVertex(...)
87 * </pre>
88 *
89 * <p>
90 * The above will create two outlines each with three vertices. By adding these two outlines to
91 * the OutlineShape, we are stating that the combination of the two outlines represent the shape.
92 * </p>
93 * <p>
94 * To specify that the shape is curved at a region, the on-curve flag should be set to false
95 * for the vertex that is in the middle of the curved region (if the curved region is defined by 3
96 * vertices (quadratic curve).
97 * </p>
98 * <p>
99 * In case the curved region is defined by 4 or more vertices the middle vertices should both have
100 * the on-curve flag set to false.
101 * </p>
102 * Example:
103 * <pre>
104 addVertex(0,0, true);
105 addVertex(0,1, false);
106 addVertex(1,1, false);
107 addVertex(1,0, true);
108 * </pre>
109 * <p>
110 * The above snippet defines a cubic nurbs curve where (0,1 and 1,1)
111 * do not belong to the final rendered shape.
112 * </p>
113 *
114 * <i>Implementation Notes:</i><br>
115 * <ul>
116 * <li> The first vertex of any outline belonging to the shape should be on-curve</li>
117 * <li> Intersections between off-curved parts of the outline is not handled</li>
118 * </ul>
119 *
120 * @see Outline
121 * @see Region
122 */
123public final class OutlineShape implements Comparable<OutlineShape> {
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");
126
127 /**
128 * Outline's vertices have undefined state until transformed.
129 */
130 public enum VerticesState {
131 UNDEFINED(0), QUADRATIC_NURBS(1);
132
133 public final int state;
134
135 VerticesState(final int state){
136 this.state = state;
137 }
138 }
139
140 /** Initial {@link #getSharpness()} value, which can be modified via {@link #setSharpness(float)}. */
141 public static final float DEFAULT_SHARPNESS = 0.5f;
142
143 private static final int DIRTY_BOUNDS = 1 << 0;
144 /**
145 * Modified shape, requires to update the vertices and triangles, here: vertices.
146 */
147 private static final int DIRTY_VERTICES = 1 << 1;
148 /**
149 * Modified shape, requires to update the vertices and triangles, here: triangulation.
150 */
151 private static final int DIRTY_TRIANGLES = 1 << 2;
152 /**
153 * Modified shape, requires to update the convex determination
154 */
155 private static final int DIRTY_CONVEX = 1 << 3;
156 private static final int OVERRIDE_CONVEX = 1 << 4;
157
158 /** The list of {@link Outline}s that are part of this
159 * outline shape.
160 */
161 /* pp */ final ArrayList<Outline> outlines;
162
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;
168
169 private VerticesState outlineState;
170
171 /** dirty bits DIRTY_BOUNDS */
172 private int dirtyBits;
173
174 private float sharpness;
175
176 private final Vec3f tmpV1 = new Vec3f();
177 private final Vec3f tmpV2 = new Vec3f();
178 private final Vec3f tmpV3 = new Vec3f();
179 // COLOR
180 // private final Vec4f tmpC1 = new Vec4f();
181 // private final Vec4f tmpC2 = new Vec4f();
182 // private final Vec4f tmpC3 = new Vec4f();
183
184 /**
185 * Create a new Outline based Shape
186 */
187 public OutlineShape() {
188 this.outlines = new ArrayList<Outline>(3);
189 this.outlines.add(new Outline());
190 this.outlineState = VerticesState.UNDEFINED;
191 this.bbox = new AABBox();
192 this.triangles = new ArrayList<Triangle>();
193 this.vertices = new ArrayList<Vertex>();
194 this.addedVerticeCount = 0;
195 if( FORCE_COMPLEXSHAPE ) {
196 complexShape = true;
197 } else {
198 complexShape = false;
199 }
200 this.dirtyBits = 0;
201 this.sharpness = DEFAULT_SHARPNESS;
202 }
203
204 /**
205 * Return the number of newly added vertices during {@link #getTriangles(VerticesState)}
206 * while transforming the outlines to {@link VerticesState#QUADRATIC_NURBS} and triangulation.
207 * @see #setIsQuadraticNurbs()
208 */
209 public final int getAddedVerticeCount() {
210 return addedVerticeCount;
211 }
212
213 /** Sharpness value, defaults to {@link #DEFAULT_SHARPNESS}. */
214 public final float getSharpness() { return sharpness; }
215
216 /** Sets sharpness, defaults to {@link #DEFAULT_SHARPNESS}. */
217 public final void setSharpness(final float s) {
218 if( this.sharpness != s ) {
219 clearCache();
220 sharpness=s;
221 }
222 }
223
224 /** Clears all data and reset all states as if this instance was newly created */
225 public final void clear() {
226 outlines.clear();
227 outlines.add(new Outline());
228 outlineState = VerticesState.UNDEFINED;
229 bbox.reset();
230 vertices.clear();
231 triangles.clear();
232 addedVerticeCount = 0;
233 if( FORCE_COMPLEXSHAPE ) {
234 complexShape = true;
235 } else {
236 complexShape = false;
237 }
238 dirtyBits = 0;
239 }
240
241 /** Clears cached triangulated data, i.e. {@link #getTriangles(VerticesState)} and {@link #getVertices()}. */
242 public final void clearCache() {
243 vertices.clear();
244 triangles.clear();
245 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
246 }
247
248 /** Returns the number of {@link Outline}s. */
249 public final int getOutlineCount() {
250 return outlines.size();
251 }
252
253 /** Returns the total {@link Outline#getVertexCount() vertex number} of all {@link Outline}s. */
254 public final int getVertexCount() {
255 int res = 0;
256 for(final Outline o : outlines) {
257 res += o.getVertexCount();
258 }
259 return res;
260 }
261
262 /**
263 * Compute the {@link Winding} of the {@link #getLastOutline()} using the {@link VectorUtil#area(ArrayList)} function over all of its vertices.
264 * @return {@link Winding#CCW} or {@link Winding#CW}
265 */
267 return getLastOutline().getWinding();
268 }
269
270 /**
271 * Sets the enforced {@link Winding} of the {@link #getLastOutline()}.
272 */
273 public final void setWindingOfLastOutline(final Winding enforced) {
274 getLastOutline().setWinding(enforced);
275 }
276
277 /**
278 * Returns cached or computed result if at least one {@code polyline} of {@link #getOutline(int)} is a complex shape, see {@link Outline#isComplex()}.
279 * <p>
280 * A polyline with less than 3 elements is marked a simple shape for simplicity.
281 * </p>
282 * <p>
283 * The result is cached.
284 * </p>
285 * @see #setOverrideConvex(boolean)
286 * @see #clearOverrideConvex()
287 */
288 public boolean isComplex() {
289 if( !FORCE_COMPLEXSHAPE && !FORCE_SIMPLESHAPE &&
290 0 == ( OVERRIDE_CONVEX & dirtyBits ) &&
291 0 != ( DIRTY_CONVEX & dirtyBits ) )
292 {
293 complexShape = false;
294 final int sz = this.getOutlineCount();
295 for(int i=0; i<sz && !complexShape; ++i) {
296 complexShape = getOutline(i).isComplex();
297 }
298 dirtyBits &= ~DIRTY_CONVEX;
299 }
300 return complexShape;
301 }
302 /**
303 * Overrides {@link #isComplex()} using the given value instead of computing via {@link Outline#isComplex()}.
304 * @see #clearOverrideConvex()
305 * @see #isComplex()
306 */
307 public void setOverrideConvex(final boolean convex) {
308 if( !FORCE_COMPLEXSHAPE && !FORCE_SIMPLESHAPE ) {
309 dirtyBits |= OVERRIDE_CONVEX;
310 complexShape = convex;
311 }
312 }
313 /**
314 * Clears the {@link #isComplex()} override done by {@link #setOverrideConvex(boolean)}
315 * @see #setOverrideConvex(boolean)
316 * @see #isComplex()
317 */
318 public void clearOverrideConvex() {
319 dirtyBits &= ~OVERRIDE_CONVEX;
320 dirtyBits |= DIRTY_CONVEX;
321 }
322
323 /**
324 * Add a new empty {@link Outline}
325 * to the end of this shape's outline list.
326 * <p>If the {@link #getLastOutline()} is empty already, no new one will be added.</p>
327 *
328 * After a call to this function all new vertices added
329 * will belong to the new outline
330 */
331 public final void addEmptyOutline() {
332 if( !getLastOutline().isEmpty() ) {
333 outlines.add(new Outline());
334 }
335 }
336
337 /**
338 * Appends the {@link Outline} element to the end,
339 * ensuring a clean tail.
340 *
341 * <p>A clean tail is ensured, no double empty Outlines are produced
342 * and a pre-existing empty outline will be replaced with the given one. </p>
343 *
344 * @param outline Outline object to be added
345 * @throws NullPointerException if the {@link Outline} element is null
346 */
347 public final void addOutline(final Outline outline) throws NullPointerException {
348 addOutline(outlines.size(), outline);
349 }
350
351 /**
352 * Insert the {@link Outline} element at the given {@code position}.
353 *
354 * <p>If the {@code position} indicates the end of this list,
355 * a clean tail is ensured, no double empty Outlines are produced
356 * and a pre-existing empty outline will be replaced with the given one. </p>
357 *
358 * @param position of the added Outline
359 * @param outline Outline object to be added
360 * @throws NullPointerException if the {@link Outline} element is null
361 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber())
362 */
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");
366 }
367 if( outlines.size() == position ) {
368 final Outline lastOutline = getLastOutline();
369 if( outline.isEmpty() && lastOutline.isEmpty() ) {
370 return;
371 }
372 if( lastOutline.isEmpty() ) {
373 outlines.set(position-1, outline);
374 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
375 bbox.resize(outline.getBounds());
376 }
377 // vertices.addAll(outline.getVertices()); // FIXME: can do and remove DIRTY_VERTICES ?
378 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
379 return;
380 }
381 }
382 outlines.add(position, outline);
383 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
384 bbox.resize(outline.getBounds());
385 }
386 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
387 }
388
389 /**
390 * Insert the {@link OutlineShape} elements of type {@link Outline}, .. at the end of this shape,
391 * using {@link #addOutline(Outline)} for each element.
392 * <p>Closes the current last outline via {@link #closeLastOutline(boolean)} before adding the new ones.</p>
393 * @param outlineShape OutlineShape elements to be added.
394 * @throws NullPointerException if the {@link OutlineShape} is null
395 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber())
396 */
397 public final void addOutlineShape(final OutlineShape outlineShape) throws NullPointerException {
398 if (null == outlineShape) {
399 throw new NullPointerException("OutlineShape is null");
400 }
401 closeLastOutline(true);
402 for(int i=0; i<outlineShape.getOutlineCount(); i++) {
403 addOutline(outlineShape.getOutline(i));
404 }
405 }
406
407 /**
408 * Replaces the {@link Outline} element at the given {@code position}.
409 * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
410 *
411 * @param position of the replaced Outline
412 * @param outline replacement Outline object
413 * @throws NullPointerException if the {@link Outline} element is null
414 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
415 */
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");
419 }
420 outlines.set(position, outline);
421 dirtyBits |= DIRTY_BOUNDS | DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
422 }
423
424 /**
425 * Removes the {@link Outline} element at the given {@code position}.
426 * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
427 *
428 * @param position of the to be removed Outline
429 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
430 */
431 public final Outline removeOutline(final int position) throws IndexOutOfBoundsException {
432 dirtyBits |= DIRTY_BOUNDS | DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
433 return outlines.remove(position);
434 }
435
436 /**
437 * Get the last added outline to the list
438 * of outlines that define the shape
439 * @return the last outline
440 */
441 public final Outline getLastOutline() {
442 return outlines.get(outlines.size()-1);
443 }
444
445 /**
446 * Returns the {@code Outline} at {@code position}
447 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
448 */
449 public final Outline getOutline(final int position) throws IndexOutOfBoundsException {
450 return outlines.get(position);
451 }
452
453 /**
454 * Adds a vertex to the last open outline to the shape's tail.
455 *
456 * @param v the vertex to be added to the OutlineShape
457 * @see <a href="#windingrules">see winding rules</a>
458 */
459 public final void addVertex(final Vertex v) {
460 final Outline lo = getLastOutline();
461 lo.addVertex(v);
462 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
463 bbox.resize(v.getCoord());
464 }
465 // vertices.add(v); // FIXME: can do and remove DIRTY_VERTICES ?
466 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
467 }
468
469 /**
470 * Adds a vertex to the last open outline to the shape at {@code position}
471 *
472 * @param position index within the last open outline, at which the vertex will be added
473 * @param v the vertex to be added to the OutlineShape
474 * @see <a href="#windingrules">see winding rules</a>
475 */
476 public final void addVertex(final int position, final Vertex v) {
477 final Outline lo = getLastOutline();
478 lo.addVertex(position, v);
479 if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
480 bbox.resize(v.getCoord());
481 }
482 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
483 }
484
485 /**
486 * Add a 2D {@link Vertex} to the last open outline to the shape's tail.
487 * The 2D vertex will be represented as Z=0.
488 *
489 * @param x the x coordinate
490 * @param y the y coordniate
491 * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
492 * @see <a href="#windingrules">see winding rules</a>
493 */
494 public final void addVertex(final float x, final float y, final boolean onCurve) {
495 addVertex(new Vertex(x, y, 0f, onCurve));
496 }
497
498 /**
499 * Add a 2D {@link Vertex} to the last open outline to the shape at {@code position}.
500 * The 2D vertex will be represented as Z=0.
501 *
502 * @param position index within the last open outline, at which the vertex will be added
503 * @param x the x coordinate
504 * @param y the y coordniate
505 * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
506 * @see <a href="#windingrules">see winding rules</a>
507 */
508 public final void addVertex(final int position, final float x, final float y, final boolean onCurve) {
509 addVertex(position, new Vertex(x, y, 0f, onCurve));
510 }
511
512 /**
513 * Add a 3D {@link Vertex} to the last open outline to the shape's tail.
514 *
515 * @param x the x coordinate
516 * @param y the y coordinate
517 * @param z the z coordinate
518 * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
519 * @see <a href="#windingrules">see winding rules</a>
520 */
521 public final void addVertex(final float x, final float y, final float z, final boolean onCurve) {
522 addVertex(new Vertex(x, y, z, onCurve));
523 }
524
525 /**
526 * Add a 3D {@link Vertex} to the last open outline to the shape at {@code position}.
527 *
528 * @param position index within the last open outline, at which the vertex will be added
529 * @param x the x coordinate
530 * @param y the y coordniate
531 * @param z the z coordinate
532 * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
533 * @see <a href="#windingrules">see winding rules</a>
534 */
535 public final void addVertex(final int position, final float x, final float y, final float z, final boolean onCurve) {
536 addVertex(position, new Vertex(x, y, z, onCurve));
537 }
538
539 /**
540 * Add a vertex to the last open outline to the shape's tail.
541 *
542 * The vertex is passed as a float array and its offset where its attributes are located.
543 * The attributes should be continuous (stride = 0).
544 * Attributes which value are not set (when length less than 3)
545 * are set implicitly to zero.
546 * @param coordsBuffer the coordinate array where the vertex attributes are to be picked from
547 * @param offset the offset in the buffer to the x coordinate
548 * @param length the number of attributes to pick from the buffer (maximum 3)
549 * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
550 * @see <a href="#windingrules">see winding rules</a>
551 */
552 public final void addVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) {
553 addVertex(new Vertex(coordsBuffer, offset, length, onCurve));
554 }
555
556 /**
557 * Add a vertex to the last open outline to the shape at {@code position}.
558 *
559 * The vertex is passed as a float array and its offset where its attributes are located.
560 * The attributes should be continuous (stride = 0).
561 * Attributes which value are not set (when length less than 3)
562 * are set implicitly to zero.
563 * @param position index within the last open outline, at which the vertex will be added
564 * @param coordsBuffer the coordinate array where the vertex attributes are to be picked from
565 * @param offset the offset in the buffer to the x coordinate
566 * @param length the number of attributes to pick from the buffer (maximum 3)
567 * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
568 * @see <a href="#windingrules">see winding rules</a>
569 */
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));
572 }
573
574 /**
575 * Closes the last outline in the shape.
576 * <p>
577 * Checks whether the last vertex equals to the first of the last outline.
578 * If not equal, it either appends a copy of the first vertex
579 * or prepends a copy of the last vertex, depending on <code>closeTail</code>.
580 * </p>
581 * @param closeTail if true, a copy of the first vertex will be appended,
582 * otherwise a copy of the last vertex will be prepended.
583 */
584 public final void closeLastOutline(final boolean closeTail) {
585 if( getLastOutline().setClosed( closeTail ) ) {
586 dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES | DIRTY_CONVEX;
587 }
588 }
589
590 /**
591 * Append the given path geometry to this outline shape.
592 *
593 * The given path geometry should be {@link Winding#CCW}.
594 *
595 * If the given path geometry is {@link Winding#CW}, use {@link #addPathRev(Path2F, boolean)}.
596 *
597 * @param path the {@link Path2F} to append to this outline shape, should be {@link Winding#CCW}.
598 * @param connect pass true to turn an initial moveTo segment into a lineTo segment to connect the new geometry to the existing path, otherwise pass false.
599 * @see Path2F#getWinding()
600 */
601 public void addPath(final Path2F path, final boolean connect) {
602 addPath(path.iterator(null), connect);
603 }
604
605 /**
606 * Add the given {@link Path2F.Iterator} to this outline shape.
607 *
608 * The given path geometry should be {@link Winding#CCW}.
609 *
610 * If the given path geometry is {@link Winding#CW}, use {@link #addPathRev(Path2F.Iterator, boolean).
611 *
612 * @param pathI the {@link Path2F.Iterator} to append to this outline shape, should be {@link Winding#CCW}.
613 * @param connect pass true to turn an initial moveTo segment into a lineTo segment to connect the new geometry to the existing path, otherwise pass false.
614 * @see Path2F.Iterator#getWinding()
615 */
616 public final void addPath(final Path2F.Iterator pathI, boolean connect) {
617 final float[] points = pathI.points();
618 while ( pathI.hasNext() ) {
619 final int idx = pathI.index();
620 final Path2F.SegmentType type = pathI.next();
621 switch(type) {
622 case MOVETO:
623 final Outline lo = this.getLastOutline();
624 final int lo_sz = lo.getVertexCount();
625 if ( 0 == lo_sz ) {
626 addVertex(points, idx, 2, true);
627 break;
628 } else if ( !connect ) {
629 closeLastOutline(false);
631 addVertex(points, idx, 2, true);
632 break;
633 }
634 {
635 // Skip if last vertex in last outline matching this point -> already connected.
636 final Vert2fImmutable llc = lo.getVertex(lo_sz-1);
637 if( llc.x() == points[idx+0] &&
638 llc.y() == points[idx+1] ) {
639 break;
640 }
641 }
642 // fallthrough: MOVETO -> LINETO
643 case LINETO:
644 addVertex(points, idx, 2, true);
645 break;
646 case QUADTO:
647 addVertex(points, idx, 2, false);
648 addVertex(points, idx+2, 2, true);
649 break;
650 case CUBICTO:
651 addVertex(points, idx, 2, false);
652 addVertex(points, idx+2, 2, false);
653 addVertex(points, idx+4, 2, true);
654 break;
655 case CLOSE:
656 closeLastOutline(true);
658 break;
659 default:
660 throw new IllegalArgumentException("Unhandled Segment Type: "+type);
661 }
662 connect = false;
663 }
664 }
665
666 /**
667 * Append the given path geometry to this outline shape in reverse order.
668 *
669 * The given path geometry should be {@link Winding#CW}.
670 *
671 * If the given path geometry is {@link Winding#CCW}, use {@link #addPath(Path2F, boolean)}.
672 *
673 * @param path the {@link Path2F} to append to this outline shape, should be {@link Winding#CW}.
674 * @param connect pass true to turn an initial moveTo segment into a lineTo segment to connect the new geometry to the existing path, otherwise pass false.
675 */
676 public void addPathRev(final Path2F path, final boolean connect) {
677 addPathRev(path.iterator(null), connect);
678 }
679
680 /**
681 * Add the given {@link Path2F.Iterator} to this outline shape in reverse order.
682 *
683 * The given path geometry should be {@link Winding#CW}.
684 *
685 * If the given path geometry is {@link Winding#CCW}, use {@link #addPath(Path2F.Iterator, boolean).
686 *
687 * @param pathI the {@link Path2F.Iterator} to append to this outline shape, should be {@link Winding#CW}.
688 * @param connect pass true to turn an initial moveTo segment into a lineTo segment to connect the new geometry to the existing path, otherwise pass false.
689 */
690 public final void addPathRev(final Path2F.Iterator pathI, boolean connect) {
691 final float[] points = pathI.points();
692 while ( pathI.hasNext() ) {
693 final int idx = pathI.index();
694 final Path2F.SegmentType type = pathI.next();
695 switch(type) {
696 case MOVETO:
697 final Outline lo = this.getLastOutline();
698 final int lo_sz = lo.getVertexCount();
699 if ( 0 == lo_sz ) {
700 addVertex(0, points, idx, 2, true);
701 break;
702 } else if ( !connect ) {
703 closeLastOutline(false);
705 addVertex(0, points, idx, 2, true);
706 break;
707 }
708 {
709 // Skip if last vertex in last outline matching this point -> already connected.
710 final Vert2fImmutable llc = lo.getVertex(0);
711 if( llc.x() == points[idx+0] &&
712 llc.y() == points[idx+1] ) {
713 break;
714 }
715 }
716 // fallthrough: MOVETO -> LINETO
717 case LINETO:
718 addVertex(0, points, idx, 2, true);
719 break;
720 case QUADTO:
721 addVertex(0, points, idx, 2, false);
722 addVertex(0, points, idx+2, 2, true);
723 break;
724 case CUBICTO:
725 addVertex(0, points, idx, 2, false);
726 addVertex(0, points, idx+2, 2, false);
727 addVertex(0, points, idx+4, 2, true);
728 break;
729 case CLOSE:
730 closeLastOutline(true);
732 break;
733 default:
734 throw new IllegalArgumentException("Unhandled Segment Type: "+type);
735 }
736 connect = false;
737 }
738 }
739
740 /**
741 * Start a new position for the next line segment at given point x/y (P1).
742 *
743 * @param x point (P1)
744 * @param y point (P1)
745 * @param z point (P1)
746 * @see Path2F#moveTo(float, float)
747 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
748 * @see <a href="#windingrules">see winding rules</a>
749 */
750 public final void moveTo(final float x, final float y, final float z) {
751 if ( 0 == getLastOutline().getVertexCount() ) {
752 addVertex(x, y, z, true);
753 } else {
754 closeLastOutline(false);
756 addVertex(x, y, z, true);
757 }
758 }
759
760 /**
761 * Add a line segment, intersecting the last point and the given point x/y (P1).
762 *
763 * @param x final point (P1)
764 * @param y final point (P1)
765 * @param z final point (P1)
766 * @see Path2F#lineTo(float, float)
767 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
768 * @see <a href="#windingrules">see winding rules</a>
769 */
770 public final void lineTo(final float x, final float y, final float z) {
771 addVertex(x, y, z, true);
772 }
773
774 /**
775 * Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2).
776 *
777 * @param x1 quadratic parametric control point (P1)
778 * @param y1 quadratic parametric control point (P1)
779 * @param z1 quadratic parametric control point (P1)
780 * @param x2 final interpolated control point (P2)
781 * @param y2 final interpolated control point (P2)
782 * @param z2 quadratic parametric control point (P2)
783 * @see Path2F#quadTo(float, float, float, float)
784 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
785 * @see <a href="#windingrules">see winding rules</a>
786 */
787 public final void quadTo(final float x1, final float y1, final float z1, final float x2, final float y2, final float z2) {
788 addVertex(x1, y1, z1, false);
789 addVertex(x2, y2, z2, true);
790 }
791
792 /**
793 * Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3).
794 *
795 * @param x1 Bézier control point (P1)
796 * @param y1 Bézier control point (P1)
797 * @param z1 Bézier control point (P1)
798 * @param x2 Bézier control point (P2)
799 * @param y2 Bézier control point (P2)
800 * @param z2 Bézier control point (P2)
801 * @param x3 final interpolated control point (P3)
802 * @param y3 final interpolated control point (P3)
803 * @param z3 final interpolated control point (P3)
804 * @see Path2F#cubicTo(float, float, float, float, float, float)
805 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
806 * @see <a href="#windingrules">see winding rules</a>
807 */
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) {
809 addVertex(x1, y1, z1, false);
810 addVertex(x2, y2, z2, false);
811 addVertex(x3, y3, z3, true);
812 }
813
814 /**
815 * Closes the current sub-path segment by drawing a straight line back to the coordinates of the last moveTo. If the path is already closed then this method has no effect.
816 * @see Path2F#closePath()
817 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
818 */
819 public final void closePath() {
820 if ( 0 < getLastOutline().getVertexCount() ) {
821 closeLastOutline(true);
823 }
824 }
825
826 /**
827 * Return the outline's vertices state, {@link OutlineShape.VerticesState}
828 */
830 return outlineState;
831 }
832
833 /**
834 * Claim this outline's vertices are all {@link OutlineShape.VerticesState#QUADRATIC_NURBS},
835 * hence no cubic transformations will be performed.
836 */
837 public final void setIsQuadraticNurbs() {
838 outlineState = VerticesState.QUADRATIC_NURBS;
839 // checkPossibleOverlaps = false;
840 }
841
842 private void subdivideTriangle(final Outline outline, final Vertex a, final Vertex b, final Vertex c, final int index){
843 VectorUtil.midpoint(tmpV1, a.getCoord(), b.getCoord());
844 VectorUtil.midpoint(tmpV3, b.getCoord(), c.getCoord());
845 VectorUtil.midpoint(tmpV2, tmpV1, tmpV3);
846
847 // COLOR
848 // tmpC1.set(a.getColor()).add(b.getColor()).scale(0.5f);
849 // tmpC3.set(b.getColor()).add(b.getColor()).scale(0.5f);
850 // tmpC2.set(tmpC1).add(tmpC1).scale(0.5f);
851
852 //drop off-curve vertex to image on the curve
853 b.setCoord(tmpV2);
854 b.setOnCurve(true);
855
856 outline.addVertex(index, new Vertex(tmpV1, false));
857 outline.addVertex(index+2, new Vertex(tmpV3, false));
858
859 addedVerticeCount += 2;
860 }
861
862 /**
863 * Check overlaps between curved triangles
864 * first check if any vertex in triangle a is in triangle b
865 * second check if edges of triangle a intersect segments of triangle b
866 * if any of the two tests is true we divide current triangle
867 * and add the other to the list of overlaps
868 *
869 * Loop until overlap array is empty. (check only in first pass)
870 */
871 private void checkOverlaps() {
872 final ArrayList<Vertex> overlaps = new ArrayList<Vertex>(3);
873 final int count = getOutlineCount();
874 boolean firstpass = true;
875 do {
876 for (int cc = 0; cc < count; cc++) {
877 final Outline outline = getOutline(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;
885
886 // check for overlap even if already set for subdivision
887 // ensuring both triangular overlaps get divided
888 // for pref. only check in first pass
889 // second pass to clear the overlaps array(reduces precision errors)
890 if( firstpass ) {
891 overlap = checkTriOverlaps0(prevV, currentVertex, nextV);
892 } else {
893 overlap = null;
894 }
895 if( null != overlap || overlaps.contains(currentVertex) ) {
896 overlaps.remove(currentVertex);
897
898 subdivideTriangle(outline, prevV, currentVertex, nextV, i);
899 i+=3;
900 vertexCount+=2;
901 addedVerticeCount+=2;
902
903 if(overlap != null && !overlap.isOnCurve()) {
904 if(!overlaps.contains(overlap)) {
905 overlaps.add(overlap);
906 }
907 }
908 }
909 }
910 }
911 }
912 firstpass = false;
913 } while( !overlaps.isEmpty() );
914 }
915
916 private Vertex checkTriOverlaps0(final Vertex a, final Vertex b, final Vertex c) {
917 final int count = getOutlineCount();
918 for (int cc = 0; cc < count; cc++) {
919 final Outline outline = getOutline(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);
926
927 //skip neighboring triangles
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) ) {
932 return currV;
933 }
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) ) {
937 return currV;
938 }
939 }
940 }
941 }
942 }
943 return null;
944 }
945
946 private void cleanupOutlines() {
947 final boolean transformOutlines2Quadratic = VerticesState.QUADRATIC_NURBS != outlineState;
948 int count = getOutlineCount();
949 for (int cc = 0; cc < count; cc++) {
950 final Outline outline = getOutline(cc);
951 int vertexCount = outline.getVertexCount();
952
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);
962 // COLOR: tmpC1.set(currentVertex.getColor()).add(nextVertex.getColor()).scale(0.5f)
963 i++;
964 vertexCount++;
965 addedVerticeCount++;
966 outline.addVertex(i, v);
967 }
968 }
969 }
970 if( 0 >= vertexCount ) {
971 outlines.remove(outline);
972 cc--;
973 count--;
974 } else if( 0 < vertexCount &&
975 outline.getVertex(0).getCoord().isEqual( outline.getLastVertex().getCoord() ) ) {
976 outline.removeVertex(vertexCount-1);
977 }
978 }
979 outlineState = VerticesState.QUADRATIC_NURBS;
980 checkOverlaps();
981 }
982
983 private int generateVertexIds() {
984 int maxVertexId = 0;
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++);
989 }
990 }
991 return maxVertexId;
992 }
993
994 /**
995 * Return list of concatenated vertices associated with all
996 * {@code Outline}s of this object.
997 * <p>
998 * Vertices are cached until marked dirty.
999 * </p>
1000 * <p>
1001 * Should always be called <i>after</i> {@link #getTriangles(VerticesState)},
1002 * since the latter will mark all cached vertices dirty!
1003 * </p>
1004 */
1005 public final ArrayList<Vertex> getVertices() {
1006 // final boolean updated;
1007 if( 0 != ( DIRTY_VERTICES & dirtyBits ) ) {
1008 vertices.clear();
1009 for(int i=0; i<outlines.size(); i++) {
1010 vertices.addAll(outlines.get(i).getVertices());
1011 }
1012 dirtyBits &= ~DIRTY_VERTICES;
1013 // updated = true;
1014 // } else {
1015 // updated = false;
1016 }
1017 /**
1018 if(Region.DEBUG_INSTANCE) {
1019 System.err.println("OutlineShape.getVertices().X: "+vertices.size()+", updated "+updated);
1020 if( updated ) {
1021 int i=0;
1022 for(final Vertex v : vertices) {
1023 System.err.printf("- [%d]: %s%n", i++, v);
1024 }
1025 }
1026 } */
1027 return vertices;
1028 }
1029
1030 public static void printPerf(final PrintStream out) {
1031 // jogamp.graph.curve.tess.Loop.printPerf(out);
1032 }
1033 private void triangulateImpl() {
1034 if( 0 < outlines.size() ) {
1035 sortOutlines();
1036 generateVertexIds();
1037
1038 triangles.clear();
1039 final Triangulator triangulator2d = Triangulation.create();
1040 triangulator2d.setComplexShape( isComplex() );
1041 for(int index = 0; index<outlines.size(); index++) {
1042 triangulator2d.addCurve(triangles, outlines.get(index), sharpness);
1043 }
1044 triangulator2d.generate(triangles);
1045 addedVerticeCount += triangulator2d.getAddedVerticeCount();
1046 triangulator2d.reset();
1047 }
1048 }
1049
1050 /**
1051 * Triangulate the {@link OutlineShape} generating a list of triangles,
1052 * while {@link #transformOutlines(VerticesState)} beforehand.
1053 * <p>
1054 * Triangles are cached until marked dirty.
1055 * </p>
1056 * @return an arraylist of triangles representing the filled region
1057 * which is produced by the combination of the outlines
1058 */
1059 public final ArrayList<Triangle> getTriangles(final VerticesState destinationType) {
1060 final boolean updated;
1061 if(destinationType != VerticesState.QUADRATIC_NURBS) {
1062 throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")");
1063 }
1064 if( 0 != ( DIRTY_TRIANGLES & dirtyBits ) ) {
1065 cleanupOutlines();
1066 triangulateImpl();
1067 updated = true;
1068 dirtyBits |= DIRTY_VERTICES;
1069 dirtyBits &= ~DIRTY_TRIANGLES;
1070 } else {
1071 updated = false;
1072 }
1074 System.err.println("OutlineShape.getTriangles().X: "+triangles.size()+", updated "+updated);
1075 if( updated ) {
1076 int i=0;
1077 for(final Triangle t : triangles) {
1078 System.err.printf("- [%d]: %s%n", i++, t);
1079 }
1080 }
1081 }
1082 return triangles;
1083 }
1084
1085 /**
1086 * Return a transformed instance with all {@link Outline}s are copied and transformed.
1087 * <p>
1088 * Note: Triangulated data is lost in returned instance!
1089 * </p>
1090 */
1092 final OutlineShape newOutlineShape = new OutlineShape();
1093 final int osize = outlines.size();
1094 for(int i=0; i<osize; i++) {
1095 newOutlineShape.addOutline( outlines.get(i).transform(t) );
1096 }
1097 return newOutlineShape;
1098 }
1099
1100 /**
1101 * Sort the outlines from large
1102 * to small depending on the AABox
1103 */
1104 private void sortOutlines() {
1105 Collections.sort(outlines, reversSizeComparator);
1106 }
1107
1108 private static Comparator<Outline> reversSizeComparator = new Comparator<Outline>() {
1109 @Override
1110 public int compare(final Outline o1, final Outline o2) {
1111 return o2.compareTo(o1); // reverse !
1112 } };
1113
1114 /**
1115 * Compare two outline shape's Bounding Box size.
1116 * @see AABBox#getSize()
1117 * @see java.lang.Comparable#compareTo(java.lang.Object)
1118 */
1119 @Override
1120 public final int compareTo(final OutlineShape other) {
1121 final float thisSize = getBounds().getSize();
1122 final float otherSize = other.getBounds().getSize();
1123 if( FloatUtil.isEqual2(thisSize, otherSize) ) {
1124 return 0;
1125 } else if( thisSize < otherSize ){
1126 return -1;
1127 } else {
1128 return 1;
1129 }
1130 }
1131
1132 private void validateBoundingBox() {
1133 dirtyBits &= ~DIRTY_BOUNDS;
1134 bbox.reset();
1135 for (int i=0; i<outlines.size(); i++) {
1136 bbox.resize(outlines.get(i).getBounds());
1137 }
1138 }
1139
1140 public final AABBox getBounds() {
1141 if( 0 != ( dirtyBits & DIRTY_BOUNDS ) ) {
1142 validateBoundingBox();
1143 }
1144 return bbox;
1145 }
1146
1147 /**
1148 * @param obj the Object to compare this OutlineShape with
1149 * @return true if {@code obj} is an OutlineShape, not null,
1150 * same outlineState, equal bounds and equal outlines in the same order
1151 */
1152 @Override
1153 public final boolean equals(final Object obj) {
1154 if( obj == this) {
1155 return true;
1156 }
1157 if( null == obj || !(obj instanceof OutlineShape) ) {
1158 return false;
1159 }
1160 final OutlineShape o = (OutlineShape) obj;
1161 if(getOutlineState() != o.getOutlineState()) {
1162 return false;
1163 }
1164 if(getOutlineCount() != o.getOutlineCount()) {
1165 return false;
1166 }
1167 if( !getBounds().equals( o.getBounds() ) ) {
1168 return false;
1169 }
1170 for (int i=getOutlineCount()-1; i>=0; i--) {
1171 if( ! getOutline(i).equals( o.getOutline(i) ) ) {
1172 return false;
1173 }
1174 }
1175 return true;
1176 }
1177
1178 @Override
1179 public final int hashCode() {
1180 throw new InternalError("hashCode not designed");
1181 }
1182
1183 @Override
1184 public String toString() {
1185 // Avoid calling this.hashCode() !
1186 return getClass().getName() + "@" + Integer.toHexString(super.hashCode());
1187 }
1188
1189 public void print(final PrintStream out) {
1190 final int oc = getOutlineCount();
1191 for (int oi = 0; oi < oc; oi++) {
1192 final Outline outline = getOutline(oi);
1193 final int vc = outline.getVertexCount();
1194 out.printf("- OL[%d]: %s%n", vc, outline.getWinding());
1195 for(int vi=0; vi < vc; vi++) {
1196 final Vertex v = outline.getVertex(vi);
1197 out.printf("-- OS[%d][%d]: %s%n", oi, vi, v);
1198 }
1199 }
1200 }
1201}
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.
Definition: Region.java:62
static final boolean DEBUG_INSTANCE
Definition: Region.java:65
static Triangulator create()
Create a new instance of a triangulation.
Define a single continuous stroke by control vertices.
Definition: Outline.java:53
final Winding getWinding()
Returns the cached or computed winding of this Outlines polyline using VectorUtil#area(ArrayList).
Definition: Outline.java:167
boolean equals(final Object obj)
Definition: Outline.java:378
final void addVertex(final Vertex vertex)
Appends a vertex to the outline loop/strip.
Definition: Outline.java:209
final Vertex getVertex(final int index)
Definition: Outline.java:248
final void setWinding(final Winding enforce)
Sets Winding to this outline.
Definition: Outline.java:146
boolean isComplex()
Returns cached or computed result if whether this Outlines polyline is a complex shape.
Definition: Outline.java:190
A Vertex exposing Vec3f vertex- and texture-coordinates.
Definition: Vertex.java:37
final void setCoord(final Vec3f coord)
Definition: Vertex.java:93
final void setOnCurve(final boolean onCurve)
Definition: Vertex.java:150
Basic Float math utility functions.
Definition: FloatUtil.java:83
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.
Definition: Vec3f.java:37
static Vec3f midpoint(final Vec3f result, final Vec3f p1, final Vec3f p2)
Calculate the midpoint of two points.
Axis Aligned Bounding Box.
Definition: AABBox.java:54
final boolean equals(final Object obj)
Definition: AABBox.java:912
final AABBox reset()
Resets this box to the inverse low/high, allowing the next resize(float, float, float) command to hit...
Definition: AABBox.java:123
final float getSize()
Get the size of this AABBox where the size is represented by the length of the vector between low and...
Definition: AABBox.java:732
final AABBox resize(final AABBox newBox)
Resize the AABBox to encapsulate another AABox.
Definition: AABBox.java:274
Path2F represents and provides construction method for a 2D shape using float[2] points.
Definition: Path2F.java:31
Outline's vertices have undefined state until transformed.
Winding direction, either clockwise (CW) or counter-clockwise (CCW).
Definition: Winding.java:6
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.