OGS
Polygon.cpp
Go to the documentation of this file.
1
15#include "Polygon.h"
16
17#include <algorithm>
18
19#include "AnalyticalGeometry.h"
20#include "BaseLib/quicksort.h"
21
22namespace GeoLib
23{
24enum class Location
25{
26 LEFT,
27 RIGHT,
28 BEYOND,
29 BEHIND,
30 BETWEEN,
31 SOURCE,
33};
34
38enum class EdgeType
39{
40 TOUCHING,
41 CROSSING,
43};
44
56 MathLib::Point3d const& destination,
57 MathLib::Point3d const& pnt)
58{
59 long double const a[2] = {destination[0] - source[0],
60 destination[1] - source[1]}; // vector
61 long double const b[2] = {pnt[0] - source[0],
62 pnt[1] - source[1]}; // vector
63
64 long double const det_2x2(a[0] * b[1] - a[1] * b[0]);
65 constexpr double eps = std::numeric_limits<double>::epsilon();
66
67 if (det_2x2 > eps)
68 {
69 return Location::LEFT;
70 }
71 if (eps < std::abs(det_2x2))
72 {
73 return Location::RIGHT;
74 }
75 if (a[0] * b[0] < 0.0 || a[1] * b[1] < 0.0)
76 {
77 return Location::BEHIND;
78 }
79 if (a[0] * a[0] + a[1] * a[1] < b[0] * b[0] + b[1] * b[1])
80 {
81 return Location::BEYOND;
82 }
83 if (MathLib::sqrDist(pnt, source) < pow(eps, 2))
84 {
85 return Location::SOURCE;
86 }
87 if (MathLib::sqrDist(pnt, destination) < std::sqrt(eps))
88 {
90 }
91 return Location::BETWEEN;
92}
93
103 MathLib::Point3d const& b,
104 MathLib::Point3d const& pnt)
105{
106 switch (getLocationOfPoint(a, b, pnt))
107 {
108 case Location::LEFT:
109 {
110 if (a[1] < pnt[1] && pnt[1] <= b[1])
111 {
112 return EdgeType::CROSSING;
113 }
114
116 }
117 case Location::RIGHT:
118 {
119 if (b[1] < pnt[1] && pnt[1] <= a[1])
120 {
121 return EdgeType::CROSSING;
122 }
123
125 }
127 case Location::SOURCE:
129 return EdgeType::TOUCHING;
130 default:
132 }
133}
134
135Polygon::Polygon(const Polyline& ply, bool init)
136 : Polyline(ply), _aabb(ply.getPointsVec(), ply.getPolylinePointIDs())
137{
138 if (init)
139 {
140 initialise();
141 }
142 _simple_polygon_list.push_back(this);
143}
144
145Polygon::Polygon(Polygon const& other) : Polyline(other), _aabb(other._aabb)
146{
147 _simple_polygon_list.push_back(this);
148 auto sub_polygon_it(other._simple_polygon_list.begin());
149 for (sub_polygon_it++; // the first entry is the polygon itself, skip the
150 // entry
151 sub_polygon_it != other._simple_polygon_list.end();
152 ++sub_polygon_it)
153 {
154 _simple_polygon_list.emplace_back(new Polygon(*(*sub_polygon_it)));
155 }
156}
157
159{
160 // remove polygons from list
161 for (auto& polygon : _simple_polygon_list)
162 {
163 // the first entry of the list can be a pointer the object itself
164 if (polygon != this)
165 {
166 delete polygon;
167 }
168 }
169}
170
172{
173 if (this->isClosed())
174 {
176 return true;
177 }
178 WARN("Polygon::initialise(): base polyline is not closed.");
179 return false;
180}
181
189std::vector<GeoLib::Point> getAllIntersectionPoints(
190 Polygon const& polygon, GeoLib::LineSegment const& segment)
191{
192 std::vector<GeoLib::Point> intersections;
194 for (auto&& seg_it : polygon)
195 {
196 if (GeoLib::lineSegmentIntersect(seg_it, segment, s))
197 {
198 intersections.push_back(s);
199 }
200 }
201
202 return intersections;
203}
204
206{
207 auto const [min_aabb_pnt, max_aabb_pnt] = _aabb.getMinMaxPoints();
208
209 if (pnt[0] < min_aabb_pnt[0] || max_aabb_pnt[0] < pnt[0] ||
210 pnt[1] < min_aabb_pnt[1] || max_aabb_pnt[1] < pnt[1])
211 {
212 return false;
213 }
214
215 if (_simple_polygon_list.size() == 1)
216 {
217 std::size_t n_intersections(0);
218 const std::size_t n_nodes(getNumberOfPoints() - 1);
219 for (std::size_t k(0); k < n_nodes; k++)
220 {
221 if (((*(getPoint(k)))[1] <= pnt[1] &&
222 pnt[1] <= (*(getPoint(k + 1)))[1]) ||
223 ((*(getPoint(k + 1)))[1] <= pnt[1] &&
224 pnt[1] <= (*(getPoint(k)))[1]))
225 {
226 switch (getEdgeType(*getPoint(k), *getPoint(k + 1), pnt))
227 {
229 return true;
231 n_intersections++;
232 break;
234 break;
235 default:
236 // do nothing
237 ;
238 }
239 }
240 }
241 if (n_intersections % 2 == 1)
242 {
243 return true;
244 }
245 }
246 else
247 {
248 for (auto it(_simple_polygon_list.begin()++);
249 it != _simple_polygon_list.end();
250 ++it)
251 {
252 if ((*it)->isPntInPolygon(pnt))
253 {
254 return true;
255 }
256 }
257 }
258 return false;
259}
260
262{
263 std::vector<GeoLib::Point> s(getAllIntersectionPoints(*this, segment));
264
265 GeoLib::Point const& a{segment.getBeginPoint()};
266 GeoLib::Point const& b{segment.getEndPoint()};
267 // no intersections -> check if at least one point of segment is in polygon
268 if (s.empty())
269 {
270 return (isPntInPolygon(a));
271 }
272
273 const double tol(std::numeric_limits<float>::epsilon());
274
275 // one intersection, intersection in line segment end point
276 if (s.size() == 1)
277 {
278 const double sqr_dist_as(MathLib::sqrDist(a, s[0]));
279 if (sqr_dist_as < tol)
280 {
281 return (isPntInPolygon(b));
282 }
283
284 const double sqr_dist_bs(MathLib::sqrDist(b, s[0]));
285 if (sqr_dist_bs < tol)
286 {
287 return (isPntInPolygon(a));
288 }
289 }
290
291 // Sorting the intersection with respect to the distance to the point a.
292 // This induces a partition of the line segment into sub segments.
293 std::sort(s.begin(), s.end(),
294 [&a](GeoLib::Point const& p0, GeoLib::Point const& p1)
295 { return MathLib::sqrDist(a, p0) < MathLib::sqrDist(a, p1); });
296
297 // remove sub segments with almost zero length
298 for (std::size_t k(0); k < s.size() - 1;)
299 {
300 if (MathLib::sqrDist(s[k], s[k + 1]) < tol)
301 {
302 s.erase(s.begin() + k + 1);
303 }
304 else
305 {
306 k++;
307 }
308 }
309
310 // Check if all sub segments are within the polygon.
311 if (!isPntInPolygon(GeoLib::Point(0.5 * (a[0] + s[0][0]),
312 0.5 * (a[1] + s[0][1]),
313 0.5 * (a[2] + s[0][2]))))
314 {
315 return false;
316 }
317 const std::size_t n_sub_segs(s.size() - 1);
318 for (std::size_t k(0); k < n_sub_segs; k++)
319 {
320 if (!isPntInPolygon(GeoLib::Point(0.5 * (s[k][0] + s[k + 1][0]),
321 0.5 * (s[k][1] + s[k + 1][1]),
322 0.5 * (s[k][2] + s[k + 1][2]))))
323 {
324 return false;
325 }
326 }
327 return isPntInPolygon(GeoLib::Point(0.5 * (s[0][0] + b[0]),
328 0.5 * (s[0][1] + b[1]),
329 0.5 * (s[0][2] + b[2])));
330}
331
333{
334 return std::all_of(ply.begin(), ply.end(),
335 [this](auto const& segment)
336 { return containsSegment(segment); });
337}
338
340{
341 const std::size_t ply_size(ply.getNumberOfPoints());
342 // check points
343 for (std::size_t k(0); k < ply_size; k++)
344 {
345 if (isPntInPolygon(*(ply.getPoint(k))))
346 {
347 return true;
348 }
349 }
350
351 auto polygon_segment_intersects_line = [&](auto const& polygon_seg)
352 {
354 return std::any_of(ply.begin(), ply.end(),
355 [&polygon_seg, &s](auto const& polyline_seg) {
356 return GeoLib::lineSegmentIntersect(
357 polyline_seg, polygon_seg, s);
358 });
359 };
360
361 return std::any_of(std::cbegin(*this), std::cend(*this),
362 polygon_segment_intersects_line);
363}
364
366 GeoLib::LineSegment const& seg, GeoLib::Point& intersection_pnt,
367 std::size_t& seg_num) const
368{
369 if (_simple_polygon_list.size() == 1)
370 {
371 for (auto seg_it(begin() + seg_num); seg_it != end(); ++seg_it)
372 {
373 if (GeoLib::lineSegmentIntersect(*seg_it, seg, intersection_pnt))
374 {
375 seg_num = seg_it.getSegmentNumber();
376 return true;
377 }
378 }
379 }
380 else
381 {
382 for (auto const* polygon : _simple_polygon_list)
383 {
384 for (auto seg_it(polygon->begin()); seg_it != polygon->end();
385 ++seg_it)
386 {
387 if (GeoLib::lineSegmentIntersect(*seg_it, seg,
388 intersection_pnt))
389 {
390 seg_num = seg_it.getSegmentNumber();
391 return true;
392 }
393 }
394 }
395 }
396 return false;
397}
398
400{
401 // *** pre processing: rotate points to xy-plan
402 // *** copy points to vector - last point is identical to the first
403 std::size_t n_pnts(this->getNumberOfPoints() - 1);
404 std::vector<GeoLib::Point*> tmp_polygon_pnts;
405 for (std::size_t k(0); k < n_pnts; k++)
406 {
407 tmp_polygon_pnts.push_back(new GeoLib::Point(*(this->getPoint(k))));
408 }
409
410 // rotate copied points into x-y-plane
411 GeoLib::rotatePointsToXY(tmp_polygon_pnts);
412
413 for (auto& tmp_polygon_pnt : tmp_polygon_pnts)
414 {
415 (*tmp_polygon_pnt)[2] =
416 0.0; // should be -= d but there are numerical errors
417 }
418
419 // *** get the left most upper point
420 std::size_t min_x_max_y_idx(0); // for orientation check
421 for (std::size_t k(0); k < n_pnts; k++)
422 {
423 if ((*(tmp_polygon_pnts[k]))[0] <=
424 (*(tmp_polygon_pnts[min_x_max_y_idx]))[0])
425 {
426 if ((*(tmp_polygon_pnts[k]))[0] <
427 (*(tmp_polygon_pnts[min_x_max_y_idx]))[0])
428 {
429 min_x_max_y_idx = k;
430 }
431 else if ((*(tmp_polygon_pnts[k]))[1] >
432 (*(tmp_polygon_pnts[min_x_max_y_idx]))[1])
433 {
434 min_x_max_y_idx = k;
435 }
436 }
437 }
438 // *** determine orientation
439 GeoLib::Orientation orient;
440 if (0 < min_x_max_y_idx && min_x_max_y_idx < n_pnts - 2)
441 {
442 orient = GeoLib::getOrientation(*tmp_polygon_pnts[min_x_max_y_idx - 1],
443 *tmp_polygon_pnts[min_x_max_y_idx],
444 *tmp_polygon_pnts[min_x_max_y_idx + 1]);
445 }
446 else
447 {
448 if (0 == min_x_max_y_idx)
449 {
450 orient = GeoLib::getOrientation(*tmp_polygon_pnts[n_pnts - 1],
451 *tmp_polygon_pnts[0],
452 *tmp_polygon_pnts[1]);
453 }
454 else
455 {
456 orient = GeoLib::getOrientation(*tmp_polygon_pnts[n_pnts - 2],
457 *tmp_polygon_pnts[n_pnts - 1],
458 *tmp_polygon_pnts[0]);
459 }
460 }
461
462 if (orient != GeoLib::CCW)
463 {
465 }
466
467 for (std::size_t k(0); k < n_pnts; k++)
468 {
469 delete tmp_polygon_pnts[k];
470 }
471}
472
474 const std::list<Polygon*>::const_iterator& polygon_it)
475{
476 GeoLib::Polyline::SegmentIterator seg_it0((*polygon_it)->begin());
477 GeoLib::Polyline::SegmentIterator seg_it1((*polygon_it)->begin());
478 GeoLib::Point intersection_pnt;
479 if (!GeoLib::lineSegmentsIntersect(*polygon_it, seg_it0, seg_it1,
480 intersection_pnt))
481 {
482 return;
483 }
484
485 std::size_t idx0(seg_it0.getSegmentNumber());
486 std::size_t idx1(seg_it1.getSegmentNumber());
487 // adding intersection point to pnt_vec
488 std::size_t const intersection_pnt_id(_ply_pnts.size());
489 const_cast<std::vector<Point*>&>(_ply_pnts).push_back(
490 new GeoLib::Point(intersection_pnt));
491
492 // split Polygon
493 if (idx0 > idx1)
494 {
495 std::swap(idx0, idx1);
496 }
497
498 GeoLib::Polyline polyline0{(*polygon_it)->getPointsVec()};
499 for (std::size_t k(0); k <= idx0; k++)
500 {
501 polyline0.addPoint((*polygon_it)->getPointID(k));
502 }
503 polyline0.addPoint(intersection_pnt_id);
504 for (std::size_t k(idx1 + 1); k < (*polygon_it)->getNumberOfPoints(); k++)
505 {
506 polyline0.addPoint((*polygon_it)->getPointID(k));
507 }
508
509 GeoLib::Polyline polyline1{(*polygon_it)->getPointsVec()};
510 polyline1.addPoint(intersection_pnt_id);
511 for (std::size_t k(idx0 + 1); k <= idx1; k++)
512 {
513 polyline1.addPoint((*polygon_it)->getPointID(k));
514 }
515 polyline1.addPoint(intersection_pnt_id);
516
517 // remove the polygon except the first
518 if (*polygon_it != this)
519 {
520 delete *polygon_it;
521 }
522 // erase polygon_it and add two new polylines
523 auto polygon1_it = _simple_polygon_list.insert(
524 _simple_polygon_list.erase(polygon_it), new GeoLib::Polygon(polyline1));
525 auto polygon0_it = _simple_polygon_list.insert(
526 polygon1_it, new GeoLib::Polygon(polyline0));
527
528 splitPolygonAtIntersection(polygon0_it);
529 splitPolygonAtIntersection(polygon1_it);
530}
531
533 const std::list<GeoLib::Polygon*>::iterator& polygon_it)
534{
535 std::size_t const n((*polygon_it)->getNumberOfPoints() - 1);
536 std::vector<std::size_t> id_vec(n);
537 std::vector<std::size_t> perm(n);
538 for (std::size_t k(0); k < n; k++)
539 {
540 id_vec[k] = (*polygon_it)->getPointID(k);
541 perm[k] = k;
542 }
543
544 BaseLib::quicksort(id_vec, 0, n, perm);
545
546 for (std::size_t k(0); k < n - 1; k++)
547 {
548 if (id_vec[k] == id_vec[k + 1])
549 {
550 std::size_t idx0 = perm[k];
551 std::size_t idx1 = perm[k + 1];
552
553 if (idx0 > idx1)
554 {
555 std::swap(idx0, idx1);
556 }
557
558 // create two closed polylines
559 GeoLib::Polyline polyline0{*(*polygon_it)};
560 for (std::size_t j(0); j <= idx0; j++)
561 {
562 polyline0.addPoint((*polygon_it)->getPointID(j));
563 }
564 for (std::size_t j(idx1 + 1);
565 j < (*polygon_it)->getNumberOfPoints();
566 j++)
567 {
568 polyline0.addPoint((*polygon_it)->getPointID(j));
569 }
570
571 GeoLib::Polyline polyline1{*(*polygon_it)};
572 for (std::size_t j(idx0); j <= idx1; j++)
573 {
574 polyline1.addPoint((*polygon_it)->getPointID(j));
575 }
576
577 // remove the polygon except the first
578 if (*polygon_it != this)
579 {
580 delete *polygon_it;
581 }
582 // erase polygon_it and add two new polygons
583 auto polygon1_it = _simple_polygon_list.insert(
584 _simple_polygon_list.erase(polygon_it), new Polygon(polyline1));
585 auto polygon0_it = _simple_polygon_list.insert(
586 polygon1_it, new Polygon(polyline0));
587
588 splitPolygonAtPoint(polygon0_it);
589 splitPolygonAtPoint(polygon1_it);
590
591 return;
592 }
593 }
594}
595
596bool operator==(Polygon const& lhs, Polygon const& rhs)
597{
598 if (lhs.getNumberOfPoints() != rhs.getNumberOfPoints())
599 {
600 return false;
601 }
602
603 const std::size_t n(lhs.getNumberOfPoints());
604 const std::size_t start_pnt(lhs.getPointID(0));
605
606 // search start point of first polygon in second polygon
607 bool nfound(true);
608 std::size_t k(0);
609 for (; k < n - 1 && nfound; k++)
610 {
611 if (start_pnt == rhs.getPointID(k))
612 {
613 nfound = false;
614 break;
615 }
616 }
617
618 // case: start point not found in second polygon
619 if (nfound)
620 {
621 return false;
622 }
623
624 // *** determine direction
625 // opposite direction
626 if (k == n - 2)
627 {
628 for (k = 1; k < n - 1; k++)
629 {
630 if (lhs.getPointID(k) != rhs.getPointID(n - 1 - k))
631 {
632 return false;
633 }
634 }
635 return true;
636 }
637
638 // same direction - start point of first polygon at arbitrary position in
639 // second polygon
640 if (lhs.getPointID(1) == rhs.getPointID(k + 1))
641 {
642 std::size_t j(k + 2);
643 for (; j < n - 1; j++)
644 {
645 if (lhs.getPointID(j - k) != rhs.getPointID(j))
646 {
647 return false;
648 }
649 }
650 j = 0; // new start point at second polygon
651 for (; j < k + 1; j++)
652 {
653 if (lhs.getPointID(n - (k + 2) + j + 1) != rhs.getPointID(j))
654 {
655 return false;
656 }
657 }
658 return true;
659 }
660 // opposite direction with start point of first polygon at arbitrary
661 // position
662 // *** ATTENTION
663 WARN(
664 "operator==(Polygon const& lhs, Polygon const& rhs) - not tested case "
665 "(implementation is probably buggy) - please contact "
666 "thomas.fischer@ufz.de mentioning the problem.");
667 // in second polygon
668 if (lhs.getPointID(1) == rhs.getPointID(k - 1))
669 {
670 std::size_t j(k - 2);
671 for (; j > 0; j--)
672 {
673 if (lhs.getPointID(k - 2 - j) != rhs.getPointID(j))
674 {
675 return false;
676 }
677 }
678 // new start point at second polygon - the point n-1 of a polygon is
679 // equal to the first point of the polygon (for this reason: n-2)
680 j = n - 2;
681 for (; j > k - 1; j--)
682 {
683 if (lhs.getPointID(n - 2 + j + k - 2) != rhs.getPointID(j))
684 {
685 return false;
686 }
687 }
688 return true;
689 }
690 return false;
691}
692
693std::list<Polygon*> const& Polygon::computeListOfSimplePolygons()
694{
697
698 for (auto polygon : _simple_polygon_list)
699 {
700 polygon->initialise();
701 }
703}
704
705} // end namespace GeoLib
Definition of analytical geometry functions.
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Definition of the Polygon class.
MinMaxPoints getMinMaxPoints() const
Definition AABB.h:174
GeoLib::Point const & getBeginPoint() const
GeoLib::Point const & getEndPoint() const
std::list< Polygon * > const & computeListOfSimplePolygons()
Definition Polygon.cpp:693
bool isPolylineInPolygon(const Polyline &ply) const
Definition Polygon.cpp:332
void splitPolygonAtPoint(const std::list< Polygon * >::iterator &polygon_it)
Definition Polygon.cpp:532
bool isPntInPolygon(MathLib::Point3d const &pnt) const
Definition Polygon.cpp:205
void splitPolygonAtIntersection(const std::list< Polygon * >::const_iterator &polygon_it)
Definition Polygon.cpp:473
bool isPartOfPolylineInPolygon(const Polyline &ply) const
Definition Polygon.cpp:339
bool containsSegment(GeoLib::LineSegment const &segment) const
Definition Polygon.cpp:261
~Polygon() override
Definition Polygon.cpp:158
std::list< Polygon * > _simple_polygon_list
Definition Polygon.h:107
bool getNextIntersectionPointPolygonLine(GeoLib::LineSegment const &seg, GeoLib::Point &intersection_pnt, std::size_t &seg_num) const
Definition Polygon.cpp:365
bool initialise()
Definition Polygon.cpp:171
void ensureCCWOrientation()
Definition Polygon.cpp:399
std::size_t getSegmentNumber() const
Definition Polyline.cpp:387
Class Polyline consists mainly of a reference to a point vector and a vector that stores the indices ...
Definition Polyline.h:40
std::size_t getPointID(std::size_t const i) const
Definition Polyline.cpp:160
friend class Polygon
Definition Polyline.h:90
std::size_t getNumberOfPoints() const
Definition Polyline.cpp:109
const Point * getPoint(std::size_t i) const
returns the i-th point contained in the polyline
Definition Polyline.cpp:179
void reverseOrientation()
Definition Polyline.cpp:356
SegmentIterator begin() const
Definition Polyline.h:188
bool isClosed() const
Definition Polyline.cpp:119
virtual bool addPoint(std::size_t pnt_id)
Definition Polyline.cpp:35
const std::vector< Point * > & _ply_pnts
Definition Polyline.h:211
SegmentIterator end() const
Definition Polyline.h:190
void quicksort(It1 first1, It1 last1, It2 first2, Comparator compare)
Definition quicksort.h:26
bool lineSegmentsIntersect(const GeoLib::Polyline *ply, GeoLib::Polyline::SegmentIterator &seg_it0, GeoLib::Polyline::SegmentIterator &seg_it1, GeoLib::Point &intersection_pnt)
std::vector< GeoLib::Point > getAllIntersectionPoints(Polygon const &polygon, GeoLib::LineSegment const &segment)
Definition Polygon.cpp:189
Eigen::Matrix3d rotatePointsToXY(InputIterator1 p_pnts_begin, InputIterator1 p_pnts_end, InputIterator2 r_pnts_begin, InputIterator2 r_pnts_end)
Location getLocationOfPoint(MathLib::Point3d const &source, MathLib::Point3d const &destination, MathLib::Point3d const &pnt)
Definition Polygon.cpp:55
bool operator==(LineSegment const &s0, LineSegment const &s1)
EdgeType getEdgeType(MathLib::Point3d const &a, MathLib::Point3d const &b, MathLib::Point3d const &pnt)
Definition Polygon.cpp:102
@ INESSENTIAL
INESSENTIAL.
@ CROSSING
CROSSING.
@ TOUCHING
TOUCHING.
bool lineSegmentIntersect(GeoLib::LineSegment const &s0, GeoLib::LineSegment const &s1, GeoLib::Point &s)
Orientation getOrientation(MathLib::Point3d const &p0, MathLib::Point3d const &p1, MathLib::Point3d const &p2)
double sqrDist(MathLib::Point3d const &p0, MathLib::Point3d const &p1)
Definition Point3d.cpp:26
Definition of the quicksort function.