[go: up one dir, main page]

File: s2loop.h

package info (click to toggle)
s2geometry 0.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 5,564 kB
  • sloc: cpp: 65,177; python: 763; makefile: 5
file content (735 lines) | stat: -rw-r--r-- 31,866 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
// Copyright 2005 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Author: ericv@google.com (Eric Veach)

#ifndef S2_S2LOOP_H_
#define S2_S2LOOP_H_

#include <atomic>
#include <bitset>
#include <cmath>
#include <cstddef>
#include <map>
#include <vector>

#include "absl/base/macros.h"
#include "absl/types/span.h"

#include "s2/base/integral_types.h"
#include "s2/base/logging.h"
#include "s2/_fp_contract_off.h"
#include "s2/mutable_s2shape_index.h"
#include "s2/s1angle.h"
#include "s2/s1chord_angle.h"
#include "s2/s2debug.h"
#include "s2/s2latlng_rect.h"
#include "s2/s2loop_measures.h"
#include "s2/s2pointutil.h"
#include "s2/s2region.h"
#include "s2/s2shape_index.h"
#include "s2/util/math/matrix3x3.h"
#include "s2/util/math/vector.h"

class Decoder;
class Encoder;
class LoopCrosser;
class LoopRelation;
class MergingIterator;
class S2Cap;
class S2Cell;
class S2CrossingEdgeQuery;
class S2Error;
class S2Loop;
struct S2XYZFaceSiTi;
namespace s2builderutil { class S2PolygonLayer; }

// An S2Loop represents a simple spherical polygon.  It consists of a single
// chain of vertices where the first vertex is implicitly connected to the
// last. All loops are defined to have a CCW orientation, i.e. the interior of
// the loop is on the left side of the edges.  This implies that a clockwise
// loop enclosing a small area is interpreted to be a CCW loop enclosing a
// very large area.
//
// Loops are not allowed to have any duplicate vertices (whether adjacent or
// not).  Non-adjacent edges are not allowed to intersect, and furthermore edges
// of length 180 degrees are not allowed (i.e., adjacent vertices cannot be
// antipodal).  Loops must have at least 3 vertices (except for the empty and
// full loops discussed below).  Although these restrictions are not enforced
// in optimized code, you may get unexpected results if they are violated.
//
// There are two special loops: the "empty loop" contains no points, while the
// "full loop" contains all points.  These loops do not have any edges, but to
// preserve the invariant that every loop can be represented as a vertex
// chain, they are defined as having exactly one vertex each (see kEmpty and
// kFull).
//
// Point containment of loops is defined such that if the sphere is subdivided
// into faces (loops), every point is contained by exactly one face.  This
// implies that loops do not necessarily contain their vertices.
//
// Note: The reason that duplicate vertices and intersecting edges are not
// allowed is that they make it harder to define and implement loop
// relationships, e.g. whether one loop contains another.  If your data does
// not satisfy these restrictions, you can use S2Builder to normalize it.
class S2Loop final : public S2Region {
 public:
  // Default constructor.  The loop must be initialized by calling Init() or
  // Decode() before it is used.
  S2Loop();

  // Convenience constructor that calls Init() with the given vertices.
  explicit S2Loop(absl::Span<const S2Point> vertices);

  // Convenience constructor to disable the automatic validity checking
  // controlled by the --s2debug flag.  Example:
  //
  //   S2Loop* loop = new S2Loop(vertices, S2Debug::DISABLE);
  //
  // This is equivalent to:
  //
  //   S2Loop* loop = new S2Loop;
  //   loop->set_s2debug_override(S2Debug::DISABLE);
  //   loop->Init(vertices);
  //
  // The main reason to use this constructor is if you intend to call
  // IsValid() explicitly.  See set_s2debug_override() for details.
  S2Loop(absl::Span<const S2Point> vertices, S2Debug override);

  // Initialize a loop with given vertices.  The last vertex is implicitly
  // connected to the first.  All points should be unit length.  Loops must
  // have at least 3 vertices (except for the empty and full loops, see
  // kEmpty and kFull).  This method may be called multiple times.
  void Init(absl::Span<const S2Point> vertices);

