Coverage Report

Created: 2024-07-23 06:31

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