1 // Copyright 2013 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15
16 // Author: ericv@google.com (Eric Veach)
17
18 #ifndef S2_S2CROSSING_EDGE_QUERY_H_
19 #define S2_S2CROSSING_EDGE_QUERY_H_
20
21 #include <type_traits>
22 #include <vector>
23
24 #include "s2/third_party/absl/base/macros.h"
25 #include "s2/third_party/absl/container/inlined_vector.h"
26 #include "s2/_fp_contract_off.h"
27 #include "s2/r2.h"
28 #include "s2/r2rect.h"
29 #include "s2/s2padded_cell.h"
30 #include "s2/s2shape_index.h"
31 #include "s2/s2shapeutil_shape_edge.h"
32 #include "s2/s2shapeutil_shape_edge_id.h"
33
34 // A parameter that controls the reporting of edge intersections.
35 //
36 // - CrossingType::INTERIOR reports intersections that occur at a point
37 // interior to both edges (i.e., not at a vertex).
38 //
39 // - CrossingType::ALL reports all intersections, even those where two edges
40 // intersect only because they share a common vertex.
41 namespace s2shapeutil {
42 enum class CrossingType { INTERIOR, ALL };
43 } // namespace s2shapeutil
44
45 // S2CrossingEdgeQuery is used to find edges or shapes that are crossed by
46 // an edge. Here is an example showing how to index a set of polylines,
47 // and then find the polylines that are crossed by a given edge AB:
48 //
49 // void Test(const vector<S2Polyline*>& polylines,`
50 // const S2Point& a0, const S2Point &a1) {
51 // MutableS2ShapeIndex index;
52 // for (S2Polyline* polyline : polylines) {
53 // index.Add(absl::make_unique<S2Polyline::Shape>(polyline));
54 // }
55 // S2CrossingEdgeQuery query(&index);
56 // for (const auto& edge : query.GetCrossingEdges(a, b, CrossingType::ALL)) {
57 // S2_CHECK_GE(S2::CrossingSign(a0, a1, edge.v0(), edge.v1()), 0);
58 // }
59 // }
60 //
61 // Note that if you need to query many edges, it is more efficient to declare
62 // a single S2CrossingEdgeQuery object and reuse it so that temporary storage
63 // does not need to be reallocated each time.
64 //
65 // If you want to find *all* pairs of crossing edges, use
66 // s2shapeutil::VisitCrossingEdgePairs() instead.
67 class S2CrossingEdgeQuery {
68 public:
69 using CrossingType = s2shapeutil::CrossingType; // Defined above.
70
71 // Convenience constructor that calls Init().
72 explicit S2CrossingEdgeQuery(const S2ShapeIndex* index);
73
74 // Default constructor; requires Init() to be called.
75 S2CrossingEdgeQuery();
76 ~S2CrossingEdgeQuery();
77
78 S2CrossingEdgeQuery(const S2CrossingEdgeQuery&) = delete;
79 void operator=(const S2CrossingEdgeQuery&) = delete;
80
index()81 const S2ShapeIndex& index() const { return *index_; }
82
83 // REQUIRES: "index" is not modified after this method is called.
84 void Init(const S2ShapeIndex* index);
85
86 // Returns all edges that intersect the given query edge (a0,a1) and that
87 // have the given CrossingType (ALL or INTERIOR). Edges are sorted and
88 // unique.
89 std::vector<s2shapeutil::ShapeEdge> GetCrossingEdges(
90 const S2Point& a0, const S2Point& a1, CrossingType type);
91
92 // A specialized version of GetCrossingEdges() that only returns the edges
93 // that belong to a particular S2Shape.
94 std::vector<s2shapeutil::ShapeEdge> GetCrossingEdges(
95 const S2Point& a0, const S2Point& a1,
96 const S2Shape& shape, CrossingType type);
97
98 // These versions can be more efficient when they are called many times,
99 // since they do not require allocating a new vector on each call.
100 void GetCrossingEdges(const S2Point& a0, const S2Point& a1, CrossingType type,
101 std::vector<s2shapeutil::ShapeEdge>* edges);
102
103 void GetCrossingEdges(const S2Point& a0, const S2Point& a1,
104 const S2Shape& shape, CrossingType type,
105 std::vector<s2shapeutil::ShapeEdge>* edges);
106
107
108 /////////////////////////// Low-Level Methods ////////////////////////////
109 //
110 // Most clients will not need the following methods. They can be slightly
111 // more efficient but are harder to use, since they require the client to do
112 // all the actual crossing tests.
113
114 // Returns a superset of the edges that intersect a query edge (a0, a1).
115 // This method is useful for clients that want to test intersections in some
116 // other way, e.g. using S2::EdgeOrVertexCrossing().
117 std::vector<s2shapeutil::ShapeEdgeId> GetCandidates(const S2Point& a0,
118 const S2Point& a1);
119
120 // A specialized version of GetCandidates() that only returns the edges that
121 // belong to a particular S2Shape.
122 std::vector<s2shapeutil::ShapeEdgeId> GetCandidates(const S2Point& a0,
123 const S2Point& a1,
124 const S2Shape& shape);
125
126 // These versions can be more efficient when they are called many times,
127 // since they do not require allocating a new vector on each call.
128 void GetCandidates(const S2Point& a0, const S2Point& a1,
129 std::vector<s2shapeutil::ShapeEdgeId>* edges);
130
131 void GetCandidates(const S2Point& a0, const S2Point& a1, const S2Shape& shape,
132 std::vector<s2shapeutil::ShapeEdgeId>* edges);
133
134 // A function that is called with each candidate intersecting edge. The
135 // function may return false in order to request that the algorithm should
136 // be terminated, i.e. no further crossings are needed.
137 using ShapeEdgeIdVisitor =
138 std::function<bool (const s2shapeutil::ShapeEdgeId& id)>;
139
140 // Visits a superset of the edges that intersect the query edge (a0, a1),
141 // terminating early if the given ShapeEdgeIdVisitor returns false (in which
142 // case this function returns false as well).
143 //
144 // CAVEAT: Edges may be visited more than once.
145 bool VisitRawCandidates(const S2Point& a0, const S2Point& a1,
146 const ShapeEdgeIdVisitor& visitor);
147
148 bool VisitRawCandidates(const S2Point& a0, const S2Point& a1,
149 const S2Shape& shape,
150 const ShapeEdgeIdVisitor& visitor);
151
152 // A function that is called with each S2ShapeIndexCell that might contain
153 // edges intersecting the given query edge. The function may return false
154 // in order to request that the algorithm should be terminated, i.e. no
155 // further crossings are needed.
156 using CellVisitor = std::function<bool (const S2ShapeIndexCell& cell)>;
157
158 // Visits all S2ShapeIndexCells that might contain edges intersecting the
159 // given query edge (a0, a1), terminating early if the given CellVisitor
160 // returns false (in which case this function returns false as well).
161 //
162 // NOTE: Each candidate cell is visited exactly once.
163 bool VisitCells(const S2Point& a0, const S2Point& a1,
164 const CellVisitor& visitor);
165
166 // Visits all S2ShapeIndexCells within "root" that might contain edges
167 // intersecting the given query edge (a0, a1), terminating early if the
168 // given CellVisitor returns false (in which case this function returns
169 // false as well).
170 //
171 // NOTE: Each candidate cell is visited exactly once.
172 //
173 // REQUIRES: root.padding() == 0
174 // [This low-level method does not support padding; the argument is supplied
175 // as an S2PaddedCell in order to avoid constructing it repeatedly when
176 // this method is called using different query edges with the same root.]
177 bool VisitCells(const S2Point& a0, const S2Point& a1,
178 const S2PaddedCell& root, const CellVisitor& visitor);
179
180
181 // Given a query edge AB and a cell "root", returns all S2ShapeIndex cells
182 // within "root" that might contain edges intersecting AB.
183 //
184 // REQUIRES: root.padding() == 0 (see above)
185 void GetCells(const S2Point& a0, const S2Point& a1, const S2PaddedCell& root,
186 std::vector<const S2ShapeIndexCell*>* cells);
187
188 private:
189 // Internal methods are documented with their definitions.
190 bool VisitCells(const S2PaddedCell& pcell, const R2Rect& edge_bound);
191 bool ClipVAxis(const R2Rect& edge_bound, double center, int i,
192 const S2PaddedCell& pcell);
193 void SplitUBound(const R2Rect& edge_bound, double u,
194 R2Rect child_bounds[2]) const;
195 void SplitVBound(const R2Rect& edge_bound, double v,
196 R2Rect child_bounds[2]) const;
197 static void SplitBound(const R2Rect& edge_bound, int u_end, double u,
198 int v_end, double v, R2Rect child_bounds[2]);
199
200 const S2ShapeIndex* index_ = nullptr;
201
202 //////////// Temporary storage used while processing a query ///////////
203
204 R2Point a0_, a1_;
205 S2ShapeIndex::Iterator iter_;
206 const CellVisitor* visitor_;
207
208 // Avoids repeated allocation when methods are called many times.
209 std::vector<s2shapeutil::ShapeEdgeId> tmp_candidates_;
210 };
211
212
213 ////////////////// Implementation details follow ////////////////////
214
215
S2CrossingEdgeQuery(const S2ShapeIndex * index)216 inline S2CrossingEdgeQuery::S2CrossingEdgeQuery(const S2ShapeIndex* index) {
217 Init(index);
218 }
219
220 #endif // S2_S2CROSSING_EDGE_QUERY_H_
221