NDEVR
API Documentation
ConcaveToConvexPolygon.h
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>
16 struct SliceVertex : public t_vertex_type
17 {
22
27 SliceVertex(t_vertex_type const& _position)
28 : t_vertex_type{ _position } {}
29
30 int index;
32 };
33
40 template<class t_type, class t_vertex = Vertex<2, t_type>>
42 {
43 typedef Polygon<t_type, t_vertex> VertexArray;
44 typedef Buffer<Polygon<t_type, t_vertex>> PolygonArray;
46 VertexArray vertices;
47 PolygonArray processed_polygons;
48
49
58 static inline fltp08 Det(fltp08 a, fltp08 b, fltp08 c, fltp08 d)
59 {
60 return a * d - b * c;
61 }
62
70 static inline int orientation(const Vertex<2, t_type>& p, const Vertex<2, t_type>& q, const Vertex<2, t_type>& r)
71 {
72 fltp08 val = (q[Y] - p[Y]) * (r[X] - q[X]) - (q[X] - p[X]) * (r[Y] - q[Y]);
73
74 if (abs(val) < 1E-10)
75 return 0; // collinear
76
77 return (val > 0) ? 1 : 2; // clock or counterclock wise
78 }
79
87 static inline bool onSegment(const Vertex<2, t_type>& p, const Vertex<2, t_type>& q, const Vertex<2, t_type>& r)
88 {
89 if (q[X] <= getMax(p[X], r[X]) && q[X] >= getMin(p[X], r[X]) &&
90 q[Y] <= getMax(p[Y], r[Y]) && q[Y] >= getMin(p[Y], r[Y]))
91 return true;
92
93 return false;
94 }
95
102 static inline bool intersects(const Line& s1, const Line& s2)
103 {
104 int o1 = orientation(s1.vertex(A), s1.vertex(B), s2.vertex(A));
105 int o2 = orientation(s1.vertex(A), s1.vertex(B), s2.vertex(B));
106 int o3 = orientation(s2.vertex(A), s2.vertex(B), s1.vertex(A));
107 int o4 = orientation(s2.vertex(A), s2.vertex(B), s1.vertex(B));
108
109 // General case
110 if (o1 != o2 && o3 != o4)
111 return true;
112
113 // Special Cases
114 if (o1 == 0 && onSegment(s1.vertex(A), s2.vertex(A), s1.vertex(B))) return true;
115
116 // p1, q1 and q2 are collinear and q2 lies on segment p1q1
117 if (o2 == 0 && onSegment(s1.vertex(A), s2.vertex(B), s1.vertex(B))) return true;
118
119 // p2, q2 and p1 are collinear and p1 lies on segment p2q2
120 if (o3 == 0 && onSegment(s2.vertex(A), s1.vertex(A), s2.vertex(B))) return true;
121
122 // p2, q2 and q1 are collinear and q1 lies on segment p2q2
123 if (o4 == 0 && onSegment(s2.vertex(A), s1.vertex(B), s2.vertex(B))) return true;
124
125 return false; // Doesn't fall in any of the above cases
126
127 /*const t_type TOLERANCE = 1e-10;
128
129 Vertex<2, t_type> p1 = s1.vertex(A);
130 Vertex<2, t_type> p2 = s2.vertex(A);
131 Vertex<2, t_type> d1 = s1.ray();
132 Vertex<2, t_type> d2 = s2.ray();
133
134 if (abs(cross(d1, d2)) < 1e-30)
135 {
136 return false;
137 }
138 t_type t1 = cross(p2 - p1, d2) / cross(d1, d2);
139
140 if ((t1 < (0.0f - TOLERANCE)) || (t1 > (1.0f + TOLERANCE)))
141 {
142 return false;
143 }
144 Vertex<2, fltp08> intersection_vertex = p1 + d1 * t1;
145
146 t_type t2 = dot(intersection_vertex - p2, s2.vertex(B) - p2);
147
148 if (t2 < (0.0f - TOLERANCE) || t2 / distanceSquared(s2.vertex(B).as<2, fltp08>(), p2) >= 1.0f - TOLERANCE)
149 {
150 return false;
151 }
152 return true;*/
153
154 // Find the four orientations needed for general and
155 }
156
163 static uint04 mod(int x, int m)
164 {
165 if (x < 0)
166 return m + x;
167 else
168 return x % m;
169 }
170
178 static t_type CalcHandedness(const t_vertex& v1, const t_vertex& v2, const t_vertex& v3)
179 {
180 t_vertex edge1 = v2 - v1;
181 t_vertex edge2 = v3 - v2;
182
183 return cross(edge1, edge2);
184 }
185
190 void flipPolygon(VertexArray& _verts)
191 {
192 _verts.flip();
193 }
194
200 static bool checkIfRightHanded(const VertexArray& _verts)
201 {
202 if (_verts.vertexCount() <= 2)
203 return false;
204 return !_verts.hasClockwiseWinding();
205 }
206
207
208
217 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)
218 {
219 Vector<2, t_type> relativePos = vert - origin;
220
221 t_type ls1Product = cross(relativePos, ls1);
222 t_type ls2Product = cross(relativePos, ls2);
223
224 if (ls1Product < t_type(0) && ls2Product > t_type(0))
225 return true;
226
227 return false;
228 }
229
230 typedef Buffer<uint04> IntArray;
231
240 static IntArray findVerticesInCone(const Vector<2, t_type>& ls1, const Vector<2, t_type>& ls2, const Vector<2, t_type>& origin, VertexArray const& inputVerts)
241 {
242 IntArray result;
243 for (uint04 i = 0; i < inputVerts.vertexCount(); ++i)
244 {
245 if (isVertexInCone(ls1, ls2, origin, inputVerts.vertex(i).template as<2, t_type>()))
246 result.add(i);
247 }
248 return result;
249 }
250
258 static bool checkVisibility(const VertexArray& vertices, uint04 origin, uint04 vertex_to_check)
259 {
260 lib_assert(origin != vertex_to_check, "Bad visibility check");
261 Line ls(vertices.vertex(vertex_to_check).template as<2, t_type>(), vertices.vertex(origin).template as<2, t_type>());
262
263 const uint04 vertex_count = vertices.vertexCount();
264 for (uint04 i = 0; i < vertex_count; ++i)
265 {
266 if (origin == i || ((i + 1) % vertex_count) == origin)
267 continue;
268 if (vertex_to_check == i || ((i + 1) % vertex_count) == vertex_to_check)
269 continue;
270 Line l = vertices.edge(i).template as<2, t_type>();
271 if (intersects(ls, l))
272 return false;
273 }
274 auto center = ls.center();
275 if (!vertices.contains(center))
276 return false;
277 //lib_assert(result.size() >= 1, "Bad intersection");
278 return true;
279 }
280
287 static uint04 IsReflexPoint(uint04 index, const VertexArray& vertices)
288 {
289 const t_vertex& prevVert = vertices.vertex(mod(cast<sint04>(index - 1), vertices.vertexCount()));
290 const t_vertex& currVert = vertices.vertex(index);
291 const t_vertex& nextVert = vertices.vertex(mod(index + 1, vertices.vertexCount()));
292 return CalcHandedness(prevVert, currVert, nextVert) < 0.0f;
293
294 }
295
307 static uint04 getBestVertexToConnect(const IntArray& indices, const VertexArray& polygonVertices, const Vector<2, t_type>& origin, uint04 origin_index)
308 {
309 uint04 vert_size = polygonVertices.vertexCount();
310
311 t_type min_distance = Constant<t_type>::Max;
312 uint04 min = Constant<uint04>::Invalid;
313
314 if (indices.size() == 1)
315 {
316 if (checkVisibility(polygonVertices, origin_index, indices[0]))
317 return indices[0];
318 }
319 else if (indices.size() > 1)
320 {
321 //first try all reflex points to potentially eliminate 2 at once
322 for (uint04 i = 0; i < indices.size(); ++i)
323 {
324 if (indices[i] < origin_index)
325 continue;//can't be reflex point
326 if (!IsReflexPoint(indices[i], polygonVertices))
327 continue;
328 if (checkVisibility(polygonVertices, origin_index, indices[i]))
329 {
330 return indices[i];
331 }
332 else
333 {
334 t_type currDistance = distanceSquared(polygonVertices.vertex(i).template as<2, t_type>(), origin);
335 if (currDistance * 1000 < min_distance)
336 {
337 min_distance = currDistance;
338 min = indices[i];
339 }
340 }
341 }
342 for (uint04 i = 0; i < indices.size(); ++i)
343 {
344 if (indices[i] > origin_index && IsReflexPoint(indices[i], polygonVertices))
345 continue;//we already know these aren't visible
346 if (checkVisibility(polygonVertices, origin_index, indices[i]))
347 {
348 return indices[i];
349 }
350 else
351 {
352 t_type currDistance = distanceSquared(polygonVertices.vertex(i).template as<2, t_type>(), origin);
353 if (currDistance * 1000 < min_distance)
354 {
355 min_distance = currDistance;
356 min = indices[i];
357 }
358 }
359 }
360 }
361 uint04 neighbor_a = mod(cast<sint04>(origin_index) - 1, vert_size);
362 uint04 neighbor_b = mod(cast<sint04>(origin_index) + 1, vert_size);
363
364 for (uint04 i = origin_index + 2; i < polygonVertices.vertexCount(); ++i)
365 {
366 if (i == neighbor_a)
367 continue;
368 if (indices.contains(i))
369 continue;
370 if (IsReflexPoint(i, polygonVertices) && checkVisibility(polygonVertices, origin_index, i))
371 return i;
372 }
373
374 for (uint04 i = 0; i < polygonVertices.vertexCount(); ++i)
375 {
376 if (i == origin_index || i == neighbor_a || i == neighbor_b)
377 continue;
378 if (i > origin_index && IsReflexPoint(i, polygonVertices))
379 continue;
380 t_type currDistance = distanceSquared(polygonVertices.vertex(i).template as<2, t_type>(), origin);
381 if (currDistance < min_distance)
382 {
383 if (indices.contains(i))
384 continue;
385 if (checkVisibility(polygonVertices, origin_index, i))
386 {
387 min_distance = currDistance;
388 min = i;
389 }
390 else if(currDistance * 1000 < min_distance)
391 {
392 min_distance = currDistance;
393 min = i;
394 }
395 }
396
397 }
398 //lib_assert(IsValid(max), "Bad max");
399 return min;
400 }
401
402
403
409 static uint04 findFirstReflexVertex(const VertexArray& _vertices)
410 {
411 lib_assert(checkIfRightHanded(_vertices), "Should be right handed");
412 for (uint04 i = 0; i < _vertices.vertexCount(); ++i)
413 {
414 if (IsReflexPoint(i, _vertices))
415 return i;
416 }
417
418 return Constant<uint04>::Invalid;
419 }
420
424 void flipPolygon()
425 {
426 flipPolygon(vertices);
427 }
428
429 typedef Dictionary<uint04, Vertex<2, fltp08>> VertexIntMap;
430 typedef std::pair<int, Vertex<2, fltp08>> VertexIntPair;
431
432
433 public:
438 ConcavePolygon(const VertexArray& _vertices)
439 : vertices{ _vertices }
440 {
441 if (vertices.vertexCount() >= 3)
442 if (checkIfRightHanded() == false)
443 flipPolygon();
444 }
445
450
456 {
457 return checkIfRightHanded(vertices);
458 }
459
468 {
470 for (uint04 i = 0; i < vertices.vertexCount(); i++)
471 {
472 bounds.addToBounds(vertices.vertex(i).template as<2, t_type>());
473 }
474 Buffer<VertexArray> sub_polygons_to_process;
475 Buffer<VertexArray> next_sub_polygons_to_process = { vertices };
476 std::mutex mtx;
477 while (next_sub_polygons_to_process.size() > 0)
478 {
479 sub_polygons_to_process.clear();
480 for (uint04 i = 0; i < next_sub_polygons_to_process.size(); i++)
481 {
482 if (next_sub_polygons_to_process[i].vertexCount() > 3)
483 sub_polygons_to_process.add(next_sub_polygons_to_process[i]);
484 else if(next_sub_polygons_to_process[i].vertexCount() == 3)
485 processed_polygons.add(next_sub_polygons_to_process[i]);
486 }
487 next_sub_polygons_to_process.clear();
488 next_sub_polygons_to_process.setSize(2 * sub_polygons_to_process.size());
489 int size = sub_polygons_to_process.size();
490 _parallel_for(int i = 0; i < size; i++)
491 {
492 std::unique_lock<std::mutex> lck(mtx, std::defer_lock);
493 const VertexArray& _vertices = sub_polygons_to_process[cast<uint04>(i)];
494 lib_assert(checkIfRightHanded(_vertices), "undexpected handiness");
495 uint04 reflexIndex = findFirstReflexVertex(_vertices);
496 if (IsInvalid(reflexIndex))
497 {
498 lib_assert(_vertices.isConvex(), "Should be convex");
499 continue;
500 }
501 Vertex<2, t_type> prevVertPos = _vertices.vertex(mod(cast<sint04>(reflexIndex) - 1, _vertices.vertexCount())).template as<2, t_type>();
502 Vertex<2, t_type> currVertPos = _vertices.vertex(reflexIndex).template as<2, t_type>();
503 Vertex<2, t_type> nextVertPos = _vertices.vertex(mod(reflexIndex + 1, _vertices.vertexCount())).template as<2, t_type>();
504
505 Vector<2, t_type> prev_ray = (currVertPos - prevVertPos);
506 Vector<2, t_type> next_ray = (currVertPos - nextVertPos);
507
508 IntArray vertsInCone = findVerticesInCone(prev_ray, next_ray, currVertPos, _vertices);
509
510 uint04 bestVert = getBestVertexToConnect(vertsInCone, _vertices, currVertPos, reflexIndex);
511 if (IsInvalid(bestVert))
512 continue;
513 lib_assert(IsValid(bestVert), "Not sure why this is true");
514 uint04 start = getMin(bestVert, reflexIndex);
515 uint04 end = getMax(bestVert, reflexIndex);
516 next_sub_polygons_to_process[2 * i + 0].addVertices(_vertices.begin(), start + 1);
517 next_sub_polygons_to_process[2 * i + 1].addVertices(_vertices.begin() + start, (end + 1) - start);
518 next_sub_polygons_to_process[2 * i + 0].addVertices(_vertices.begin() + end, _vertices.vertexCount() - end);
519 sub_polygons_to_process[cast<uint04>(i)].clear();//indicate we are finished processing
520
521 //add to polygons
522 lib_assert(next_sub_polygons_to_process[2 * i + 0].vertexCount() > 2, "Bad a size");
523 lib_assert(next_sub_polygons_to_process[2 * i + 1].vertexCount() > 2, "Bad b size");
524 lib_assert(checkIfRightHanded(next_sub_polygons_to_process[2 * i + 0]), "Vertex A not right");
525 lib_assert(checkIfRightHanded(next_sub_polygons_to_process[2 * i + 1]), "Vertex B not right");
526 }
527 //gather all polygons that are convex
528 for (uint04 i = 0; i < sub_polygons_to_process.size(); i++)
529 {
530 if (sub_polygons_to_process[i].vertexCount() > 3)
531 {
532 lib_assert(sub_polygons_to_process[i].isConvex(), "Should be convex");
533 processed_polygons.add(sub_polygons_to_process[i]);
534 }
535 }
536 }
537 }
538
543 const VertexArray& getVertices() const
544 {
545 return vertices;
546 }
547
548
554 {
555 returnArr.addAll(processed_polygons);
556 }
557
563 t_vertex getPoint(uint04 index) const
564 {
565 if (index >= 0 && index < vertices.size())
566 return vertices[index];
567 lib_assert(false, "Bad vertex get");
568 return t_vertex(0.0f);
569 }
570
575 int getPointCount() const
576 {
577 return vertices.vertexCount();
578 }
579 };
580
581}
A specification of upper and lower bounds in N-dimensions.
Definition Bounds.hpp:54
constexpr void addToBounds(const t_vertex &vector)
Definition Bounds.hpp:415
The equivelent of std::vector but with a bit more control.
Definition Buffer.hpp:58
void add(t_type &&object)
Adds object to the end of the buffer.
Definition Buffer.hpp:190
ConcavePolygon()
Default constructor.
void returnLowestLevelPolys(Buffer< Polygon< t_type, t_vertex > > &returnArr)
Appends all decomposed convex sub-polygons to the given buffer.
void convexDecomp()
Decomposes this concave polygon into convex sub-polygons.
int getPointCount() const
Returns the number of vertices in the polygon.
t_vertex getPoint(uint04 index) const
Retrieves a vertex by index.
ConcavePolygon(const VertexArray &_vertices)
Constructs a ConcavePolygon from a vertex array, ensuring right-handed winding.
const VertexArray & getVertices() const
Returns a const reference to the polygon's vertex array.
bool checkIfRightHanded()
Checks whether this polygon's vertices are in right-handed (counter-clockwise) winding order.
A hash-based key-value store, useful for quick associative lookups.
Definition Dictionary.h:64
Class: LineSegment.
Definition Line.hpp:52
constexpr const t_vertex & vertex(uint01 index) const
Definition Line.hpp:155
constexpr t_vertex center() const
Definition Line.hpp:138
An N-sided polygon.
Definition Polygon.hpp:55
const t_vertex & vertex(uint04 index) const
Definition Polygon.hpp:158
uint04 vertexCount() const
Definition Polygon.hpp:206
A fixed-size array with N dimensions used as the basis for geometric and mathematical types.
Definition Vector.hpp:62
A point in N-dimensional space, used primarily for spatial location information.
Definition Vertex.hpp:44
The primary namespace for the NDEVR SDK.
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...
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 > ...
static constexpr bool IsValid(const Angle< t_type > &value)
Checks whether the given Angle holds a valid value.
Definition Angle.h:398
constexpr Angle< t_angle_type > abs(const Angle< t_angle_type > &value)
Changes an input with a negative sign, to a positive sign.
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
double fltp08
Defines an alias representing an 8 byte floating-point number.
constexpr Vector< 1, t_type > cross(const Vector< 1, t_type > &, const Vector< 1, t_type > &)
static constexpr bool IsInvalid(const Angle< t_type > &value)
Checks whether the given Angle holds an invalid value.
Definition Angle.h:388
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408
Defines for a given type (such as sint04, fltp08, UUID, etc) a maximum, minimum, and reserved 'invali...
SliceVertex(t_vertex_type const &_position)
Constructs a SliceVertex from a vertex position.
t_type distanceToSlice
The distance from this vertex to the slice line.
SliceVertex()
Default constructor.
int index
The index of this vertex within the polygon.