[go: up one dir, main page]

File: s2polyline.cc

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 (809 lines) | stat: -rw-r--r-- 29,235 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
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
// 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)

#include "s2/s2polyline.h"

#include <algorithm>
#include <cmath>
#include <functional>
#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "absl/container/fixed_array.h"
#include "absl/flags/flag.h"
#include "absl/memory/memory.h"
#include "absl/utility/utility.h"

#include "s2/base/commandlineflags.h"
#include "s2/base/logging.h"
#include "s2/s1angle.h"
#include "s2/s1interval.h"
#include "s2/s2builderutil_s2polyline_layer.h"
#include "s2/s2builderutil_snap_functions.h"
#include "s2/s2cap.h"
#include "s2/s2cell.h"
#include "s2/s2debug.h"
#include "s2/s2edge_crosser.h"
#include "s2/s2edge_distances.h"
#include "s2/s2error.h"
#include "s2/s2latlng_rect_bounder.h"
#include "s2/s2point.h"
#include "s2/s2point_compression.h"
#include "s2/s2pointutil.h"
#include "s2/s2polyline_measures.h"
#include "s2/s2predicates.h"
#include "s2/util/coding/coder.h"
#include "s2/util/math/matrix3x3.h"

using absl::make_unique;
using absl::Span;
using s2builderutil::S2CellIdSnapFunction;
using s2builderutil::S2PolylineLayer;
using std::max;
using std::min;
using std::set;
using std::vector;

static const unsigned char kCurrentLosslessEncodingVersionNumber = 1;
static const unsigned char kCurrentCompressedEncodingVersionNumber = 2;

S2Polyline::S2Polyline()
  : s2debug_override_(S2Debug::ALLOW) {}

S2Polyline::S2Polyline(S2Polyline&& other)
  : s2debug_override_(other.s2debug_override_),
    num_vertices_(absl::exchange(other.num_vertices_, 0)),
    vertices_(std::move(other.vertices_)) {
}

S2Polyline& S2Polyline::operator=(S2Polyline&& other) {
  s2debug_override_ = other.s2debug_override_;
  num_vertices_ = absl::exchange(other.num_vertices_, 0);
  vertices_ = std::move(other.vertices_);
  return *this;
}

S2Polyline::S2Polyline(Span<const S2Point> vertices)
  : S2Polyline(vertices, S2Debug::ALLOW) {}

S2Polyline::S2Polyline(Span<const S2LatLng> vertices)
  : S2Polyline(vertices, S2Debug::ALLOW) {}

S2Polyline::S2Polyline(Span<const S2Point> vertices,
                       S2Debug override)
  : s2debug_override_(override) {
  Init(vertices);
}

S2Polyline::S2Polyline(Span<const S2LatLng> vertices, S2Debug override)
  : s2debug_override_(override) {
  Init(vertices);
}

S2Polyline::~S2Polyline() {
}

void S2Polyline::set_s2debug_override(S2Debug override) {
  s2debug_override_ = override;
}

S2Debug S2Polyline::s2debug_override() const {
  return s2debug_override_;
}

void S2Polyline::Init(Span<const S2Point> vertices) {
  num_vertices_ = vertices.size();
  vertices_ = make_unique<S2Point[]>(num_vertices_);
  std::copy(vertices.begin(), vertices.end(), &vertices_[0]);
  if (absl::GetFlag(FLAGS_s2debug) && s2debug_override_ == S2Debug::ALLOW) {
    S2_CHECK(IsValid());
  }
}

void S2Polyline::Init(Span<const S2LatLng> vertices) {
  num_vertices_ = vertices.size();
  vertices_ = make_unique<S2Point[]>(num_vertices_);
  for (int i = 0; i < num_vertices_; ++i) {
    vertices_[i] = vertices[i].ToPoint();
  }
  if (absl::GetFlag(FLAGS_s2debug) && s2debug_override_ == S2Debug::ALLOW) {
    S2_CHECK(IsValid());
  }
}