  // A special vertex chain of length 1 that creates an empty loop (i.e., a
  // loop with no edges that contains no points).  Example usage:
  //
  //    S2Loop empty(S2Loop::kEmpty());
  //
  // The loop may be safely encoded lossily (e.g. by snapping it to an S2Cell
  // center) as long as its position does not move by 90 degrees or more.
  static std::vector<S2Point> kEmpty();

  // A special vertex chain of length 1 that creates a full loop (i.e., a loop
  // with no edges that contains all points).  See kEmpty() for details.
  static std::vector<S2Point> kFull();

  // Construct a loop corresponding to the given cell.
  //
  // Note that the loop and cell *do not* contain exactly the same set of
  // points, because S2Loop and S2Cell have slightly different definitions of
  // point containment.  For example, an S2Cell vertex is contained by all
  // four neighboring S2Cells, but it is contained by exactly one of four
  // S2Loops constructed from those cells.  As another example, the S2Cell
  // coverings of "cell" and "S2Loop(cell)" will be different, because the
  // loop contains points on its boundary that actually belong to other cells
  // (i.e., the covering will include a layer of neighboring cells).
  explicit S2Loop(const S2Cell& cell);

  ~S2Loop() override;

  // Allows overriding the automatic validity checks controlled by the
  // --s2debug flag.  If this flag is true, then loops are automatically
  // checked for validity as they are initialized.  The main reason to disable
  // this flag is if you intend to call IsValid() explicitly, like this:
  //
  //   S2Loop loop;
  //   loop.set_s2debug_override(S2Debug::DISABLE);
  //   loop.Init(...);
  //   if (!loop.IsValid()) { ... }
  //
  // Without the call to set_s2debug_override(), invalid data would cause a
  // fatal error in Init() whenever the --s2debug flag is enabled.
  //
  // This setting is preserved across calls to Init() and Decode().
  void set_s2debug_override(S2Debug override);
  S2Debug s2debug_override() const;

  // Returns true if this is a valid loop.  Note that validity is checked
  // automatically during initialization when --s2debug is enabled (true by
  // default in debug binaries).
  bool IsValid() const;

  // Returns true if this is *not* a valid loop and sets "error"
  // appropriately.  Otherwise returns false and leaves "error" unchanged.
  //
  // REQUIRES: error != nullptr
  bool FindValidationError(S2Error* error) const;

  int num_vertices() const { return num_vertices_; }

  // For convenience, we make two entire copies of the vertex list available:
  // vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == num_vertices().
  //
  // REQUIRES: 0 <= i < 2 * num_vertices()
  const S2Point& vertex(int i) const {
    S2_DCHECK_GE(i, 0);
    S2_DCHECK_LT(i, 2 * num_vertices());
    int j = i - num_vertices();
    return vertices_[j < 0 ? i : j];
  }

  // Like vertex(), but this method returns vertices in reverse order if the
  // loop represents a polygon hole.  For example, arguments 0, 1, 2 are
  // mapped to vertices n-1, n-2, n-3, where n == num_vertices().  This
  // ensures that the interior of the polygon is always to the left of the
  // vertex chain.
  //
  // REQUIRES: 0 <= i < 2 * num_vertices()
  const S2Point& oriented_vertex(int i) const {
    S2_DCHECK_GE(i, 0);
    S2_DCHECK_LT(i, 2 * num_vertices());
    int j = i - num_vertices();
    if (j < 0) j = i;
    if (is_hole()) j = num_vertices() - 1 - j;
    return vertices_[j];
  }

  // Returns an S2PointLoopSpan containing the loop vertices, for use with the
  // functions defined in s2loop_measures.h.
  S2PointLoopSpan vertices_span() const {
    return S2PointLoopSpan(vertices_, num_vertices());
  }

  // Returns true if this is the special empty loop that contains no points.
  bool is_empty() const;

  // Returns true if this is the special full loop that contains all points.
  bool is_full() const;

  // Returns true if this loop is either empty or full.
  bool is_empty_or_full() const;

  // The depth of a loop is defined as its nesting level within its containing
  // polygon.  "Outer shell" loops have depth 0, holes within those loops have
  // depth 1, shells within those holes have depth 2, etc.  This field is only
  // used by the S2Polygon implementation.
  int depth() const { return depth_; }
  void set_depth(int depth) { depth_ = depth; }

  // Returns true if this loop represents a hole in its containing polygon.
  bool is_hole() const { return (depth_ & 1) != 0; }

