Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
OutlineShape.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com> (C++, Java) and Rami Santina (Java)
3 * Copyright Gothel Software e.K. and the authors
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11#ifndef JAU_GAMP_GRAPH_OUTLINESHAPE_HPP_
12#define JAU_GAMP_GRAPH_OUTLINESHAPE_HPP_
13
14#include <jau/basic_algos.hpp>
15#include <jau/basic_types.hpp>
16#include <jau/debug.hpp>
17#include <jau/enum_util.hpp>
20
21#include <gamp/graph/Graph.hpp>
24
25namespace gamp::graph {
26
27 using namespace jau::math;
28 using namespace jau::math::geom;
29 using namespace jau::enums;
30 using jau::math::geom::plane::AffineTransform;
31
32 /** \addtogroup Gamp_Graph
33 *
34 * @{
35 */
36
37 /**
38 * A Generic shape objects which is defined by a list of Outlines.
39 * This Shape can be transformed to triangulations.
40 * The list of triangles generated are render-able by a Region object.
41 * The triangulation produced by this Shape will define the
42 * closed region defined by the outlines.
43 *
44 * One or more OutlineShape Object can be associated to a region
45 * this is left as a high-level representation of the Objects. For
46 * optimizations, flexibility requirements for future features.
47
48 * <a name="windingrules">
49 * Outline shape general {@link Winding} rules
50 * - Outer boundary-shapes are required as Winding::CCW
51 * - Inner hole-shapes should be Winding::CW
52 * - If unsure
53 * - You may check Winding via getWindingOfLastOutline() or Outline::getWinding() (optional, might be incorrect)
54 * - Use setWindingOfLastOutline(Winding) before {@link #closeLastOutline(boolean)} or {@link #closePath()} } to enforce Winding::CCW, or
55 * - use Outline::setWinding(Winding) on a specific Outline to enforce Winding::CCW.
56 * - If e.g. the Winding has changed for an Outline by above operations, its vertices have been reversed.
57 * - Safe path: Simply create all outer boundary-shapes with Winding::CCW and inner hole-shapes with Winding::CW.
58 *
59 * Example to creating an Outline Shape:
60 * <pre>
61 addVertex(...)
62 addVertex(...)
63 addVertex(...)
64 addEmptyOutline()
65 addVertex(...)
66 addVertex(...)
67 addVertex(...)
68 * </pre>
69 *
70 * The above will create two outlines each with three vertices. By adding these two outlines to
71 * the OutlineShape, we are stating that the combination of the two outlines represent the shape.
72 *
73 * To specify that the shape is curved at a region, the on-curve flag should be set to false
74 * for the vertex that is in the middle of the curved region (if the curved region is defined by 3
75 * vertices (quadratic curve).
76 *
77 * In case the curved region is defined by 4 or more vertices the middle vertices should both have
78 * the on-curve flag set to false.
79 *
80 * Example:
81 * <pre>
82 addVertex(0,0, true);
83 addVertex(0,1, false);
84 addVertex(1,1, false);
85 addVertex(1,0, true);
86 * </pre>
87 *
88 * The above snippet defines a cubic nurbs curve where (0,1 and 1,1)
89 * do not belong to the final rendered shape.
90 *
91 * <i>Implementation Notes:</i><br>
92 * - The first vertex of any outline belonging to the shape should be on-curve
93 * - Intersections between off-curved parts of the outline is not handled
94 *
95 * @see Outline
96 * @see Region
97 */
99 public:
100 typedef uint32_t size_type;
101
102 /// byte-size uint32_t limit: 1'073'741'823 (FIXME: Adjust to actual type, i.e. Vertex = 2x Vec3f?)
103 constexpr static size_type max_elements = std::numeric_limits<uint32_t>::max() / sizeof(uint32_t);
104
105 /** Initial sharpness() value, which can be modified via setSharpness(float). */
106 static constexpr float DEFAULT_SHARPNESS = 0.5f;
107
108 enum class DirtyBits : uint16_t {
109 none = 0,
110 bounds = 1 << 0,
111 vertices = 1 << 1, /// <Modified shape, requires to update the vertices and triangles, here: vertices
112 triangles = 1 << 2, /// <Modified shape, requires to update the vertices and triangles, here: triangulation.
113 convex = 1 << 3, /// <Modified shape, requires to update the convex determination
115 };
117
118 enum class VertexState : uint16_t { undefined = 0, quadratic_nurbs = 1 };
120
121 private:
122 size_type m_outlineVertCapacity;
123 Vec3f m_normal;
124 OutlineList m_outlines;
125 mutable AABBox3f m_bbox;
126 mutable DirtyBits m_dirtyBits;
127 size_type m_addedVertexCount;
128 mutable bool m_complexShape;
129 VertexState m_outlineState;
130 float m_sharpness;
131 VertexList m_vertices;
132 TriangleRefList m_triangles;
133
134 public:
135
137 OutlineShape(size_type capacity, size_type outlineVertCapacity)
138 : m_outlineVertCapacity(capacity),
139 m_normal(0, 0, 1),
140 m_outlines(m_outlineVertCapacity),
141 m_bbox(),
142 m_dirtyBits(DirtyBits::none),
143 m_addedVertexCount(0),
144 m_complexShape(false),
145 m_outlineState(VertexState::undefined),
146 m_sharpness(DEFAULT_SHARPNESS)
147 {
148 m_outlines.emplace_back(outlineVertCapacity);
149 }
150
151
152 /** Normal vector, optionally used by tesselator to add (interleaved) normals. */
153 constexpr const Vec3f& normal() const noexcept { return m_normal; }
154 /** Writing the normal vector, optionally used by tesselator to add (interleaved) normals. */
155 constexpr Vec3f& normal() noexcept { return m_normal; }
156
157 /**
158 * Return the number of newly added vertices during getTriangles(VerticesState)
159 * while transforming the outlines to VerticesState::QUADRATIC_NURBS and triangulation.
160 * @see setIsQuadraticNurbs()
161 */
162 constexpr size_type addedVertexCount() const noexcept { return m_addedVertexCount; }
163
164 /** Sharpness value, defaults to DEFAULT_SHARPNESS. */
165 constexpr float sharpness() const noexcept { return m_sharpness; }
166
167 /** Sets sharpness, defaults to DEFAULT_SHARPNESS. */
168 void setSharpness(float s) noexcept {
169 if( m_sharpness != s ) {
170 clearCache();
171 m_sharpness=s;
172 }
173 }
174
175 /** Clears all data and reset all states as if this instance was newly created */
176 void clear() {
177 m_outlines.clear();
178 m_outlines.emplace_back(m_outlineVertCapacity);
179 m_outlineState = VertexState::undefined;
180 m_bbox.reset();
181 m_vertices.clear();
182 m_triangles.clear();
183 m_addedVertexCount = 0;
184 m_complexShape = false;
185 m_dirtyBits = DirtyBits::none;
186 }
187
188 /** Clears cached triangulated data, i.e. {@link #getTriangles(VerticesState)} and {@link #getVertices()}. */
189 void clearCache() noexcept {
190 m_vertices.clear();
191 m_triangles.clear();
193 }
194
195 constexpr void reserve(size_type newCapacity) { m_outlines.reserve(newCapacity); }
196
197 constexpr DirtyBits dirtyBits() const noexcept { return m_dirtyBits; }
198 constexpr bool verticesDirty() const noexcept { return is_set(m_dirtyBits, DirtyBits::vertices); }
199 constexpr bool trianglesDirty() const noexcept { return is_set(m_dirtyBits, DirtyBits::triangles); }
200 void markClean(DirtyBits flags) noexcept { m_dirtyBits &= ~flags; }
201
202 private:
203 void validateBoundingBox() const noexcept {
204 m_dirtyBits &= ~DirtyBits::bounds;
205 m_bbox.reset();
206 for (const auto & m_outline : m_outlines) {
207 m_bbox.resize(m_outline.bounds());
208 }
209 }
210
211 public:
212 const AABBox3f& bounds() const noexcept {
213 if ( is_set(m_dirtyBits, DirtyBits::bounds) ) {
214 validateBoundingBox();
215 }
216 return m_bbox;
217 }
218
219 bool empty() const noexcept { return m_outlines.empty(); }
220
221 /** Returns the number of {@link Outline}s. */
222 size_type outlineCount() const noexcept {
223 return m_outlines.size();
224 }
225
226 /** Returns the total {@link Outline#getVertexCount() vertex number} of all {@link Outline}s. */
227 size_type vertexCount() const noexcept {
228 size_type res = 0;
229 for(const Outline& o : m_outlines) {
230 res += o.vertexCount();
231 }
232 return res;
233 }
234
235 const OutlineList& outlines() const noexcept { return m_outlines; }
236 OutlineList& outlines() noexcept { return m_outlines; }
237
238 const Outline& outline(size_type i) const noexcept { return m_outlines[i]; }
239 Outline& outline(size_type i) noexcept { return m_outlines[i]; }
240
241 /**
242 * Get the last added outline to the list
243 * of outlines that define the shape
244 * @return the last outline
245 */
246 const Outline& lastOutline() const noexcept {
247 return m_outlines[m_outlines.size()-1];
248 }
249 /**
250 * Get the last added outline to the list
251 * of outlines that define the shape
252 * @return the last outline
253 */
254 Outline& lastOutline() noexcept {
255 return m_outlines[m_outlines.size()-1];
256 }
257
258 /**
259 * Compute the {@link Winding} of the {@link #getLastOutline()} using the {@link VectorUtil#area(ArrayList)} function over all of its vertices.
260 * @return {@link Winding#CCW} or {@link Winding#CW}
261 */
262 Winding windingOfLastOutline() const noexcept {
263 return lastOutline().getWinding();
264 }
265
266 /**
267 * Sets the enforced {@link Winding} of the {@link #getLastOutline()}.
268 */
270 lastOutline().setWinding(enforced);
271 }
272
273 /**
274 * Returns cached or computed result if at least one `polyline` outline(size_type) is a complex shape, see Outline::isComplex().
275 * <p>
276 * A polyline with less than 3 elements is marked a simple shape for simplicity.
277 * </p>
278 * <p>
279 * The result is cached.
280 * </p>
281 * @see #setOverrideConvex(boolean)
282 * @see #clearOverrideConvex()
283 */
284 bool isComplex() const noexcept {
285 if( !is_set(m_dirtyBits, DirtyBits::convexOverride) &&
286 is_set(m_dirtyBits, DirtyBits::convex) )
287 {
288 m_complexShape = false;
289 size_type sz = outlineCount();
290 for(size_type i=0; i<sz && !m_complexShape; ++i) {
291 m_complexShape = outline(i).isComplex();
292 }
293 m_dirtyBits &= ~DirtyBits::convex;
294 }
295 return m_complexShape;
296 }
297 /**
298 * Overrides {@link #isComplex()} using the given value instead of computing via {@link Outline#isComplex()}.
299 * @see #clearOverrideConvex()
300 * @see #isComplex()
301 */
302 void setOverrideConvex(bool convex) noexcept {
303 m_dirtyBits |= DirtyBits::convexOverride;
304 m_complexShape = convex;
305 }
306
307 /**
308 * Clears the {@link #isComplex()} override done by {@link #setOverrideConvex(boolean)}
309 * @see #setOverrideConvex(boolean)
310 * @see #isComplex()
311 */
312 void clearOverrideConvex() noexcept {
313 m_dirtyBits &= ~DirtyBits::convexOverride;
314 m_dirtyBits |= DirtyBits::convex;
315 }
316
317 /**
318 * Add a new empty {@link Outline}
319 * to the end of this shape's outline list.
320 * <p>If the {@link #getLastOutline()} is empty already, no new one will be added.</p>
321 *
322 * After a call to this function all new vertices added
323 * will belong to the new outline
324 */
326 if( !lastOutline().empty() ) {
327 m_outlines.emplace_back(m_outlineVertCapacity);
328 }
329 }
330
331 /**
332 * Appends the {@link Outline} element to the end,
333 * ensuring a clean tail.
334 *
335 * <p>A clean tail is ensured, no double empty Outlines are produced
336 * and a pre-existing empty outline will be replaced with the given one. </p>
337 *
338 * @param outline Outline object to be added
339 * @throws NullPointerException if the {@link Outline} element is null
340 */
342 addOutline(m_outlines.size(), outline);
343 }
344
345 /**
346 * Insert the {@link Outline} element at the given {@code position}.
347 *
348 * <p>If the {@code position} indicates the end of this list,
349 * a clean tail is ensured, no double empty Outlines are produced
350 * and a pre-existing empty outline will be replaced with the given one. </p>
351 *
352 * @param position of the added Outline
353 * @param outline Outline object to be added
354 * @throws NullPointerException if the {@link Outline} element is null
355 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber())
356 */
357 void addOutline(size_type position, const Outline& outline) {
358 if( m_outlines.size() == position ) {
359 const Outline& last = lastOutline();
360 if( outline.empty() && last.empty() ) {
361 return;
362 }
363 if( last.empty() ) {
364 m_outlines[position-1] = outline;
365 if( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
366 m_bbox.resize(outline.bounds());
367 }
368 // vertices.addAll(outline.getVertices()); // FIXME: can do and remove DIRTY_VERTICES ?
370 return;
371 }
372 }
373 m_outlines.insert(position, outline);
374 if( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
375 m_bbox.resize(outline.bounds());
376 }
378 }
379
380 /**
381 * Insert the {@link OutlineShape} elements of type {@link Outline}, .. at the end of this shape,
382 * using {@link #addOutline(Outline)} for each element.
383 * <p>Closes the current last outline via {@link #closeLastOutline(boolean)} before adding the new ones.</p>
384 * @param outlineShape OutlineShape elements to be added.
385 * @throws NullPointerException if the {@link OutlineShape} is null
386 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber())
387 */
388 void addOutlineShape(const OutlineShape& outlineShape) {
389 closeLastOutline(true);
390 for(size_type i=0; i<outlineShape.outlineCount(); i++) {
391 addOutline(outlineShape.outline(i));
392 }
393 }
394
395 /**
396 * Replaces the {@link Outline} element at the given {@code position}.
397 * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
398 *
399 * @param position of the replaced Outline
400 * @param outline replacement Outline object
401 * @throws NullPointerException if the {@link Outline} element is null
402 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
403 */
404 void setOutline(size_type position, const Outline& outline) {
405 m_outlines.insert(position, outline);
407 }
408
409 /**
410 * Removes the {@link Outline} element at the given {@code position}.
411 * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
412 *
413 * @param position of the to be removed Outline
414 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
415 */
416 void removeOutline(size_type position) {
418 m_outlines.erase(position);
419 }
420
421 //
422 //
423
424 /**
425 * Adds a vertex to the last open outline to the shape's tail.
426 *
427 * @param v the vertex to be added to the OutlineShape
428 * @see <a href="#windingrules">see winding rules</a>
429 */
430 void addVertex(const Vertex& v) {
431 Outline& lo = lastOutline();
432 lo.addVertex(v);
433 if( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
434 m_bbox.resize(v.coord());
435 }
436 // vertices.add(v); // FIXME: can do and remove DIRTY_VERTICES ?
438 }
439
440 /**
441 * Adds a vertex to the last open outline to the shape at {@code position}
442 *
443 * @param position index within the last open outline, at which the vertex will be added
444 * @param v the vertex to be added to the OutlineShape
445 * @see <a href="#windingrules">see winding rules</a>
446 */
447 void addVertex(size_type position, const Vertex& v) {
448 Outline& lo = lastOutline();
449 lo.addVertex(position, v);
450 if( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
451 m_bbox.resize(v.coord());
452 }
453 // vertices.add(v); // FIXME: can do and remove DIRTY_VERTICES ?
455 }
456
457 /**
458 * Add a 3D {@link Vertex} to the last open outline to the shape's tail.
459 *
460 * @param x the x coordinate
461 * @param y the y coordinate
462 * @param z the z coordinate
463 * @param onCurve flag if this vertex is on the curve or defines a curved region of the shape around this vertex.
464 * @see <a href="#windingrules">see winding rules</a>
465 */
466 void addVertex(float x, float y, float z, bool onCurve) {
467 addVertex(Vertex(x, y, z, onCurve));
468 }
469
470 /**
471 * Add a 3D {@link Vertex} to the last open outline to the shape's tail.
472 *
473 * @param v the Vec3f coordinates
474 * @param onCurve flag if this vertex is on the curve or defines a curved region of the shape around this vertex.
475 * @see <a href="#windingrules">see winding rules</a>
476 */
477 void addVertex(const Vec3f& v, bool onCurve) {
478 addVertex(Vertex(v, onCurve));
479 }
480
481 /**
482 * Add a 2D {@link Vertex} to the last open outline to the shape's tail.
483 *
484 * @param x the x coordinate
485 * @param y the y coordinate
486 * @param onCurve flag if this vertex is on the curve or defines a curved region of the shape around this vertex.
487 * @see <a href="#windingrules">see winding rules</a>
488 */
489 void addVertex(float x, float y, bool onCurve) {
490 addVertex(Vertex(x, y, onCurve));
491 }
492
493 /**
494 * Add a 2D {@link Vertex} to the last open outline to the shape's tail.
495 *
496 * @param v the Vec2f coordinates
497 * @param onCurve flag if this vertex is on the curve or defines a curved region of the shape around this vertex.
498 * @see <a href="#windingrules">see winding rules</a>
499 */
500 void addVertex(const Vec2f& v, bool onCurve) {
501 addVertex(Vertex(v, onCurve));
502 }
503
504 /**
505 * Add a 3D {@link Vertex} to the last open outline to the shape at {@code position}.
506 *
507 * @param position index within the last open outline, at which the vertex will be added
508 * @param x the x coordinate
509 * @param y the y coordniate
510 * @param z the z coordinate
511 * @param onCurve flag if this vertex is on the curve or defines a curved region of the shape around this vertex.
512 * @see <a href="#windingrules">see winding rules</a>
513 */
514 void addVertex(size_type position, float x, float y, float z, bool onCurve) {
515 addVertex(position, Vertex(x, y, z, onCurve));
516 }
517
518 /**
519 * Start a new position for the next line segment at given point x/y (P1).
520 *
521 * @param x point (P1)
522 * @param y point (P1)
523 * @param z point (P1)
524 * @see Path2F#moveTo(float, float)
525 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
526 * @see <a href="#windingrules">see winding rules</a>
527 */
528 void moveTo(float x, float y, float z) {
529 if ( 0 == lastOutline().vertexCount() ) {
530 addVertex(x, y, z, true);
531 } else {
532 closeLastOutline(false);
534 addVertex(x, y, z, true);
535 }
536 }
537
538 /**
539 * Add a line segment, intersecting the last point and the given point x/y (P1).
540 *
541 * @param x final point (P1)
542 * @param y final point (P1)
543 * @param z final point (P1)
544 * @see Path2F#lineTo(float, float)
545 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
546 * @see <a href="#windingrules">see winding rules</a>
547 */
548 void lineTo(float x, float y, float z) {
549 addVertex(x, y, z, true);
550 }
551
552 /**
553 * Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2).
554 *
555 * @param x1 quadratic parametric control point (P1)
556 * @param y1 quadratic parametric control point (P1)
557 * @param z1 quadratic parametric control point (P1)
558 * @param x2 final interpolated control point (P2)
559 * @param y2 final interpolated control point (P2)
560 * @param z2 quadratic parametric control point (P2)
561 * @see Path2F#quadTo(float, float, float, float)
562 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
563 * @see <a href="#windingrules">see winding rules</a>
564 */
565 void quadTo(float x1, float y1, float z1, float x2, float y2, float z2) {
566 addVertex(x1, y1, z1, false);
567 addVertex(x2, y2, z2, true);
568 }
569
570 /**
571 * Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3).
572 *
573 * @param x1 Bézier control point (P1)
574 * @param y1 Bézier control point (P1)
575 * @param z1 Bézier control point (P1)
576 * @param x2 Bézier control point (P2)
577 * @param y2 Bézier control point (P2)
578 * @param z2 Bézier control point (P2)
579 * @param x3 final interpolated control point (P3)
580 * @param y3 final interpolated control point (P3)
581 * @param z3 final interpolated control point (P3)
582 * @see Path2F#cubicTo(float, float, float, float, float, float)
583 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
584 * @see <a href="#windingrules">see winding rules</a>
585 */
586 void cubicTo(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) {
587 addVertex(x1, y1, z1, false);
588 addVertex(x2, y2, z2, false);
589 addVertex(x3, y3, z3, true);
590 }
591
592 /**
593 * Closes the last outline in the shape.
594 * <p>
595 * Checks whether the last vertex equals to the first of the last outline.
596 * If not equal, it either appends a copy of the first vertex
597 * or prepends a copy of the last vertex, depending on <code>closeTail</code>.
598 * </p>
599 * @param closeTail if true, a copy of the first vertex will be appended,
600 * otherwise a copy of the last vertex will be prepended.
601 */
602 void closeLastOutline(bool closeTail) {
603 if( lastOutline().setClosed( closeTail ) ) {
605 }
606 }
607
608 /**
609 * 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.
610 * @see Path2F#closePath()
611 * @see #addPath(com.jogamp.math.geom.plane.Path2F.Iterator, boolean)
612 */
613 void closePath() {
614 if ( 0 < lastOutline().vertexCount() ) {
615 closeLastOutline(true);
617 }
618 }
619
620 VertexState outlineState() const noexcept { return m_outlineState; }
621
622 /**
623 * Claim this outline's vertices are all VertexState::quadratic_nurbs,
624 * hence no cubic transformations will be performed.
625 */
626 void setIsQuadraticNurbs() noexcept {
627 m_outlineState = VertexState::quadratic_nurbs;
628 // checkPossibleOverlaps = false;
629 }
630
631 /**
632 * Return a transformed instance with all {@link Outline}s are copied and transformed.
633 * <p>
634 * Note: Triangulated data is lost in returned instance!
635 * </p>
636 */
638 OutlineShape newOutlineShape;
639 size_type osize = m_outlines.size();
640 for(size_type i=0; i<osize; i++) {
641 newOutlineShape.addOutline( m_outlines[i].transform(t) );
642 }
643 return newOutlineShape;
644 }
645
646 /// Returns a copy of this instance with normal() and all outlines() vertices()'s z-axis sign-flipped,
647 /// used to generate a back-face from a front-face shape.
648 OutlineShape flipFace(float zoffset=0) const {
649 OutlineShape nshape = *this;
650 nshape.normal().z *= -1;
651 for(Outline& o : nshape.outlines()) {
652 for(Vertex& v : o.vertices()) {
653 v.coord().z = ( v.coord().z * -1.0f ) + zoffset;
654 }
655 }
656 return nshape;
657 }
658
659 /**
660 * Compare two outline shape's Bounding Box size.
661 * @return <0, 0, >0 if this this object is less than, equal to, or greater than the other object.
662 * @see AABBox3f::size()
663 */
664 int compareTo(const OutlineShape& other) const noexcept {
665 const float thisSize = bounds().size();
666 const float otherSize = other.bounds().size();
667 if( jau::equals2(thisSize, otherSize) ) {
668 return 0;
669 } else if(thisSize < otherSize){
670 return -1;
671 } else {
672 return 1;
673 }
674 }
675
676 /**
677 * @return true if {@code o} equals bounds and vertices in the same order
678 */
679 constexpr bool operator==(const OutlineShape& o) const noexcept {
680 if( this == &o) {
681 return true;
682 }
683 if(outlineState() != o.outlineState()) {
684 return false;
685 }
686 if(outlineCount() != o.outlineCount()) {
687 return false;
688 }
689 if( bounds() != o.bounds() ) {
690 return false;
691 }
692 for (size_type i=outlineCount(); i-- > 0;) {
693 if( outline(i) != o.outline(i) ) {
694 return false;
695 }
696 }
697 return true;
698 }
699
700 private:
701 //
702 // prost-processing
703 //
704
705 void subdivideTriangle(Outline& outline, const Vertex& a, Vertex& b, const Vertex& c, size_type index);
706
707 /**
708 * Check overlaps between curved triangles
709 * first check if any vertex in triangle a is in triangle b
710 * second check if edges of triangle a intersect segments of triangle b
711 * if any of the two tests is true we divide current triangle
712 * and add the other to the list of overlaps
713 *
714 * Loop until overlap array is empty. (check only in first pass)
715 */
716 void checkOverlaps();
717 Vertex* checkTriOverlaps0(const Vertex& a, const Vertex& b, const Vertex& c);
718 void cleanupOutlines();
719 uint32_t generateVertexIds();
720
721 /**
722 * Sort the outlines in descending size from large
723 * to small depending on the AABBox
724 */
725 void sortOutlines();
726
727 void triangulateImpl();
728
729 public:
730 /**
731 * Triangulate the {@link OutlineShape} generating a list of triangles,
732 * while {@link #transformOutlines(VerticesState)} beforehand.
733 *
734 * Triangles are cached until marked dirty.
735 *
736 * After generating a the triangles, getVertices() can be used for all vertices.
737 *
738 * @return an arraylist of triangles representing the filled region
739 * which is produced by the combination of the outlines
740 *
741 * @see getVertices()
742 */
744
745 /**
746 * Return list of concatenated vertices associated with all
747 * {@code Outline}s of this object.
748 *
749 * Vertices are cached until marked dirty.
750 *
751 * Should always be called <i>after</i> getTriangles(VerticesState),
752 * since the latter will mark all cached vertices dirty!
753 */
754 const VertexList& getVertices();
755
756 std::string toString() const noexcept {
757 std::string r("OutlineShape[");
758 r.append("dirty ").append(to_string(m_dirtyBits))
759 .append(", outlines ").append(std::to_string(outlineCount()))
760 .append(", vertices ").append(std::to_string(vertexCount()))
761 .append("]");
762 return r;
763 }
764
765 private:
766
767 };
768
769
770 /**@}*/
771
772} // namespace gamp::graph
773
774#endif /* JAU_GAMP_GRAPH_OUTLINESHAPE_HPP_ */
constexpr size_type addedVertexCount() const noexcept
Return the number of newly added vertices during getTriangles(VerticesState) while transforming the o...
const VertexList & getVertices()
Return list of concatenated vertices associated with all Outlines of this object.
size_type outlineCount() const noexcept
Returns the number of Outlines.
JAU_MAKE_BITFIELD_ENUM_STRING_MEMBER(DirtyBits, bounds, vertices, triangles, convex, convexOverride)
void addVertex(size_type position, float x, float y, float z, bool onCurve)
Add a 3D Vertex to the last open outline to the shape at position.
void addVertex(float x, float y, float z, bool onCurve)
Add a 3D Vertex to the last open outline to the shape's tail.
const AABBox3f & bounds() const noexcept
void setSharpness(float s) noexcept
Sets sharpness, defaults to DEFAULT_SHARPNESS.
void clearCache() noexcept
Clears cached triangulated data, i.e.
static constexpr size_type max_elements
byte-size uint32_t limit: 1'073'741'823 (FIXME: Adjust to actual type, i.e. Vertex = 2x Vec3f?...
VertexState outlineState() const noexcept
void setOverrideConvex(bool convex) noexcept
Overrides isComplex() using the given value instead of computing via Outline#isComplex().
bool isComplex() const noexcept
Returns cached or computed result if at least one polyline outline(size_type) is a complex shape,...
int compareTo(const OutlineShape &other) const noexcept
Compare two outline shape's Bounding Box size.
OutlineList & outlines() noexcept
Winding windingOfLastOutline() const noexcept
Compute the Winding of the getLastOutline() using the VectorUtil#area(ArrayList) function over all of...
@ triangles
<Modified shape, requires to update the vertices and triangles, here: vertices
@ convexOverride
<Modified shape, requires to update the convex determination
@ convex
<Modified shape, requires to update the vertices and triangles, here: triangulation.
JAU_MAKE_ENUM_STRING_MEMBER(VertexState, quadratic_nurbs)
void cubicTo(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3)
Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3).
void removeOutline(size_type position)
Removes the Outline element at the given position.
void addVertex(float x, float y, bool onCurve)
Add a 2D Vertex to the last open outline to the shape's tail.
const Outline & lastOutline() const noexcept
Get the last added outline to the list of outlines that define the shape.
const OutlineList & outlines() const noexcept
void addVertex(const Vec2f &v, bool onCurve)
Add a 2D Vertex to the last open outline to the shape's tail.
size_type vertexCount() const noexcept
Returns the total vertex number of all Outlines.
const Outline & outline(size_type i) const noexcept
std::string toString() const noexcept
void addVertex(const Vec3f &v, bool onCurve)
Add a 3D Vertex to the last open outline to the shape's tail.
constexpr Vec3f & normal() noexcept
Writing the normal vector, optionally used by tesselator to add (interleaved) normals.
void closeLastOutline(bool closeTail)
Closes the last outline in the shape.
OutlineShape(size_type capacity, size_type outlineVertCapacity)
constexpr bool verticesDirty() const noexcept
void addOutline(size_type position, const Outline &outline)
Insert the Outline element at the given position.
void setWindingOfLastOutline(Winding enforced)
Sets the enforced Winding of the getLastOutline().
constexpr void reserve(size_type newCapacity)
void setIsQuadraticNurbs() noexcept
Claim this outline's vertices are all VertexState::quadratic_nurbs, hence no cubic transformations wi...
constexpr bool operator==(const OutlineShape &o) const noexcept
void clear()
Clears all data and reset all states as if this instance was newly created.
bool empty() const noexcept
void quadTo(float x1, float y1, float z1, float x2, float y2, float z2)
Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2).
void markClean(DirtyBits flags) noexcept
void addOutlineShape(const OutlineShape &outlineShape)
Insert the OutlineShape elements of type Outline, .
constexpr const Vec3f & normal() const noexcept
Normal vector, optionally used by tesselator to add (interleaved) normals.
void moveTo(float x, float y, float z)
Start a new position for the next line segment at given point x/y (P1).
void clearOverrideConvex() noexcept
Clears the isComplex() override done by setOverrideConvex(boolean).
void closePath()
Closes the current sub-path segment by drawing a straight line back to the coordinates of the last mo...
void addVertex(size_type position, const Vertex &v)
Adds a vertex to the last open outline to the shape at position @endiliteral.
static constexpr float DEFAULT_SHARPNESS
Initial sharpness() value, which can be modified via setSharpness(float).
const TriangleRefList & getTriangles(VertexState destinationType=VertexState::quadratic_nurbs)
Triangulate the OutlineShape generating a list of triangles, while transformOutlines(VerticesState) b...
constexpr float sharpness() const noexcept
Sharpness value, defaults to DEFAULT_SHARPNESS.
constexpr bool trianglesDirty() const noexcept
Outline & lastOutline() noexcept
Get the last added outline to the list of outlines that define the shape.
OutlineShape transform(const AffineTransform &t) const
Return a transformed instance with all Outlines are copied and transformed.
Outline & outline(size_type i) noexcept
OutlineShape flipFace(float zoffset=0) const
Returns a copy of this instance with normal() and all outlines() vertices()'s z-axis sign-flipped,...
void lineTo(float x, float y, float z)
Add a line segment, intersecting the last point and the given point x/y (P1).
void addEmptyOutline()
Add a new empty Outline to the end of this shape's outline list.
void setOutline(size_type position, const Outline &outline)
Replaces the Outline element at the given position.
void addOutline(const Outline &outline)
Appends the Outline element to the end, ensuring a clean tail.
void addVertex(const Vertex &v)
Adds a vertex to the last open outline to the shape's tail.
constexpr DirtyBits dirtyBits() const noexcept
Define a single continuous stroke by control vertices.
Definition Outline.hpp:51
void addVertex(const Vertex &vertex)
Appends a vertex to the outline loop/strip.
Definition Outline.hpp:241
bool isComplex() const noexcept
Returns cached or computed result if whether this Outlines polyline is a complex shape.
Definition Outline.hpp:207
constexpr bool empty() const noexcept
Definition Outline.hpp:87
void setWinding(Winding enforce)
Sets Winding to this outline.
Definition Outline.hpp:184
Winding getWinding() const noexcept
Returns the cached or computed winding of this Outlines polyline using VectorUtil#area(ArrayList).
Definition Outline.hpp:159
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
value_type z
Definition vec3f.hpp:68
Axis Aligned Bounding Box.
Definition aabbox3f.hpp:43
constexpr AABBox3f & reset() noexcept
Reset this box to the inverse low/high, allowing the next resize(float, float, float) command to hit.
Definition aabbox3f.hpp:91
constexpr float size() const noexcept
Get the size of this aabbox3f where the size is represented by the length of the vector between low a...
Definition aabbox3f.hpp:112
AABBox3f & resize(const AABBox3f &o) noexcept
Resize the aabbox3f to encapsulate another AABox.
Definition aabbox3f.hpp:228
Represents a affine 2x3 transformation matrix in column major order (memory layout).
constexpr bool is_set(const E mask, const E bits) noexcept
constexpr bool equals2(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
jau::darray< Vertex, uint32_t > VertexList
jau::darray< Outline, uint32_t > OutlineList
Definition Outline.hpp:320
jau::darray< TriangleRef, uint32_t > TriangleRefList
Vector2F< float > Vec2f
Definition vec2f.hpp:404
Vector3F< float > Vec3f
Definition vec3f.hpp:422
std::string to_string(const math_error_t v) noexcept
Returns std::string representation of math_error_t.