void S2Polyline::InitToSnapped(const S2Polyline& polyline, int snap_level) {
  S2Builder builder{S2Builder::Options(S2CellIdSnapFunction(snap_level))};
  InitFromBuilder(polyline, &builder);
}

void S2Polyline::InitToSimplified(
    const S2Polyline& polyline, const S2Builder::SnapFunction& snap_function) {
  S2Builder::Options options(snap_function);
  options.set_simplify_edge_chains(true);
  S2Builder builder(options);
  InitFromBuilder(polyline, &builder);
}

void S2Polyline::InitFromBuilder(const S2Polyline& polyline,
                                 S2Builder* builder) {
  builder->StartLayer(absl::make_unique<S2PolylineLayer>(this));
  builder->AddPolyline(polyline);
  S2Error error;
  S2_CHECK(builder->Build(&error)) << "Could not build polyline: " << error;
}

bool S2Polyline::IsValid() const {
  S2Error error;
  if (FindValidationError(&error)) {
    S2_LOG_IF(ERROR, absl::GetFlag(FLAGS_s2debug)) << error;
    return false;
  }
  return true;
}

bool S2Polyline::FindValidationError(S2Error* error) const {
  // All vertices must be unit length.
  for (int i = 0; i < num_vertices(); ++i) {
    if (!S2::IsUnitLength(vertex(i))) {
      error->Init(S2Error::NOT_UNIT_LENGTH, "Vertex %d is not unit length", i);
      return true;
    }
  }
  // Adjacent vertices must not be identical or antipodal.
  for (int i = 1; i < num_vertices(); ++i) {
    if (vertex(i - 1) == vertex(i)) {
      error->Init(S2Error::DUPLICATE_VERTICES,
                  "Vertices %d and %d are identical", i - 1, i);
      return true;
    }
    if (vertex(i - 1) == -vertex(i)) {
      error->Init(S2Error::ANTIPODAL_VERTICES,
                  "Vertices %d and %d are antipodal", i - 1, i);
      return true;
    }
  }
  return false;
}

S2Polyline::S2Polyline(const S2Polyline& src)
  : num_vertices_(src.num_vertices_),
    vertices_(new S2Point[num_vertices_]) {
  // TODO(user, ericv): Decide whether to use a canonical empty
  // representation.
  // If num_vertices_ == 0, then src.vertices_ will be null if src was default
  // constructed.
  if (num_vertices_ != 0) {
    std::copy(&src.vertices_[0], &src.vertices_[num_vertices_], &vertices_[0]);
  }
}

S2Polyline* S2Polyline::Clone() const {
  return new S2Polyline(*this);
}

S1Angle S2Polyline::GetLength() const {
  return S2::GetLength(vertices_span());
}

S2Point S2Polyline::GetCentroid() const {
  return S2::GetCentroid(vertices_span());
}

int S2Polyline::GetSnapLevel() const {
  int snap_level = -1;
  for (int i = 0; i < num_vertices_; ++i) {
    int face;
    unsigned int si, ti;
    const int level = S2::XYZtoFaceSiTi(vertices_[i], &face, &si, &ti);
    if (level < 0) return level;  // Vertex is not a cell center.
    if (level != snap_level) {
      if (snap_level < 0) {
        snap_level = level;  // First vertex.
      } else {
        return -1;  // Vertices at more than one cell level.
      }
    }
  }
  return snap_level;
}

