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