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