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