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 // Original author: ericv@google.com (Eric Veach)
16 // Converted to D:  madric@gmail.com (Vijay Nayar)
17 
18 module s2.s2crossing_edge_query;
19 
20 import s2.r2point;
21 import s2.r2rect;
22 import s2.s2cell_id;
23 import s2.s2edge_clipping;
24 import s2.s2edge_crosser;
25 import s2.s2padded_cell;
26 import s2.s2point;
27 import s2.s2shape;
28 import s2.s2shape_index;
29 import s2.shapeutil.count_edges;
30 import s2.shapeutil.shape_edge;
31 import s2.shapeutil.shape_edge_id;
32 import s2.util.container.btree_map;
33 
34 import std.algorithm : sort, uniq;
35 import std.array : array;
36 import std.exception : enforce;
37 import std.range : empty;
38 
39 /**
40  * A parameter that controls the reporting of edge intersections.
41  *
42  *  - CrossingType::INTERIOR reports intersections that occur at a point
43  *    interior to both edges (i.e., not at a vertex).
44  *
45  *  - CrossingType::ALL reports all intersections, even those where two edges
46  *    intersect only because they share a common vertex.
47  */
48 enum CrossingType { INTERIOR, ALL }
49 
50 // For small loops it is faster to use brute force.  The threshold below was
51 // determined using the benchmarks in the unit test.
52 private enum int MAX_BRUTE_FORCE_EDGES = 27;
53 
54 /**
55  * S2CrossingEdgeQuery is used to find edges or shapes that are crossed by
56  * an edge.  Here is an example showing how to index a set of polylines,
57  * and then find the polylines that are crossed by a given edge AB:
58  *
59  * void Test(const vector<S2Polyline*>& polylines,`
60  *           const S2Point& a0, const S2Point &a1) {
61  *   MutableS2ShapeIndex index;
62  *   for (S2Polyline* polyline : polylines) {
63  *     index.Add(absl::make_unique<S2Polyline::Shape>(polyline));
64  *   }
65  *   S2CrossingEdgeQuery query(&index);
66  *   for (const auto& edge : query.GetCrossingEdges(a, b, CrossingType::ALL)) {
67  *     CHECK_GE(S2::CrossingSign(a0, a1, edge.v0(), edge.v1()), 0);
68  *   }
69  * }
70  *
71  * Note that if you need to query many edges, it is more efficient to declare
72  * a single S2CrossingEdgeQuery object and reuse it so that temporary storage
73  * does not need to be reallocated each time.
74  *
75  * If you want to find *all* pairs of crossing edges, use
76  * s2shapeutil::VisitCrossingEdgePairs() instead.
77  */
78 class S2CrossingEdgeQuery {
79 public:
80 
81   /// Default constructor; requires Init() to be called.
82   this() {
83     _iter = new S2ShapeIndex.Iterator();
84   }
85 
86   /// Convenience constructor that calls Init().
87   this(S2ShapeIndex index) {
88     this();
89     initialize(index);
90   }
91 
92   inout(S2ShapeIndex) index() inout {
93     return _index;
94   }
95 
96   /// REQUIRES: "index" is not modified after this method is called.
97   void initialize(S2ShapeIndex index) {
98     _index = index;
99     _iter.initialize(index);
100   }
101 
102   /**
103    * Returns all edges that intersect the given query edge (a0,a1) and that
104    * have the given CrossingType (ALL or INTERIOR).  Edges are sorted and
105    * unique.
106    */
107   ShapeEdge[] getCrossingEdges(in S2Point a0, in S2Point a1, CrossingType type) {
108     ShapeEdge[] edges;
109     getCrossingEdges(a0, a1, type, edges);
110     return edges;
111   }
112 
113   /**
114    * A specialized version of GetCrossingEdges() that only returns the edges
115    * that belong to a particular S2Shape.
116    */
117   ShapeEdge[] getCrossingEdges(
118       in S2Point a0, in S2Point a1, in S2Shape shape, CrossingType type) {
119     ShapeEdge[] edges;
120     getCrossingEdges(a0, a1, shape, type, edges);
121     return edges;
122   }
123 
124   /**
125    * These versions can be more efficient when they are called many times,
126    * since they do not require allocating a new vector on each call.
127    */
128   void getCrossingEdges(
129       in S2Point a0, in S2Point a1, CrossingType type, ref ShapeEdge[] edges) {
130     edges.length = 0;
131     getCandidates(a0, a1, _tmpCandidates);
132     int min_sign = (type == CrossingType.ALL) ? 0 : 1;
133     auto crosser = new S2CopyingEdgeCrosser(a0, a1);
134     int shape_id = -1;
135     S2Shape shape = null;
136     foreach (ShapeEdgeId candidate; _tmpCandidates) {
137       if (candidate.shapeId != shape_id) {
138         shape_id = candidate.shapeId;
139         shape = _index.shape(shape_id);
140       }
141       int edge_id = candidate.edgeId;
142       S2Shape.Edge b = shape.edge(edge_id);
143       if (crosser.crossingSign(b.v0, b.v1) >= min_sign) {
144         edges ~= new ShapeEdge(shape_id, edge_id, b);
145       }
146     }
147   }
148 
149   void getCrossingEdges(
150       in S2Point a0, in S2Point a1, in S2Shape shape, CrossingType type, ref ShapeEdge[] edges) {
151     edges.length = 0;
152     getCandidates(a0, a1, shape, _tmpCandidates);
153     int min_sign = (type == CrossingType.ALL) ? 0 : 1;
154     auto crosser = new S2CopyingEdgeCrosser(a0, a1);
155     foreach (ShapeEdgeId candidate; _tmpCandidates) {
156       int edge_id = candidate.edgeId;
157       S2Shape.Edge b = shape.edge(edge_id);
158       if (crosser.crossingSign(b.v0, b.v1) >= min_sign) {
159         edges ~= new ShapeEdge(shape.id(), edge_id, b);
160       }
161     }
162   }
163 
164   /////////////////////////// Low-Level Methods ////////////////////////////
165   //
166   // Most clients will not need the following methods.  They can be slightly
167   // more efficient but are harder to use, since they require the client to do
168   // all the actual crossing tests.
169 
170   // Returns a superset of the edges that intersect a query edge (a0, a1).
171   // This method is useful for clients that want to test intersections in some
172   // other way, e.g. using S2::EdgeOrVertexCrossing().
173   ShapeEdgeId[] getCandidates(in S2Point a0, in S2Point a1) {
174     ShapeEdgeId[] edges;
175     getCandidates(a0, a1, edges);
176     return edges;
177   }
178 
179 
180   // A specialized version of GetCandidates() that only returns the edges that
181   // belong to a particular S2Shape.
182   ShapeEdgeId[] getCandidates(in S2Point a0, in S2Point a1, in S2Shape shape) {
183     ShapeEdgeId[] edges;
184     getCandidates(a0, a1, shape, edges);
185     return edges;
186   }
187 
188   // These versions can be more efficient when they are called many times,
189   // since they do not require allocating a new vector on each call.
190   void getCandidates(in S2Point a0, in S2Point a1, out ShapeEdgeId[] edges) {
191     int num_edges = countEdgesUpTo(_index, MAX_BRUTE_FORCE_EDGES + 1);
192     if (num_edges <= MAX_BRUTE_FORCE_EDGES) {
193       edges.reserve(num_edges);
194     }
195     visitRawCandidates(a0, a1, (in ShapeEdgeId id) {
196           edges ~= id;
197           return true;
198         });
199     if (edges.length > 1) {
200       edges = edges.sort().uniq().array();
201     }
202   }
203 
204   void getCandidates(in S2Point a0, in S2Point a1, in S2Shape shape, out ShapeEdgeId[] edges) {
205     int num_edges = shape.numEdges();
206     if (num_edges <= MAX_BRUTE_FORCE_EDGES) {
207       edges.reserve(num_edges);
208     }
209     visitRawCandidates(a0, a1, shape, (in ShapeEdgeId id) {
210           edges ~= id;
211           return true;
212         });
213     if (edges.length > 1) {
214       edges = edges.sort.uniq.array;
215     }
216   }
217 
218   // A function that is called with each candidate intersecting edge.  The
219   // function may return false in order to request that the algorithm should
220   // be terminated, i.e. no further crossings are needed.
221   alias ShapeEdgeIdVisitor = bool delegate(in ShapeEdgeId id);
222 
223   // Visits a superset of the edges that intersect the query edge (a0, a1),
224   // terminating early if the given ShapeEdgeIdVisitor returns false (in which
225   // case this function returns false as well).
226   //
227   // CAVEAT: Edges may be visited more than once.
228   bool visitRawCandidates(in S2Point a0, in S2Point a1, ShapeEdgeIdVisitor visitor) {
229     int num_edges = countEdgesUpTo(_index, MAX_BRUTE_FORCE_EDGES + 1);
230     if (num_edges <= MAX_BRUTE_FORCE_EDGES) {
231       int num_shape_ids = _index.numShapeIds();
232       for (int s = 0; s < num_shape_ids; ++s) {
233         const(S2Shape) shape = _index.shape(s);
234         if (shape is null) continue;
235         int num_shape_edges = shape.numEdges();
236         for (int e = 0; e < num_shape_edges; ++e) {
237           if (!visitor(ShapeEdgeId(s, e))) return false;
238         }
239       }
240       return true;
241     }
242     return visitCells(a0, a1, (in S2ShapeIndexCell cell) {
243           for (int s = 0; s < cell.numClipped(); ++s) {
244             const(S2ClippedShape) clipped = cell.clipped(s);
245             for (int j = 0; j < clipped.numEdges(); ++j) {
246               if (!visitor(ShapeEdgeId(clipped.shapeId(), clipped.edge(j)))) {
247                 return false;
248               }
249             }
250           }
251           return true;
252         });
253   }
254 
255   bool visitRawCandidates(
256       in S2Point a0, in S2Point a1, in S2Shape shape, ShapeEdgeIdVisitor visitor) {
257     int num_edges = shape.numEdges();
258     if (num_edges <= MAX_BRUTE_FORCE_EDGES) {
259       for (int e = 0; e < num_edges; ++e) {
260         if (!visitor(ShapeEdgeId(shape.id(), e))) return false;
261       }
262       return true;
263     }
264     return visitCells(a0, a1, (in S2ShapeIndexCell cell) {
265           const(S2ClippedShape)* clipped = cell.findClipped(shape.id());
266           if (clipped is null) return true;
267           for (int j = 0; j < clipped.numEdges(); ++j) {
268             if (!visitor(ShapeEdgeId(shape.id(), clipped.edge(j)))) return false;
269           }
270           return true;
271         });
272   }
273 
274   // A function that is called with each S2ShapeIndexCell that might contain
275   // edges intersecting the given query edge.  The function may return false
276   // in order to request that the algorithm should be terminated, i.e. no
277   // further crossings are needed.
278   alias CellVisitor = bool delegate(in S2ShapeIndexCell);
279 
280   // Visits all S2ShapeIndexCells that might contain edges intersecting the
281   // given query edge (a0, a1), terminating early if the given CellVisitor
282   // returns false (in which case this function returns false as well).
283   //
284   // NOTE: Each candidate cell is visited exactly once.
285   bool visitCells(in S2Point a0, in S2Point a1, CellVisitor visitor) {
286     _visitor = visitor;
287     FaceSegmentVector segments;
288     getFaceSegments(a0, a1, segments);
289     foreach (segment; segments) {
290       _a0 = segment.a;
291       _a1 = segment.b;
292       // Optimization: rather than always starting the recursive subdivision at
293       // the top level face cell, instead we start at the smallest S2CellId that
294       // contains the edge (the "edge root cell").  This typically lets us skip
295       // quite a few levels of recursion since most edges are short.
296       R2Rect edge_bound = R2Rect.fromPointPair(_a0, _a1);
297       auto pcell = new S2PaddedCell(S2CellId.fromFace(segment.face), 0);
298       S2CellId edge_root = pcell.shrinkToFit(edge_bound);
299 
300       // Now we need to determine how the edge root cell is related to the cells
301       // in the spatial index (cell_map_).  There are three cases:
302       //
303       //  1. edge_root is an index cell or is contained within an index cell.
304       //     In this case we only need to look at the contents of that cell.
305       //  2. edge_root is subdivided into one or more index cells.  In this case
306       //     we recursively subdivide to find the cells intersected by a0a1.
307       //  3. edge_root does not intersect any index cells.  In this case there
308       //     is nothing to do.
309       S2ShapeIndex.CellRelation relation = _iter.locate(edge_root);
310       if (relation == S2ShapeIndex.CellRelation.INDEXED) {
311         // edge_root is an index cell or is contained by an index cell (case 1).
312         enforce(_iter.id().contains(edge_root));
313         if (!visitor(_iter.cell())) return false;
314       } else if (relation == S2ShapeIndex.CellRelation.SUBDIVIDED) {
315         // edge_root is subdivided into one or more index cells (case 2).  We
316         // find the cells intersected by a0a1 using recursive subdivision.
317         if (!edge_root.isFace()) pcell = new S2PaddedCell(edge_root, 0);
318         if (!visitCells(pcell, edge_bound)) return false;
319       }
320     }
321     return true;
322   }
323 
324 
325   // Visits all S2ShapeIndexCells within "root" that might contain edges
326   // intersecting the given query edge (a0, a1), terminating early if the
327   // given CellVisitor returns false (in which case this function returns
328   // false as well).
329   //
330   // NOTE: Each candidate cell is visited exactly once.
331   bool visitCells(in S2Point a0, in S2Point a1, in S2PaddedCell root, CellVisitor visitor) {
332     _visitor = visitor;
333     FaceSegmentVector segments;
334     getFaceSegments(a0, a1, segments);
335     foreach (segment; segments) {
336       _a0 = segment.a;
337       _a1 = segment.b;
338 
339       // Optimization: rather than always starting the recursive subdivision at
340       // the top level face cell, instead we start at the smallest S2CellId that
341       // contains the edge (the "edge root cell").  This typically lets us skip
342       // quite a few levels of recursion since most edges are short.
343       R2Rect edge_bound = R2Rect.fromPointPair(_a0, _a1);
344       auto pcell = new S2PaddedCell(S2CellId.fromFace(segment.face), 0);
345       S2CellId edge_root = pcell.shrinkToFit(edge_bound);
346 
347       // Now we need to determine how the edge root cell is related to the cells
348       // in the spatial index (cell_map_).  There are three cases:
349       //
350       //  1. edge_root is an index cell or is contained within an index cell.
351       //     In this case we only need to look at the contents of that cell.
352       //  2. edge_root is subdivided into one or more index cells.  In this case
353       //     we recursively subdivide to find the cells intersected by a0a1.
354       //  3. edge_root does not intersect any index cells.  In this case there
355       //     is nothing to do.
356       S2ShapeIndex.CellRelation relation = _iter.locate(edge_root);
357       if (relation == S2ShapeIndex.CellRelation.INDEXED) {
358         // edge_root is an index cell or is contained by an index cell (case 1).
359         enforce(_iter.id().contains(edge_root));
360         if (!visitor(_iter.cell())) return false;
361       } else if (relation == S2ShapeIndex.CellRelation.SUBDIVIDED) {
362         // edge_root is subdivided into one or more index cells (case 2).  We
363         // find the cells intersected by a0a1 using recursive subdivision.
364         if (!edge_root.isFace()) pcell = new S2PaddedCell(edge_root, 0);
365         if (!visitCells(pcell, edge_bound)) return false;
366       }
367     }
368     return true;
369   }
370 
371 
372   // Given a query edge AB and a cell "root", returns all S2ShapeIndex cells
373   // within "root" that might contain edges intersecting AB.
374   void getCells(in S2Point a0, in S2Point a1, in S2PaddedCell root, out const(S2ShapeIndexCell)[] cells) {
375     cells.length = 0;
376     visitCells(
377         a0, a1, root, (in S2ShapeIndexCell cell) {
378           cells ~= cell;
379           return true;
380         });
381   }
382 
383   ///////////////////// DEPRECATED METHODS //////////////////////////////
384 
385   alias EdgeMap = int[][S2Shape];
386 
387   deprecated("Use GetCrossingEdges")
388   bool getCrossings(
389       in S2Point a0, in S2Point a1, in S2Shape shape, CrossingType type, ref int[] edges) {
390     edges.length = 0;
391     foreach (const(ShapeEdge) edge; getCrossingEdges(a0, a1, shape, type)) {
392       edges ~= edge.id().edgeId;
393     }
394     return !edges.empty();
395   }
396 
397   deprecated("Use GetCrossingEdges")
398   bool getCrossings(in S2Point a0, in S2Point a1, CrossingType type, out EdgeMap edge_map) {
399     // Since this API is obsolete, don't worry about reserving vectors, etc.
400     foreach (const(ShapeEdge) edge; getCrossingEdges(a0, a1, type)) {
401       S2Shape shape = _index.shape(edge.id().shapeId);
402       edge_map[shape] ~= edge.id().edgeId;
403     }
404     return !edge_map.empty();
405   }
406 
407   deprecated("Use method returning std::vector")
408   bool getCandidates(in S2Point a0, in S2Point a1, in S2Shape shape, out int[] edges) {
409     foreach (const(ShapeEdgeId) edge; getCandidates(a0, a1, shape)) {
410       edges ~= edge.edgeId;
411     }
412     return !edges.empty();
413   }
414 
415   deprecated("Use method returning std::vector")
416   bool getCandidates(in S2Point a0, in S2Point a1, out EdgeMap edge_map) {
417     // Since this API is obsolete, don't worry about reserving vectors, etc.
418     foreach (const(ShapeEdgeId) edge; getCandidates(a0, a1)) {
419       S2Shape shape = _index.shape(edge.shapeId);
420       edge_map[shape] ~= edge.edgeId;
421     }
422     return !edge_map.empty();
423   }
424 
425  private:
426   // Internal methods are documented with their definitions.
427 
428   /**
429    * Computes the index cells intersected by the current edge that are
430    * descendants of "pcell" and calls visitor_ for each one.
431    *
432    * WARNING: This function is recursive with a maximum depth of 30.  The frame
433    * size is about 2K in versions of GCC prior to 4.7 due to poor overlapping
434    * of storage for temporaries.  This is fixed in GCC 4.7, reducing the frame
435    * size to about 350 bytes (i.e., worst-case total stack usage of about 10K).
436    */
437   bool visitCells(S2PaddedCell pcell, in R2Rect edge_bound) {
438     _iter.seek(pcell.id().rangeMin());
439     if (_iter.done() || _iter.id() > pcell.id().rangeMax()) {
440       // The index does not contain "pcell" or any of its descendants.
441       return true;
442     }
443     if (_iter.id() == pcell.id()) {
444       return _visitor(_iter.cell());
445     }
446 
447     // Otherwise, split the edge among the four children of "pcell".
448     R2Point center = pcell.middle().lo();
449     if (edge_bound[0].hi() < center[0]) {
450       // Edge is entirely contained in the two left children.
451       return clipVAxis(edge_bound, center[1], 0, pcell);
452     } else if (edge_bound[0].lo() >= center[0]) {
453       // Edge is entirely contained in the two right children.
454       return clipVAxis(edge_bound, center[1], 1, pcell);
455     } else {
456       R2Rect[2] child_bounds;
457       splitUBound(edge_bound, center[0], child_bounds);
458       if (edge_bound[1].hi() < center[1]) {
459         // Edge is entirely contained in the two lower children.
460         return visitCells(new S2PaddedCell(pcell, 0, 0), child_bounds[0])
461             && visitCells(new S2PaddedCell(pcell, 1, 0), child_bounds[1]);
462       } else if (edge_bound[1].lo() >= center[1]) {
463         // Edge is entirely contained in the two upper children.
464         return visitCells(new S2PaddedCell(pcell, 0, 1), child_bounds[0])
465             && visitCells(new S2PaddedCell(pcell, 1, 1), child_bounds[1]);
466       } else {
467         // The edge bound spans all four children.  The edge itself intersects
468         // at most three children (since no padding is being used).
469         return clipVAxis(child_bounds[0], center[1], 0, pcell)
470             && clipVAxis(child_bounds[1], center[1], 1, pcell);
471       }
472     }
473   }
474 
475   /**
476    * Given either the left (i=0) or right (i=1) side of a padded cell "pcell",
477    * determine whether the current edge intersects the lower child, upper child,
478    * or both children, and call VisitCells() recursively on those children.
479    * "center" is the v-coordinate at the center of "pcell".
480    */
481   bool clipVAxis(in R2Rect edge_bound, double center, int i, S2PaddedCell pcell) {
482     if (edge_bound[1].hi() < center) {
483       // Edge is entirely contained in the lower child.
484       return visitCells(new S2PaddedCell(pcell, i, 0), edge_bound);
485     } else if (edge_bound[1].lo() >= center) {
486       // Edge is entirely contained in the upper child.
487       return visitCells(new S2PaddedCell(pcell, i, 1), edge_bound);
488     } else {
489       // The edge intersects both children.
490       R2Rect[2] child_bounds;
491       splitVBound(edge_bound, center, child_bounds);
492       return visitCells(new S2PaddedCell(pcell, i, 0), child_bounds[0])
493           && visitCells(new S2PaddedCell(pcell, i, 1), child_bounds[1]);
494     }
495   }
496 
497   /**
498    * Split the current edge into two child edges at the given u-value "u" and
499    * return the bound for each child.
500    */
501   void splitUBound(in R2Rect edge_bound, double u, ref R2Rect[2] child_bounds) const {
502     // See comments in MutableS2ShapeIndex::ClipUBound.
503     double v = edge_bound[1].project(interpolateDouble(u, _a0[0], _a1[0], _a0[1], _a1[1]));
504 
505     // "diag_" indicates which diagonal of the bounding box is spanned by a0a1:
506     // it is 0 if a0a1 has positive slope, and 1 if a0a1 has negative slope.
507     int diag = (_a0[0] > _a1[0]) != (_a0[1] > _a1[1]);
508     splitBound(edge_bound, 0, u, diag, v, child_bounds);
509   }
510 
511   /**
512    * Split the current edge into two child edges at the given v-value "v" and
513    * return the bound for each child.
514    */
515   void splitVBound(in R2Rect edge_bound, double v, ref R2Rect[2] child_bounds) const {
516     double u = edge_bound[0].project(interpolateDouble(v, _a0[1], _a1[1], _a0[0], _a1[0]));
517     int diag = (_a0[0] > _a1[0]) != (_a0[1] > _a1[1]);
518     splitBound(edge_bound, diag, u, 0, v, child_bounds);
519   }
520 
521   /**
522    * Split the current edge into two child edges at the given point (u,v) and
523    * return the bound for each child.  "u_end" and "v_end" indicate which bound
524    * endpoints of child 1 will be updated.
525    */
526   static void splitBound(
527       in R2Rect edge_bound, int u_end, double u, int v_end, double v, ref R2Rect[2] child_bounds) {
528     child_bounds[0] = edge_bound;
529     child_bounds[0][0][1 - u_end] = u;
530     child_bounds[0][1][1 - v_end] = v;
531     enforce(!child_bounds[0].isEmpty());
532     enforce(edge_bound.contains(child_bounds[0]));
533 
534     child_bounds[1] = edge_bound;
535     child_bounds[1][0][u_end] = u;
536     child_bounds[1][1][v_end] = v;
537     enforce(!child_bounds[1].isEmpty());
538     enforce(edge_bound.contains(child_bounds[1]));
539   }
540 
541   S2ShapeIndex _index;
542 
543   //////////// Temporary storage used while processing a query ///////////
544 
545   R2Point _a0, _a1;
546   S2ShapeIndex.Iterator _iter;
547   CellVisitor _visitor;
548 
549   // Avoids repeated allocation when methods are called many times.
550   ShapeEdgeId[] _tmpCandidates;
551 }