  // The sign of a loop is -1 if the loop represents a hole in its containing
  // polygon, and +1 otherwise.
  int sign() const { return is_hole() ? -1 : 1; }

  // Returns true if the loop area is at most 2*Pi.  Degenerate loops are
  // handled consistently with s2pred::Sign(), i.e., if a loop can be
  // expressed as the union of degenerate or nearly-degenerate CCW triangles,
  // then it will always be considered normalized.
  bool IsNormalized() const;

  // Invert the loop if necessary so that the area enclosed by the loop is at
  // most 2*Pi.
  void Normalize();

  // Reverse the order of the loop vertices, effectively complementing the
  // region represented by the loop.  For example, the loop ABCD (with edges
  // AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD).
  // Notice that the last edge is the same in both cases except that its
  // direction has been reversed.
  void Invert();

  // Returns the area of the loop interior, i.e. the region on the left side of
  // the loop.  The return value is between 0 and 4*Pi.  (Note that the return
  // value is not affected by whether this loop is a "hole" or a "shell".)
  double GetArea() const;

  // Returns the true centroid of the loop multiplied by the area of the loop
  // (see s2centroids.h for details on centroids).  The result is not unit
  // length, so you may want to normalize it.  Also note that in general, the
  // centroid may not be contained by the loop.
  //
  // We prescale by the loop area for two reasons: (1) it is cheaper to
  // compute this way, and (2) it makes it easier to compute the centroid of
  // more complicated shapes (by splitting them into disjoint regions and
  // adding their centroids).
  //
  // Note that the return value is not affected by whether this loop is a
  // "hole" or a "shell".
  S2Point GetCentroid() const;

  // Returns the geodesic curvature of the loop, defined as the sum of the turn
  // angles at each vertex (see S2::TurnAngle).  The result is positive if the
  // loop is counter-clockwise, negative if the loop is clockwise, and zero if
  // the loop is a great circle.  The geodesic curvature is equal to 2*Pi minus
  // the area of the loop.
  //
  // Degenerate and nearly-degenerate loops are handled consistently with
  // s2pred::Sign().  So for example, if a loop has zero area (i.e., it is a
  // very small CCW loop) then its geodesic curvature will always be positive.
  double GetCurvature() const;

  // Returns the maximum error in GetCurvature().  The return value is not
  // constant; it depends on the loop.
  double GetCurvatureMaxError() const;

  // Returns the distance from the given point to the loop interior.  If the
  // loop is empty, return S1Angle::Infinity().  "x" should be unit length.
  S1Angle GetDistance(const S2Point& x) const;

  // Returns the distance from the given point to the loop boundary.  If the
  // loop is empty or full, return S1Angle::Infinity() (since the loop has no
  // boundary).  "x" should be unit length.
  S1Angle GetDistanceToBoundary(const S2Point& x) const;

  // If the given point is contained by the loop, return it.  Otherwise return
  // the closest point on the loop boundary.  If the loop is empty, return the
  // input argument.  Note that the result may or may not be contained by the
  // loop.  "x" should be unit length.
  S2Point Project(const S2Point& x) const;

  // Returns the closest point on the loop boundary to the given point.  If the
  // loop is empty or full, return the input argument (since the loop has no
  // boundary).  "x" should be unit length.
  S2Point ProjectToBoundary(const S2Point& x) const;

  // Returns true if the region contained by this loop is a superset of the
  // region contained by the given other loop.
  bool Contains(const S2Loop& b) const;

  // Returns true if the region contained by this loop intersects the region
  // contained by the given other loop.
  bool Intersects(const S2Loop& b) const;

  // Returns true if two loops have the same vertices in the same linear order
  // (i.e., cyclic rotations are not allowed).
  bool Equals(const S2Loop& b) const;

  // Returns true if two loops have the same boundary.  This is true if and
  // only if the loops have the same vertices in the same cyclic order (i.e.,
  // the vertices may be cyclically rotated).  The empty and full loops are
  // considered to have different boundaries.
  bool BoundaryEquals(const S2Loop& b) const;

  // Returns true if two loops have the same boundary except for vertex
  // perturbations.  More precisely, the vertices in the two loops must be in
  // the same cyclic order, and corresponding vertex pairs must be separated
  // by no more than "max_error".
  bool BoundaryApproxEquals(const S2Loop& b,
                            S1Angle max_error = S1Angle::Radians(1e-15)) const;

