1 // Copyright 2012 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 // S2ShapeIndex is an abstract base class for indexing polygonal geometry in
19 // memory.  The main documentation is with the class definition below.
20 // (Some helper classes are defined first.)
21 
22 module s2.s2shape_index;
23 
24 import std.array : appender, RefAppender;
25 import std.bitmanip : bitfields;
26 import core.atomic : MemoryOrder, atomicStore, atomicLoad;
27 import s2.s2shape;
28 import s2.r1interval;
29 import s2.s2cell_id;
30 import s2.s2point;
31 
32 import std.conv : to;
33 
34 /**
35  * S2ClippedShape represents the part of a shape that intersects an S2Cell.
36  * It consists of the set of edge ids that intersect that cell, and a boolean
37  * indicating whether the center of the cell is inside the shape (for shapes
38  * that have an interior).
39  *
40  * Note that the edges themselves are not clipped; we always use the original
41  * edges for intersection tests so that the results will be the same as the
42  * original shape.
43  */
44 struct S2ClippedShape {
45 public:
46 
47   /// The shape id of the clipped shape.
48   int shapeId() const {
49     return _shapeId;
50   }
51 
52   /**
53    * Returns true if the center of the S2CellId is inside the shape.  Returns
54    * false for shapes that do not have an interior.
55    */
56   bool containsCenter() const {
57     return _containsCenter;
58   }
59 
60   /// The number of edges that intersect the S2CellId.
61   int numEdges() const {
62     return _numEdges;
63   }
64 
65   /**
66    * Returns the edge id of the given edge in this clipped shape.  Edges are
67    * sorted in increasing order of edge id.
68    */
69   int edge(int i) const
70   in {
71     assert(0 <= i && i < numEdges());
72   } do {
73     return isInline() ? _inlineEdges[i] : _edges[i];
74   }
75 
76   /// Returns true if the clipped shape contains the given edge id.
77   bool containsEdge(int id) const {
78     // Linear search is fast because the number of edges per shape is typically
79     // very small (less than 10).
80     for (int e = 0; e < numEdges(); ++e) {
81       if (edge(e) == id) return true;
82     }
83     return false;
84   }
85 
86   string toString() const {
87     return "S2ClippedShape[shapeId=" ~ _shapeId.to!string
88         ~ ", numEdges=" ~ _numEdges.to!string ~ "]";
89   }
90 
91 package:
92   // Initialize an S2ClippedShape to hold the given number of edges.
93   void initialize(int shape_id, int num_edges) {
94     _shapeId = shape_id;
95     _numEdges = cast(uint) num_edges;
96     _containsCenter = false;
97     if (!isInline()) {
98       _edges = new int[_numEdges];
99     }
100   }
101 
102   // This class may be copied by value, but note that it does *not* own its
103   // underlying data.  (It is owned by the containing S2ShapeIndexCell.)
104 
105   // Free any memory allocated by this S2ClippedShape.  We don't do this in
106   // the destructor because S2ClippedShapes are copied by STL code, and we
107   // don't want to repeatedly copy and free the edge data.  Instead the data
108   // is owned by the containing S2ShapeIndexCell.
109   void destruct() {
110     if (!isInline()) _edges.length = 0;
111   }
112 
113   bool isInline() const {
114     return _numEdges <= _inlineEdges.length;
115   }
116 
117   // Set "contains_center_" to indicate whether this clipped shape contains the
118   // center of the cell to which it belongs.
119   void setContainsCenter(bool contains_center) {
120     _containsCenter = contains_center;
121   }
122 
123   // Set the i-th edge of this clipped shape to be the given edge of the
124   // original shape.
125   void setEdge(int i, int edge) {
126     if (isInline()) {
127       _inlineEdges[i] = edge;
128     } else {
129       _edges[i] = edge;
130     }
131   }
132 
133 private:
134   // All fields are packed into 16 bytes (assuming 64-bit pointers).  Up to
135   // two edge ids are stored inline; this is an important optimization for
136   // clients that use S2Shapes consisting of a single edge.
137   int _shapeId;
138   mixin(bitfields!(
139       bool, "_containsCenter", 1,  // shape contains the cell center
140       uint, "_numEdges",  31));
141 
142   // If there are more than two edges, this field holds a pointer.
143   // Otherwise it holds an array of edge ids.
144   union {
145     int[] _edges;           // This pointer is owned by the containing Cell.
146     int[2] _inlineEdges;
147   }
148 }
149 
150 // S2ShapeIndexCell stores the index contents for a particular S2CellId.
151 // It consists of a set of clipped shapes.
152 class S2ShapeIndexCell {
153 public:
154 
155   // Returns the number of clipped shapes in this cell.
156   int numClipped() const {
157     return cast(int) _shapes.length;
158   }
159 
160   // Returns the clipped shape at the given index.  Shapes are kept sorted in
161   // increasing order of shape id.
162   //
163   // REQUIRES: 0 <= i < num_clipped()
164   ref const(S2ClippedShape) clipped(int i) const
165   in {
166     assert(0 <= i && i < numClipped(),
167         "clipped(" ~ i.to!string ~ ") but numClipped()=" ~ numClipped.to!string);
168   } do {
169     return _shapes[i];
170   }
171 
172   // Returns a pointer to the clipped shape corresponding to the given shape,
173   // or nullptr if the shape does not intersect this cell.
174   const(S2ClippedShape)* findClipped(in S2Shape shape) const {
175     return findClipped(shape.id());
176   }
177 
178   const(S2ClippedShape)* findClipped(int shape_id) const {
179     // Linear search is fine because the number of shapes per cell is typically
180     // very small (most often 1), and is large only for pathological inputs
181     // (e.g. very deeply nested loops).
182     foreach (ref const s; _shapes) {
183       if (s.shapeId() == shape_id) return &s;
184     }
185     return null;
186   }
187 
188   // Convenience method that returns the total number of edges in all clipped
189   // shapes.
190   int numEdges() const {
191     int n = 0;
192     foreach (i; 0 .. numClipped()) {
193       n += clipped(i).numEdges();
194     }
195     return n;
196   }
197 
198   override
199   string toString() const {
200     import std.conv;
201     return "S2ShapeIndexCell[shapes=" ~ _shapes.to!string ~ "]";
202   }
203 
204 package:
205 
206   // Allocate room for "n" additional clipped shapes in the cell, and return a
207   // pointer to the first new clipped shape.  Expects that all new clipped
208   // shapes will have a larger shape id than any current shape, and that shapes
209   // will be added in increasing shape id order.
210   RefAppender!(S2ClippedShape[]) addShapes(int n) {
211     _shapes.reserve(_shapes.length + n);
212     return appender(&_shapes);
213   }
214 
215 private:
216   S2ClippedShape[] _shapes;
217 }
218 
219 /**
220  * S2ShapeIndex is an abstract base class for indexing polygonal geometry in
221  * memory.  The objects in the index are known as "shapes", and may consist of
222  * points, polylines, and/or polygons, possibly overlapping.  The index makes
223  * it very fast to answer queries such as finding nearby shapes, measuring
224  * distances, testing for intersection and containment, etc.
225  *
226  * Each object in the index implements the S2Shape interface.  An S2Shape is a
227  * collection of edges that optionally defines an interior.  The edges do not
228  * need to be connected, so for example an S2Shape can represent a polygon
229  * with multiple shells and/or holes, or a set of polylines, or a set of
230  * points.  All geometry within a single S2Shape must have the same dimension,
231  * so for example if you want to create an S2ShapeIndex containing a polyline
232  * and 10 points, then you will need at least two different S2Shape objects.
233  *
234  * The most important type of S2ShapeIndex is MutableS2ShapeIndex, which
235  * allows you to build an index incrementally by adding or removing shapes.
236  * Soon there will also be an EncodedS2ShapeIndex type that makes it possible
237  * to keep the index data in encoded form.  Code that only needs read-only
238  * ("const") access to an index should use the S2ShapeIndex base class as the
239  * parameter type, so that it will work with any S2ShapeIndex subtype.  For
240  * example:
241  *
242  *   void DoSomething(const S2ShapeIndex& index) {
243  *     ... works with MutableS2ShapeIndex or EncodedS2ShapeIndex ...
244  *   }
245  *
246  * There are a number of built-in classes that work with S2ShapeIndex objects.
247  * Generally these classes accept any collection of geometry that can be
248  * represented by an S2ShapeIndex, i.e. any combination of points, polylines,
249  * and polygons.  Such classes include:
250  *
251  * - S2ContainsPointQuery: returns the shape(s) that contain a given point.
252  *
253  * - S2ClosestEdgeQuery: returns the closest edge(s) to a given point, edge,
254  *                       S2CellId, or S2ShapeIndex.
255  *
256  * - S2CrossingEdgeQuery: returns the edge(s) that cross a given edge.
257  *
258  * - S2BooleanOperation: computes boolean operations such as union,
259  *                       and boolean predicates such as containment.
260  *
261  * - S2ShapeIndexRegion: computes approximations for a collection of geometry.
262  *
263  * - S2ShapeIndexBufferedRegion: computes approximations that have been
264  *                               expanded by a given radius.
265  *
266  * Here is an example showing how to index a set of polygons and then
267  * determine which polygon(s) contain each of a set of query points:
268  *
269  *   void TestContainment(const vector<S2Point>& points,
270  *                        const vector<S2Polygon*>& polygons) {
271  *     MutableS2ShapeIndex index;
272  *     for (auto polygon : polygons) {
273  *       index.Add(absl::make_unique<S2Polygon::Shape>(polygon));
274  *     }
275  *     auto query = MakeS2ContainsPointQuery(&index);
276  *     for (const auto& point : points) {
277  *       for (S2Shape* shape : query.GetContainingShapes(point)) {
278  *         S2Polygon* polygon = polygons[shape->id()];
279  *         ... do something with (point, polygon) ...
280  *       }
281  *     }
282  *   }
283  *
284  * This example uses S2Polygon::Shape, which is one example of an S2Shape
285  * object.  S2Polyline and S2Loop also have nested Shape classes, and there are
286  * additional S2Shape types defined in *_shape.h.
287  *
288  * Internally, an S2ShapeIndex is essentially a map from S2CellIds to the set
289  * of shapes that intersect each S2CellId.  It is adaptively refined to ensure
290  * that no cell contains more than a small number of edges.
291  *
292  * In addition to implementing a shared set of virtual methods, all
293  * S2ShapeIndex subtypes define an Iterator type with the same API.  This
294  * makes it easy to convert code that uses a particular S2ShapeIndex subtype
295  * to instead use the abstract base class (or vice versa).  You can also
296  * choose to avoid the overhead of virtual method calls by making the
297  * S2ShapeIndex type a template argument, like this:
298  *
299  *   template <class IndexType>
300  *   void DoSomething(const IndexType& index) {
301  *     for (typename IndexType::Iterator it(&index, S2ShapeIndex::BEGIN);
302  *          !it.done(); it.Next()) {
303  *       ...
304  *     }
305  *   }
306  *
307  * Subtypes provided by the S2 library have the same thread-safety properties
308  * as std::vector.  That is, const methods may be called concurrently from
309  * multiple threads, and non-const methods require exclusive access to the
310  * S2ShapeIndex.
311  */
312 abstract class S2ShapeIndex {
313 public:
314   // Returns the number of distinct shape ids in the index.  This is the same
315   // as the number of shapes provided that no shapes have ever been removed.
316   // (Shape ids are never reused.)
317   abstract int numShapeIds() const;
318 
319   // Returns a pointer to the shape with the given id, or nullptr if the shape
320   // has been removed from the index.
321   abstract inout(S2Shape) shape(int id) inout;
322 
323   // Returns the number of bytes currently occupied by the index (including any
324   // unused space at the end of vectors, etc).
325   abstract size_t spaceUsed() const;
326 
327   // Minimizes memory usage by requesting that any data structures that can be
328   // rebuilt should be discarded.  This method invalidates all iterators.
329   //
330   // Like all non-const methods, this method is not thread-safe.
331   abstract void minimize();
332 
333   // The possible relationships between a "target" cell and the cells of the
334   // S2ShapeIndex.  If the target is an index cell or is contained by an index
335   // cell, it is "INDEXED".  If the target is subdivided into one or more
336   // index cells, it is "SUBDIVIDED".  Otherwise it is "DISJOINT".
337   enum CellRelation {
338     INDEXED,       // Target is contained by an index cell
339     SUBDIVIDED,    // Target is subdivided into one or more index cells
340     DISJOINT       // Target does not intersect any index cells
341   }
342 
343   // When passed to an Iterator constructor, specifies whether the iterator
344   // should be positioned at the beginning of the index (BEGIN), the end of
345   // the index (END), or arbitrarily (UNPOSITIONED).  By default iterators are
346   // unpositioned, since this avoids an extra seek in this situation where one
347   // of the seek methods (such as Locate) is immediately called.
348   enum InitialPosition {
349     BEGIN,
350     END,
351     UNPOSITIONED
352   }
353 
354   /**
355    * A random access iterator that provides low-level access to the cells of
356    * the index.  Cells are sorted in increasing order of S2CellId.
357    */
358   static class Iterator {
359   public:
360     /// Default constructor; must be followed by a call to Init().
361     this() {
362       _iter = null;
363     }
364 
365     /**
366      * Constructs an iterator positioned as specified.  By default iterators
367      * are unpositioned, since this avoids an extra seek in this situation
368      * where one of the seek methods (such as Locate) is immediately called.
369      *
370      * If you want to position the iterator at the beginning, e.g. in order to
371      * loop through the entire index, do this instead:
372      *
373      *   for (auto it = S2ShapeIndex.Iterator(&index, S2ShapeIndex.InitialPosition.BEGIN);
374      *        !it.done(); it.Next()) { ... }
375      */
376     this(S2ShapeIndex index, InitialPosition pos = InitialPosition.UNPOSITIONED) {
377       _iter = index.newIterator(pos);
378     }
379 
380     /**
381      * Initializes an iterator for the given S2ShapeIndex.  This method may
382      * also be called in order to restore an iterator to a valid state after
383      * the underlying index has been updated (although it is usually easier
384      * just to declare a new iterator whenever required, since iterator
385      * construction is cheap).
386      */
387     void initialize(S2ShapeIndex index, InitialPosition pos = InitialPosition.UNPOSITIONED) {
388       _iter = index.newIterator(pos);
389     }
390 
391     /**
392      * Returns the S2CellId of the current index cell.  If done() is true,
393      * returns a value larger than any valid S2CellId (S2CellId::Sentinel()).
394      */
395     S2CellId id() const {
396       return _iter.id();
397     }
398 
399     /// Returns the center point of the cell.
400     S2Point center() const
401     in {
402       assert(!done());
403     } do {
404       return id().toS2Point();
405     }
406 
407     /// Returns a reference to the contents of the current index cell.
408     inout(S2ShapeIndexCell) cell() inout
409     in {
410       assert(!done());
411     } do {
412       return _iter.cell();
413     }
414 
415     /// Returns true if the iterator is positioned past the last index cell.
416     bool done() const {
417       return _iter.done();
418     }
419 
420     /// Positions the iterator at the first index cell (if any).
421     void begin() {
422       _iter.begin();
423     }
424 
425     /// Positions the iterator past the last index cell.
426     void finish() {
427       _iter.finish();
428     }
429 
430     /// Positions the iterator at the next index cell.
431     void next()
432     in {
433       assert(!done());
434     } do {
435       _iter.next();
436     }
437 
438     /**
439      * If the iterator is already positioned at the beginning, returns false.
440      * Otherwise positions the iterator at the previous entry and returns true.
441      */
442     bool prev() {
443       return _iter.prev();
444     }
445 
446     /**
447      * Positions the iterator at the first cell with id() >= target, or at the
448      * end of the index if no such cell exists.
449      */
450     void seek(S2CellId target) {
451       _iter.seek(target);
452     }
453 
454     /**
455      * Positions the iterator at the cell containing "target".  If no such cell
456      * exists, returns false and leaves the iterator positioned arbitrarily.
457      * The returned index cell is guaranteed to contain all edges that might
458      * intersect the line segment between "target" and the cell center.
459      */
460     bool locate(in S2Point target) {
461       return IteratorBase.locateImpl(target, this);
462     }
463 
464     /**
465      * Let T be the target S2CellId.  If T is contained by some index cell I
466      * (including equality), this method positions the iterator at I and
467      * returns INDEXED.  Otherwise if T contains one or more (smaller) index
468      * cells, it positions the iterator at the first such cell I and returns
469      * SUBDIVIDED.  Otherwise it returns DISJOINT and leaves the iterator
470      * positioned arbitrarily.
471      */
472     CellRelation locate(S2CellId target) {
473       return IteratorBase.locateImpl(target, this);
474     }
475 
476     void copy(Iterator other) {
477       _iter = other._iter.clone();
478     }
479 
480   private:
481     // Although S2ShapeIndex::Iterator can be used to iterate over any
482     // index subtype, it is more efficient to use the subtype's iterator when
483     // the subtype is known at compile time.  For example, MutableS2ShapeIndex
484     // should use a MutableS2ShapeIndex::Iterator.
485     //
486     // The following declarations prevent accidental use of
487     // S2ShapeIndex::Iterator when the actual subtype is known.  (If you
488     // really want to do this, you can down_cast the index argument to
489     // S2ShapeIndex.)
490     // template <class T>
491     // explicit Iterator(const T* index, InitialPosition pos = UNPOSITIONED) {}
492 
493     // template <class T>
494     // void Init(const T* index, InitialPosition pos = UNPOSITIONED) {}
495 
496     IteratorBase _iter;
497   }
498 
499 protected:
500   /**
501    * Each subtype of S2ShapeIndex should define an Iterator type derived
502    * from the following base class.
503    */
504   abstract static class IteratorBase {
505   public:
506     this(IteratorBase other) {
507       _id = other._id;
508       _cell = other.rawCell();
509     }
510 
511     /**
512      * Returns the S2CellId of the current index cell.  If done() is true,
513      * returns a value larger than any valid S2CellId (S2CellId::Sentinel()).
514      */
515     @property
516     S2CellId id() const {
517       return _id;
518     }
519 
520     /// Returns the center point of the cell.
521     S2Point center() const
522     in {
523       assert(!done());
524     } do {
525       return id().toS2Point();
526     }
527 
528     /// Returns a reference to the contents of the current index cell.
529     inout(S2ShapeIndexCell) cell() inout
530     in {
531       assert(!done());
532     } do {
533       auto cell = _cell;
534       // if (cell == null) {
535       //   cell = getCell();
536       //   setCell(cell);
537       // }
538       return cell;
539     }
540 
541     /// Returns true if the iterator is positioned past the last index cell.
542     bool done() const {
543       return _id == S2CellId.sentinel();
544     }
545 
546     /// Positions the iterator at the first index cell (if any).
547     abstract void begin();
548 
549     /// Positions the iterator past the last index cell.
550     abstract void finish();
551 
552     /// Positions the iterator at the next index cell.
553     abstract void next()
554     in {
555       assert(!done());
556     }
557 
558     /**
559      * If the iterator is already positioned at the beginning, returns false.
560      * Otherwise positions the iterator at the previous entry and returns true.
561      */
562     abstract bool prev();
563 
564     /**
565      * Positions the iterator at the first cell with id() >= target, or at the
566      * end of the index if no such cell exists.
567      */
568     abstract void seek(S2CellId target);
569 
570     /**
571      * Positions the iterator at the cell containing "target".  If no such cell
572      * exists, returns false and leaves the iterator positioned arbitrarily.
573      * The returned index cell is guaranteed to contain all edges that might
574      * intersect the line segment between "target" and the cell center.
575      */
576     abstract bool locate(in S2Point target);
577 
578     /**
579      * Let T be the target S2CellId.  If T is contained by some index cell I
580      * (including equality), this method positions the iterator at I and
581      * returns INDEXED.  Otherwise if T contains one or more (smaller) index
582      * cells, it positions the iterator at the first such cell I and returns
583      * SUBDIVIDED.  Otherwise it returns DISJOINT and leaves the iterator
584      * positioned arbitrarily.
585      */
586     abstract CellRelation locate(in S2CellId target);
587 
588     /**
589      * Makes a copy of the given source iterator.
590      * REQUIRES: "other" has the same concrete type as "this".
591      */
592     abstract void copy(IteratorBase other);
593 
594   protected:
595     this() {
596       _id = S2CellId.sentinel();
597       _cell = null;
598     }
599 
600     /**
601      * Sets the iterator state.  "cell" typically points to the cell contents,
602      * but may also be given as "nullptr" in order to implement decoding on
603      * demand.  In that situation, the first that the client attempts to
604      * access the cell contents, the GetCell() method is called and "cell_" is
605      * updated in a thread-safe way.
606      */
607     void setState(S2CellId id, S2ShapeIndexCell cell)
608     in {
609       assert(cell !is null);
610     } do {
611       _id = id;
612       setCell(cell);
613     }
614 
615     /// Sets the iterator state so that done() is true.
616     void setFinished() {
617       _id = S2CellId.sentinel();
618       setCell(null);
619     }
620 
621     /**
622      * Returns the current contents of the "cell_" field, which may be null
623      * if the cell contents have not been decoded yet.
624      */
625     inout(S2ShapeIndexCell) rawCell() inout {
626       return _cell;
627     }
628 
629     /**
630      * This method is called to decode the contents of the current cell, if
631      * set_state() was previously called with a nullptr "cell" argument.  This
632      * allows decoding on demand for subtypes that keep the cell contents in
633      * an encoded state.  It does not need to be implemented at all if
634      * set_state() is always called with (cell != nullptr).
635      *
636      * REQUIRES: This method is thread-safe.
637      * REQUIRES: Multiple calls to this method return the same value.
638      */
639     abstract const(S2ShapeIndexCell) getCell() const;
640 
641     /// Returns an exact copy of this iterator.
642     abstract IteratorBase clone();
643 
644     /**
645      * The default implementation of Locate(S2Point).  It is instantiated by
646      * each subtype in order to (1) minimize the number of virtual method
647      * calls (since subtypes are typically "final") and (2) ensure that the
648      * correct versions of non-virtual methods such as cell() are called.
649      */
650     static bool locateImpl(IterT)(in S2Point target_point, IterT it) {
651       // Let I = cell_map_->lower_bound(T), where T is the leaf cell containing
652       // "target_point".  Then if T is contained by an index cell, then the
653       // containing cell is either I or I'.  We test for containment by comparing
654       // the ranges of leaf cells spanned by T, I, and I'.
655 
656       auto target = S2CellId(target_point);
657       it.seek(target);
658       if (!it.done() && it.id().rangeMin() <= target) return true;
659       if (it.prev() && it.id().rangeMax() >= target) return true;
660       return false;
661     }
662 
663 
664     // The default implementation of Locate(S2CellId) (see comments above).
665     static CellRelation locateImpl(IterT)(in S2CellId target, IterT it) {
666       // Let T be the target, let I = cell_map_->lower_bound(T.range_min()), and
667       // let I' be the predecessor of I.  If T contains any index cells, then T
668       // contains I.  Similarly, if T is contained by an index cell, then the
669       // containing cell is either I or I'.  We test for containment by comparing
670       // the ranges of leaf cells spanned by T, I, and I'.
671 
672       it.seek(target.rangeMin());
673       if (!it.done()) {
674         if (it.id() >= target && it.id().rangeMin() <= target) return CellRelation.INDEXED;
675         if (it.id() <= target.rangeMax()) return CellRelation.SUBDIVIDED;
676       }
677       if (it.prev() && it.id().rangeMax() >= target) return CellRelation.INDEXED;
678       return CellRelation.DISJOINT;
679     }
680 
681   private:
682 
683     void setCell(S2ShapeIndexCell cell) {
684       _cell = cell;
685     }
686 
687     S2CellId _id;
688 
689     // Must be accessed atomically using atomicLoad and atomicStore.
690     S2ShapeIndexCell _cell;
691   }
692 
693   // Returns a new iterator positioned as specified.
694   abstract IteratorBase newIterator(InitialPosition pos);
695 }