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