  // Returns true if the two loop boundaries are within "max_error" of each
  // other along their entire lengths.  The two loops may have different
  // numbers of vertices.  More precisely, this method returns true if the two
  // loops have parameterizations a:[0,1] -> S^2, b:[0,1] -> S^2 such that
  // distance(a(t), b(t)) <= max_error for all t.  You can think of this as
  // testing whether it is possible to drive two cars all the way around the
  // two loops such that no car ever goes backward and the cars are always
  // within "max_error" of each other.
  bool BoundaryNear(const S2Loop& b,
                    S1Angle max_error = S1Angle::Radians(1e-15)) const;

#ifndef SWIG
  ABSL_DEPRECATED("Inline the implementation")
  bool Contains(const S2Loop* b) const { return Contains(*b); }
  ABSL_DEPRECATED("Inline the implementation")
  bool Intersects(const S2Loop* b) const { return Intersects(*b); }
  ABSL_DEPRECATED("Inline the implementation")
  bool Equals(const S2Loop* b) const { return Equals(*b); }
  ABSL_DEPRECATED("Inline the implementation")
  bool BoundaryEquals(const S2Loop* b) const { return BoundaryEquals(*b); }
#endif

  // This method computes the oriented surface integral of some quantity f(x)
  // over the loop interior, given a function f_tri(A,B,C) that returns the
  // corresponding integral over the spherical triangle ABC.  Here "oriented
  // surface integral" means:
  //
  // (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise,
  //     and the integral of -f if ABC is clockwise.
  //
  // (2) The result of this function is *either* the integral of f over the
  //     loop interior, or the integral of (-f) over the loop exterior.
  //
  // Note that there are at least two common situations where it easy to work
  // around property (2) above:
  //
  //  - If the integral of f over the entire sphere is zero, then it doesn't
  //    matter which case is returned because they are always equal.
  //
  //  - If f is non-negative, then it is easy to detect when the integral over
  //    the loop exterior has been returned, and the integral over the loop
  //    interior can be obtained by adding the integral of f over the entire
  //    unit sphere (a constant) to the result.
  //
  // Also requires that the default constructor for T must initialize the
  // value to zero.  (This is true for built-in types such as "double".)
  template <class T>
  T GetSurfaceIntegral(T f_tri(const S2Point&, const S2Point&, const S2Point&))
      const;

  // Constructs a regular polygon with the given number of vertices, all
  // located on a circle of the specified radius around "center".  The radius
  // is the actual distance from "center" to each vertex.
  static std::unique_ptr<S2Loop> MakeRegularLoop(const S2Point& center,
                                                 S1Angle radius,
                                                 int num_vertices);

  // Like the function above, but this version constructs a loop centered
  // around the z-axis of the given coordinate frame, with the first vertex in
  // the direction of the positive x-axis.  (This allows the loop to be
  // rotated for testing purposes.)
  static std::unique_ptr<S2Loop> MakeRegularLoop(const Matrix3x3_d& frame,
                                                 S1Angle radius,
                                                 int num_vertices);

  // Returns the total number of bytes used by the loop.
  size_t SpaceUsed() const;

  ////////////////////////////////////////////////////////////////////////
  // S2Region interface (see s2region.h for details):

  S2Loop* Clone() const override;

  // GetRectBound() returns essentially tight results, while GetCapBound()
  // might have a lot of extra padding.  Both bounds are conservative in that
  // if the loop contains a point P, then the bound contains P also.
  S2Cap GetCapBound() const override;
  S2LatLngRect GetRectBound() const override { return bound_; }

  bool Contains(const S2Cell& cell) const override;
  bool MayIntersect(const S2Cell& cell) const override;

  // The point 'p' does not need to be normalized.
  bool Contains(const S2Point& p) const override;

  // Appends a serialized representation of the S2Loop to "encoder".
  //
  // Generally clients should not use S2Loop::Encode().  Instead they should
  // encode an S2Polygon, which unlike this method supports (lossless)
  // compression.
  //
  // REQUIRES: "encoder" uses the default constructor, so that its buffer
  //           can be enlarged as necessary by calling Ensure(int).
  void Encode(Encoder* const encoder) const;

  // Decodes a loop encoded with Encode() or the private method
  // EncodeCompressed() (used by the S2Polygon encoder).  Returns true on
  // success.
  //
  // This method may be called with loops that have already been initialized.
  bool Decode(Decoder* const decoder);

