header_utils
Loading...
Searching...
No Matches
polygon.h
1
4
5#pragma once
6
7#include "geometry_common.h"
8#include "./segment.h"
9#include "./triangles.h"
10#include "shape_concepts.h"
11#include <array>
12
13namespace ghassanpl::geometry
14{
19
20 /*
21 enum class polygon_class
22 {
23 simple_convex,
24 simple_concave,
25 complex,
26 };
27 */
28
29 struct polygon_classification
30 {
31 bool simple = false;
32 bool convex = false;
33 winding_order winding{};
34 constexpr bool is_simple() const { return simple; }
35 constexpr bool intersects_itself() const { return !simple; }
36 constexpr bool is_convex() const { return convex; }
37 constexpr bool is_concave() const noexcept { return simple && !convex; }
38 };
39
41 template <std::floating_point T>
42 struct tpolygon
43 {
44 using tvec = glm::tvec2<T>;
45 using value_type = T;
46
47 std::vector<glm::tvec2<T>> vertices;
48
49 template <typename... ARGS>
50 static tpolygon from_vertices(ARGS&&... args) {
51 return tpolygon{ { std::forward<ARGS>(args)... } };
52 }
53
54 template <typename S>
55 requires polygon_shape<T, S>
56 static tpolygon from_shape(S const& shape)
57 {
58 tpolygon result;
59 shape.for_each_vertex([&result](auto const& v) {
60 result.vertices.push_back(v);
61 });
62 return result;
63 }
64
65 tvec& operator[](size_t index) { return vertices.at(index); }
66 tvec const& operator[](size_t index) const { return vertices.at(index); }
67
68 auto cbegin() const { return std::ranges::cbegin(vertices); }
69 auto cend() const { return std::ranges::cend(vertices); }
70 auto begin() { return std::ranges::begin(vertices); }
71 auto end() { return std::ranges::end(vertices); }
72 auto begin() const { return std::ranges::cbegin(vertices); }
73 auto end() const { return std::ranges::cend(vertices); }
74 auto size() const { return std::ranges::size(vertices); }
75
76 bool is_valid() const noexcept { return vertices.size() > 2; }
77
78 /*
79 polygon_classification classify() const noexcept
80 {
81 // Returns 0 if the polygon is complex (has intersecting edges).
82 // Returns +-1 if it is simple and convex. Returns +-2 if it is simple and concave.
83 // The sign of the returned value indicates whether the polygon is CCW (C) or CW (�
84 Int i, ii, j, jj, np, schg = 0, wind = 0;
85 //Initialize sign change and winding number.
86 Doub p0, p1, d0, d1, pp0, pp1, dd0, dd1, t, tp, t1, t2, crs, crsp = 0.0;
87 np = vt.size();
88 p0 = vt[0].x[0] - vt[np - 1].x[0];
89 p1 = vt[0].x[1] - vt[np - 1].x[1];
90 for (i = 0, ii = 1; i < np; i++, ii++) {
91 //Loop over edges.
92 if (ii == np) ii = 0;
93 d0 = vt[ii].x[0] - vt[i].x[0];
94 d1 = vt[ii].x[1] - vt[i].x[1];
95 crs = p0 * d1 - p1 * d0;
96 //Cross product at this vertex.
97 if (crs * crsp < 0) schg = 1;
98 //Sign change(i.e., concavity) found.
99 if (p1 <= 0.0) {
100 //Winding number logic as in polywind.
101 if (d1 > 0.0 && crs > 0.0) wind++;
102 }
103 else {
104 if (d1 <= 0.0 && crs < 0.0) wind--;
105 }
106 p0 = d0;
107 p1 = d1;
108 if (crs != 0.0) crsp = crs;
109 //Save previous cross product only if it has a sign!
110 }
111 if (abs(wind) != 1) return 0;
112 //Can already conclude polygon is complex.
113 if (schg == 0) return (wind > 0 ? 1 : -1); //Polygon is simple and convex.
114 //Drat, we’ve exhausted all the quick tricks and now have to check all pairs of edges for
115 //intersections:
116 for (i = 0, ii = 1; i < np; i++, ii++) {
117 if (ii == np) ii = 0;
118 d0 = vt[ii].x[0];
119 d1 = vt[ii].x[1];
120 p0 = vt[i].x[0];
121 p1 = vt[i].x[1];
122 tp = 0.0;
123 for (j = i + 1, jj = i + 2; j < np; j++, jj++) {
124 if (jj == np) { if (i == 0) break; jj = 0; }
125 dd0 = vt[jj].x[0];
126 dd1 = vt[jj].x[1];
127 t = (dd0 - d0) * (p1 - d1) - (dd1 - d1) * (p0 - d0);
128 if (t * tp <= 0.0 && j > i + 1) {
129 //First loop is only to compute starting tp, hence test on j.
130 pp0 = vt[j].x[0];
131 pp1 = vt[j].x[1];
132 t1 = (p0 - dd0) * (pp1 - dd1) - (p1 - dd1) * (pp0 - dd0);
133 t2 = (d0 - dd0) * (pp1 - dd1) - (d1 - dd1) * (pp0 - dd0);
134 if (t1 * t2 <= 0.0) return 0;
135 // Found an intersection, so done.
136 }
137 tp = t;
138 }
139 }
140 return (wind > 0 ? 2 : -2);
141 }
142 */
143 tpolygon<T> convex_hull() const noexcept;
144
145 size_t vertex_count() const { return vertices.size(); }
146 size_t edge_count() const { return vertices.size() < 2 ? 0 : vertices.size() - 1; }
147
148 std::optional<std::pair<tvec, tvec>> edge(size_t index) const
149 {
150 const auto c = vertices.size();
151 if (c < 2 || index >= c - 1) return std::nullopt;
152 return std::pair{ vertices[index], vertices[index + 1] };
153 }
154
155 std::optional<tvec> vertex(size_t index) const
156 {
157 const auto c = vertices.size();
158 if (c < 2 || index >= c) return std::nullopt;
159 return vertices[index];
160 }
161
162 template <typename FUNC>
163 void for_each_vertex(FUNC&& func) const
164 {
165 for (auto& v : vertices)
166 func(v);
167 }
168
169 template <typename FUNC>
170 void for_each_edge(FUNC&& func) const
171 {
172 const auto c = vertices.size();
173 if (c < 2) return;
174
175 if constexpr (std::invocable<FUNC, tvec const&, tvec const&>)
176 {
177 for (size_t i = 0; i < c - 1; ++i)
178 func(vertices[i], vertices[i + 1]);
179 }
180 else
181 {
182 for (size_t i = 0; i < c - 1; ++i)
183 func(std::pair{ vertices[i], vertices[i + 1] });
184 }
185 }
186
187 std::vector<tsegment<T>> edges() const noexcept
188 {
189 return resulting([this](std::vector<tsegment<T>>& result) {
190 for_each_edge(op::push_back_to(result));
191 });
192 }
193
194 std::vector<T> interior_angles() const noexcept;
195
196 T edge_length() const
197 {
198 T result{};
199 for_each_edge([&](tvec const& v1, tvec const& v2) {
200 result += glm::distance(v1, v2);
201 });
202 return result;
203 }
204
205 tvec edge_point(T t) const
206 {
207 const auto el = edge_length();
208 return edge_point(t, el);
209 }
210
211 tvec edge_point_alpha(T t) const
212 {
213 const auto el = edge_length();
214 return edge_point(t * el, el);
215 }
216
217 tvec closest_point_to(tvec pt) const;
218
219 T calculate_area() const
220 {
221 T result{};
222 for_each_edge([&](tvec const& v1, tvec const& v2) {
223 result += v1.x * v2.y - v2.x * v1.y;
224 });
225 return result * T(0.5);
226 }
227
228 tvec centroid() const
229 {
230 T A = 0;
231 T Cx = 0;
232 T Cy = 0;
233 for (size_t i = 0; i < vertices.size(); ++i)
234 {
235 const auto xi = vertices[i].x;
236 const auto yi = vertices[i].y;
237 const auto xi1 = vertices[(i + 1) % vertices.size()].x;
238 const auto yi1 = vertices[(i + 1) % vertices.size()].y;
239 const auto d = xi * yi1 - xi1 * yi;
240 A += d;
241 Cx += (xi + xi1) * d;
242 Cy += (yi + yi1) * d;
243 }
244 A *= 3;
245 return { Cx / A, Cy / A };
246 }
247
248 trec2<T> bounding_box() const
249 {
250 trec2<T> res = trec2<T>::exclusive();
251 for (auto& v : vertices)
252 res.include(v);
253 return res;
254 }
255
259 bool contains(tvec test) const
260 {
261 bool ret = false;
262 const size_t nvert = vertices.size();
263 for (size_t i = 0, j = nvert - 1; i < nvert; j = i++)
264 {
265 if (((vertices[i].y > test.y) != (vertices[j].y > test.y)) &&
266 (test.x < (vertices[j].x - vertices[i].x) * (test.y - vertices[i].y) / (vertices[j].y - vertices[i].y) + vertices[i].x))
267 {
268 ret = !ret;
269 }
270 }
271 return ret;
272 }
273
274 private:
275
276 tvec edge_point(T t, T precalced_length) const
277 {
278 auto c = vertices.size();
279 if (c < 2)
280 return c ? vertices[0] : tvec{};
281
282 t = fmod(t, precalced_length);
283 for (size_t i = 0; i < c - 1; ++i)
284 {
285 const auto d = glm::distance(vertices[i], vertices[i + 1]);
286 if (t <= d)
287 return glm::mix(vertices[i], vertices[i + 1], t / d);
288
289 t -= d;
290 }
291 return vertices[0];
292 }
293 };
294
295 using polygon = tpolygon<float>;
296 static_assert(polygon_shape<float, polygon>);
297 static_assert(std::ranges::random_access_range<polygon>);
298
301 template <std::floating_point T, std::integral IDX = size_t>
303 {
304 using tvec = glm::tvec2<T>;
305 using value_type = T;
306
307 tpolygon<T> const& poly;
308 std::vector<tindexed_triangle<IDX>> triangles;
309
310 template <typename FUNC>
311 void for_each_triangle(FUNC&& func)
312 {
313 for (auto& tr : triangles)
314 {
315 return tr.as_triangle(poly);
316 }
317 }
318
319 T edge_length() const { return poly.edge_length(); }
320 tvec edge_point_alpha(T t) const { return poly.edge_point_alpha(t); }
321 tvec edge_point(T t) const { return poly.edge_point(t); }
322 trec2<T> bounding_box() const { return poly.bounding_box(); }
323 tvec closest_point_to(glm::tvec2<T> pt) const { return poly.closest_point_to(pt); }
324
326 bool contains(glm::tvec2<T> pt) const;
327 T calculate_area() const;
328 tvec centroid() const;
329 };
330
332
335 template <std::floating_point T, std::integral IDX = size_t>
337 {
338 using tvec = glm::tvec2<T>;
339 using value_type = T;
340
341 tpolygon<T> poly;
342 std::vector<tindexed_triangle<IDX>> triangles;
343
344 T edge_length() const { return poly.edge_length(); }
345 tvec edge_point_alpha(T t) const { return poly.edge_point_alpha(t); }
346 tvec edge_point(T t) const { return poly.edge_point(t); }
347 trec2<T> bounding_box() const { return poly.bounding_box(); }
348 tvec closest_point_to(glm::tvec2<T> pt) const { return poly.closest_point_to(pt); }
349
351 bool contains(glm::tvec2<T> pt) const;
352 T calculate_area() const;
353 tvec centroid() const;
354 };
355
357
358 template <indexable_polygonlike POLY, typename IDX_TYPE = size_t>
359 auto calculate_indexed_triangle_area(POLY const& poly, tindexed_triangle<IDX_TYPE> const& triangle)
360 {
361 const auto a = poly[triangle.indices[0]];
362 const auto b = poly[triangle.indices[1]];
363 const auto c = poly[triangle.indices[2]];
364 using T = decltype(a)::value_type;
365 return geometry::ttriangle<T>{a, b, c}.calculate_area();
366 }
367
368 template <typename TR>
369 auto calculate_total_area(TR const& trpoly)
370 {
371 using T = decltype(trpoly.poly[0])::value_type;
372 T result{};
373 for (auto& tr : trpoly.triangles)
374 result += calculate_indexed_triangle_area(trpoly.poly, tr);
375 return result;
376 }
377
378 /*
379 public static void Triangulate(IList<Vector2> points, List<int> populate)
380 {
381 IList<Vector2> points2 = points;
382 if (points2.Count < 3)
383 {
384 return;
385 }
386
387 Span<int> span = ((points2.Count >= 1000) ? ((Span<int>)new int[points2.Count]) : stackalloc int[points2.Count]);
388 Span<int> list2 = span;
389 if (Area() > 0f)
390 {
391 for (int i = 0; i < points2.Count; i++)
392 {
393 list2[i] = i;
394 }
395 }
396 else
397 {
398 for (int j = 0; j < points2.Count; j++)
399 {
400 list2[j] = points2.Count - 1 - j;
401 }
402 }
403
404 int num = points2.Count;
405 int num2 = 2 * num;
406 int num3 = num - 1;
407 while (num > 2)
408 {
409 if (num2-- <= 0)
410 {
411 return;
412 }
413
414 int num4 = num3;
415 if (num <= num4)
416 {
417 num4 = 0;
418 }
419
420 num3 = num4 + 1;
421 if (num <= num3)
422 {
423 num3 = 0;
424 }
425
426 int num5 = num3 + 1;
427 if (num <= num5)
428 {
429 num5 = 0;
430 }
431
432 if (Snip(num4, num3, num5, num, list2))
433 {
434 populate.Add(list2[num4]);
435 populate.Add(list2[num3]);
436 populate.Add(list2[num5]);
437 int num6 = num3;
438 for (int k = num3 + 1; k < num; k++)
439 {
440 list2[num6] = list2[k];
441 num6++;
442 }
443
444 num--;
445 num2 = 2 * num;
446 }
447 }
448
449 populate.Reverse();
450 float Area()
451 {
452 float num7 = 0f;
453 int index = points2.Count - 1;
454 int num8 = 0;
455 while (num8 < points2.Count)
456 {
457 Vector2 vector = points2[index];
458 Vector2 vector2 = points2[num8];
459 num7 += vector.X * vector2.Y - vector2.X * vector.Y;
460 index = num8++;
461 }
462
463 return num7 * 0.5f;
464 }
465
466 bool Snip(int u, int v, int w, int n, Span<int> list)
467 {
468 Vector2 a = points2[list[u]];
469 Vector2 b = points2[list[v]];
470 Vector2 c = points2[list[w]];
471 if (float.Epsilon > (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X))
472 {
473 return false;
474 }
475
476 for (int l = 0; l < n; l++)
477 {
478 if (l != u && l != v && l != w && InsideTriangle(a, b, c, points2[list[l]]))
479 {
480 return false;
481 }
482 }
483
484 return true;
485 }
486 }
487 */
488 template <typename T>
489 polygon_triangulation<T> triangulate(tpolygon<T> const& poly)
490 {
491 polygon_triangulation<T> result{poly};
493 throw "unimplemented";
494 return result;
495 }
496
497 namespace immutable
498 {
500 template <std::floating_point T>
501 struct tpolygon
502 {
503 using tvec = glm::tvec2<T>;
504 using value_type = T;
505 using mutable_polygon = geometry::tpolygon<T>;
506
507 tpolygon() noexcept = default;
508 tpolygon(tpolygon const&) noexcept = default;
509 tpolygon(tpolygon&&) noexcept = default;
510 tpolygon& operator=(tpolygon const&) noexcept = default;
511 tpolygon& operator=(tpolygon&&) noexcept = default;
512
513 template <typename... ARGS>
514 requires std::constructible_from<mutable_polygon, ARGS...>
515 tpolygon(ARGS&&... args) noexcept
516 : m_poly(std::forward<ARGS>(args)...)
517 {
518 }
519
521 template <typename POLY_OR_ARR>
522 tpolygon(POLY_OR_ARR&& poly, std::vector<indexed_triangle> triangles, std::vector<T> cached_triangle_areas, T cached_area)
523 : m_poly(std::forward<POLY_OR_ARR>(poly))
524 , m_triangles(std::move(triangles))
525 , m_cached_triangle_areas(std::move(cached_triangle_areas))
526 , m_cached_area(cached_area)
527 {
528
529 }
530
531
532 tvec const& operator[](size_t index) const { return m_poly.vertices.at(index); }
533
534 auto cbegin() const noexcept { return std::ranges::cbegin(m_poly.vertices); }
535 auto cend() const noexcept { return std::ranges::cend(m_poly.vertices); }
536 auto begin() const noexcept { return std::ranges::cbegin(m_poly.vertices); }
537 auto end() const noexcept { return std::ranges::cend(m_poly.vertices); }
538 auto size() const noexcept { return std::ranges::size(m_poly.vertices); }
539
540 const auto* operator->() const noexcept { return &m_poly; }
541
542 const auto& polygon() const noexcept { return m_poly; }
543
544 T edge_length() const { return m_poly.edge_length(); }
545 tvec edge_point_alpha(T t) const { return m_poly.edge_point_alpha(t); }
546 tvec edge_point(T t) const { return m_poly.edge_point(t); }
547 trec2<T> bounding_box() const { return m_poly.bounding_box(); }
548 tvec closest_point_to(glm::tvec2<T> pt) const { return m_poly.closest_point_to(pt); }
549
551 bool contains(glm::tvec2<T> pt) const;
552
553 T calculate_area() const
554 {
555 triangulate();
556 return m_cached_area;
557 }
558
559 tvec centroid() const
560 {
561 triangulate();
562 return m_poly.centroid();
563 }
564
565 auto const& triangles() const
566 {
567 triangulate();
568 return m_triangles;
569 }
570
571 auto const& areas() const
572 {
573 triangulate();
574 return m_cached_triangle_areas;
575 }
576
577 bool has_triangle(size_t i) const { triangulate(); return i < m_triangles.size(); }
578 auto triangle(size_t i) const { triangulate(); return m_triangles.at(i).as_triangle(m_poly); }
579 T triangle_area(size_t i) const { triangulate(); return m_cached_triangle_areas.at(i); }
580
581 protected:
582
583 geometry::tpolygon<T> m_poly;
584 mutable std::vector<indexed_triangle> m_triangles;
585 mutable std::vector<T> m_cached_triangle_areas;
586 mutable T m_cached_area = std::numeric_limits<T>::lowest();
587 mutable tvec m_cached_centroid;
588 mutable polygon_classification m_cached_classification;
589
590 void triangulate() const
591 {
592 if (m_triangles.empty() && m_poly.vertices.size() > 2)
593 {
594 auto&& [poly, triangles] = geometry::triangulate(m_poly);
595 m_triangles = std::move(triangles);
596
597 m_cached_triangle_areas.reserve(m_triangles.size());
598 T cached_area{};
599 for (auto& tr : m_triangles)
600 {
601 const auto area = calculate_indexed_triangle_area(poly, tr);
602 m_cached_triangle_areas.push_back(area);
603 cached_area += area;
604 }
605 m_cached_area = cached_area;
606 }
607 }
608 };
609
610 using polygon = tpolygon<float>;
611 static_assert(area_shape<float, polygon>);
612 }
613
615 template <std::floating_point T>
616 struct tpolyline;
617
619}
A shape for which the concept of an "area" has a finite interpretation.
A shape with vertices and edges.
Any contiguous non-empty set of 2D points.
constexpr T resulting(FUNC &&func)
Returns a new T transformed by func.
Definition functional.h:306
An immutable version of tpolygon that performs and caches its triangulation lazily upon request.
Definition polygon.h:502
bool contains(glm::tvec2< T > pt) const
area_shape concept
tpolygon(POLY_OR_ARR &&poly, std::vector< indexed_triangle > triangles, std::vector< T > cached_triangle_areas, T cached_area)
Deserialization constructor.
Definition polygon.h:522
The results of a triangulation of an external tpolygon<T>.
Definition polygon.h:303
bool contains(glm::tvec2< T > pt) const
area_shape concept
Stores three indices to a indexable_polygonlike that together form a triangle.
Definition triangles.h:167
Represents a 2D polygon polygon_shape.
Definition polygon.h:43
bool contains(tvec test) const
Assumes a simple polygon.
Definition polygon.h:259
Represents a 2D rectangle polygon_shape.
Definition rec2.h:31
Contains both a tpolygon<T> as well as its triangulation.
Definition polygon.h:337
bool contains(glm::tvec2< T > pt) const
area_shape concept
Represents a 2D segment (technically a polygon_shape).
Definition segment.h:15