bool CheckPolygonSelfIntersections(Iter beg, Iter end) { Iter last = end; --last; for (Iter i = beg; i != last; ++i) { for (Iter j = i; j != end; ++j) { // do not check intersection of neibour segments if (std::distance(i, j) <= 1 || (i == beg && j == last)) continue; Iter ii = base::NextIterInCycle(i, beg, end); Iter jj = base::NextIterInCycle(j, beg, end); PointD a = *i, b = *ii, c = *j, d = *jj; // check for rect intersection if (std::max(a.x, b.x) < std::min(c.x, d.x) || std::min(a.x, b.x) > std::max(c.x, d.x) || std::max(a.y, b.y) < std::min(c.y, d.y) || std::min(a.y, b.y) > std::max(c.y, d.y)) { continue; } double const s1 = OrientedS(a, b, c); double const s2 = OrientedS(a, b, d); double const s3 = OrientedS(c, d, a); double const s4 = OrientedS(c, d, b); // check if sections have any intersection if (s1 * s2 > 0.0 || s3 * s4 > 0.0) continue; // Common principle if any point lay exactly on section, check 2 variants: // - only touching (><) - don't return as intersection; // - 'X'-crossing - return as intersection; // 'X'-crossing defines when points lay in different cones. if (s1 == 0.0 && IsInSection(a, b, c)) { PointD const prev = *base::PrevIterInCycle(j, beg, end); PointD test[] = {a, b}; if (a == c) test[0] = *base::PrevIterInCycle(i, beg, end); if (b == c) test[1] = *base::NextIterInCycle(ii, beg, end); if (IsSegmentInCone(c, test[0], prev, d) == IsSegmentInCone(c, test[1], prev, d)) continue; } if (s2 == 0.0 && IsInSection(a, b, d)) { PointD const next = *base::NextIterInCycle(jj, beg, end); PointD test[] = {a, b}; if (a == d) test[0] = *base::PrevIterInCycle(i, beg, end); if (b == d) test[1] = *base::NextIterInCycle(ii, beg, end); if (IsSegmentInCone(d, test[0], c, next) == IsSegmentInCone(d, test[1], c, next)) continue; } if (s3 == 0.0 && IsInSection(c, d, a)) { PointD const prev = *base::PrevIterInCycle(i, beg, end); PointD test[] = {c, d}; if (c == a) test[0] = *base::PrevIterInCycle(j, beg, end); if (d == a) test[1] = *base::NextIterInCycle(jj, beg, end); if (IsSegmentInCone(a, test[0], prev, b) == IsSegmentInCone(a, test[1], prev, b)) continue; } if (s4 == 0.0 && IsInSection(c, d, b)) { PointD const next = *base::NextIterInCycle(ii, beg, end); PointD test[] = {c, d}; if (c == b) test[0] = *base::PrevIterInCycle(j, beg, end); if (d == b) test[1] = *base::NextIterInCycle(jj, beg, end); if (IsSegmentInCone(b, test[0], a, next) == IsSegmentInCone(b, test[1], a, next)) continue; } return true; } } return false; }
#include "geometry/polygon.hpp" #include "geometry/triangle2d.hpp" #include "base/macros.hpp" #include "std/algorithm.hpp" namespace { typedef m2::PointD P; } UNIT_TEST(IsSegmentInCone) { TEST(IsSegmentInCone(P(0,0), P( 0, 3), P(-1,-1), P(1,-1)), ()); TEST(IsSegmentInCone(P(0,0), P( 2, 3), P(-1,-1), P(1,-1)), ()); TEST(IsSegmentInCone(P(0,0), P(-3, 3), P(-1,-1), P(1,-1)), ()); TEST(IsSegmentInCone(P(0,0), P(-3, 0), P(-1,-1), P(1,-1)), ()); TEST(IsSegmentInCone(P(0,0), P( 3, 0), P(-1,-1), P(1,-1)), ()); TEST(!IsSegmentInCone(P(0,0), P( 0,-1), P(-1,-1), P(1,-1)), ()); TEST(!IsSegmentInCone(P(0,0), P( 1,-3), P(-1,-1), P(1,-1)), ()); TEST(!IsSegmentInCone(P(0,0), P(-1,-3), P(-1,-1), P(1,-1)), ()); TEST(IsSegmentInCone(P(0,0), P( 0, 3), P(-1,1), P(1,1)), ()); TEST(IsSegmentInCone(P(0,0), P( 2, 3), P(-1,1), P(1,1)), ()); TEST(!IsSegmentInCone(P(0,0), P(-3, 3), P(-1,1), P(1,1)), ()); TEST(!IsSegmentInCone(P(0,0), P(-3, 0), P(-1,1), P(1,1)), ()); TEST(!IsSegmentInCone(P(0,0), P( 3, 0), P(-1,1), P(1,1)), ()); TEST(!IsSegmentInCone(P(0,0), P( 0,-1), P(-1,1), P(1,1)), ()); TEST(!IsSegmentInCone(P(0,0), P( 1,-3), P(-1,1), P(1,1)), ());