  // Provides the same functionality as Decode, except that decoded regions
  // are allowed to point directly into the Decoder's memory buffer rather
  // than copying the data.  This can be much faster, but the decoded loop is
  // only valid within the scope (lifetime) of the Decoder's memory buffer.
  bool DecodeWithinScope(Decoder* const decoder);

  ////////////////////////////////////////////////////////////////////////
  // Methods intended primarily for use by the S2Polygon implementation:

  // Given two loops of a polygon, return true if A contains B.  This version
  // of Contains() is cheap because it does not test for edge intersections.
  // The loops must meet all the S2Polygon requirements; for example this
  // implies that their boundaries may not cross or have any shared edges
  // (although they may have shared vertices).
  bool ContainsNested(const S2Loop& b) const;

  // Returns +1 if A contains the boundary of B, -1 if A excludes the boundary
  // of B, and 0 if the boundaries of A and B cross.  Shared edges are handled
  // as follows: If XY is a shared edge, define Reversed(XY) to be true if XY
  // appears in opposite directions in A and B.  Then A contains XY if and
  // only if Reversed(XY) == B->is_hole().  (Intuitively, this checks whether
  // A contains a vanishingly small region extending from the boundary of B
  // toward the interior of the polygon to which loop B belongs.)
  //
  // This method is used for testing containment and intersection of
  // multi-loop polygons.  Note that this method is not symmetric, since the
  // result depends on the direction of loop A but not on the direction of
  // loop B (in the absence of shared edges).
  //
  // REQUIRES: neither loop is empty.
  // REQUIRES: if b->is_full(), then !b->is_hole().
  int CompareBoundary(const S2Loop& b) const;

  // Given two loops whose boundaries do not cross (see CompareBoundary),
  // return true if A contains the boundary of B.  If "reverse_b" is true, the
  // boundary of B is reversed first (which only affects the result when there
  // are shared edges).  This method is cheaper than CompareBoundary() because
  // it does not test for edge intersections.
  //
  // REQUIRES: neither loop is empty.
  // REQUIRES: if b->is_full(), then reverse_b == false.
  bool ContainsNonCrossingBoundary(const S2Loop& b, bool reverse_b) const;

#ifndef SWIG
  ABSL_DEPRECATED("Inline the implementation")
  bool ContainsNested(const S2Loop* b) const { return ContainsNested(*b); }
  ABSL_DEPRECATED("Inline the implementation")
  int CompareBoundary(const S2Loop* b) const { return CompareBoundary(*b); }
  ABSL_DEPRECATED("Inline the implementation")
  bool ContainsNonCrossingBoundary(const S2Loop* b, bool reverse_b) const {
    return ContainsNonCrossingBoundary(*b, reverse_b);
  }
#endif

  // Wrapper class for indexing a loop (see S2ShapeIndex).  Once this object
  // is inserted into an S2ShapeIndex it is owned by that index, and will be
  // automatically deleted when no longer needed by the index.  Note that this
  // class does not take ownership of the loop itself (see OwningShape below).
  // You can also subtype this class to store additional data (see S2Shape for
  // details).
#ifndef SWIG
  class Shape : public S2Shape {
   public:
    Shape() : loop_(nullptr) {}  // Must call Init().

    // Initialize the shape.  Does not take ownership of "loop".
    explicit Shape(const S2Loop* loop) { Init(loop); }
    void Init(const S2Loop* loop) { loop_ = loop; }

    const S2Loop* loop() const { return loop_; }

    // S2Shape interface:
    int num_edges() const final {
      return loop_->is_empty_or_full() ? 0 : loop_->num_vertices();
    }
    Edge edge(int e) const final {
      return Edge(loop_->vertex(e), loop_->vertex(e + 1));
    }
    int dimension() const final { return 2; }
    ReferencePoint GetReferencePoint() const final {
      return ReferencePoint(S2::Origin(), loop_->contains_origin());
    }
    int num_chains() const final;
    Chain chain(int i) const final;
    Edge chain_edge(int i, int j) const final {
      S2_DCHECK_EQ(i, 0);
      return Edge(loop_->vertex(j), loop_->vertex(j + 1));
    }
    ChainPosition chain_position(int e) const final {
      return ChainPosition(0, e);
    }