S2Point S2Polyline::GetSuffix(double fraction, int* next_vertex) const {
  S2_DCHECK_GT(num_vertices(), 0);
  // We intentionally let the (fraction >= 1) case fall through, since
  // we need to handle it in the loop below in any case because of
  // possible roundoff errors.
  if (fraction <= 0) {
    *next_vertex = 1;
    return vertex(0);
  }
  S1Angle length_sum;
  for (int i = 1; i < num_vertices(); ++i) {
    length_sum += S1Angle(vertex(i-1), vertex(i));
  }
  S1Angle target = fraction * length_sum;
  for (int i = 1; i < num_vertices(); ++i) {
    S1Angle length(vertex(i-1), vertex(i));
    if (target < length) {
      // This interpolates with respect to arc length rather than
      // straight-line distance, and produces a unit-length result.
      S2Point result = S2::GetPointOnLine(vertex(i - 1), vertex(i), target);
      // It is possible that (result == vertex(i)) due to rounding errors.
      *next_vertex = (result == vertex(i)) ? (i + 1) : i;
      return result;
    }
    target -= length;
  }
  *next_vertex = num_vertices();
  return vertex(num_vertices() - 1);
}

S2Point S2Polyline::Interpolate(double fraction) const {
  int next_vertex;
  return GetSuffix(fraction, &next_vertex);
}

double S2Polyline::UnInterpolate(const S2Point& point, int next_vertex) const {
  S2_DCHECK_GT(num_vertices(), 0);
  if (num_vertices() < 2) {
    return 0;
  }
  S1Angle length_sum;
  for (int i = 1; i < next_vertex; ++i) {
    length_sum += S1Angle(vertex(i-1), vertex(i));
  }
  S1Angle length_to_point = length_sum + S1Angle(vertex(next_vertex-1), point);
  for (int i = next_vertex; i < num_vertices(); ++i) {
    length_sum += S1Angle(vertex(i-1), vertex(i));
  }
  // The ratio can be greater than 1.0 due to rounding errors or because the
  // point is not exactly on the polyline.
  return min(1.0, length_to_point / length_sum);
}

S2Point S2Polyline::Project(const S2Point& point, int* next_vertex) const {
  S2_DCHECK_GT(num_vertices(), 0);

  if (num_vertices() == 1) {
    // If there is only one vertex, it is always closest to any given point.
    *next_vertex = 1;
    return vertex(0);
  }

  // Initial value larger than any possible distance on the unit sphere.
  S1Angle min_distance = S1Angle::Radians(10);
  int min_index = -1;

  // Find the line segment in the polyline that is closest to the point given.
  for (int i = 1; i < num_vertices(); ++i) {
    S1Angle distance_to_segment = S2::GetDistance(point, vertex(i-1),
                                                          vertex(i));
    if (distance_to_segment < min_distance) {
      min_distance = distance_to_segment;
      min_index = i;
    }
  }
  S2_DCHECK_NE(min_index, -1);

  // Compute the point on the segment found that is closest to the point given.
  S2Point closest_point =
      S2::Project(point, vertex(min_index - 1), vertex(min_index));

  *next_vertex = min_index + (closest_point == vertex(min_index) ? 1 : 0);
  return closest_point;
}

bool S2Polyline::IsOnRight(const S2Point& point) const {
  S2_DCHECK_GE(num_vertices(), 2);

  int next_vertex;
  S2Point closest_point = Project(point, &next_vertex);

  S2_DCHECK_GE(next_vertex, 1);
  S2_DCHECK_LE(next_vertex, num_vertices());

  // If the closest point C is an interior vertex of the polyline, let B and D
  // be the previous and next vertices.  The given point P is on the right of
  // the polyline (locally) if B, P, D are ordered CCW around vertex C.
  if (closest_point == vertex(next_vertex-1) && next_vertex > 1 &&
      next_vertex < num_vertices()) {
    if (point == vertex(next_vertex-1))
      return false;  // Polyline vertices are not on the RHS.
    return s2pred::OrderedCCW(vertex(next_vertex-2), point, vertex(next_vertex),
                              vertex(next_vertex-1));
  }

  // Otherwise, the closest point C is incident to exactly one polyline edge.
  // We test the point P against that edge.
  if (next_vertex == num_vertices())
    --next_vertex;

  return s2pred::Sign(point, vertex(next_vertex), vertex(next_vertex - 1)) > 0;
}

