Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
CDTriangulator2D.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_TESS_IMPL_CDTesselator2D_HPP_
12#define JAU_GAMP_GRAPH_TESS_IMPL_CDTesselator2D_HPP_
13
14#include <gamp/GampTypes.hpp>
15#include <gamp/graph/Graph.hpp>
20
21#include <jau/backtrace.hpp>
22#include <jau/cpp_lang_util.hpp>
23#include <jau/debug.hpp>
24#include <jau/int_types.hpp>
25#include <jau/io/io_util.hpp>
27#include <jau/string_util.hpp>
28
30
31 using namespace jau::math;
32 using namespace jau::math::geom;
33 using namespace gamp::graph;
34
35 /** \addtogroup Gamp_GraphImpl
36 *
37 * @{
38 */
39
40 /**
41 * Constrained Delaunay Triangulation
42 * implementation of a list of Outlines that define a set of
43 * Closed Regions with optional n holes.
44 */
45 class CDTriangulator2D { // : public Triangulator {
46 private:
48
49 bool complexShape;
50 size_t m_addedVerticeCount;
51 int32_t m_maxTriID;
52
53 public:
54 /** Constructor for a new Delaunay triangulator
55 */
56 constexpr CDTriangulator2D() noexcept
57 : complexShape(false) { reset(); }
58
59 constexpr void setComplexShape(bool complex) noexcept { complexShape = complex; }
60
61 constexpr void reset() noexcept {
62 m_maxTriID = 0;
63 m_addedVerticeCount = 0;
64 loops.clear();
65 }
66
67 constexpr size_t getAddedVerticeCount() const noexcept { return m_addedVerticeCount; }
68
70
71 void addCurve(TriangleRefList& sink, Outline& polyline, float sharpness) {
72 impl::LoopRef loop = getContainerLoop(polyline);
73
74 if (!loop) {
75 // HEdge.BOUNDARY -> Winding.CCW
76 if constexpr (!FixedWindingRule) {
77 const Winding winding = polyline.getWinding();
78 if( Winding::CCW != winding ) {
79 WARN_PRINT("CDT2.add.xx.BOUNDARY: !CCW but %s", to_string(winding).c_str());
80 // polyline.print(System.err);
81 polyline.setWinding(Winding::CCW); // FIXME: Too late?
82 }
83 }
85 impl::GraphOutlineRef innerPoly = extractBoundaryTriangles(sink, outline, false /* hole */, sharpness);
86 // vertices.addAll(polyline.getVertices());
87 if (innerPoly->graphPoints().size() >= 3) {
88 loop = impl::Loop::createBoundary(innerPoly, complexShape);
89 if( loop ) {
90 loops.push_back(loop);
91 }
92 } else if (Graph::DEBUG_MODE) {
93 /*
94 * Font FreeMono-Bold: ID 0 + 465: Glyph[id 465 'uni020F', advance 600, leftSideBearings 42, kerning[size 0, horiz true, cross true], shape true], OutlineShape@5e8a459[outlines 2, vertices 34]
95 Drop innerPoly ctrlpts < 3
96 - innerPo[vertices 2, ctrlpts 2] < 3
97 - outline[vertices 4, ctrlpts 4]
98 - Input[vertices 4]
99 *
100 * Font FreeSans-Regular: ID 0 + 409: Glyph[id 409 'Udieresiscaron', advance 720, leftSideBearings 80, kerning[size 0, horiz true, cross false], shape true], OutlineShape@5eb97ced[outlines 3, vertices 33]
101 Drop innerPoly ctrlpts < 3
102 - innerPo[vertices 1, ctrlpts 1] < 3
103 - outline[vertices 1, ctrlpts 1]
104 - Input[vertices 1]
105
106 * Stack:
107 at jogamp.graph.curve.tess.CDTriangulator2D.addCurve(CDTriangulator2D.java:97)
108 at com.jogamp.graph.curve.OutlineShape.triangulateImpl(OutlineShape.java:988)
109 at com.jogamp.graph.curve.OutlineShape.getTriangles(OutlineShape.java:1012)
110 at com.jogamp.graph.curve.Region.countOutlineShape(Region.java:503)
111 at com.jogamp.graph.ui.shapes.GlyphShape.<init>(GlyphShape.java:77)
112 */
113 jau::PLAIN_PRINT(true, "Drop innerPoly ctrlpts < 3");
114 // jau::PLAIN_PRINT(true, "- innerPo[vertices "+innerPoly.getOutline().getVertexCount()+", ctrlpts "+innerPoly.getGraphPoint().size()+"] < 3");
115 // jau::PLAIN_PRINT(true, "- outline[vertices "+outline.getOutline().getVertexCount()+", ctrlpts "+outline.getGraphPoint().size()+"]");
116 // jau::PLAIN_PRINT(true, "- Input[vertices "+polyline.getVertexCount()+"]");
117 jau::print_backtrace(true, 4);
118 }
119 } else {
120 // HEdge.HOLE -> Winding.CW, but Winding.CCW is also accepted!
121 // Winding.CW not required, handled in Loop.initFromPolyline(): polyline.setWinding(winding);
123 impl::GraphOutlineRef innerPoly = extractBoundaryTriangles(sink, outline, true /* hole */, sharpness);
124 // vertices.addAll(innerPoly.getVertices());
125 loop->addConstraintCurveHole(innerPoly);
126 }
127 }
128
130 size_t loopsSize = loops.size();
131 size_t size;
132
133 for (size_t i = 0; i < loopsSize; i++) {
134 impl::LoopRef &loop = loops[i];
135 size_t numTries = 0;
136 size = loop->computeLoopSize();
137
138 while (!loop->isSimplex()) {
139 TriangleRef tri;
140 bool delaunay;
141 if (numTries > size) {
142 tri = loop->cut(false);
143 delaunay = false;
144 } else {
145 tri = loop->cut(true);
146 delaunay = true;
147 }
148 numTries++;
149
150 if (tri) {
151 tri->id() = m_maxTriID++;
152 sink.push_back(tri);
153 // if (Graph::DEBUG_MODE ) {
154 // jau::PLAIN_PRINT(true, "CDTri.gen["+i+"].0: delaunay "+delaunay+", tries "+numTries+", size "+size+", "+tri); // FIXME
155 // }
156 numTries = 0;
157 size--;
158 }
159 if (numTries > size * 2) {
160 // if (Graph::DEBUG_MODE ) {
161 // jau::PLAIN_PRINT("CDTri.gen["+i+"].X: Triangulation not complete!"); // FIXME
162 // }
163 break;
164 }
165 (void)delaunay;
166 }
167 TriangleRef tri = loop->cut(true);
168 if (tri) {
169 tri->id() = m_maxTriID++;
170 sink.push_back(tri);
171 // if (Graph::DEBUG_MODE ) {
172 // jau::PLAIN_PRINT("CDTri.gen["+i+"].1: size "+size+"/"+loopsSize+", "+tri); // FIXME
173 // }
174 }
175 }
176 }
177
178 private:
179 impl::GraphOutlineRef extractBoundaryTriangles(TriangleRefList& sink, const impl::GraphOutlineRef& outline, bool hole, float sharpness) {
181 const impl::GraphVertexRefList& outVertices = outline->graphPoints();
182 size_t size = outVertices.size();
183
184 for (size_t i = 0; i < size; i++) {
185 const impl::GraphVertexRef &gv1 = outVertices[i]; // currentVertex
186 const impl::GraphVertexRef& gv0 = outVertices[(i + size - 1) % size]; // -1
187 const impl::GraphVertexRef& gv2 = outVertices[(i + 1) % size]; // +1
188
189 if (!gv1->vertex().onCurve()) {
190 Vertex v0 = gv0->vertex().clone(); // deep copy w/ a max id marker
191 Vertex v2 = gv2->vertex().clone();
192 Vertex v1 = gv1->vertex().clone();
193 m_addedVerticeCount += 3;
194 Triangle::tribit_t boundaryVertices;
195 boundaryVertices.put(0, true);
196 boundaryVertices.put(1, true);
197 boundaryVertices.put(2, true);
198
199 gv0->setBoundaryContained(true);
200 gv1->setBoundaryContained(true);
201 gv2->setBoundaryContained(true);
202
203 const bool holeLike = !is2DCCW(v0.coord(), v1.coord(), v2.coord());
204 if (hole || holeLike) {
205 v0.texCoord() = Vec3f(0.0f, -0.1f, 0);
206 v2.texCoord() = Vec3f(1.0f, -0.1f, 0);
207 v1.texCoord() = Vec3f(0.5f, -sharpness -0.1f, 0);
208 innerOutline->addVertex(gv1);
209 } else {
210 v0.texCoord() = Vec3f(0.0f, 0.1f, 0);
211 v2.texCoord() = Vec3f(1.0f, 0.1f, 0);
212 v1.texCoord() = Vec3f(0.5f, sharpness + 0.1f, 0);
213 }
214 TriangleRef t;
215 if (holeLike) {
216 t = Triangle::create(v2, v1, v0, boundaryVertices);
217 } else {
218 t = Triangle::create(v0, v1, v2, boundaryVertices);
219 }
220 t->id() = m_maxTriID++;
221 sink.push_back(t);
222 // if (Graph::DEBUG_MODE ) {
223 // PLAIN_PRINT(true, "CDTri.ebt[%zu].0: hole %d %s, %s", i, (hole || holeLike), gv1->toString().c_str(), t->toString().c_str());
224 // }
225 } else {
226 if( !gv2->vertex().onCurve() || !gv0->vertex().onCurve() ) {
227 gv1->setBoundaryContained(true);
228 }
229 innerOutline->addVertex(gv1);
230 // if (Graph::DEBUG_MODE ) {
231 // PLAIN_PRINT(true, "CDTri.ebt[%zu].1: %s", i, gv1->toString().c_str());
232 // }
233 }
234 }
235 return innerOutline;
236 }
237
238 impl::LoopRef getContainerLoop(const Outline& polyline) const noexcept {
239 size_t count = loops.size();
240 if (0 < count) {
241 const VertexList &vertices = polyline.vertices();
242 for (const impl::LoopRef &loop : loops) {
243 for (size_t j = 0; j < vertices.size(); ++j) { // NOLINT
244 if (loop->checkInside(vertices[j])) {
245 return loop;
246 }
247 }
248 }
249 }
250 return nullptr;
251 }
252 };
253
254 /**@}*/
255
256} // namespace gamp::graph::tess::impl
257
258#endif /* JAU_GAMP_GRAPH_TESS_IMPL_CDTesselator2D_HPP_ */
static bool DEBUG_MODE
Definition Graph.hpp:24
Define a single continuous stroke by control vertices.
Definition Outline.hpp:51
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
static TriangleRef create(const Vertex &v1, const Vertex &v2, const Vertex &v3, const tribit_t &boundaryVertices) noexcept
jau::bitfield_t< uint8_t, 3 > tribit_t
constexpr Vertex clone() const noexcept
Definition PrimTypes.hpp:56
constexpr const Vec3f & texCoord() const noexcept
Definition PrimTypes.hpp:96
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
void addCurve(TriangleRefList &sink, Outline &polyline, float sharpness)
constexpr CDTriangulator2D() noexcept
Constructor for a new Delaunay triangulator.
constexpr void setComplexShape(bool complex) noexcept
constexpr size_t getAddedVerticeCount() const noexcept
static constexpr bool FixedWindingRule
Definition Loop.hpp:48
static LoopRef createBoundary(const GraphOutlineRef &polyline, bool isConvex)
Definition Loop.hpp:279
constexpr bool put(size_type bitpos, bool v) noexcept
Writes the bit value v to position bitpos into this storage.
Definition bitfield.hpp:123
constexpr size_type size() const noexcept
Like std::vector::size().
Definition darray.hpp:1115
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition darray.hpp:1568
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
Definition debug.hpp:134
std::shared_ptr< Loop > LoopRef
Definition Loop.hpp:43
std::shared_ptr< GraphOutline > GraphOutlineRef
std::vector< GraphVertexRef > GraphVertexRefList
Definition HEdge.hpp:39
std::shared_ptr< GraphVertex > GraphVertexRef
Definition HEdge.hpp:38
std::vector< LoopRef > LoopRefList
Definition Loop.hpp:44
jau::darray< Vertex, uint32_t > VertexList
std::shared_ptr< Triangle > TriangleRef
jau::darray< TriangleRef, uint32_t > TriangleRefList
constexpr bool is2DCCW(const Vec3f &a, const Vec3f &b, const Vec3f &c) noexcept
Check if points are in ccw order.
Definition geom3f2D.hpp:133
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.
@ CCW
Counter-Clockwise.
Definition geom.hpp:39
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:264
void print_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames=-1, const jau::snsize_t skip_frames=2) noexcept
Prints the de-mangled backtrace string separated by newline excluding this function to stderr,...
Definition debug.cpp:173