   private:
    const S2Loop* loop_;
  };

  // Like Shape, except that the S2Loop is automatically deleted when this
  // object is deleted by the S2ShapeIndex.  This is useful when an S2Loop
  // is constructed solely for the purpose of indexing it.
  class OwningShape : public Shape {
   public:
    OwningShape() {}  // Must call Init().
    explicit OwningShape(std::unique_ptr<const S2Loop> loop)
        : Shape(loop.release()) {
    }
    void Init(std::unique_ptr<const S2Loop> loop) {
      Shape::Init(loop.release());
    }
    ~OwningShape() override { delete loop(); }
  };
#endif  // SWIG

 private:
  // All of the following need access to contains_origin().  Possibly this
  // method should be public.
  friend class Shape;
  friend class S2Polygon;
  friend class S2Stats;
  friend class S2LoopTestBase;
  friend class LoopCrosser;
  friend class s2builderutil::S2PolygonLayer;

  // Internal copy constructor used only by Clone() that makes a deep copy of
  // its argument.
  S2Loop(const S2Loop& src);

  // Returns true if this loop contains S2::Origin().
  bool contains_origin() const { return origin_inside_; }

  // The single vertex in the "empty loop" vertex chain.
  static S2Point kEmptyVertex();

  // The single vertex in the "full loop" vertex chain.
  static S2Point kFullVertex();

  void InitOriginAndBound();
  void InitBound();
  void InitIndex();

  // A version of Contains(S2Point) that does not use the S2ShapeIndex.
  // Used by the S2Polygon implementation.
  bool BruteForceContains(const S2Point& p) const;

  // Like FindValidationError(), but skips any checks that would require
  // building the S2ShapeIndex (i.e., self-intersection tests).  This is used
  // by the S2Polygon implementation, which uses its own index to check for
  // loop self-intersections.
  bool FindValidationErrorNoIndex(S2Error* error) const;

  // Internal implementation of the Decode and DecodeWithinScope methods above.
  // If within_scope is true, memory is allocated for vertices_ and data
  // is copied from the decoder using std::copy. If it is false, vertices_
  // will point to the memory area inside the decoder, and the field
  // owns_vertices_ is set to false.
  bool DecodeInternal(Decoder* const decoder, bool within_scope);

  // Converts the loop vertices to the S2XYZFaceSiTi format and store the result
  // in the given array, which must be large enough to store all the vertices.
  void GetXYZFaceSiTiVertices(S2XYZFaceSiTi* vertices) const;

  // Encode the loop's vertices using S2EncodePointsCompressed.  Uses
  // approximately 8 bytes for the first vertex, going down to less than 4 bytes
  // per vertex on Google's geographic repository, plus 24 bytes per vertex that
  // does not correspond to the center of a cell at level 'snap_level'. The loop
  // vertices must first be converted to the S2XYZFaceSiTi format with
  // GetXYZFaceSiTiVertices.
  //
  // REQUIRES: the loop is initialized and valid.
  void EncodeCompressed(Encoder* encoder, const S2XYZFaceSiTi* vertices,
                        int snap_level) const;

  // Decode a loop encoded with EncodeCompressed. The parameters must be the
  // same as the one used when EncodeCompressed was called.
  bool DecodeCompressed(Decoder* decoder, int snap_level);

  // Returns a bitset of properties used by EncodeCompressed
  // to efficiently encode boolean values.  Properties are
  // origin_inside and whether the bound was encoded.
  std::bitset<2> GetCompressedEncodingProperties() const;

  // Given an iterator that is already positioned at the S2ShapeIndexCell
  // containing "p", returns Contains(p).
  bool Contains(const MutableS2ShapeIndex::Iterator& it,
                const S2Point& p) const;

  // Returns true if the loop boundary intersects "target".  It may also
  // return true when the loop boundary does not intersect "target" but
  // some edge comes within the worst-case error tolerance.
  //
  // REQUIRES: it.id().contains(target.id())
  // [This condition is true whenever it.Locate(target) returns INDEXED.]
  bool BoundaryApproxIntersects(const MutableS2ShapeIndex::Iterator& it,
                                const S2Cell& target) const;

  // Returns an index "first" and a direction "dir" such that the vertex
  // sequence (first, first + dir, ..., first + (n - 1) * dir) does not change
  // when the loop vertex order is rotated or reversed.  This allows the loop
  // vertices to be traversed in a canonical order.
  S2::LoopOrder GetCanonicalLoopOrder() const;

