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