Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
gamp_graph.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
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
12#include <ctime>
13#include <jau/debug.hpp>
14#include <jau/environment.hpp>
15#include <jau/string_util.hpp>
16#include <jau/io/file_util.hpp>
17
19
20#include <gamp/graph/Graph.hpp>
23
26
27using namespace jau::enums;
28using namespace jau::math;
30// using jau::math::geom::AABBox3f;
31// using jau::math::geom::plane::AffineTransform;
32
33using namespace gamp::graph;
34
35//
36//
37//
38
39Winding Outline::computeWinding() const noexcept {
40 return gamp::graph::impl::getWinding( m_vertices );
41}
42
43bool Outline::computeIsComplex() const noexcept {
44 return gamp::graph::impl::isConvex1(m_vertices, true);
45}
46
47//
48// OutlineShape prost-processing
49//
50
51static struct SizeDescending {
52 bool operator()(Outline& a, Outline& b) const {
53 return a.compareTo(b) >= 1;
54 }
56
57void OutlineShape::sortOutlines() {
58 std::sort(m_outlines.begin(), m_outlines.end(), sizeDescending); // NOLINT(modernize-use-ranges)
59}
60
61uint32_t OutlineShape::generateVertexIds() {
62 uint32_t maxVertexId = 0;
63 for(size_type i=0; i<m_outlines.size(); ++i) {
65 for(auto & vertice : vertices) {
66 vertice.id() = maxVertexId++;
67 }
68 }
69 return maxVertexId;
70}
71
72void OutlineShape::subdivideTriangle(Outline& outline, const Vertex& a, Vertex& b, const Vertex& c, size_type index) {
73 Vec3f v1 = midpoint( a.coord(), b.coord() );
74 Vec3f v3 = midpoint( b.coord(), c.coord() );
75 Vec3f v2 = midpoint( v1, v3 );
76
77 // COLOR
78 // tmpC1.set(a.getColor()).add(b.getColor()).scale(0.5f);
79 // tmpC3.set(b.getColor()).add(b.getColor()).scale(0.5f);
80 // tmpC2.set(tmpC1).add(tmpC1).scale(0.5f);
81
82 //drop off-curve vertex to image on the curve
83 b.coord() = v2;
84 b.onCurve() = true;
85
86 outline.addVertex(index, Vertex(v1, false));
87 outline.addVertex(index+2, Vertex(v3, false));
88
89 m_addedVertexCount += 2;
90}
91
92Vertex* OutlineShape::checkTriOverlaps0(const Vertex& a, const Vertex& b, const Vertex& c) {
93 size_type count = outlineCount();
94 for (size_type cc = 0; cc < count; ++cc) {
95 Outline& ol = outline(cc);
97 for(size_type i=0; i < vertexCount; ++i) {
98 Vertex& currV = ol.vertex(i);
99 if( !currV.onCurve() && currV != a && currV != b && currV != c) {
100 Vertex& nextV = ol.vertex((i+1)%vertexCount);
101 Vertex& prevV = ol.vertex((i+vertexCount-1)%vertexCount);
102
103 //skip neighboring triangles
104 if(prevV != c && nextV != a) {
105 if( isInTriangle3(a.coord(), b.coord(), c.coord(),
106 currV.coord(), nextV.coord(), prevV.coord()) ) {
107 return &currV;
108 }
109 if(gamp::graph::impl::testTri2SegIntersection2D(a, b, c, prevV, currV) ||
110 gamp::graph::impl::testTri2SegIntersection2D(a, b, c, currV, nextV) ||
111 gamp::graph::impl::testTri2SegIntersection2D(a, b, c, prevV, nextV) ) {
112 return &currV;
113 }
114 }
115 }
116 }
117 }
118 return nullptr;
119}
120
121void OutlineShape::checkOverlaps() {
122 VertexList overlaps(3);
123 const size_type count = outlineCount();
124 bool firstpass = true;
125 do {
126 for (size_type cc = 0; cc < count; ++cc) {
127 Outline& ol = outline(cc);
129 for(size_type i=0; i < ol.vertexCount(); ++i) {
130 Vertex& currentVertex = ol.vertex(i);
131 if ( !currentVertex.onCurve()) {
132 const Vertex& nextV = ol.vertex((i+1)%vertexCount);
133 const Vertex& prevV = ol.vertex((i+vertexCount-1)%vertexCount);
134 Vertex* overlap;
135
136 // check for overlap even if already set for subdivision
137 // ensuring both triangular overlaps get divided
138 // for pref. only check in first pass
139 // second pass to clear the overlaps array(reduces precision errors)
140 if( firstpass ) {
141 overlap = checkTriOverlaps0(prevV, currentVertex, nextV);
142 } else {
143 overlap = nullptr;
144 }
145 if( overlap || jau::contains(overlaps, currentVertex) ) {
146 jau::eraseFirst(overlaps, currentVertex);
147
148 subdivideTriangle(ol, prevV, currentVertex, nextV, i);
149 i+=3;
150 vertexCount+=2;
151 m_addedVertexCount+=2;
152
153 if(overlap && !overlap->onCurve()) {
154 if(!jau::contains(overlaps, *overlap)) {
155 overlaps.push_back(*overlap);
156 }
157 }
158 }
159 }
160 }
161 }
162 firstpass = false;
163 } while( !overlaps.empty() );
164}
165
166void OutlineShape::cleanupOutlines() {
167 const bool transformOutlines2Quadratic = VertexState::quadratic_nurbs != m_outlineState;
168 size_type count = outlineCount();
169 for (size_type cc = 0; cc < count; ++cc) {
170 Outline& ol = outline(cc);
172
173 if( transformOutlines2Quadratic ) {
174 for(size_type i=0; i < vertexCount; ++i) {
175 Vertex& currentVertex = ol.vertex(i);
176 size_type j = (i+1)%vertexCount;
177 Vertex& nextVertex = ol.vertex(j);
178 if ( !currentVertex.onCurve() && !nextVertex.onCurve() ) {
179 Vec3f mp = midpoint(currentVertex.coord(), nextVertex.coord());
180 // System.err.println("XXX: Cubic: "+i+": "+currentVertex+", "+j+": "+nextVertex);
181 Vertex v(mp, true);
182 // COLOR: tmpC1.set(currentVertex.getColor()).add(nextVertex.getColor()).scale(0.5f)
183 ++i;
184 ++vertexCount;
185 ++m_addedVertexCount;
186 ol.addVertex(i, v);
187 }
188 }
189 }
190 if( 0 == vertexCount ) {
191 jau::eraseFirst(m_outlines, ol); // empty
192 --cc;
193 --count;
194 } else if( 0 < vertexCount &&
195 ol.vertex(0).coord() == ol.lastVertex().coord() )
196 {
197 ol.removeVertex(vertexCount-1); // closing vertex
198 }
199 }
200 m_outlineState = VertexState::quadratic_nurbs;
201 checkOverlaps();
202}
203
204void OutlineShape::triangulateImpl() {
205 if( 0 < m_outlines.size() ) {
206 sortOutlines();
207 generateVertexIds();
208
209 m_triangles.clear();
210 tess::impl::CDTriangulator2D triangulator2d;
211 triangulator2d.setComplexShape( isComplex() );
212 for(Outline& ol : m_outlines) {
213 triangulator2d.addCurve(m_triangles, ol, m_sharpness);
214 }
215 triangulator2d.generate(m_triangles);
216 m_addedVertexCount += triangulator2d.getAddedVerticeCount();
217 triangulator2d.reset();
218 }
219}
220
222 bool updated = false;
223 if(destinationType != VertexState::quadratic_nurbs) {
224 throw jau::IllegalStateError("destinationType "+to_string(destinationType)+" not supported (currently "+to_string(m_outlineState)+")", E_FILE_LINE);
225 }
226 if( is_set(m_dirtyBits, DirtyBits::triangles ) ) {
227 cleanupOutlines();
228 triangulateImpl();
229 updated = true;
230 m_dirtyBits |= DirtyBits::vertices;
231 m_dirtyBits &= ~DirtyBits::triangles;
232 } else {
233 updated = false;
234 }
236 jau::PLAIN_PRINT(true, "OutlineShape.getTriangles().X: %u, updated %d", m_triangles.size(), updated);
237 if( updated ) {
238 size_type i=0;
239 for(TriangleRef& t : m_triangles) {
240 jau::PLAIN_PRINT(false, "- [%u]: %s", i++, t->toString().c_str());
241 }
242 }
243 }
244 return m_triangles;
245}
246
247//
248//
249//
250
252 bool updated = false;
253 if( is_set(m_dirtyBits, DirtyBits::vertices ) ) {
254 m_vertices.clear();
255 for(const Outline& ol : m_outlines) {
256 const VertexList& v = ol.vertices();
257 m_vertices.push_back(v.begin(), v.end());
258 }
259 m_dirtyBits &= ~DirtyBits::vertices;
260 updated = true;
261 }
263 jau::PLAIN_PRINT(true, "OutlineShape.getVertices().X: %u, updated %d", m_vertices.size(), updated);
264 if( updated ) {
265 size_type i=0;
266 for(Vertex& v : m_vertices) {
267 jau::PLAIN_PRINT(false, "- [%u]: %s", i++, v.toString().c_str());
268 }
269 }
270 }
271 return m_vertices;
272}
273
274//
275//
276//
#define E_FILE_LINE
static bool DEBUG_MODE
Definition Graph.hpp:24
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.
bool isComplex() const noexcept
Returns cached or computed result if at least one polyline outline(size_type) is a complex shape,...
@ triangles
<Modified shape, requires to update the vertices and triangles, here: vertices
size_type vertexCount() const noexcept
Returns the total vertex number of all Outlines.
const Outline & outline(size_type i) const noexcept
const TriangleRefList & getTriangles(VertexState destinationType=VertexState::quadratic_nurbs)
Triangulate the OutlineShape generating a list of triangles, while transformOutlines(VerticesState) b...
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
constexpr const VertexList & vertices() const noexcept
Definition Outline.hpp:91
constexpr const Vertex & vertex(size_type i) const noexcept
Definition Outline.hpp:94
constexpr size_type vertexCount() const noexcept
Definition Outline.hpp:89
int compareTo(const Outline &other) const noexcept
Compare two outline's Bounding Box size.
Definition Outline.hpp:285
void removeVertex(size_type position)
Removes the Vertex element at the given position.
Definition Outline.hpp:104
const Vertex & lastVertex() const noexcept
Definition Outline.hpp:115
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
constexpr bool onCurve() const noexcept
Definition PrimTypes.hpp:99
void addCurve(TriangleRefList &sink, Outline &polyline, float sharpness)
constexpr void setComplexShape(bool complex) noexcept
constexpr size_t getAddedVerticeCount() const noexcept
constexpr iterator end() noexcept
Definition darray.hpp:833
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition darray.hpp:1568
constexpr iterator begin() noexcept
Definition darray.hpp:829
static struct SizeDescending sizeDescending
constexpr bool contains(InputArray &array, const T &value) noexcept
Return true if value is contained in array.
constexpr bool eraseFirst(InputArray &array, const T &value)
constexpr bool is_set(const E mask, const E bits) noexcept
static constexpr bool isConvex1(const VertexList &polyline, bool shortIsConvex) noexcept
Returns whether the given on-curve polyline points denotes a convex shape with O(n) complexity.
constexpr bool testTri2SegIntersection2D(const Vertex &a, const Vertex &b, const Vertex &c, const Vertex &d, const Vertex &e)
Check if a segment intersects with a triangle using FloatUtil#EPSILON w/o considering collinear-case.
constexpr Winding getWinding(const VertexList &vertices) noexcept
Compute the winding using the area2D() function over all vertices for complex shapes.
jau::darray< Vertex, uint32_t > VertexList
std::shared_ptr< Triangle > TriangleRef
jau::darray< TriangleRef, uint32_t > TriangleRefList
constexpr Vec3f midpoint(const Vec3f &a, const Vec3f &b) noexcept
Definition geom3f.hpp:126
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.
constexpr bool isInTriangle3(const Vec3f &a, const Vec3f &b, const Vec3f &c, const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) noexcept
Check if one of three vertices are in triangle using barycentric coordinates computation.
Definition geom3f.hpp:140
Author: Sven Gothel sgothel@jausoft.com Copyright Gothel Software e.K.
Definition enum_util.hpp:65
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
bool operator()(Outline &a, Outline &b) const