bool S2Polyline::Intersects(const S2Polyline& line) const {
  if (num_vertices() <= 0 || line.num_vertices() <= 0) {
    return false;
  }

  if (!GetRectBound().Intersects(line.GetRectBound())) {
    return false;
  }

  // TODO(ericv): Use S2ShapeIndex here.
  for (int i = 1; i < num_vertices(); ++i) {
    S2EdgeCrosser crosser(&vertex(i - 1), &vertex(i), &line.vertex(0));
    for (int j = 1; j < line.num_vertices(); ++j) {
      if (crosser.CrossingSign(&line.vertex(j)) >= 0) {
        return true;
      }
    }
  }
  return false;
}

void S2Polyline::Reverse() {
  if (num_vertices_ > 0) {
    std::reverse(&vertices_[0], &vertices_[num_vertices_]);
  }
}

S2LatLngRect S2Polyline::GetRectBound() const {
  S2LatLngRectBounder bounder;
  for (int i = 0; i < num_vertices(); ++i) {
    bounder.AddPoint(vertex(i));
  }
  return bounder.GetBound();
}

S2Cap S2Polyline::GetCapBound() const {
  return GetRectBound().GetCapBound();
}

bool S2Polyline::MayIntersect(const S2Cell& cell) const {
  if (num_vertices() == 0) return false;

  // We only need to check whether the cell contains vertex 0 for correctness,
  // but these tests are cheap compared to edge crossings so we might as well
  // check all the vertices.
  for (int i = 0; i < num_vertices(); ++i) {
    if (cell.Contains(vertex(i))) return true;
  }
  S2Point cell_vertices[4];
  for (int i = 0; i < 4; ++i) {
    cell_vertices[i] = cell.GetVertex(i);
  }
  for (int j = 0; j < 4; ++j) {
    S2EdgeCrosser crosser(&cell_vertices[j], &cell_vertices[(j+1)&3],
                                    &vertex(0));
    for (int i = 1; i < num_vertices(); ++i) {
      if (crosser.CrossingSign(&vertex(i)) >= 0) {
        // There is a proper crossing, or two vertices were the same.
        return true;
      }
    }
  }
  return false;
}

void S2Polyline::Encode(Encoder* const encoder) const {
  EncodeUncompressed(encoder);
}

void S2Polyline::EncodeUncompressed(Encoder* const encoder) const {
  encoder->Ensure(num_vertices_ * sizeof(vertices_[0]) + 10);  // sufficient

  encoder->put8(kCurrentLosslessEncodingVersionNumber);
  encoder->put32(num_vertices_);
  encoder->putn(vertices_.get(), sizeof(vertices_[0]) * num_vertices_);

  S2_DCHECK_GE(encoder->avail(), 0);
}

bool S2Polyline::Decode(Decoder* const decoder) {
  if (decoder->avail() < sizeof(unsigned char)) {
    return false;
  }
  const unsigned char version = decoder->get8();
  switch (version) {
    case kCurrentLosslessEncodingVersionNumber:
      return DecodeUncompressed(decoder);
    case kCurrentCompressedEncodingVersionNumber:
      return DecodeCompressed(decoder);
  }
  return false;
}

bool S2Polyline::DecodeUncompressed(Decoder* const decoder) {
  if (decoder->avail() < sizeof(uint32)) {
    return false;
  }
  num_vertices_ = decoder->get32();

  // Check the bytes available before allocating memory in case of
  // corrupt/malicious input.
  if (decoder->avail() < num_vertices_ * sizeof(S2Point)) return false;
  vertices_ = make_unique<S2Point[]>(num_vertices_);
  decoder->getn(&vertices_[0], num_vertices_ * sizeof(vertices_[0]));

  if (absl::GetFlag(FLAGS_s2debug) && s2debug_override_ == S2Debug::ALLOW) {
    S2_CHECK(IsValid());
  }
  return true;
}

