Gamp v0.0.7-36-g24b1eb6
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
VertexMath.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
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_VERTEXMATH_HPP_
12#define JAU_GAMP_GRAPH_VERTEXMATH_HPP_
13
14#include <sys/types.h>
15#include <cstring>
16#include <jau/darray.hpp>
17#include <jau/bitfield.hpp>
19#include <jau/math/vec3f.hpp>
24
25#include <gamp/GampTypes.hpp>
27#include <limits>
28
29namespace gamp::graph {
30
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 /**
42 * Computes the area of a list of vertices via shoelace formula.
43 *
44 * This method is utilized e.g. to reliably compute the {@link Winding} of complex shapes.
45 *
46 * Implementation uses double precision.
47 *
48 * @param vertices
49 * @return positive area if ccw else negative area value
50 * @see #getWinding()
51 */
52 constexpr double area2D(const VertexList& vertices) noexcept {
53 size_t n = vertices.size();
54 double area = 0.0;
55 for (size_t p = n - 1, q = 0; q < n; p = q++) {
56 const Vec3f& pCoord = vertices[p].coord();
57 const Vec3f& qCoord = vertices[q].coord();
58 area += (double)pCoord.x * (double)qCoord.y - (double)qCoord.x * (double)pCoord.y;
59 }
60 return area;
61 }
62
63 /**
64 * Compute the winding using the area2D() function over all vertices for complex shapes.
65 *
66 * Uses the {@link #area(List)} function over all points
67 * on complex shapes for a reliable result!
68 *
69 * Implementation uses double precision.
70 *
71 * @param vertices array of Vertices
72 * @return Winding::CCW or Winding::CLW
73 * @see area2D()
74 */
75 constexpr Winding getWinding(const VertexList& vertices) noexcept {
76 return area2D(vertices) >= 0 ? Winding::CCW : Winding::CW ;
77 }
78
79 namespace impl {
80 static ssize_t cmod(ssize_t i, ssize_t count) noexcept {
81 if( i >= 0 ) {
82 return i % count;
83 } else {
84 return i + count;
85 }
86 }
87 }
88
89 /**
90 * Returns whether the given on-curve {@code polyline} points denotes a convex shape with O(n) complexity.
91 * <p>
92 * See [Determine whether a polygon is convex based on its vertices](https://math.stackexchange.com/questions/1743995/determine-whether-a-polygon-is-convex-based-on-its-vertices/1745427#1745427)
93 * </p>
94 * <p>
95 * All off-curve points are ignored.
96 * </p>
97 * @param polyline connected {@link Vert2fImmutable}, i.e. a poly-line
98 * @param shortIsConvex return value if {@code vertices} have less than three elements, allows simplification
99 */
100 static bool isConvex1(const VertexList& polyline, bool shortIsConvex) noexcept {
101 const ssize_t polysz = static_cast<ssize_t>(polyline.size());
102 if( polysz < 3 ) {
103 return shortIsConvex;
104 }
105 constexpr float eps = std::numeric_limits<float>::epsilon();
106
107 float wSign = 0; // First nonzero orientation (positive or negative)
108
109 int xSign = 0;
110 int xFirstSign = 0; // Sign of first nonzero edge vector x
111 int xFlips = 0; // Number of sign changes in x
112
113 int ySign = 0;
114 int yFirstSign = 0; // Sign of first nonzero edge vector y
115 int yFlips = 0; // Number of sign changes in y
116
117 ssize_t offset=-3;
118 Vertex v0, v1;
119 {
120 do {
121 ++offset; // -2
122 v0 = polyline[impl::cmod(offset, polysz)]; // current, polyline[-2] if on-curve
123 } while( !v0.onCurve() && offset < polysz );
124 if( offset >= polysz ) {
125 return shortIsConvex;
126 }
127 do {
128 ++offset; // -1
129 v1 = polyline[impl::cmod(offset, polysz)]; // next, polyline[-1] if both on-curve
130 } while( !v1.onCurve() && offset < polysz );
131 if( offset >= polysz ) {
132 return shortIsConvex;
133 }
134 }
135
136 while( offset < polysz ) {
137 Vertex vp = v0; // previous on-curve vertex
138 v0 = v1; // current on-curve vertex
139 do {
140 ++offset; // 0, ...
141 v1 = polyline[impl::cmod(offset, polysz)]; // next on-curve vertex
142 } while( !v1.onCurve() && offset < polysz );
143 if( offset >= polysz ) {
144 break;
145 }
146
147 // Previous edge vector ("before"):
148 const float bx = v0.coord().x - vp.coord().x;
149 const float by = v0.coord().y - vp.coord().y;
150
151 // Next edge vector ("after"):
152 const float ax = v1.coord().x - v0.coord().x;
153 const float ay = v1.coord().y - v0.coord().y;
154
155 // Calculate sign flips using the next edge vector ("after"),
156 // recording the first sign.
157 if( ax > eps ) {
158 if( xSign == 0 ) {
159 xFirstSign = +1;
160 } else if( xSign < 0 ) {
161 xFlips = xFlips + 1;
162 }
163 xSign = +1;
164 } else if( ax < -eps ) {
165 if( xSign == 0 ) {
166 xFirstSign = -1;
167 } else if ( xSign > 0 ) {
168 xFlips = xFlips + 1;
169 }
170 xSign = -1;
171 }
172 if( xFlips > 2 ) {
173 return false;
174 }
175
176 if( ay > eps ) {
177 if( ySign == 0 ) {
178 yFirstSign = +1;
179 } else if( ySign < 0 ) {
180 yFlips = yFlips + 1;
181 }
182 ySign = +1;
183 } else if( ay < -eps ) {
184 if( ySign == 0 ) {
185 yFirstSign = -1;
186 } else if( ySign > 0 ) {
187 yFlips = yFlips + 1;
188 }
189 ySign = -1;
190 }
191 if( yFlips > 2 ) {
192 return false;
193 }
194
195 // Find out the orientation of this pair of edges,
196 // and ensure it does not differ from previous ones.
197 const float w = bx*ay - ax*by;
198 if( jau::is_zero(wSign) && !jau::is_zero(w) ) {
199 wSign = w;
200 } else if( wSign > eps && w < -eps ) {
201 return false;
202 } else if( wSign < -eps && w > eps ) {
203 return false;
204 }
205 }
206
207 // Final/wraparound sign flips:
208 if( xSign != 0 && xFirstSign != 0 && xSign != xFirstSign ) {
209 xFlips = xFlips + 1;
210 }
211 if( ySign != 0 && yFirstSign != 0 && ySign != yFirstSign ) {
212 yFlips = yFlips + 1;
213 }
214
215 // Concave polygons have two sign flips along each axis.
216 if( xFlips != 2 || yFlips != 2 ) {
217 return false;
218 }
219
220 // This is a convex polygon.
221 return true;
222 }
223
224 /**
225 * Check if a segment intersects with a triangle using {@link FloatUtil#EPSILON} w/o considering collinear-case
226 * <p>
227 * Implementation uses float precision.
228 * </p>
229 * @param a vertex 1 of the triangle
230 * @param b vertex 2 of the triangle
231 * @param c vertex 3 of the triangle
232 * @param d vertex 1 of first segment
233 * @param e vertex 2 of first segment
234 * @return true if the segment intersects at least one segment of the triangle, false otherwise
235 * @see #testSeg2SegIntersection(Vert2fImmutable, Vert2fImmutable, Vert2fImmutable, Vert2fImmutable, float, boolean)
236 */
237 constexpr bool testTri2SegIntersection2D(const Vertex& a, const Vertex& b, const Vertex& c,
238 const Vertex& d, const Vertex& e) {
239 using namespace jau::math::geom;
240 return testSeg2SegIntersection2D(a.coord(), b.coord(), d.coord(), e.coord()) ||
241 testSeg2SegIntersection2D(b.coord(), c.coord(), d.coord(), e.coord()) ||
242 testSeg2SegIntersection2D(a.coord(), c.coord(), d.coord(), e.coord()) ;
243 }
244
245 /**@}*/
246
247} // namespace gamp::graph
248
249#endif /* JAU_GAMP_GRAPH_VERTEXMATH_HPP_ */
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
constexpr bool onCurve() const noexcept
Definition PrimTypes.hpp:99
value_type x
Definition vec3f.hpp:69
value_type y
Definition vec3f.hpp:70
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
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.
constexpr bool testSeg2SegIntersection2D(Vec2f *result, const Vec2f &p, const Vec2f &p2, const Vec2f &q, const Vec2f &q2, const bool do_collinear=false) noexcept
See p + t r = q + u s and its terse C# implementation
Definition geom2f.hpp:61
constexpr double area2D(const Vec3fList &vertices) noexcept
Computes the area of a list of vertices via shoelace formula.
Definition geom3f2D.hpp:45
Vector3F< float > Vec3f
Definition vec3f.hpp:436
constexpr bool testTri2SegIntersection2D(const Vec3f &a, const Vec3f &b, const Vec3f &c, const Vec3f &d, const Vec3f &e)
Check if a segment intersects with a triangle using FloatUtil#EPSILON w/o considering collinear-case.
Definition geom3f2D.hpp:220
static ssize_t cmod(ssize_t i, ssize_t count) noexcept