Gamp v0.0.7-36-g24b1eb6
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 <jau/basic_types.hpp>
15#include <jau/darray.hpp>
16#include <jau/int_math.hpp>
17#include <jau/int_types.hpp>
19#include <jau/math/vec3f.hpp>
23
26#include <limits>
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 int32_t limit: 536'870'911 (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 = gamp::graph::getWinding( m_vertices );
168 }
169 m_dirtyBits &= ~DirtyBits::winding;
170 return m_winding;
171 }
172
173 /**
174 * Sets Winding to this outline
175 *
176 * If the enforced Winding doesn't match this Outline, the vertices are reversed.
177 *
178 * @param enforce to be enforced {@link Winding}
179 */
180 void setWinding(Winding enforce) {
181 Winding had_winding = getWinding();
182 if( enforce != had_winding ) {
183 size_type count = m_vertices.size();
184 VertexList ccw(count);
185 for(size_type i=count; i-- > 0; ) {
186 ccw.push_back(m_vertices[i]);
187 }
188 m_vertices = ccw;
189 m_winding = enforce;
190 m_dirtyBits &= ~DirtyBits::winding;
191 }
192 }
193
194 /**
195 * Returns cached or computed result if whether this {@link Outline}s {@code polyline} is a complex shape.
196 * <p>
197 * A polyline with less than 3 elements is marked a simple shape for simplicity.
198 * </p>
199 * <p>
200 * The result is cached.
201 * </p>
202 */
203 bool isComplex() const noexcept {
204 if( !is_set(m_dirtyBits, DirtyBits::complexShape) ) {
205 return m_complexShape;
206 }
207 m_complexShape = !isConvex1(m_vertices, true);
208 // complexShape = isSelfIntersecting1(m_vertices);
209 m_dirtyBits &= ~DirtyBits::complexShape;
210 return m_complexShape;
211 }
212
213 private:
214 void validateBoundingBox() const noexcept {
215 m_dirtyBits &= ~DirtyBits::bounds;
216 m_bbox.reset();
217 for(const Vertex& v : m_vertices) {
218 m_bbox.resize(v.coord());
219 }
220 }
221
222 public:
223 const AABBox3f& bounds() const noexcept {
224 if ( is_set(m_dirtyBits, DirtyBits::bounds) ) {
225 validateBoundingBox();
226 }
227 return m_bbox;
228 }
229
230 /**
231 * Appends a vertex to the outline loop/strip.
232 * @param vertex Vertex to be added
233 * @throws NullPointerException if the {@link Vertex} element is null
234 */
235 void addVertex(const Vertex& vertex) {
236 if( m_vertices.size() == max_elements ) {
237 throw jau::RuntimeException("Max elements "+std::to_string(max_elements)+" reached", E_FILE_LINE);
238 }
239 m_vertices.push_back(vertex);
240 if ( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
241 m_bbox.resize(vertex.coord());
242 }
243 m_dirtyBits |= DirtyBits::winding | DirtyBits::complexShape;
244 }
245
246 /**
247 * Insert the {@link Vertex} element at the given {@code position} to the outline loop/strip.
248 * @param position of the added Vertex
249 * @param vertex Vertex object to be added
250 * @throws NullPointerException if the {@link Vertex} element is null
251 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getVertexNumber())
252 */
253 void addVertex(size_type position, const Vertex& vertex) {
254 if( m_vertices.size() == max_elements ) {
255 throw jau::RuntimeException("Max elements "+std::to_string(max_elements)+" reached", E_FILE_LINE);
256 }
257 m_vertices.insert(position, vertex);
258 if ( !is_set(m_dirtyBits, DirtyBits::bounds) ) {
259 m_bbox.resize(vertex.coord());
260 }
261 m_dirtyBits |= DirtyBits::winding | DirtyBits::complexShape;
262 }
263
264 /// Returns a transformed copy of this instance using the given AffineTransform.
266 Outline newOutline;
267 for(const Vertex& v : m_vertices) {
268 newOutline.addVertex(v.transform(t));
269 }
270 newOutline.m_closed = m_closed;
271 return newOutline;
272 }
273
274 /**
275 * Compare two outline's Bounding Box size.
276 * @return <0, 0, >0 if this this object is less than, equal to, or greater than the other object.
277 * @see AABBox3f::size()
278 */
279 int compareTo(const Outline& other) const noexcept {
280 const float thisSize = bounds().size();
281 const float otherSize = other.bounds().size();
282 if( jau::equals2(thisSize, otherSize) ) {
283 return 0;
284 } else if(thisSize < otherSize){
285 return -1;
286 } else {
287 return 1;
288 }
289 }
290
291 /**
292 * @return true if {@code o} equals bounds and vertices in the same order
293 */
294 constexpr bool operator==(const Outline& o) const noexcept {
295 if( this == &o) {
296 return true;
297 }
298 if(vertexCount() != o.vertexCount()) {
299 return false;
300 }
301 if( bounds() != o.bounds() ) {
302 return false;
303 }
304 for (size_type i=vertexCount(); i-- > 0;) {
305 if( vertex(i) != o.vertex(i) ) {
306 return false;
307 }
308 }
309 return true;
310 }
311
312 };
313 // typedef std::shared_ptr<Outline> OutlineRef;
315
316
317 /**@}*/
318
319} // namespace gamp::graph
320
321#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:235
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:203
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:294
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:180
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:265
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:279
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:223
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:253
static constexpr size_type max_elements
byte-size int32_t limit: 536'870'911 (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:153
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition darray.hpp:1522
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
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr 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< Outline, jau::nsize_t > OutlineList
Definition Outline.hpp:314
jau::darray< Vertex, uint32_t > VertexList
static 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 Winding getWinding(const VertexList &vertices) noexcept
Compute the winding using the area2D() function over all vertices for complex shapes.
@ CCW
Counter-Clockwise.
Definition geom.hpp:31