void S2Polyline::EncodeMostCompact(Encoder* encoder) const {
  if (num_vertices_ == 0) {
    EncodeCompressed(encoder, {}, S2::kMaxCellLevel);
    return;
  }
  // Convert S2Points to (face, si, ti) representation.
  absl::FixedArray<S2XYZFaceSiTi> all_vertices(num_vertices_);
  for (int i = 0; i < num_vertices_; ++i) {
    all_vertices[i].xyz = vertices_[i];
    all_vertices[i].cell_level =
        S2::XYZtoFaceSiTi(all_vertices[i].xyz, &all_vertices[i].face,
                          &all_vertices[i].si, &all_vertices[i].ti);
  }

  // Computes a histogram of the cell levels at which the vertices are snapped.
  // cell_level is -1 for unsnapped, or 0 through kMaxCellLevel if snapped,
  // so we add one to it to get a non-negative index.  (histogram[0] is the
  // number of unsnapped vertices, histogram[i] the number of vertices
  // snapped at level i-1).
  std::array<int, S2::kMaxCellLevel + 2> histogram;
  histogram.fill(0);
  for (int i = 0; i < num_vertices_; ++i) {
    histogram[all_vertices[i].cell_level + 1] += 1;
  }
  // Compute the level at which most of the vertices are snapped.
  // If multiple levels have the same maximum number of vertices
  // snapped to it, the first one (lowest level number / largest
  // area / smallest encoding length) will be chosen, so this
  // is desired.  Start with histogram[1] since histogram[0] is
  // the number of unsnapped vertices, which we don't care about.
  const auto max_iter =
      std::max_element(histogram.begin() + 1, histogram.end());
  // snap_level will be at position histogram[snap_level + 1], see above.
  const int snap_level = max_iter - (histogram.begin() + 1);
  const int num_snapped = *max_iter;

  // The compressed encoding requires approximately 4 bytes per vertex plus
  // "exact_point_size" for each unsnapped vertex (encoded as an S2Point plus
  // the index at which it is located).
  int exact_point_size = sizeof(S2Point) + 2;
  int num_unsnapped = num_vertices_ - num_snapped;
  int compressed_size = 4 * num_vertices_ + exact_point_size * num_unsnapped;
  int lossless_size = sizeof(S2Point) * num_vertices_;
  if (compressed_size < lossless_size) {
    EncodeCompressed(encoder, all_vertices, snap_level);
  } else {
    EncodeUncompressed(encoder);
  }
}

void S2Polyline::EncodeCompressed(Encoder* encoder,
                                  absl::Span<const S2XYZFaceSiTi> all_vertices,
                                  int snap_level) const {
  // Set version number.
  encoder->Ensure(2 + Encoder::kVarintMax32);
  encoder->put8(kCurrentCompressedEncodingVersionNumber);
  encoder->put8(snap_level);
  encoder->put_varint32(num_vertices_);
  S2EncodePointsCompressed(all_vertices, snap_level, encoder);
}

bool S2Polyline::DecodeCompressed(Decoder* decoder) {
  if (decoder->avail() < sizeof(uint8)) return false;
  const int snap_level = decoder->get8();
  if (snap_level > S2::kMaxCellLevel) return false;

  vector<S2Point> points;
  uint32 num_vertices;
  if (!decoder->get_varint32(&num_vertices)) return false;
  if (num_vertices == 0) {
    // Empty polylines are allowed.
    Init(points);
    return true;
  }

  // TODO(b/209937354): Prevent large allocations like in DecodeUncompressed.
  // This is more complicated due to the compressed encoding, but perhaps the
  // minimum required size can be bounded.
  points.resize(num_vertices);
  if (!S2DecodePointsCompressed(decoder, snap_level, absl::MakeSpan(points))) {
    return false;
  }
  Init(points);
  return true;
}

