API Documentation
Loading...
Searching...
No Matches
ConcaveToConvexPolygon.h
Go to the documentation of this file.
1#pragma once
2#include <NDEVR/Polygon.h>
3#include <NDEVR/Dictionary.h>
4#include <NDEVR/OpenMP.h>
5#include <vector>
6#include <cmath>
7#include <map>
8#include <iostream>
9namespace NDEVR
10{
11 template<class t_type, class t_vertex_type>
12 /**--------------------------------------------------------------------------------------------------
13 \brief Stores an index to a particular vertex used with ConcaveToConvexPolygon.
14 The Slice index is used with ConcavePolygon to break it into convex polygons or triangles.
15 **/
16 struct SliceVertex : public t_vertex_type
17 {
19 SliceVertex(t_vertex_type const& _position)
20 : t_vertex_type{ _position } {}
21
22 int index;
24 };
25
26 /**--------------------------------------------------------------------------------------------------
27 \brief Stores convenience functions for a concavec polygon
28 Works on a Polygon object to decompose it into triangles or sub-polygons.
29 **/
30 template<class t_type, class t_vertex = Vertex<2, t_type>>
32 {
36 VertexArray vertices;
37 PolygonArray processed_polygons;
38
39
40 static inline fltp08 Det(fltp08 a, fltp08 b, fltp08 c, fltp08 d)
41 {
42 return a * d - b * c;
43 }
44 static inline int orientation(const Vertex<2, t_type>& p, const Vertex<2, t_type>& q, const Vertex<2, t_type>& r)
45 {
46 fltp08 val = (q[Y] - p[Y]) * (r[X] - q[X]) - (q[X] - p[X]) * (r[Y] - q[Y]);
47
48 if (abs(val) < 1E-10)
49 return 0; // collinear
50
51 return (val > 0) ? 1 : 2; // clock or counterclock wise
52 }
53 static inline bool onSegment(const Vertex<2, t_type>& p, const Vertex<2, t_type>& q, const Vertex<2, t_type>& r)
54 {
55 if (q[X] <= getMax(p[X], r[X]) && q[X] >= getMin(p[X], r[X]) &&
56 q[Y] <= getMax(p[Y], r[Y]) && q[Y] >= getMin(p[Y], r[Y]))
57 return true;
58
59 return false;
60 }
61 static inline bool intersects(const Line& s1, const Line& s2)
62 {
63 int o1 = orientation(s1.vertex(A), s1.vertex(B), s2.vertex(A));
64 int o2 = orientation(s1.vertex(A), s1.vertex(B), s2.vertex(B));
65 int o3 = orientation(s2.vertex(A), s2.vertex(B), s1.vertex(A));
66 int o4 = orientation(s2.vertex(A), s2.vertex(B), s1.vertex(B));
67
68 // General case
69 if (o1 != o2 && o3 != o4)
70 return true;
71
72 // Special Cases
73 if (o1 == 0 && onSegment(s1.vertex(A), s2.vertex(A), s1.vertex(B))) return true;
74
75 // p1, q1 and q2 are collinear and q2 lies on segment p1q1
76 if (o2 == 0 && onSegment(s1.vertex(A), s2.vertex(B), s1.vertex(B))) return true;
77
78 // p2, q2 and p1 are collinear and p1 lies on segment p2q2
79 if (o3 == 0 && onSegment(s2.vertex(A), s1.vertex(A), s2.vertex(B))) return true;
80
81 // p2, q2 and q1 are collinear and q1 lies on segment p2q2
82 if (o4 == 0 && onSegment(s2.vertex(A), s1.vertex(B), s2.vertex(B))) return true;
83
84 return false; // Doesn't fall in any of the above cases
85
86 /*const t_type TOLERANCE = 1e-10;
87
88 Vertex<2, t_type> p1 = s1.vertex(A);
89 Vertex<2, t_type> p2 = s2.vertex(A);
90 Vertex<2, t_type> d1 = s1.ray();
91 Vertex<2, t_type> d2 = s2.ray();
92
93 if (abs(cross(d1, d2)) < 1e-30)
94 {
95 return false;
96 }
97 t_type t1 = cross(p2 - p1, d2) / cross(d1, d2);
98
99 if ((t1 < (0.0f - TOLERANCE)) || (t1 > (1.0f + TOLERANCE)))
100 {
101 return false;
102 }
103 Vertex<2, fltp08> intersection_vertex = p1 + d1 * t1;
104
105 t_type t2 = dot(intersection_vertex - p2, s2.vertex(B) - p2);
106
107 if (t2 < (0.0f - TOLERANCE) || t2 / distanceSquared(s2.vertex(B).as<2, fltp08>(), p2) >= 1.0f - TOLERANCE)
108 {
109 return false;
110 }
111 return true;*/
112
113 // Find the four orientations needed for general and
114 }
115 static uint04 mod(int x, int m)
116 {
117 if (x < 0)
118 return m + x;
119 else
120 return x % m;
121 }
122 static t_type CalcHandedness(const t_vertex& v1, const t_vertex& v2, const t_vertex& v3)
123 {
124 t_vertex edge1 = v2 - v1;
125 t_vertex edge2 = v3 - v2;
126
127 return cross(edge1, edge2);
128 }
129 void flipPolygon(VertexArray& _verts)
130 {
131 _verts.flip();
132 }
133
134 static bool checkIfRightHanded(const VertexArray& _verts)
135 {
136 if (_verts.vertexCount() <= 2)
137 return false;
138 return !_verts.hasClockwiseWinding();
139 }
140
141
142
143 static bool isVertexInCone(const Vector<2, t_type>& ls1, const Vector<2, t_type>& ls2, const Vector<2, t_type>& origin, const Vector<2, t_type>& vert)
144 {
145 Vector<2, t_type> relativePos = vert - origin;
146
147 t_type ls1Product = cross(relativePos, ls1);
148 t_type ls2Product = cross(relativePos, ls2);
149
150 if (ls1Product < t_type(0) && ls2Product > t_type(0))
151 return true;
152
153 return false;
154 }
155
156 typedef Buffer<uint04> IntArray;
157
158 static IntArray findVerticesInCone(const Vector<2, t_type>& ls1, const Vector<2, t_type>& ls2, const Vector<2, t_type>& origin, VertexArray const& inputVerts)
159 {
160 IntArray result;
161 for (uint04 i = 0; i < inputVerts.vertexCount(); ++i)
162 {
163 if (isVertexInCone(ls1, ls2, origin, inputVerts.vertex(i).template as<2, t_type>()))
164 result.add(i);
165 }
166 return result;
167 }
168
169 static bool checkVisibility(const VertexArray& vertices, uint04 origin, uint04 vertex_to_check)
170 {
171 lib_assert(origin != vertex_to_check, "Bad visibility check");
172 Line ls(vertices.vertex(vertex_to_check).template as<2, t_type>(), vertices.vertex(origin).template as<2, t_type>());
173
174 const uint04 vertex_count = vertices.vertexCount();
175 for (uint04 i = 0; i < vertex_count; ++i)
176 {
177 if (origin == i || ((i + 1) % vertex_count) == origin)
178 continue;
179 if (vertex_to_check == i || ((i + 1) % vertex_count) == vertex_to_check)
180 continue;
181 Line l = vertices.edge(i).template as<2, t_type>();
182 if (intersects(ls, l))
183 return false;
184 }
185 auto center = ls.center();
186 if (!vertices.contains(center))
187 return false;
188 //lib_assert(result.size() >= 1, "Bad intersection");
189 return true;
190 }
191 static uint04 IsReflexPoint(uint04 index, const VertexArray& vertices)
192 {
193 const t_vertex& prevVert = vertices.vertex(mod(cast<sint04>(index - 1), vertices.vertexCount()));
194 const t_vertex& currVert = vertices.vertex(index);
195 const t_vertex& nextVert = vertices.vertex(mod(index + 1, vertices.vertexCount()));
196 return CalcHandedness(prevVert, currVert, nextVert) < 0.0f;
197
198 }
199
200 static uint04 getBestVertexToConnect(const IntArray& indices, const VertexArray& polygonVertices, const Vector<2, t_type>& origin, uint04 origin_index)
201 {
202 uint04 vert_size = polygonVertices.vertexCount();
203
204 t_type min_distance = Constant<t_type>::Max;
206
207 if (indices.size() == 1)
208 {
209 if (checkVisibility(polygonVertices, origin_index, indices[0]))
210 return indices[0];
211 }
212 else if (indices.size() > 1)
213 {
214 //first try all reflex points to potentially eliminate 2 at once
215 for (uint04 i = 0; i < indices.size(); ++i)
216 {
217 if (indices[i] < origin_index)
218 continue;//can't be reflex point
219 if (!IsReflexPoint(indices[i], polygonVertices))
220 continue;
221 if (checkVisibility(polygonVertices, origin_index, indices[i]))
222 {
223 return indices[i];
224 }
225 else
226 {
227 t_type currDistance = distanceSquared(polygonVertices.vertex(i).template as<2, t_type>(), origin);
228 if (currDistance * 1000 < min_distance)
229 {
230 min_distance = currDistance;
231 min = indices[i];
232 }
233 }
234 }
235 for (uint04 i = 0; i < indices.size(); ++i)
236 {
237 if (indices[i] > origin_index && IsReflexPoint(indices[i], polygonVertices))
238 continue;//we already know these aren't visible
239 if (checkVisibility(polygonVertices, origin_index, indices[i]))
240 {
241 return indices[i];
242 }
243 else
244 {
245 t_type currDistance = distanceSquared(polygonVertices.vertex(i).template as<2, t_type>(), origin);
246 if (currDistance * 1000 < min_distance)
247 {
248 min_distance = currDistance;
249 min = indices[i];
250 }
251 }
252 }
253 }
254 uint04 neighbor_a = mod(cast<sint04>(origin_index) - 1, vert_size);
255 uint04 neighbor_b = mod(cast<sint04>(origin_index) + 1, vert_size);
256
257 for (uint04 i = origin_index + 2; i < polygonVertices.vertexCount(); ++i)
258 {
259 if (i == neighbor_a)
260 continue;
261 if (indices.contains(i))
262 continue;
263 if (IsReflexPoint(i, polygonVertices) && checkVisibility(polygonVertices, origin_index, i))
264 return i;
265 }
266
267 for (uint04 i = 0; i < polygonVertices.vertexCount(); ++i)
268 {
269 if (i == origin_index || i == neighbor_a || i == neighbor_b)
270 continue;
271 if (i > origin_index && IsReflexPoint(i, polygonVertices))
272 continue;
273 t_type currDistance = distanceSquared(polygonVertices.vertex(i).template as<2, t_type>(), origin);
274 if (currDistance < min_distance)
275 {
276 if (indices.contains(i))
277 continue;
278 if (checkVisibility(polygonVertices, origin_index, i))
279 {
280 min_distance = currDistance;
281 min = i;
282 }
283 else if(currDistance * 1000 < min_distance)
284 {
285 min_distance = currDistance;
286 min = i;
287 }
288 }
289
290 }
291 //lib_assert(!IsInvalid(max), "Bad max");
292 return min;
293 }
294
295
296
297 static uint04 findFirstReflexVertex(const VertexArray& _vertices)
298 {
299 lib_assert(checkIfRightHanded(_vertices), "Should be right handed");
300 for (uint04 i = 0; i < _vertices.vertexCount(); ++i)
301 {
302 if (IsReflexPoint(i, _vertices))
303 return i;
304 }
305
307 }
308
309 void flipPolygon()
310 {
311 flipPolygon(vertices);
312 }
313
315 typedef std::pair<int, Vertex<2, fltp08>> VertexIntPair;
316
317
318 public:
319 ConcavePolygon(const VertexArray& _vertices)
320 : vertices{ _vertices }
321 {
322 if (vertices.vertexCount() >= 3)
323 if (checkIfRightHanded() == false)
324 flipPolygon();
325 }
327
329 {
330 return checkIfRightHanded(vertices);
331 }
333 {
335 for (uint04 i = 0; i < vertices.vertexCount(); i++)
336 {
337 bounds.addToBounds(vertices.vertex(i).template as<2, t_type>());
338 }
339 Buffer<VertexArray> sub_polygons_to_process;
340 Buffer<VertexArray> next_sub_polygons_to_process = { vertices };
341 std::mutex mtx;
342 while (next_sub_polygons_to_process.size() > 0)
343 {
344 sub_polygons_to_process.clear();
345 for (uint04 i = 0; i < next_sub_polygons_to_process.size(); i++)
346 {
347 if (next_sub_polygons_to_process[i].vertexCount() > 3)
348 sub_polygons_to_process.add(next_sub_polygons_to_process[i]);
349 else if(next_sub_polygons_to_process[i].vertexCount() == 3)
350 processed_polygons.add(next_sub_polygons_to_process[i]);
351 }
352 next_sub_polygons_to_process.clear();
353 next_sub_polygons_to_process.setSize(2 * sub_polygons_to_process.size());
354 int size = sub_polygons_to_process.size();
355 _parallel_for(int i = 0; i < size; i++)
356 {
357 std::unique_lock<std::mutex> lck(mtx, std::defer_lock);
358 const VertexArray& _vertices = sub_polygons_to_process[cast<uint04>(i)];
359 lib_assert(checkIfRightHanded(_vertices), "undexpected handiness");
360 uint04 reflexIndex = findFirstReflexVertex(_vertices);
361 if (IsInvalid(reflexIndex))
362 {
363 lib_assert(_vertices.isConvex(), "Should be convex");
364 continue;
365 }
366 Vertex<2, t_type> prevVertPos = _vertices.vertex(mod(cast<sint04>(reflexIndex) - 1, _vertices.vertexCount())).template as<2, t_type>();
367 Vertex<2, t_type> currVertPos = _vertices.vertex(reflexIndex).template as<2, t_type>();
368 Vertex<2, t_type> nextVertPos = _vertices.vertex(mod(reflexIndex + 1, _vertices.vertexCount())).template as<2, t_type>();
369
370 Vector<2, t_type> prev_ray = (currVertPos - prevVertPos);
371 Vector<2, t_type> next_ray = (currVertPos - nextVertPos);
372
373 IntArray vertsInCone = findVerticesInCone(prev_ray, next_ray, currVertPos, _vertices);
374
375 uint04 bestVert = getBestVertexToConnect(vertsInCone, _vertices, currVertPos, reflexIndex);
376 if (IsInvalid(bestVert))
377 continue;
378 lib_assert(!IsInvalid(bestVert), "Not sure why this is true");
379 uint04 start = getMin(bestVert, reflexIndex);
380 uint04 end = getMax(bestVert, reflexIndex);
381 next_sub_polygons_to_process[2 * i + 0].addVertices(_vertices.begin(), start + 1);
382 next_sub_polygons_to_process[2 * i + 1].addVertices(_vertices.begin() + start, (end + 1) - start);
383 next_sub_polygons_to_process[2 * i + 0].addVertices(_vertices.begin() + end, _vertices.vertexCount() - end);
384 sub_polygons_to_process[cast<uint04>(i)].clear();//indicate we are finished processing
385
386 //add to polygons
387 lib_assert(next_sub_polygons_to_process[2 * i + 0].vertexCount() > 2, "Bad a size");
388 lib_assert(next_sub_polygons_to_process[2 * i + 1].vertexCount() > 2, "Bad b size");
389 lib_assert(checkIfRightHanded(next_sub_polygons_to_process[2 * i + 0]), "Vertex A not right");
390 lib_assert(checkIfRightHanded(next_sub_polygons_to_process[2 * i + 1]), "Vertex B not right");
391 }
392 //gather all polygons that are convex
393 for (uint04 i = 0; i < sub_polygons_to_process.size(); i++)
394 {
395 if (sub_polygons_to_process[i].vertexCount() > 3)
396 {
397 lib_assert(sub_polygons_to_process[i].isConvex(), "Should be convex");
398 processed_polygons.add(sub_polygons_to_process[i]);
399 }
400 }
401 }
402 }
403
405 {
406 return vertices;
407 }
408
409
411 {
412 returnArr.addAll(processed_polygons);
413 }
414
415 t_vertex getPoint(uint04 index) const
416 {
417 if (index >= 0 && index < vertices.size())
418 return vertices[index];
419 lib_assert(false, "Bad vertex get");
420 return t_vertex(0.0f);
421 }
422
423 int getPointCount() const
424 {
425 return vertices.vertexCount();
426 }
427 };
428
429}
#define lib_assert(expression, message)
Definition LibAssert.h:61
#define _parallel_for
Definition OpenMP.hpp:17
A specification of upper and lower bounds in N-dimensions.
Definition Bounds.hpp:52
constexpr void addToBounds(const t_vertex &vector)
Definition Bounds.hpp:390
The equivelent of std::vector but with a bit more control. The basic array unit of the library.
Definition Buffer.hpp:56
void add(t_type &&object)
Adds object to the end of the buffer.
Definition Buffer.hpp:186
bool contains(const t_type &element) const
Definition Buffer.hpp:465
constexpr t_index_type size() const
Definition Buffer.hpp:823
void setSize(t_index_type new_size)
Definition Buffer.hpp:803
void clear()
Definition Buffer.hpp:422
Stores convenience functions for a concavec polygon Works on a Polygon object to decompose it into tr...
Definition ConcaveToConvexPolygon.h:32
void returnLowestLevelPolys(Buffer< Polygon< t_type, t_vertex >, uint04, ObjectAllocator< false > > &returnArr)
Definition ConcaveToConvexPolygon.h:410
t_vertex getPoint(uint04 index) const
Definition ConcaveToConvexPolygon.h:415
bool checkIfRightHanded()
Definition ConcaveToConvexPolygon.h:328
void convexDecomp()
Definition ConcaveToConvexPolygon.h:332
int getPointCount() const
Definition ConcaveToConvexPolygon.h:423
ConcavePolygon()
Definition ConcaveToConvexPolygon.h:326
const VertexArray & getVertices() const
Definition ConcaveToConvexPolygon.h:404
ConcavePolygon(const VertexArray &_vertices)
Definition ConcaveToConvexPolygon.h:319
A hash-based key-value store, useful for quick associative lookups. Key features include:
Definition Dictionary.h:61
A line segment represented by two vertices, a start and end.
Definition Line.hpp:49
constexpr const t_vertex & vertex(uint01 index) const
Definition Line.hpp:152
constexpr t_vertex center() const
Definition Line.hpp:135
Definition MemoryManager.h:261
An N-sided polygon.
Definition Polygon.hpp:53
LineSegment< 2, t_type, t_vertex > edge(uint04 index) const
Definition Polygon.hpp:174
void flip()
Definition Polygon.hpp:374
bool contains(const Vertex< 2, t_type > &vector) const
Definition Polygon.hpp:478
uint04 vertexCount() const
Definition Polygon.hpp:204
bool hasClockwiseWinding() const
Definition Polygon.hpp:694
decltype(auto) begin()
Definition Polygon.hpp:563
bool isConvex() const
Definition Polygon.hpp:223
const t_vertex & vertex(uint04 index) const
Definition Polygon.hpp:156
A fixed-size array with better performance compared to dynamic containers.
Definition Vector.hpp:60
A vertex or point. A specific type of Vector used primarily for spacial location information.
Definition Vertex.hpp:48
Definition ACIColor.h:37
constexpr bool IsInvalid(const t_type &value)
Query if 'value' is valid or invalid. Invalid values should return invalid if used for calculations o...
Definition BaseFunctions.hpp:170
constexpr t_type getMax(const t_type &left, const t_type &right)
Finds the max of the given arguments using the > operator The only requirement is that t_type have > ...
Definition BaseFunctions.hpp:94
t_type distanceSquared(const Bounds< t_dims, t_type, t_vertex > &bounds, const Vector< t_dims, t_type > &vertex)
Definition Distance.hpp:46
constexpr Vector< 1, t_type > cross(const Vector< 1, t_type > &, const Vector< 1, t_type > &)
Definition VectorFunctions.hpp:898
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
Definition BaseValues.hpp:96
constexpr t_to cast(const Angle< t_from > &value)
Definition Angle.h:375
constexpr Angle< t_angle_type > abs(const Angle< t_angle_type > &value)
Changes an input with a negative sign, to a positive sign.
Definition AngleFunctions.h:645
@ B
Definition BaseValues.hpp:170
@ A
Definition BaseValues.hpp:168
@ Y
Definition BaseValues.hpp:169
@ X
Definition BaseValues.hpp:167
double fltp08
Defines an alias representing an 8 byte floating-point number.
Definition BaseValues.hpp:149
constexpr t_type getMin(const t_type &left, const t_type &right)
Finds the minimum of the given arguments based on the < operator Author: Tyler Parke Date: 2017-11-05...
Definition BaseFunctions.hpp:56
Defines for a given type (such as sint04, fltp08, UUID, etc) a maximum, minimum, and reserved 'invali...
Definition BaseValues.hpp:233
Stores an index to a particular vertex used with ConcaveToConvexPolygon. The Slice index is used with...
Definition ConcaveToConvexPolygon.h:17
SliceVertex()
Definition ConcaveToConvexPolygon.h:18
int index
Definition ConcaveToConvexPolygon.h:22
t_type distanceToSlice
Definition ConcaveToConvexPolygon.h:23
SliceVertex(t_vertex_type const &_position)
Definition ConcaveToConvexPolygon.h:19