inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) { const std::pair<int32_t, int32_t> m1 = std::minmax(s1.first().location().y(), s1.second().location().y()); const std::pair<int32_t, int32_t> m2 = std::minmax(s2.first().location().y(), s2.second().location().y()); if (m1.first > m2.second || m2.first > m1.second) { return false; } return true; }
/** * Calculate the intersection between two NodeRefSegments. The * result is returned as a Location. Note that because the Location * uses integers with limited precision internally, the result * might be slightly different than the numerically correct * location. * * This function uses integer arithmentic as much as possible and * will not work if the segments are longer than about half the * planet. This shouldn't happen with real data, so it isn't a big * problem. * * If the segments touch in one of their endpoints, it doesn't * count as an intersection. * * If the segments intersect not in a single point but in multiple * points, ie if they overlap, this is NOT detected. * * @returns Undefined osmium::Location if there is no intersection * or a defined Location if the segments intersect. */ inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) { if (s1.first().location() == s2.first().location() || s1.first().location() == s2.second().location() || s1.second().location() == s2.first().location() || s1.second().location() == s2.second().location()) { return osmium::Location(); } int64_t s1ax = s1.first().x(); int64_t s1ay = s1.first().y(); int64_t s1bx = s1.second().x(); int64_t s1by = s1.second().y(); int64_t s2ax = s2.first().x(); int64_t s2ay = s2.first().y(); int64_t s2bx = s2.second().x(); int64_t s2by = s2.second().y(); int64_t d = (s2by - s2ay) * (s1bx - s1ax) - (s2bx - s2ax) * (s1by - s1ay); if (d != 0) { int64_t na = (s2bx - s2ax) * (s1ay - s2ay) - (s2by - s2ay) * (s1ax - s2ax); int64_t nb = (s1bx - s1ax) * (s1ay - s2ay) - (s1by - s1ay) * (s1ax - s2ax); if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) || (d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) { double ua = double(na) / d; int32_t ix = int32_t(s1ax + ua*(s1bx - s1ax)); int32_t iy = int32_t(s1ay + ua*(s1by - s1ay)); return osmium::Location(ix, iy); } } return osmium::Location(); }
/** * A NodeRefSegment is "smaller" if the first point is to the * left and down of the first point of the second segment. * If both first points are the same, the segment with the higher * slope comes first. If the slope is the same, the shorter * segment comes first. */ inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { if (lhs.first().location() == rhs.first().location()) { const vec p0{lhs.first().location()}; const vec p1{lhs.second().location()}; const vec q0{rhs.first().location()}; const vec q1{rhs.second().location()}; const vec p = p1 - p0; const vec q = q1 - q0; if (p.x == 0 && q.x == 0) { return p.y < q.y; } const auto a = p.y * q.x; const auto b = q.y * p.x; if (a == b) { return p.x < q.x; } return a > b; } return lhs.first().location() < rhs.first().location(); }
inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { if (s1.first().location().x() > s2.second().location().x()) { return true; } return false; }
/** * NodeRefSegments are "smaller" if they are to the left and down of another * segment. The first() location is checked first() and only if they have the * same first() location the second() location is taken into account. */ inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { return (lhs.first().location() == rhs.first().location() && lhs.second().location() < rhs.second().location()) || lhs.first().location() < rhs.first().location(); }
/** * Calculate the intersection between two NodeRefSegments. The * result is returned as a Location. Note that because the Location * uses integers with limited precision internally, the result * might be slightly different than the numerically correct * location. * * This function uses integer arithmetic as much as possible and * will not work if the segments are longer than about half the * planet. This shouldn't happen with real data, so it isn't a big * problem. * * If the segments touch in one or both of their endpoints, it * doesn't count as an intersection. * * If the segments intersect not in a single point but in multiple * points, ie if they are collinear and overlap, the smallest * of the endpoints that is in the overlapping section is returned. * * @returns Undefined osmium::Location if there is no intersection * or a defined Location if the segments intersect. */ inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { // See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect // for some hints about how the algorithm works. const vec p0{s1.first()}; const vec p1{s1.second()}; const vec q0{s2.first()}; const vec q1{s2.second()}; if ((p0 == q0 && p1 == q1) || (p0 == q1 && p1 == q0)) { // segments are the same return osmium::Location{}; } const vec pd = p1 - p0; const int64_t d = pd * (q1 - q0); if (d != 0) { // segments are not collinear if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) { // touching at an end point return osmium::Location{}; } // intersection in a point const int64_t na = (q1.x - q0.x) * (p0.y - q0.y) - (q1.y - q0.y) * (p0.x - q0.x); const int64_t nb = (p1.x - p0.x) * (p0.y - q0.y) - (p1.y - p0.y) * (p0.x - q0.x); if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) || (d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) { const double ua = double(na) / d; const vec i = p0 + ua * (p1 - p0); return osmium::Location{int32_t(i.x), int32_t(i.y)}; } return osmium::Location{}; } // segments are collinear if (pd * (q0 - p0) == 0) { // segments are on the same line struct seg_loc { int segment; osmium::Location location; }; seg_loc sl[4]; sl[0] = {0, s1.first().location() }; sl[1] = {0, s1.second().location()}; sl[2] = {1, s2.first().location() }; sl[3] = {1, s2.second().location()}; std::sort(sl, sl+4, [](const seg_loc& lhs, const seg_loc& rhs) { return lhs.location < rhs.location; }); if (sl[1].location == sl[2].location) { return osmium::Location(); } if (sl[0].segment != sl[1].segment) { if (sl[0].location == sl[1].location) { return sl[2].location; } return sl[1].location; } } return osmium::Location{}; }
inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { const std::pair<int32_t, int32_t> m1 = std::minmax(s1.first().location().y(), s1.second().location().y()); const std::pair<int32_t, int32_t> m2 = std::minmax(s2.first().location().y(), s2.second().location().y()); return !(m1.first > m2.second || m2.first > m1.second); }
inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { return s1.first().location().x() > s2.second().location().x(); }
#include "catch.hpp" #include <osmium/area/detail/node_ref_segment.hpp> using osmium::area::detail::NodeRefSegment; TEST_CASE("NodeRefSegmentClass") { SECTION("instantiation_with_default_parameters") { NodeRefSegment s; REQUIRE(s.first().ref() == 0); REQUIRE(s.first().location() == osmium::Location()); REQUIRE(s.second().ref() == 0); REQUIRE(s.second().location() == osmium::Location()); } SECTION("instantiation") { osmium::NodeRef nr1(1, { 1.2, 3.4 }); osmium::NodeRef nr2(2, { 1.4, 3.1 }); osmium::NodeRef nr3(3, { 1.2, 3.6 }); osmium::NodeRef nr4(4, { 1.2, 3.7 }); NodeRefSegment s1(nr1, nr2); REQUIRE(s1.first().ref() == 1); REQUIRE(s1.second().ref() == 2); NodeRefSegment s2(nr2, nr3); REQUIRE(s2.first().ref() == 3); REQUIRE(s2.second().ref() == 2); NodeRefSegment s3(nr3, nr4);