namespace {

// Given a polyline, a tolerance distance, and a start index, this function
// returns the maximal end index such that the line segment between these two
// vertices passes within "tolerance" of all interior vertices, in order.
int FindEndVertex(const S2Polyline& polyline,
                  S1Angle tolerance, int index) {
  S2_DCHECK_GE(tolerance.radians(), 0);
  S2_DCHECK_LT((index + 1), polyline.num_vertices());

  // The basic idea is to keep track of the "pie wedge" of angles from the
  // starting vertex such that a ray from the starting vertex at that angle
  // will pass through the discs of radius "tolerance" centered around all
  // vertices processed so far.

  // First we define a "coordinate frame" for the tangent and normal spaces
  // at the starting vertex.  Essentially this means picking three
  // orthonormal vectors X,Y,Z such that X and Y span the tangent plane at
  // the starting vertex, and Z is "up".  We use the coordinate frame to
  // define a mapping from 3D direction vectors to a one-dimensional "ray
  // angle" in the range (-Pi, Pi].  The angle of a direction vector is
  // computed by transforming it into the X,Y,Z basis, and then calculating
  // atan2(y,x).  This mapping allows us to represent a wedge of angles as a
  // 1D interval.  Since the interval wraps around, we represent it as an
  // S1Interval, i.e. an interval on the unit circle.
  Matrix3x3_d frame;
  const S2Point& origin = polyline.vertex(index);
  S2::GetFrame(origin, &frame);

  // As we go along, we keep track of the current wedge of angles and the
  // distance to the last vertex (which must be non-decreasing).
  S1Interval current_wedge = S1Interval::Full();
  double last_distance = 0;

  for (++index; index < polyline.num_vertices(); ++index) {
    const S2Point& candidate = polyline.vertex(index);
    double distance = origin.Angle(candidate);

    // We don't allow simplification to create edges longer than 90 degrees,
    // to avoid numeric instability as lengths approach 180 degrees.  (We do
    // need to allow for original edges longer than 90 degrees, though.)
    if (distance > M_PI/2 && last_distance > 0) break;

    // Vertices must be in increasing order along the ray, except for the
    // initial disc around the origin.
    if (distance < last_distance && last_distance > tolerance.radians()) break;
    last_distance = distance;

    // Points that are within the tolerance distance of the origin do not
    // constrain the ray direction, so we can ignore them.
    if (distance <= tolerance.radians()) continue;

    // If the current wedge of angles does not contain the angle to this
    // vertex, then stop right now.  Note that the wedge of possible ray
    // angles is not necessarily empty yet, but we can't continue unless we
    // are willing to backtrack to the last vertex that was contained within
    // the wedge (since we don't create new vertices).  This would be more
    // complicated and also make the worst-case running time more than linear.
    S2Point direction = S2::ToFrame(frame, candidate);
    double center = atan2(direction.y(), direction.x());
    if (!current_wedge.Contains(center)) break;

    // To determine how this vertex constrains the possible ray angles,
    // consider the triangle ABC where A is the origin, B is the candidate
    // vertex, and C is one of the two tangent points between A and the
    // spherical cap of radius "tolerance" centered at B.  Then from the
    // spherical law of sines, sin(a)/sin(A) = sin(c)/sin(C), where "a" and
    // "c" are the lengths of the edges opposite A and C.  In our case C is a
    // 90 degree angle, therefore A = asin(sin(a) / sin(c)).  Angle A is the
    // half-angle of the allowable wedge.

    double half_angle = asin(sin(tolerance.radians()) / sin(distance));
    S1Interval target = S1Interval::FromPoint(center).Expanded(half_angle);
    current_wedge = current_wedge.Intersection(target);
    S2_DCHECK(!current_wedge.is_empty());
  }
  // We break out of the loop when we reach a vertex index that can't be
  // included in the line segment, so back up by one vertex.
  return index - 1;
}
}  // namespace

