Coverage Report

Created: 2024-09-23 06:29

/src/s2geometry/src/s2/s2shape_index.h
Line
Count
Source (jump to first uncovered line)
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
16
// Author: ericv@google.com (Eric Veach)
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
// S2ShapeIndex has two major subtypes:
23
//
24
//  - MutableS2ShapeIndex is for building new S2ShapeIndexes.  Indexes may
25
//    also be updated dynamically by inserting or deleting shapes.  Once an
26
//    index has been built it can be encoded compactly and later decoded as
27
//    either a MutableS2ShapeIndex or an EncodedS2ShapeIndex.
28
//
29
//  - EncodedS2ShapeIndex is an S2ShapeIndex implementation that works
30
//    directly with encoded data.  Rather than decoding everything in advance,
31
//    geometry is decoded incrementally (down to individual edges) as needed.
32
//    It can be initialized from a single block of data in nearly constant
33
//    time.  This saves large amounts of memory and is also much faster in the
34
//    common situation where geometric data is loaded from somewhere, decoded,
35
//    and then only a single operation is performed on it.  (Speedups for
36
//    large geometric objects can be over 1000x.)  It supports all
37
//    S2ShapeIndex operations including boolean operations, measuring
38
//    distances, etc.
39
40
#ifndef S2_S2SHAPE_INDEX_H_
41
#define S2_S2SHAPE_INDEX_H_
42
43
#include <array>
44
#include <cstddef>
45
#include <cstdint>
46
#include <iterator>
47
#include <memory>
48
#include <type_traits>
49
#include <utility>
50
51
#include "absl/log/absl_check.h"
52
#include "absl/types/span.h"
53
54
#include "s2/_fp_contract_off.h"  // IWYU pragma: keep
55
#include "s2/s2cell_id.h"
56
#include "s2/s2cell_iterator.h"
57
#include "s2/s2point.h"
58
#include "s2/s2shape.h"
59
#include "s2/util/coding/coder.h"
60
#include "s2/util/gtl/compact_array.h"
61
62
class R1Interval;
63
class S2PaddedCell;
64
65
// S2ClippedShape represents the part of a shape that intersects an S2Cell.
66
// It consists of the set of edge ids that intersect that cell, and a boolean
67
// indicating whether the center of the cell is inside the shape (for shapes
68
// that have an interior).
69
//
70
// Note that the edges themselves are not clipped; we always use the original
71
// edges for intersection tests so that the results will be the same as the
72
// original shape.
73
class S2ClippedShape {
74
 public:
75
0
  S2ClippedShape() : num_edges_(0) {}
76
77
  // The shape id of the clipped shape.
78
  int shape_id() const;
79
80
  // Returns true if the center of the S2CellId is inside the shape.  Returns
81
  // false for shapes that do not have an interior.
82
  bool contains_center() const;
83
84
  // The number of edges that intersect the S2CellId.
85
  int num_edges() const;
86
87
  // Returns the edge id of the given edge in this clipped shape.  Edges are
88
  // sorted in increasing order of edge id.
89
  //
90
  // REQUIRES: 0 <= i < num_edges()
91
  int edge(int i) const;
92
93
  // Returns true if the clipped shape contains the given edge id.
94
  bool ContainsEdge(int id) const;
95
96
 private:
97
  // This class may be copied by value, but note that it does *not* own its
98
  // underlying data.  (It is owned by the containing S2ShapeIndexCell.)
99
100
  friend class MutableS2ShapeIndex;
101
  friend class S2ShapeIndexCell;
102
  friend class S2Stats;
103
104
  // Internal methods are documented with their definition.
105
  void Init(int32_t shape_id, int32_t num_edges);
106
  void Destruct();
107
  bool is_inline() const;
108
  void set_contains_center(bool contains_center);
109
  void set_edge(int i, int edge);
110
111
  // All fields are packed into 16 bytes (assuming 64-bit pointers).  Up to
112
  // two edge ids are stored inline; this is an important optimization for
113
  // clients that use S2Shapes consisting of a single edge.
114
  int32_t shape_id_;
115
  uint32_t contains_center_ : 1;  // shape contains the cell center
116
  // TODO(user): Use in-class initializer when C++20 is allowed in
117
  // opensource version.
118
  uint32_t num_edges_ : 31;
119
120
  // The maximum number of edges that we can store inline in the union.
121
  static constexpr int kMaxInlineEdges = 2;
122
123
  // If there are more than two edges, this field holds a pointer.
124
  // Otherwise it holds an array of edge ids.
125
  union {
126
    int32_t* edges_;  // Owned by the containing S2ShapeIndexCell.
127
    std::array<int32_t, kMaxInlineEdges> inline_edges_;
128
  };
129
};
130
131
// S2ShapeIndexCell stores the index contents for a particular S2CellId.
132
// It consists of a set of clipped shapes.
133
class S2ShapeIndexCell {
134
 public:
135
0
  S2ShapeIndexCell() = default;
136
  ~S2ShapeIndexCell();
137
138
  // Returns the number of clipped shapes in this cell.
139
0
  int num_clipped() const { return shapes_.size(); }
140
141
  // Returns the clipped shape at the given index.  Shapes are kept sorted in
142
  // increasing order of shape id.
143
  //
144
  // REQUIRES: 0 <= i < num_clipped()
145
0
  const S2ClippedShape& clipped(int i) const { return shapes_[i]; }
146
147
  // Returns a pointer to the clipped shape corresponding to the given shape,
148
  // or nullptr if the shape does not intersect this cell.
149
  const S2ClippedShape* find_clipped(int shape_id) const;
150
151
  // Returns a read-only span over the clipped shapes in the cell.
152
0
  absl::Span<const S2ClippedShape> clipped_shapes() const {
153
0
    if (!shapes_.empty()) {
154
0
      return {shapes_.begin(), shapes_.size()};
155
0
    }
156
0
    return {};
157
0
  }
158
159
  // Convenience method that returns the total number of edges in all clipped
160
  // shapes.
161
  int num_edges() const;
162
163
  // Appends an encoded representation of the S2ShapeIndexCell to "encoder".
164
  // "num_shape_ids" should be set to index.num_shape_ids(); this information
165
  // allows the encoding to be more compact in some cases.
166
  //
167
  // REQUIRES: "encoder" uses the default constructor, so that its buffer
168
  //           can be enlarged as necessary by calling Ensure(int).
169
  void Encode(int num_shape_ids, Encoder* encoder) const;
170
171
  // Decodes an S2ShapeIndexCell, returning true on success.
172
  // "num_shape_ids" should be set to index.num_shape_ids().
173
  bool Decode(int num_shape_ids, Decoder* decoder);
174
175
 private:
176
  friend class MutableS2ShapeIndex;
177
  friend class EncodedS2ShapeIndex;
178
  friend class S2Stats;
179
180
  // Internal methods are documented with their definitions.
181
  S2ClippedShape* add_shapes(int n);
182
  static void EncodeEdges(const S2ClippedShape& clipped, Encoder* encoder);
183
  static bool DecodeEdges(int num_edges, S2ClippedShape* clipped,
184
                          Decoder* decoder);
185
186
  using S2ClippedShapeSet = gtl::compact_array<S2ClippedShape>;
187
  S2ClippedShapeSet shapes_;
188
189
  S2ShapeIndexCell(const S2ShapeIndexCell&) = delete;
190
  void operator=(const S2ShapeIndexCell&) = delete;
191
};
192
193
// S2ShapeIndex is an abstract base class for indexing polygonal geometry in
194
// memory.  The objects in the index are known as "shapes", and may consist of
195
// points, polylines, and/or polygons, possibly overlapping.  The index makes
196
// it very fast to answer queries such as finding nearby shapes, measuring
197
// distances, testing for intersection and containment, etc.
198
//
199
// Each object in the index implements the S2Shape interface.  An S2Shape is a
200
// collection of edges that optionally defines an interior.  The edges do not
201
// need to be connected, so for example an S2Shape can represent a polygon
202
// with multiple shells and/or holes, or a set of polylines, or a set of
203
// points.  All geometry within a single S2Shape must have the same dimension,
204
// so for example if you want to create an S2ShapeIndex containing a polyline
205
// and 10 points, then you will need at least two different S2Shape objects.
206
//
207
// There are two important types of S2ShapeIndex.  MutableS2ShapeIndex allows
208
// you to build an index incrementally by adding or removing shapes, whereas
209
// EncodedS2ShapeIndex works very efficiently with existing indexes by keeping
210
// the index data in encoded form (see introduction at the top of this file).
211
//
212
// Code that only needs read-only ("const") access to an index should use the
213
// S2ShapeIndex base class as the parameter type, so that it will work with
214
// any S2ShapeIndex subtype.  For example:
215
//
216
//   void DoSomething(const S2ShapeIndex& index) {
217
//     ... works with MutableS2ShapeIndex or EncodedS2ShapeIndex ...
218
//   }
219
//
220
// There are a number of built-in classes that work with S2ShapeIndex objects.
221
// Generally these classes accept any collection of geometry that can be
222
// represented by an S2ShapeIndex, i.e. any combination of points, polylines,
223
// and polygons.  Such classes include:
224
//
225
// - S2ContainsPointQuery: returns the shape(s) that contain a given point.
226
//
227
// - S2ClosestEdgeQuery: returns the closest edge(s) to a given point, edge,
228
//                       S2CellId, or S2ShapeIndex.
229
//
230
// - S2CrossingEdgeQuery: returns the edge(s) that cross a given edge.
231
//
232
// - S2BooleanOperation: computes boolean operations such as union,
233
//                       and boolean predicates such as containment.
234
//
235
// - S2ShapeIndexRegion: can be used together with S2RegionCoverer to
236
//                       approximate geometry as a set of S2CellIds.
237
//
238
// - S2ShapeIndexBufferedRegion: computes approximations that have been
239
//                               expanded by a given radius.
240
//
241
// Here is an example showing how to index a set of polygons and then
242
// determine which polygon(s) contain each of a set of query points:
243
//
244
//   void TestContainment(const vector<S2Point>& points,
245
//                        const vector<S2Polygon*>& polygons) {
246
//     MutableS2ShapeIndex index;
247
//     for (auto polygon : polygons) {
248
//       index.Add(std::make_unique<S2Polygon::Shape>(polygon));
249
//     }
250
251
//     auto query = MakeS2ContainsPointQuery(&index);
252
//     for (const auto& point : points) {
253
//       for (int shape_id : query.GetContainingShapeIds(point)) {
254
//         S2Polygon* polygon = polygons[shape_id];
255
//         ... do something with (point, polygon) ...
256
//       }
257
//     }
258
//   }
259
//
260
// This example uses S2Polygon::Shape, which is one example of an S2Shape
261
// object.  S2Polyline and S2Loop also have nested Shape classes, and there are
262
// additional S2Shape types defined in *_shape.h.
263
//
264
// Internally, an S2ShapeIndex is essentially a map from S2CellIds to the set
265
// of shapes that intersect each S2CellId.  It is adaptively refined to ensure
266
// that no cell contains more than a small number of edges.
267
//
268
// In addition to implementing a shared set of virtual methods, all
269
// S2ShapeIndex subtypes define an Iterator type with the same API.  This
270
// makes it easy to convert code that uses a particular S2ShapeIndex subtype
271
// to instead use the abstract base class (or vice versa).  You can also
272
// choose to avoid the overhead of virtual method calls by making the
273
// S2ShapeIndex type a template argument, like this:
274
//
275
//   template <class IndexType>
276
//   void DoSomething(const IndexType& index) {
277
//     for (typename IndexType::Iterator it(&index, S2ShapeIndex::BEGIN);
278
//          !it.done(); it.Next()) {
279
//       ...
280
//     }
281
//   }
282
//
283
// The S2ShapeIndex subtypes provided by the S2 library are thread-compatible,
284
// meaning that const methods may be called concurrently from multiple threads,
285
// while non-const methods require exclusive access to the S2ShapeIndex.
286
class S2ShapeIndex {
287
 public:
288
  // Each subtype of S2ShapeIndex should define an Iterator type derived
289
  // from the following base class.
290
  class IteratorBase : public S2CellIterator {
291
   public:
292
    // Returns a reference to the contents of the current index cell.
293
    // REQUIRES: !done()
294
    virtual const S2ShapeIndexCell& cell() const = 0;
295
296
    // Returns a newly allocated copy of this iterator.
297
    virtual std::unique_ptr<IteratorBase> Clone() const = 0;
298
299
   protected:
300
    // Protect the default constructor and move/copy constructors and operators.
301
    // This allows sub-classes to still be default/copy/move-constructible but
302
    // prevents accidental slicing through a base class pointer.
303
7.21k
    IteratorBase() = default;
304
0
    IteratorBase(const IteratorBase&) = default;
305
    IteratorBase& operator=(const IteratorBase&) = default;
306
0
    IteratorBase(IteratorBase&&) = default;
307
    IteratorBase& operator=(IteratorBase&&) = default;
308
  };
309
310
  // A type function to check if a type is derived from S2ShapeIndex.  This is
311
  // useful for writing static checks on template parameters when we want to
312
  // inline a particular iterator call, but we need to make sure it implements
313
  // the interface that we want.  We don't have access to c++ concepts, so this
314
  // is the next best thing:
315
  //
316
  //   template <typename Index>
317
  //   void Process(Index& iter) {
318
  //     static_assert(S2ShapeIndex::ImplementedBy<Index>{},
319
  //       "We require an S2ShapeIndex.");
320
  //   }
321
  template <typename T>
322
  using ImplementedBy = std::is_convertible<std::decay_t<T>*, S2ShapeIndex*>;
323
324
7.21k
  virtual ~S2ShapeIndex() = default;
325
326
  // Returns the number of distinct shape ids in the index.  This is the same
327
  // as the number of shapes provided that no shapes have ever been removed.
328
  // (Shape ids are never reused.)
329
  virtual int num_shape_ids() const = 0;
330
331
  // Returns a pointer to the shape with the given id, or nullptr if the shape
332
  // has been removed from the index.
333
  virtual const S2Shape* shape(int id) const = 0;
334
335
  // Stores an encoded representation of the index into the given encoder.
336
  virtual void Encode(Encoder* encoder) const = 0;
337
338
  // Allows iterating over the indexed shapes using range-based for loops:
339
  //
340
  //   for (S2Shape* shape : index) { ... }
341
  //
342
  // CAVEAT: Returns nullptr for shapes that have been removed from the index.
343
  class ShapeIterator {
344
   public:
345
    using iterator_category = std::forward_iterator_tag;
346
    using value_type = const S2Shape*;
347
    using difference_type = std::ptrdiff_t;
348
    using pointer = const S2Shape**;
349
    using reference = const S2Shape*&;
350
351
    ShapeIterator() = default;
352
    const S2Shape* operator*() const;
353
    ShapeIterator& operator++();
354
    ShapeIterator operator++(int);
355
356
    // REQUIRES: "it" and *this must reference the same S2ShapeIndex.
357
    bool operator==(ShapeIterator it) const;
358
359
    // REQUIRES: "it" and *this must reference the same S2ShapeIndex.
360
    bool operator!=(ShapeIterator it) const;
361
362
   private:
363
    friend class S2ShapeIndex;
364
    ShapeIterator(const S2ShapeIndex* index, int shape_id)
365
0
        : index_(index), shape_id_(shape_id) {}
366
367
    const S2ShapeIndex* index_ = nullptr;
368
    int shape_id_ = 0;
369
  };
370
  ShapeIterator begin() const;
371
  ShapeIterator end() const;
372
373
  // Returns the number of bytes currently occupied by the index (including any
374
  // unused space at the end of vectors, etc).
375
  virtual size_t SpaceUsed() const = 0;
376
377
  // Minimizes memory usage by requesting that any data structures that can be
378
  // rebuilt should be discarded.  This method invalidates all iterators.
379
  //
380
  // Like all non-const methods, this method is not thread-safe.
381
  virtual void Minimize() = 0;
382
383
  // When passed to an Iterator constructor, specifies whether the iterator
384
  // should be positioned at the beginning of the index (BEGIN), the end of
385
  // the index (END), or arbitrarily (UNPOSITIONED).  By default iterators are
386
  // unpositioned, since this avoids an extra seek in this situation where one
387
  // of the seek methods (such as Locate) is immediately called.
388
  enum InitialPosition { BEGIN, END, UNPOSITIONED };
389
390
  // A random access iterator that provides low-level access to the cells of
391
  // the index.  Cells are sorted in increasing order of S2CellId.
392
  class Iterator final : public IteratorBase {
393
   public:
394
    // Default constructor; must be followed by a call to Init().
395
0
    Iterator() = default;
396
397
    // Constructs an iterator positioned as specified.  By default iterators
398
    // are unpositioned, since this avoids an extra seek in this situation
399
    // where one of the seek methods (such as Locate) is immediately called.
400
    //
401
    // If you want to position the iterator at the beginning, e.g. in order to
402
    // loop through the entire index, do this instead:
403
    //
404
    //   for (S2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
405
    //        !it.done(); it.Next()) { ... }
406
    explicit Iterator(const S2ShapeIndex* index,
407
0
                      InitialPosition pos = UNPOSITIONED) {
408
0
      Init(index, pos);
409
0
    }
410
411
    // Initializes an iterator for the given S2ShapeIndex.  This method may
412
    // also be called in order to restore an iterator to a valid state after
413
    // the underlying index has been updated (although it is usually easier
414
    // just to declare a new iterator whenever required, since iterator
415
    // construction is cheap).
416
    void Init(const S2ShapeIndex* index,
417
0
              InitialPosition pos = UNPOSITIONED) {
418
0
      iter_ = index->NewIterator(pos);
419
0
    }
420
421
    // Iterators are copyable and movable.
422
    Iterator(const Iterator&);
423
    Iterator& operator=(const Iterator&);
424
0
    Iterator(Iterator&&) = default;
425
    Iterator& operator=(Iterator&&) = default;
426
427
    // Returns the S2CellId of the current index cell.  If done() is true,
428
    // returns a value larger than any valid S2CellId (S2CellId::Sentinel()).
429
0
    S2CellId id() const override { return iter_->id(); }
430
431
    // Returns the center point of the cell.
432
    // REQUIRES: !done()
433
0
    S2Point center() const { return id().ToPoint(); }
434
435
    // Returns a reference to the contents of the current index cell.
436
    // REQUIRES: !done()
437
0
    const S2ShapeIndexCell& cell() const override { return iter_->cell(); }
438
439
    // Returns true if the iterator is positioned past the last index cell.
440
0
    bool done() const override { return iter_->done(); }
441
442
    // Positions the iterator at the first index cell (if any).
443
0
    void Begin() override { iter_->Begin(); }
444
445
    // Positions the iterator past the last index cell.
446
0
    void Finish() override { iter_->Finish(); }
447
448
    // Positions the iterator at the next index cell.
449
    // REQUIRES: !done()
450
0
    void Next() override { iter_->Next(); }
451
452
    // If the iterator is already positioned at the beginning, returns false.
453
    // Otherwise positions the iterator at the previous entry and returns true.
454
0
    bool Prev() override { return iter_->Prev(); }
455
456
    // Positions the iterator at the first cell with id() >= target, or at the
457
    // end of the index if no such cell exists.
458
0
    void Seek(S2CellId target) override { iter_->Seek(target); }
459
460
    // Positions the iterator at the cell containing target and returns true. If
461
    // no such cell exists, return false and leave the iterator in an undefined
462
    // (but valid) state.
463
0
    bool Locate(const S2Point& target) override {
464
0
      return LocateImpl(*this, target);
465
0
    }
466
467
    // Let T be the target S2CellId.  If T is contained by some index cell I
468
    // (including equality), this method positions the iterator at I and
469
    // returns INDEXED.  Otherwise if T contains one or more (smaller) index
470
    // cells, it positions the iterator at the first such cell I and returns
471
    // SUBDIVIDED.  Otherwise it returns DISJOINT and leaves the iterator
472
    // positioned arbitrarily.
473
0
    S2CellRelation Locate(S2CellId target) override {
474
0
      return LocateImpl(*this, target);
475
0
    }
476
477
    // Clone our underlying iterator to clone this iterator.
478
0
    std::unique_ptr<IteratorBase> Clone() const override {
479
0
      return std::make_unique<Iterator>(Iterator(iter_->Clone()));
480
0
    };
481
482
   private:
483
    // Although S2ShapeIndex::Iterator can be used to iterate over any
484
    // index subtype, it is more efficient to use the subtype's iterator when
485
    // the subtype is known at compile time.  For example, MutableS2ShapeIndex
486
    // should use a MutableS2ShapeIndex::Iterator.
487
    //
488
    // The following declarations prevent accidental use of
489
    // S2ShapeIndex::Iterator when the actual subtype is known.  (If you
490
    // really want to do this, you can down_cast the index argument to
491
    // S2ShapeIndex.)
492
    template <class T>
493
    explicit Iterator(const T* index, InitialPosition pos = UNPOSITIONED) {}
494
495
    explicit Iterator(std::unique_ptr<IteratorBase> iter)
496
0
        : iter_(std::move(iter)) {}
497
498
    std::unique_ptr<IteratorBase> iter_;
499
  };
500
501
  // ShapeFactory is an interface for decoding vectors of S2Shapes.  It allows
502
  // random access to the shapes in order to support lazy decoding.  See
503
  // s2shapeutil_coding.h for useful subtypes.
504
  class ShapeFactory {
505
   public:
506
    virtual ~ShapeFactory() = default;
507
508
    // Returns the number of S2Shapes in the vector.
509
    virtual int size() const = 0;
510
511
    // Returns the S2Shape object corresponding to the given "shape_id".
512
    // Returns nullptr if a shape cannot be decoded or a shape is missing
513
    // (e.g., because MutableS2ShapeIndex::Release() was called).
514
    virtual std::unique_ptr<S2Shape> operator[](int shape_id) const = 0;
515
516
    // Returns a deep copy of this ShapeFactory.
517
    virtual std::unique_ptr<ShapeFactory> Clone() const = 0;
518
  };
519
520
 protected:
521
  // Returns a new iterator positioned as specified.
522
  virtual std::unique_ptr<IteratorBase> NewIterator(InitialPosition pos)
523
      const = 0;
524
525
7.21k
  S2ShapeIndex() = default;
526
  S2ShapeIndex(const S2ShapeIndex&) = delete;
527
  S2ShapeIndex(S2ShapeIndex&&) = delete;
528
  S2ShapeIndex& operator=(const S2ShapeIndex&) = delete;
529
  S2ShapeIndex& operator=(S2ShapeIndex&&) = delete;
530
};
531
532
//////////////////   Implementation details follow   ////////////////////
533
534
535
0
inline int S2ClippedShape::shape_id() const {
536
0
  return shape_id_;
537
0
}
538
539
0
inline bool S2ClippedShape::contains_center() const {
540
0
  return contains_center_;
541
0
}
542
543
0
inline int S2ClippedShape::num_edges() const {
544
0
  return num_edges_;
545
0
}
546
547
0
inline int S2ClippedShape::edge(int i) const {
548
0
  return is_inline() ? inline_edges_[i] : edges_[i];
549
0
}
550
551
// Initialize an S2ClippedShape to hold the given number of edges.
552
0
inline void S2ClippedShape::Init(int32_t shape_id, int32_t num_edges) {
553
0
  shape_id_ = shape_id;
554
0
  num_edges_ = num_edges;
555
0
  contains_center_ = false;
556
0
  if (!is_inline()) {
557
0
    edges_ = new int32_t[num_edges];
558
0
  }
559
0
}
560
561
// Free any memory allocated by this S2ClippedShape.  We don't do this in
562
// the destructor because S2ClippedShapes are copied by STL code, and we
563
// don't want to repeatedly copy and free the edge data.  Instead the data
564
// is owned by the containing S2ShapeIndexCell.
565
0
inline void S2ClippedShape::Destruct() {
566
0
  if (!is_inline()) delete[] edges_;
567
0
}
568
569
0
inline bool S2ClippedShape::is_inline() const {
570
0
  return num_edges_ <= kMaxInlineEdges;
571
0
}
572
573
// Set "contains_center_" to indicate whether this clipped shape contains the
574
// center of the cell to which it belongs.
575
0
inline void S2ClippedShape::set_contains_center(bool contains_center) {
576
0
  contains_center_ = contains_center;
577
0
}
578
579
// Set the i-th edge of this clipped shape to be the given edge of the
580
// original shape.
581
0
inline void S2ClippedShape::set_edge(int i, int edge) {
582
0
  if (is_inline()) {
583
0
    inline_edges_[i] = edge;
584
0
  } else {
585
0
    edges_[i] = edge;
586
0
  }
587
0
}
588
589
// Inline because an index cell frequently contains just one shape.
590
0
inline int S2ShapeIndexCell::num_edges() const {
591
0
  int n = 0;
592
0
  for (int i = 0; i < num_clipped(); ++i) n += clipped(i).num_edges();
593
0
  return n;
594
0
}
595
596
0
inline const S2Shape* S2ShapeIndex::ShapeIterator::operator*() const {
597
0
  return index_->shape(shape_id_);
598
0
}
599
600
0
inline S2ShapeIndex::ShapeIterator& S2ShapeIndex::ShapeIterator::operator++() {
601
0
  ++shape_id_;
602
0
  return *this;
603
0
}
604
605
inline S2ShapeIndex::ShapeIterator S2ShapeIndex::ShapeIterator::operator++(
606
0
    int) {
607
0
  return ShapeIterator(index_, shape_id_++);
608
0
}
609
610
0
inline bool S2ShapeIndex::ShapeIterator::operator==(ShapeIterator it) const {
611
0
  ABSL_DCHECK_EQ(index_, it.index_);
612
0
  return shape_id_ == it.shape_id_;
613
0
}
614
615
0
inline bool S2ShapeIndex::ShapeIterator::operator!=(ShapeIterator it) const {
616
0
  ABSL_DCHECK_EQ(index_, it.index_);
617
0
  return shape_id_ != it.shape_id_;
618
0
}
619
620
0
inline S2ShapeIndex::ShapeIterator S2ShapeIndex::begin() const {
621
0
  return ShapeIterator(this, 0);
622
0
}
623
624
0
inline S2ShapeIndex::ShapeIterator S2ShapeIndex::end() const {
625
0
  return ShapeIterator(this, num_shape_ids());
626
0
}
627
628
inline S2ShapeIndex::Iterator::Iterator(const Iterator& other)
629
0
    : iter_(other.iter_->Clone()) {
630
0
}
631
632
inline S2ShapeIndex::Iterator& S2ShapeIndex::Iterator::operator=(
633
0
    const Iterator& other) {
634
0
  iter_ = other.iter_->Clone();
635
0
  return *this;
636
0
}
637
638
#endif  // S2_S2SHAPE_INDEX_H_