1 #include <mbgl/util/intersection_tests.hpp>
2 #include <mbgl/util/math.hpp>
3 
4 namespace mbgl {
5 namespace util {
6 
polygonContainsPoint(const GeometryCoordinates & ring,const GeometryCoordinate & p)7 bool polygonContainsPoint(const GeometryCoordinates& ring, const GeometryCoordinate& p) {
8     bool c = false;
9     for (auto i = ring.begin(), j = ring.end() - 1; i != ring.end(); j = i++) {
10         auto& p1 = *i;
11         auto& p2 = *j;
12         if (((p1.y > p.y) != (p2.y > p.y)) && (p.x < float(p2.x - p1.x) * float(p.y - p1.y) / float(p2.y - p1.y) + p1.x)) {
13             c = !c;
14         }
15     }
16     return c;
17 }
18 
19 // Code from http://stackoverflow.com/a/1501725/331379.
distToSegmentSquared(const GeometryCoordinate & p,const GeometryCoordinate & v,const GeometryCoordinate & w)20 float distToSegmentSquared(const GeometryCoordinate& p, const GeometryCoordinate& v, const GeometryCoordinate& w) {
21     if (v == w) return util::distSqr<float>(p, v);
22     const auto l2 = util::distSqr<float>(v, w);
23     const float t = float((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
24     if (t < 0) return util::distSqr<float>(p, v);
25     if (t > 1) return util::distSqr<float>(p, w);
26     return util::distSqr<float>(p, convertPoint<float>(w - v) * t + convertPoint<float>(v));
27 }
28 
pointIntersectsBufferedLine(const GeometryCoordinate & p,const GeometryCoordinates & line,const float radius)29 bool pointIntersectsBufferedLine(const GeometryCoordinate& p, const GeometryCoordinates& line, const float radius) {
30     const float radiusSquared = radius * radius;
31 
32     if (line.size() == 1) return util::distSqr<float>(p, line.at(0)) < radiusSquared;
33     if (line.size() == 0) return false;
34 
35     for (auto i = line.begin() + 1; i != line.end(); i++) {
36         // Find line segments that have a distance <= radius^2 to p
37         // In that case, we treat the line as "containing point p".
38         auto& v = *(i - 1);
39         auto& w = *i;
40         if (distToSegmentSquared(p, v, w) < radiusSquared) return true;
41     }
42     return false;
43 }
44 
45 // http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
isCounterClockwise(const GeometryCoordinate & a,const GeometryCoordinate & b,const GeometryCoordinate & c)46 bool isCounterClockwise(const GeometryCoordinate& a, const GeometryCoordinate& b, const GeometryCoordinate& c) {
47     return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
48 }
49 
lineSegmentIntersectsLineSegment(const GeometryCoordinate & a0,const GeometryCoordinate & a1,const GeometryCoordinate & b0,const GeometryCoordinate & b1)50 bool lineSegmentIntersectsLineSegment(const GeometryCoordinate& a0, const GeometryCoordinate& a1, const GeometryCoordinate& b0, const GeometryCoordinate& b1) {
51     return isCounterClockwise(a0, b0, b1) != isCounterClockwise(a1, b0, b1) &&
52         isCounterClockwise(a0, a1, b0) != isCounterClockwise(a0, a1, b1);
53 }
lineIntersectsLine(const GeometryCoordinates & lineA,const GeometryCoordinates & lineB)54 bool lineIntersectsLine(const GeometryCoordinates& lineA, const GeometryCoordinates& lineB) {
55     if (lineA.size() == 0 || lineB.size() == 0) return false;
56     for (auto i = lineA.begin(); i != lineA.end() - 1; i++) {
57         auto& a0 = *i;
58         auto& a1 = *(i + 1);
59         for (auto j = lineB.begin(); j != lineB.end() - 1; j++) {
60             auto& b0 = *j;
61             auto& b1 = *(j + 1);
62             if (lineSegmentIntersectsLineSegment(a0, a1, b0, b1)) return true;
63         }
64     }
65     return false;
66 }
67 
lineIntersectsBufferedLine(const GeometryCoordinates & lineA,const GeometryCoordinates & lineB,float radius)68 bool lineIntersectsBufferedLine(const GeometryCoordinates& lineA, const GeometryCoordinates& lineB, float radius) {
69     if (lineA.size() > 1) {
70         if (lineIntersectsLine(lineA, lineB)) return true;
71 
72         // Check whether any point in either line is within radius of the other line
73         for (auto& p : lineB) {
74             if (pointIntersectsBufferedLine(p, lineA, radius)) return true;
75         }
76     }
77 
78     for (auto& p : lineA) {
79         if (pointIntersectsBufferedLine(p, lineB, radius)) return true;
80     }
81 
82     return false;
83 }
84 
polygonIntersectsBufferedPoint(const GeometryCoordinates & polygon,const GeometryCoordinate & point,float radius)85 bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius) {
86     if (polygonContainsPoint(polygon, point)) return true;
87     if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
88     return false;
89 }
90 
polygonIntersectsBufferedMultiPoint(const GeometryCoordinates & polygon,const GeometryCollection & rings,float radius)91 bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates& polygon, const GeometryCollection& rings, float radius) {
92     for (auto& ring : rings) {
93         for (auto& point : ring) {
94             if (polygonIntersectsBufferedPoint(polygon, point, radius)) return true;
95         }
96     }
97     return false;
98 }
99 
polygonIntersectsBufferedMultiLine(const GeometryCoordinates & polygon,const GeometryCollection & multiLine,float radius)100 bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates& polygon, const GeometryCollection& multiLine, float radius) {
101     for (auto& line : multiLine) {
102         if (polygon.size() >= 3) {
103             for (auto& p : line) {
104                 if (polygonContainsPoint(polygon, p)) return true;
105             }
106         }
107 
108         if (lineIntersectsBufferedLine(polygon, line, radius)) return true;
109     }
110 
111     return false;
112 }
113 
polygonIntersectsPolygon(const GeometryCoordinates & polygonA,const GeometryCoordinates & polygonB)114 bool polygonIntersectsPolygon(const GeometryCoordinates& polygonA, const GeometryCoordinates& polygonB) {
115     for (auto& p : polygonA) {
116         if (polygonContainsPoint(polygonB, p)) return true;
117     }
118 
119     for (auto& p : polygonB) {
120         if (polygonContainsPoint(polygonA, p)) return true;
121     }
122 
123     if (lineIntersectsLine(polygonA, polygonB)) return true;
124 
125     return false;
126 }
127 
polygonIntersectsMultiPolygon(const GeometryCoordinates & polygon,const GeometryCollection & multiPolygon)128 bool polygonIntersectsMultiPolygon(const GeometryCoordinates& polygon, const GeometryCollection& multiPolygon) {
129     for (auto& ring : multiPolygon) {
130         if (polygonIntersectsPolygon(polygon, ring)) return true;
131     }
132 
133     return false;
134 }
135 
136 } // namespace util
137 } // namespace mbgl
138