void S2Polyline::SubsampleVertices(S1Angle tolerance,
                                   vector<int>* indices) const {
  indices->clear();
  if (num_vertices() == 0) return;

  indices->push_back(0);
  S1Angle clamped_tolerance = max(tolerance, S1Angle::Radians(0));
  for (int index = 0; index + 1 < num_vertices(); ) {
    int next_index = FindEndVertex(*this, clamped_tolerance, index);
    // Don't create duplicate adjacent vertices.
    if (vertex(next_index) != vertex(index)) {
      indices->push_back(next_index);
    }
    index = next_index;
  }
}

bool S2Polyline::Equals(const S2Polyline& b) const {
  if (num_vertices() != b.num_vertices()) return false;
  for (int offset = 0; offset < num_vertices(); ++offset) {
    if (vertex(offset) != b.vertex(offset)) return false;
  }
  return true;
}

bool S2Polyline::ApproxEquals(const S2Polyline& b, S1Angle max_error) const {
  if (num_vertices() != b.num_vertices()) return false;
  for (int offset = 0; offset < num_vertices(); ++offset) {
    if (!S2::ApproxEquals(vertex(offset), b.vertex(offset), max_error)) {
      return false;
    }
  }
  return true;
}

size_t S2Polyline::SpaceUsed() const {
  return sizeof(*this) + num_vertices() * sizeof(S2Point);
}

namespace {
// Return the first i > "index" such that the ith vertex of "pline" is not at
// the same point as the "index"th vertex.  Returns pline.num_vertices() if
// there is no such value of i.
inline int NextDistinctVertex(const S2Polyline& pline, int index) {
  const S2Point& initial = pline.vertex(index);
  do {
    ++index;
  } while (index < pline.num_vertices() && pline.vertex(index) == initial);
  return index;
}

// This struct represents a search state in the NearlyCovers algorithm
// below.  See the description of the algorithm for details.
struct SearchState {
  inline SearchState(int i_val, int j_val, bool i_in_progress_val)
      : i(i_val), j(j_val), i_in_progress(i_in_progress_val) {}

  int i;
  int j;
  bool i_in_progress;
};

// This operator is needed for storing SearchStates in a set.  The ordering
// chosen has no special meaning.
struct SearchStateKeyCompare {
  bool operator() (const SearchState& a, const SearchState& b) const {
    if (a.i != b.i) return a.i < b.i;
    if (a.j != b.j) return a.j < b.j;
    return a.i_in_progress < b.i_in_progress;
  }
};

}  // namespace