  // Returns the index of a vertex at point "p", or -1 if not found.
  // The return value is in the range 1..num_vertices_ if found.
  int FindVertex(const S2Point& p) const;

  // This method checks all edges of loop A for intersection against all edges
  // of loop B.  If there is any shared vertex, the wedges centered at this
  // vertex are sent to "relation".
  //
  // If the two loop boundaries cross, this method is guaranteed to return
  // true.  It also returns true in certain cases if the loop relationship is
  // equivalent to crossing.  For example, if the relation is Contains() and a
  // point P is found such that B contains P but A does not contain P, this
  // method will return true to indicate that the result is the same as though
  // a pair of crossing edges were found (since Contains() returns false in
  // both cases).
  //
  // See Contains(), Intersects() and CompareBoundary() for the three uses of
  // this function.
  static bool HasCrossingRelation(const S2Loop& a, const S2Loop& b,
                                  LoopRelation* relation);

  // When the loop is modified (Invert(), or Init() called again) then the
  // indexing structures need to be cleared since they become invalid.
  void ClearIndex();

  // The nesting depth, if this field belongs to an S2Polygon.  We define it
  // here to optimize field packing.
  int depth_ = 0;

  // We store the vertices in an array rather than a vector because we don't
  // need any STL methods, and computing the number of vertices using size()
  // would be relatively expensive (due to division by sizeof(S2Point) == 24).
  // When DecodeWithinScope is used to initialize the loop, we do not
  // take ownership of the memory for vertices_, and the owns_vertices_ field
  // is used to prevent deallocation and overwriting.
  int num_vertices_ = 0;
  S2Point* vertices_ = nullptr;
  bool owns_vertices_ = false;

  S2Debug s2debug_override_ = S2Debug::ALLOW;
  bool origin_inside_ = false;  // Does the loop contain S2::Origin()?

  // In general we build the index the first time it is needed, but we make an
  // exception for Contains(S2Point) because this method has a simple brute
  // force implementation that is also relatively cheap.  For this one method
  // we keep track of the number of calls made and only build the index once
  // enough calls have been made that we think an index would be worthwhile.
  mutable std::atomic<int32> unindexed_contains_calls_;

  // "bound_" is a conservative bound on all points contained by this loop:
  // if A.Contains(P), then A.bound_.Contains(S2LatLng(P)).
  S2LatLngRect bound_;

  // Since "bound_" is not exact, it is possible that a loop A contains
  // another loop B whose bounds are slightly larger.  "subregion_bound_"
  // has been expanded sufficiently to account for this error, i.e.
  // if A.Contains(B), then A.subregion_bound_.Contains(B.bound_).
  S2LatLngRect subregion_bound_;

  // Spatial index for this loop.
  MutableS2ShapeIndex index_;

  // SWIG doesn't understand "= delete".
#ifndef SWIG
  void operator=(const S2Loop&) = delete;
#endif  // SWIG
};


//////////////////// Implementation Details Follow ////////////////////////


// Any single-vertex loop is interpreted as being either the empty loop or the
// full loop, depending on whether the vertex is in the northern or southern
// hemisphere respectively.
inline S2Point S2Loop::kEmptyVertex() { return S2Point(0, 0, 1); }
inline S2Point S2Loop::kFullVertex() { return S2Point(0, 0, -1); }

inline std::vector<S2Point> S2Loop::kEmpty() {
  return std::vector<S2Point>(1, kEmptyVertex());
}

inline std::vector<S2Point> S2Loop::kFull() {
  return std::vector<S2Point>(1, kFullVertex());
}

inline bool S2Loop::is_empty() const {
  return is_empty_or_full() && !contains_origin();
}

inline bool S2Loop::is_full() const {
  return is_empty_or_full() && contains_origin();
}

inline bool S2Loop::is_empty_or_full() const {
  return num_vertices() == 1;
}

// Since this method is templatized and public, the implementation needs to be
// in the .h file.

template <class T>
T S2Loop::GetSurfaceIntegral(T f_tri(const S2Point&, const S2Point&,
                                     const S2Point&)) const {
  return S2::GetSurfaceIntegral(vertices_span(), f_tri);
}

#endif  // S2_S2LOOP_H_