Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
Outline.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_OUTLINE_HPP_
12#define JAU_GAMP_GRAPH_OUTLINE_HPP_
13
14#include <limits>
15
16#include <jau/basic_types.hpp>
17#include <jau/darray.hpp>
18#include <jau/int_math.hpp>
19#include <jau/int_types.hpp>
20#include <jau/math/vec3f.hpp>
25
27
28namespace gamp::graph {
29
30 using namespace jau::enums;
31 using namespace jau::math;
33 using jau::math::geom::AABBox3f;
34 using jau::math::geom::plane::AffineTransform;
35
36 /** \addtogroup Gamp_Graph
37 *
38 * @{
39 */
40
41 /** Define a single continuous stroke by control vertices.
42 * The vertices define the shape of the region defined by this
43 * outline. The Outline can contain a list of off-curve and on-curve
44 * vertices which define curved regions.
45 *
46 * Note: An outline should be closed to be rendered as a region.
47 *
48 * @see OutlineShape
49 * @see Region
50 */
51 class Outline {
52 private:
53 static constexpr Vertex zeroVec = Vertex();
54
55 enum class DirtyBits : uint16_t {
56 none = 0,
57 bounds = 1 << 0,
58 winding = 1 << 1,
59 complexShape = 1 << 2
60 };
61 JAU_MAKE_BITFIELD_ENUM_STRING_MEMBER(DirtyBits, bounds, winding, complexShape);
62
63 VertexList m_vertices;
64 bool m_closed;
65 mutable AABBox3f m_bbox;
66 mutable Winding m_winding;
67 mutable DirtyBits m_dirtyBits;
68 mutable bool m_complexShape;
69
70 public:
71 typedef uint32_t size_type;
72 /// byte-size uint32_t limit: 1'073'741'823 (FIXME: Adjust to actual type, i.e. Vertex = 2x Vec3f?)
73 constexpr static size_type max_elements = std::numeric_limits<uint32_t>::max() / sizeof(uint32_t);
74
75 constexpr Outline() : Outline(3) {}
76
77 constexpr Outline(size_type vertCapacity)
78 : m_vertices(vertCapacity),
79 m_closed(false), m_bbox(),
80 m_winding(Winding::CCW),
81 m_dirtyBits(DirtyBits::none),
82 m_complexShape(false)
83 {}
84
85 constexpr void reserve(size_type newVertCapacity) { m_vertices.reserve(newVertCapacity); }
86
87 constexpr bool empty() const noexcept { return m_vertices.empty(); }
88
89 constexpr size_type vertexCount() const noexcept { return m_vertices.size(); }
90
91 constexpr const VertexList& vertices() const noexcept { return m_vertices; }
92 constexpr VertexList& vertices() noexcept { return m_vertices; }
93
94 constexpr const Vertex& vertex(size_type i) const noexcept { return m_vertices[i]; }
95 constexpr Vertex& vertex(size_type i) noexcept { return m_vertices[i]; }
96
97 /**
98 * Removes the {@link Vertex} element at the given {@code position}.
99 *
100 * Sets the bounding box dirty, hence a next call to bounds() will validate it.
101 *
102 * @param position of the to be removed Vertex
103 */
104 void removeVertex(size_type position) {
105 if( position < m_vertices.size() ) {
106 m_dirtyBits |= DirtyBits::bounds | DirtyBits::winding | DirtyBits::complexShape;
107 m_vertices.erase(position);
108 }
109 }
110
111 bool isEmpty() const noexcept {
112 return m_vertices.size() == 0;
113 }
114
115 const Vertex& lastVertex() const noexcept {
116 if( empty() ) {
117 return zeroVec;
118 }
119 return m_vertices[m_vertices.size()-1];
120 }
121
122 constexpr bool isClosed() const noexcept { return m_closed; }
123
124 /**
125 * Ensure this outline is closed.
126 * <p>
127 * Checks whether the last vertex equals to the first.
128 * If not equal, it either appends a copy of the first vertex
129 * or prepends a copy of the last vertex, depending on <code>closeTail</code>.
130 * </p>
131 * @param closeTail if true, a copy of the first vertex will be appended,
132 * otherwise a copy of the last vertex will be prepended.
133 * @return true if closing performed, otherwise false for NOP
134 */
135 bool setClosed(bool closeTail) {
136 m_closed = true;
137 if( !empty() ) {
138 const Vertex& first = vertex(0);
139 const Vertex& last = lastVertex();
140 if( first.coord() != last.coord() ) {
141 if( closeTail ) {
142 m_vertices.push_back(first);
143 } else {
144 m_vertices.insert(m_vertices.begin(), last);
145 }
146 return true;
147 }
148 }
149 return false;
150 }
151
152 /**
153 * Returns the cached or computed winding of this {@link Outline}s {@code polyline} using {@link VectorUtil#area(ArrayList)}.
154 * <p>
155 * The result is cached.
156 * </p>
157 * @return {@link Winding#CCW} or {@link Winding#CW}
158 */
159 Winding getWinding() const noexcept {
160 if( !is_set(m_dirtyBits, DirtyBits::winding) ) {
161 return m_winding;
162 }
163 size_type count = m_vertices.size();
164 if( 3 > count ) {
165 m_winding = Winding::CCW;
166 } else {
167 m_winding = computeWinding();
168 }
169 m_dirtyBits &= ~DirtyBits::winding;
170 return m_winding;
171 }
172
173 private:
174 Winding computeWinding() const noexcept;
175
176 public:
177 /**
178 * Sets Winding to this outline
179 *
180 * If the enforced Winding doesn't match this Outline, the vertices are reversed.
181 *
182 * @param enforce to be enforced {@link Winding}
183 */
184 void setWinding(Winding enforce) {
185 Winding had_winding = getWinding();
186 if( enforce != had_winding ) {
187 size_type count = m_vertices.size();
188 VertexList ccw(count);
189 for(size_type i=count; i-- > 0; ) {
190 ccw.push_back(m_vertices[i]);
191 }
192 m_vertices = ccw;
193 m_winding = enforce;
194 m_dirtyBits &= ~DirtyBits::winding;
195 }
196 }
197
198 /**
199 * Returns cached or computed result if whether this {@link Outline}s {@code polyline} is a complex shape.
200 * <p>
201 * A polyline with less than 3 elements is marked a simple shape for simplicity.
202 * </p>
203 * <p>
204 * The result is cached.
205 * </p>
206 */
207 bool isComplex() const noexcept {
208 if( !is_set(m_dirtyBits, DirtyBits::complexShape) ) {
209 return m_complexShape;
210 }
211 m_complexShape = !computeIsComplex();
212 // complexShape = isSelfIntersecting1(m_vertices);
213 m_dirtyBits &= ~DirtyBits::complexShape;
214 return m_complexShape;
215 }
216
217 private:
218 bool computeIsComplex() const noexcept;
219
220 void validateBoundingBox() const noexcept {
221 m_dirtyBits &= ~DirtyBits::bounds;
222 m_bbox.reset();
223 for(const Vertex& v : m_vertices) {
224 m_bbox.resize(v.coord());
225 }
226 }
227
228 public:
229 const AABBox3f& bounds() const noexcept {
230 if ( is_set(m_dirtyBits, DirtyBits::bounds) ) {
231 validateBoundingBox();
232 }
233 return m_bbox;
234 }
235
236 /**
237 * Appends a vertex to the outline loop/strip.
238 * @param vertex Vertex to be added
239 * @throws NullPointerException if the {@link Vertex} element is null
240 */
241 void addVertex(const Vertex& vertex) {
242 if( m_vertices.size() == max_elements ) {
243 throw jau::RuntimeException("Max elements "+std::to_string(max_elements)+" reached", E_FILE_LINE);
244 }
245 m_vertices.push_back(vertex);
246 if ( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
247 m_bbox.resize(vertex.coord());
248 }
249 m_dirtyBits |= DirtyBits::winding | DirtyBits::complexShape;
250 }
251
252 /**
253 * Insert the {@link Vertex} element at the given {@code position} to the outline loop/strip.
254 * @param position of the added Vertex
255 * @param vertex Vertex object to be added
256 * @throws NullPointerException if the {@link Vertex} element is null
257 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getVertexNumber())
258 */
259 void addVertex(size_type position, const Vertex& vertex) {
260 if( m_vertices.size() == max_elements ) {
261 throw jau::RuntimeException("Max elements "+std::to_string(max_elements)+" reached", E_FILE_LINE);
262 }
263 m_vertices.insert(position, vertex);
264 if ( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
265 m_bbox.resize(vertex.coord());
266 }
267 m_dirtyBits |= DirtyBits::winding | DirtyBits::complexShape;
268 }
269
270 /// Returns a transformed copy of this instance using the given AffineTransform.
272 Outline newOutline;
273 for(const Vertex& v : m_vertices) {
274 newOutline.addVertex(v.transform(t));
275 }
276 newOutline.m_closed = m_closed;
277 return newOutline;
278 }
279
280 /**
281 * Compare two outline's Bounding Box size.
282 * @return <0, 0, >0 if this this object is less than, equal to, or greater than the other object.
283 * @see AABBox3f::size()
284 */
285 int compareTo(const Outline& other) const noexcept {
286 const float thisSize = bounds().size();
287 const float otherSize = other.bounds().size();
288 if( jau::equals2(thisSize, otherSize) ) {
289 return 0;
290 } else if(thisSize < otherSize){
291 return -1;
292 } else {
293 return 1;
294 }
295 }
296
297 /**
298 * @return true if {@code o} equals bounds and vertices in the same order
299 */
300 constexpr bool operator==(const Outline& o) const noexcept {
301 if( this == &o) {
302 return true;
303 }
304 if(vertexCount() != o.vertexCount()) {
305 return false;
306 }
307 if( bounds() != o.bounds() ) {
308 return false;
309 }
310 for (size_type i=vertexCount(); i-- > 0;) {
311 if( vertex(i) != o.vertex(i) ) {
312 return false;
313 }
314 }
315 return true;
316 }
317
318 };
319 // typedef std::shared_ptr<Outline> OutlineRef;
321
322
323 /**@}*/
324
325} // namespace gamp::graph
326
327#endif /* JAU_GAMP_GRAPH_OUTLINE_HPP_ */
#define E_FILE_LINE
bool setClosed(bool closeTail)
Ensure this outline is closed.
Definition Outline.hpp:135
constexpr void reserve(size_type newVertCapacity)
Definition Outline.hpp:85
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
bool isEmpty() const noexcept
Definition Outline.hpp:111
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
constexpr bool isClosed() const noexcept
Definition Outline.hpp:122
constexpr Outline()
Definition Outline.hpp:75
constexpr bool operator==(const Outline &o) const noexcept
Definition Outline.hpp:300
constexpr const Vertex & vertex(size_type i) const noexcept
Definition Outline.hpp:94
constexpr size_type vertexCount() const noexcept
Definition Outline.hpp:89
void setWinding(Winding enforce)
Sets Winding to this outline.
Definition Outline.hpp:184
constexpr Vertex & vertex(size_type i) noexcept
Definition Outline.hpp:95
constexpr Outline(size_type vertCapacity)
Definition Outline.hpp:77
Outline transform(const AffineTransform &t) const
Returns a transformed copy of this instance using the given AffineTransform.
Definition Outline.hpp:271
constexpr VertexList & vertices() noexcept
Definition Outline.hpp:92
int compareTo(const Outline &other) const noexcept
Compare two outline's Bounding Box size.
Definition Outline.hpp:285
Winding getWinding() const noexcept
Returns the cached or computed winding of this Outlines polyline using VectorUtil#area(ArrayList).
Definition Outline.hpp:159
const AABBox3f & bounds() const noexcept
Definition Outline.hpp:229
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
void addVertex(size_type position, const Vertex &vertex)
Insert the Vertex element at the given position to the outline loop/strip.
Definition Outline.hpp:259
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?...
Definition Outline.hpp:73
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
Implementation of a dynamic linear array storage, aka vector, including relative positional access.
Definition darray.hpp:154
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition darray.hpp:1568
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).
#define JAU_MAKE_BITFIELD_ENUM_STRING_MEMBER(type,...)
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
@ CCW
Counter-Clockwise.
Definition geom.hpp:31