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 }