bool S2Polyline::NearlyCovers(const S2Polyline& covered,
                              S1Angle max_error) const {
  // NOTE: This algorithm is described assuming that adjacent vertices in a
  // polyline are never at the same point.  That is, the ith and i+1th vertices
  // of a polyline are never at the same point in space.  The implementation
  // does not make this assumption.

  // DEFINITIONS:
  //   - edge "i" of a polyline is the edge from the ith to i+1th vertex.
  //   - covered_j is a polyline consisting of edges 0 through j of "covered."
  //   - this_i is a polyline consisting of edges 0 through i of this polyline.
  //
  // A search state is represented as an (int, int, bool) tuple, (i, j,
  // i_in_progress).  Using the "drive a car" analogy from the header comment, a
  // search state signifies that you can drive one car along "covered" from its
  // first vertex through a point on its jth edge, and another car along this
  // polyline from some point on or before its ith edge to a to a point on its
  // ith edge, such that no car ever goes backward, and the cars are always
  // within "max_error" of each other.  If i_in_progress is true, it means that
  // you can definitely drive along "covered" through the jth vertex (beginning
  // of the jth edge). Otherwise, you can definitely drive along "covered"
  // through the point on the jth edge of "covered" closest to the ith vertex of
  // this polyline.
  //
  // The algorithm begins by finding all edges of this polyline that are within
  // "max_error" of the first vertex of "covered," and adding search states
  // representing all of these possible starting states to the stack of
  // "pending" states.
  //
  // The algorithm proceeds by popping the next pending state,
  // (i,j,i_in_progress), off of the stack.  First it checks to see if that
  // state represents finding a valid covering of "covered" and returns true if
  // so.  Next, if the state represents reaching the end of this polyline
  // without finding a successful covering, the algorithm moves on to the next
  // state in the stack.  Otherwise, if state (i+1,j,false) is valid, it is
  // added to the stack of pending states.  Same for state (i,j+1,true).
  //
  // We need the stack because when "i" and "j" can both be incremented,
  // sometimes only one choice leads to a solution.  We use a set to keep track
  // of visited states to avoid duplicating work.  With the set, the worst-case
  // number of states examined is O(n+m) where n = this->num_vertices() and m =
  // covered.num_vertices().  Without it, the amount of work could be as high as
  // O((n*m)^2).  Using set, the running time is O((n*m) log (n*m)).
  //
  // TODO(user): Benchmark this, and see if the set is worth it.

  if (covered.num_vertices() == 0) return true;
  if (this->num_vertices() == 0) return false;

  vector<SearchState> pending;
  set<SearchState, SearchStateKeyCompare> done;

  // Find all possible starting states.
  for (int i = 0, next_i = NextDistinctVertex(*this, 0), next_next_i;
       next_i < this->num_vertices(); i = next_i, next_i = next_next_i) {
    next_next_i = NextDistinctVertex(*this, next_i);
    S2Point closest_point = S2::Project(
        covered.vertex(0), this->vertex(i), this->vertex(next_i));

    // In order to avoid duplicate starting states, we exclude the end vertex
    // of each edge *except* for the last non-degenerate edge.
    if ((next_next_i == this->num_vertices() ||
         closest_point != this->vertex(next_i)) &&
        S1Angle(closest_point, covered.vertex(0)) <= max_error) {
      pending.push_back(SearchState(i, 0, true));
    }
  }

  while (!pending.empty()) {
    const SearchState state = pending.back();
    pending.pop_back();
    if (!done.insert(state).second) continue;

    const int next_i = NextDistinctVertex(*this, state.i);
    const int next_j = NextDistinctVertex(covered, state.j);
    if (next_j == covered.num_vertices()) {
      return true;
    } else if (next_i == this->num_vertices()) {
      continue;
    }

    S2Point i_begin, j_begin;
    if (state.i_in_progress) {
      j_begin = covered.vertex(state.j);
      i_begin = S2::Project(
          j_begin, this->vertex(state.i), this->vertex(next_i));
    } else {
      i_begin = this->vertex(state.i);
      j_begin = S2::Project(
          i_begin, covered.vertex(state.j), covered.vertex(next_j));
    }

    if (S2::IsEdgeBNearEdgeA(j_begin, covered.vertex(next_j),
                             i_begin, this->vertex(next_i), max_error)) {
      pending.push_back(SearchState(next_i, state.j, false));
    }
    if (S2::IsEdgeBNearEdgeA(i_begin, this->vertex(next_i),
                             j_begin, covered.vertex(next_j), max_error)) {
      pending.push_back(SearchState(state.i, next_j, true));
    }
  }
  return false;
}

void S2Polyline::Shape::Init(const S2Polyline* polyline) {
  S2_LOG_IF(WARNING, polyline->num_vertices() == 1)
      << "S2Polyline::Shape with one vertex has no edges";
  polyline_ = polyline;
}

int S2Polyline::Shape::num_chains() const {
  return min(1, Shape::num_edges());  // Avoid virtual call.
}

S2Shape::Chain S2Polyline::Shape::chain(int i) const {
  S2_DCHECK_EQ(i, 0);
  return Chain(0, Shape::num_edges());  // Avoid virtual call.
}