1 // Copyright 2005 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 // Original Author: ericv@google.com (Eric Veach)
17 // Converted to D:  madric@gmail.com (Vijay Nayar)
18 
19 module s2.s2polyline;
20 
21 import s2.logger;
22 import s2.s1angle;
23 import s2.s1interval;
24 import s2.s2cap;
25 import s2.s2cell;
26 import s2.s2cell_id;
27 import s2.s2debug;
28 import s2.s2edge_crosser;
29 import s2.s2edge_distances : getDistance, interpolateAtDistance, isEdgeBNearEdgeA;
30 import s2.s2error;
31 import s2.s2latlng;
32 import s2.s2latlng_rect;
33 import s2.s2latlng_rect_bounder;
34 import s2.s2point;
35 import s2.s2pointutil : approxEquals, getFrame, isUnitLength, toFrame;
36 import s2.s2predicates : orderedCCW, sign;
37 import s2.s2region;
38 import s2.s2shape;
39 import s2.util.coding.coder;
40 import s2.util.math.matrix3x3;
41 
42 import std.math;
43 import std.algorithm : copy, max, min;
44 import std.exception : enforce;
45 import std.range : back, empty, popBack;
46 import std.typecons : Rebindable;
47 
48 /**
49  * An S2Polyline represents a sequence of zero or more vertices connected by
50  * straight edges (geodesics).  Edges of length 0 and 180 degrees are not
51  * allowed, i.e. adjacent vertices should not be identical or antipodal.
52  */
53 class S2Polyline : S2Region {
54 public:
55   /// Creates an empty S2Polyline that should be initialized by calling Init() or Decode().
56   this() {
57     _s2debugOverride = S2Debug.ALLOW;
58   }
59 
60   /// Convenience constructors that call Init() with the given vertices.
61   this(S2Point[] vertices) {
62     this(vertices, S2Debug.ALLOW);
63   }
64 
65   this(S2LatLng[] vertices) {
66     this(vertices, S2Debug.ALLOW);
67   }
68 
69   /**
70    * Convenience constructors to disable the automatic validity checking
71    * controlled by the --s2debug flag.  Example:
72    *
73    *   S2Polyline* line = new S2Polyline(vertices, S2Debug::DISABLE);
74    *
75    * This is equivalent to:
76    *
77    *   S2Polyline* line = new S2Polyline;
78    *   line->set_s2debug_override(S2Debug::DISABLE);
79    *   line->Init(vertices);
80    *
81    * The main reason to use this constructors is if you intend to call
82    * IsValid() explicitly.  See set_s2debug_override() for details.
83    */
84   this(S2Point[] vertices, S2Debug s2debugOverride) {
85     _s2debugOverride = s2debugOverride;
86     initialize(vertices);
87   }
88 
89   this(S2LatLng[] vertices, S2Debug s2debugOverride) {
90     _s2debugOverride = s2debugOverride;
91     initialize(vertices);
92   }
93 
94   /**
95    * Initialize a polyline that connects the given vertices. Empty polylines are
96    * allowed.  Adjacent vertices should not be identical or antipodal.  All
97    * vertices should be unit length.
98    */
99   void initialize(S2Point[] vertices) {
100     _vertices.length = vertices.length;
101     copy(vertices, _vertices);
102     if (flagsS2Debug && _s2debugOverride == S2Debug.ALLOW) {
103       enforce(isValid());
104     }
105   }
106 
107   /**
108    * Convenience initialization function that accepts latitude-longitude
109    * coordinates rather than S2Points.
110    */
111   void initialize(S2LatLng[] vertices) {
112     _vertices.length = vertices.length;
113     for (int i = 0; i < _vertices.length; ++i) {
114       _vertices[i] = vertices[i].toS2Point();
115     }
116     if (flagsS2Debug && _s2debugOverride == S2Debug.ALLOW) {
117       enforce(isValid());
118     }
119   }
120 
121   /**
122    * Allows overriding the automatic validity checks controlled by the
123    * --s2debug flag.  If this flag is true, then polylines are automatically
124    * checked for validity as they are initialized.  The main reason to disable
125    * this flag is if you intend to call IsValid() explicitly, like this:
126    *
127    *   S2Polyline line;
128    *   line.set_s2debug_override(S2Debug::DISABLE);
129    *   line.Init(...);
130    *   if (!line.IsValid()) { ... }
131    *
132    * Without the call to set_s2debug_override(), invalid data would cause a
133    * fatal error in Init() whenever the --s2debug flag is enabled.
134    */
135   void setS2debugOverride(S2Debug s2debugOverride) {
136     _s2debugOverride = s2debugOverride;
137   }
138 
139   S2Debug s2debugOverride() const {
140     return _s2debugOverride;
141   }
142 
143   /// Return true if the given vertices form a valid polyline.
144   bool isValid() const {
145     S2Error error;
146     if (findValidationError(error)) {
147       if (flagsS2Debug) {
148         logger.logError(error);
149       }
150       return false;
151     }
152     return true;
153   }
154 
155   /**
156    * Returns true if this is *not* a valid polyline and sets "error"
157    * appropriately.  Otherwise returns false and leaves "error" unchanged.
158    *
159    * REQUIRES: error != nullptr
160    */
161   bool findValidationError(out S2Error error) const {
162     // All vertices must be unit length.
163     for (int i = 0; i < numVertices(); ++i) {
164       if (!isUnitLength(vertex(i))) {
165         error.initialize(
166             S2Error.Code.NOT_UNIT_LENGTH, "Vertex %d is not unit length", i);
167         return true;
168       }
169     }
170     // Adjacent vertices must not be identical or antipodal.
171     for (int i = 1; i < numVertices(); ++i) {
172       if (vertex(i - 1) == vertex(i)) {
173         error.initialize(
174             S2Error.Code.DUPLICATE_VERTICES, "Vertices %d and %d are identical", i - 1, i);
175         return true;
176       }
177       if (vertex(i - 1) == -vertex(i)) {
178         error.initialize(
179             S2Error.Code.ANTIPODAL_VERTICES, "Vertices %d and %d are antipodal", i - 1, i);
180         return true;
181       }
182     }
183     return false;
184   }
185 
186   int numVertices() const {
187     return cast(int) _vertices.length;
188   }
189 
190   ref const(S2Point) vertex(int k) const
191   in {
192     assert(k >= 0);
193     assert(k < _vertices.length);
194   } do {
195     return _vertices[k];
196   }
197 
198   const(S2Point[]) vertices() const {
199     return _vertices;
200   }
201 
202   /// Return the length of the polyline.
203   S1Angle getLength() const {
204     S1Angle length;
205     for (int i = 1; i < numVertices(); ++i) {
206       length += S1Angle(vertex(i-1), vertex(i));
207     }
208     return length;
209   }
210 
211   /**
212    * Return the true centroid of the polyline multiplied by the length of the
213    * polyline (see s2centroids.h for details on centroids).  The result is not
214    * unit length, so you may want to normalize it.
215    *
216    * Prescaling by the polyline length makes it easy to compute the centroid
217    * of several polylines (by simply adding up their centroids).
218    */
219   S2Point getCentroid() const {
220     S2Point centroid;
221     for (int i = 1; i < numVertices(); ++i) {
222       // The centroid (multiplied by length) is a vector toward the midpoint
223       // of the edge, whose length is twice the sin of half the angle between
224       // the two vertices.  Defining theta to be this angle, we have:
225       S2Point vsum = vertex(i-1) + vertex(i);    // Length == 2*cos(theta)
226       S2Point vdiff = vertex(i-1) - vertex(i);   // Length == 2*sin(theta)
227       double cos2 = vsum.norm2();
228       double sin2 = vdiff.norm2();
229       enforce(cos2 > 0.0);  // Otherwise edge is undefined, and result is NaN.
230       centroid += sqrt(sin2 / cos2) * vsum;  // Length == 2*sin(theta)
231     }
232     return centroid;
233   }
234 
235   /**
236    * Return the point whose distance from vertex 0 along the polyline is the
237    * given fraction of the polyline's total length.  Fractions less than zero
238    * or greater than one are clamped.  The return value is unit length.  This
239    * cost of this function is currently linear in the number of vertices.
240    * The polyline must not be empty.
241    */
242   S2Point interpolate(double fraction) const {
243     int next_vertex;
244     return getSuffix(fraction, next_vertex);
245   }
246 
247   /**
248    * Like Interpolate(), but also return the index of the next polyline
249    * vertex after the interpolated point P.  This allows the caller to easily
250    * construct a given suffix of the polyline by concatenating P with the
251    * polyline vertices starting at "next_vertex".  Note that P is guaranteed
252    * to be different than vertex(*next_vertex), so this will never result in
253    * a duplicate vertex.
254    *
255    * The polyline must not be empty.  Note that if "fraction" >= 1.0, then
256    * "next_vertex" will be set to num_vertices() (indicating that no vertices
257    * from the polyline need to be appended).  The value of "next_vertex" is
258    * always between 1 and num_vertices().
259    *
260    * This method can also be used to construct a prefix of the polyline, by
261    * taking the polyline vertices up to "next_vertex - 1" and appending the
262    * returned point P if it is different from the last vertex (since in this
263    * case there is no guarantee of distinctness).
264    */
265   S2Point getSuffix(double fraction, out int next_vertex) const
266   in {
267     assert(numVertices() > 0);
268   } do {
269     // We intentionally let the (fraction >= 1) case fall through, since
270     // we need to handle it in the loop below in any case because of
271     // possible roundoff errors.
272     if (fraction <= 0) {
273       next_vertex = 1;
274       return vertex(0);
275     }
276     S1Angle length_sum;
277     for (int i = 1; i < numVertices(); ++i) {
278       length_sum += S1Angle(vertex(i-1), vertex(i));
279     }
280     S1Angle target = fraction * length_sum;
281     for (int i = 1; i < numVertices(); ++i) {
282       auto length = S1Angle(vertex(i-1), vertex(i));
283       if (target < length) {
284         // This interpolates with respect to arc length rather than
285         // straight-line distance, and produces a unit-length result.
286         S2Point result = interpolateAtDistance(target, vertex(i-1), vertex(i));
287         // It is possible that (result == vertex(i)) due to rounding errors.
288         next_vertex = (result == vertex(i)) ? (i + 1) : i;
289         return result;
290       }
291       target -= length;
292     }
293     next_vertex = numVertices();
294     return vertex(numVertices() - 1);
295   }
296 
297   /**
298    * The inverse operation of GetSuffix/Interpolate.  Given a point on the
299    * polyline, returns the ratio of the distance to the point from the
300    * beginning of the polyline over the length of the polyline.  The return
301    * value is always betwen 0 and 1 inclusive.  See GetSuffix() for the
302    * meaning of "next_vertex".
303    *
304    * The polyline should not be empty.  If it has fewer than 2 vertices, the
305    * return value is zero.
306    */
307   double unInterpolate(in S2Point point, int next_vertex) const
308   in {
309     assert(numVertices() > 0);
310   } do {
311     if (numVertices() < 2) {
312       return 0;
313     }
314     S1Angle length_sum;
315     for (int i = 1; i < next_vertex; ++i) {
316       length_sum += S1Angle(vertex(i-1), vertex(i));
317     }
318     S1Angle length_to_point = length_sum + S1Angle(vertex(next_vertex-1), point);
319     for (int i = next_vertex; i < numVertices(); ++i) {
320       length_sum += S1Angle(vertex(i-1), vertex(i));
321     }
322     // The ratio can be greater than 1.0 due to rounding errors or because the
323     // point is not exactly on the polyline.
324     return min(1.0, (length_to_point / length_sum).radians());
325   }
326 
327   /**
328    * Given a point, returns a point on the polyline that is closest to the given
329    * point.  See GetSuffix() for the meaning of "next_vertex", which is chosen
330    * here w.r.t. the projected point as opposed to the interpolated point in
331    * GetSuffix().
332    *
333    * The polyline must be non-empty.
334    */
335   S2Point project(in S2Point point, out int next_vertex) const
336   in {
337     assert(numVertices() > 0);
338   } do {
339     import s2.s2edge_distances : project;
340 
341     if (numVertices() == 1) {
342       // If there is only one vertex, it is always closest to any given point.
343       next_vertex = 1;
344       return vertex(0);
345     }
346 
347     // Initial value larger than any possible distance on the unit sphere.
348     S1Angle min_distance = S1Angle.fromRadians(10.0);
349     int min_index = -1;
350 
351     // Find the line segment in the polyline that is closest to the point given.
352     for (int i = 1; i < numVertices(); ++i) {
353       S1Angle distance_to_segment = getDistance(point, vertex(i-1), vertex(i));
354       if (distance_to_segment < min_distance) {
355         min_distance = distance_to_segment;
356         min_index = i;
357       }
358     }
359     enforce(min_index != -1);
360 
361     // Compute the point on the segment found that is closest to the point given.
362     S2Point closest_point = project(point, vertex(min_index - 1), vertex(min_index));
363 
364     next_vertex = min_index + (closest_point == vertex(min_index) ? 1 : 0);
365     return closest_point;
366   }
367 
368   /**
369    * Returns true if the point given is on the right hand side of the polyline,
370    * using a naive definition of "right-hand-sideness" where the point is on
371    * the RHS of the polyline iff the point is on the RHS of the line segment in
372    * the polyline which it is closest to.
373    *
374    * The polyline must have at least 2 vertices.
375    */
376   bool isOnRight(in S2Point point) const
377   in {
378     assert(numVertices() >= 2);
379   } do {
380     int next_vertex;
381     S2Point closest_point = project(point, next_vertex);
382 
383     enforce(next_vertex >= 1);
384     enforce(next_vertex <= numVertices());
385 
386     // If the closest point C is an interior vertex of the polyline, let B and D
387     // be the previous and next vertices.  The given point P is on the right of
388     // the polyline (locally) if B, P, D are ordered CCW around vertex C.
389     if (closest_point == vertex(next_vertex - 1) && next_vertex > 1
390         && next_vertex < numVertices()) {
391       if (point == vertex(next_vertex-1))
392         return false;  // Polyline vertices are not on the RHS.
393       return orderedCCW(vertex(next_vertex-2), point, vertex(next_vertex), vertex(next_vertex-1));
394     }
395 
396     // Otherwise, the closest point C is incident to exactly one polyline edge.
397     // We test the point P against that edge.
398     if (next_vertex == numVertices())
399       --next_vertex;
400 
401     return sign(point, vertex(next_vertex), vertex(next_vertex - 1)) > 0;
402   }
403 
404   /**
405    * Return true if this polyline intersects the given polyline. If the
406    * polylines share a vertex they are considered to be intersecting. When a
407    * polyline endpoint is the only intersection with the other polyline, the
408    * function may return true or false arbitrarily.
409    *
410    * The running time is quadratic in the number of vertices.  (To intersect
411    * polylines more efficiently, or compute the actual intersection geometry,
412    * use S2BooleanOperation.)
413    */
414   bool intersects(S2Polyline line) {
415     if (numVertices() <= 0 || line.numVertices() <= 0) {
416       return false;
417     }
418 
419     if (!getRectBound().intersects(line.getRectBound())) {
420       return false;
421     }
422 
423     // TODO(ericv): Use S2ShapeIndex here.
424     for (int i = 1; i < numVertices(); ++i) {
425       auto crosser = new S2EdgeCrosser(vertex(i - 1), vertex(i), line.vertex(0));
426       for (int j = 1; j < line.numVertices(); ++j) {
427         if (crosser.crossingSign(line.vertex(j)) >= 0) {
428           return true;
429         }
430       }
431     }
432     return false;
433   }
434 
435   /// Reverse the order of the polyline vertices.
436   void reverse() {
437     import std.algorithm : reverse;
438     reverse(_vertices);
439   }
440 
441   /**
442    * Return a subsequence of vertex indices such that the polyline connecting
443    * these vertices is never further than "tolerance" from the original
444    * polyline.  Provided the first and last vertices are distinct, they are
445    * always preserved; if they are not, the subsequence may contain only a
446    * single index.
447    *
448    * Some useful properties of the algorithm:
449    *
450    *  - It runs in linear time.
451    *
452    *  - The output is always a valid polyline.  In particular, adjacent
453    *    output vertices are never identical or antipodal.
454    *
455    *  - The method is not optimal, but it tends to produce 2-3% fewer
456    *    vertices than the Douglas-Peucker algorithm with the same tolerance.
457    *
458    *  - The output is *parametrically* equivalent to the original polyline to
459    *    within the given tolerance.  For example, if a polyline backtracks on
460    *    itself and then proceeds onwards, the backtracking will be preserved
461    *    (to within the given tolerance).  This is different than the
462    *    Douglas-Peucker algorithm, which only guarantees geometric equivalence.
463    *
464    * See also S2PolylineSimplifier, which uses the same algorithm but is more
465    * efficient and supports more features, and also S2Builder, which can
466    * simplify polylines and polygons, supports snapping (e.g. to E7 lat/lng
467    * coordinates or S2CellId centers), and can split polylines at intersection
468    * points.
469    */
470   void subsampleVertices(S1Angle tolerance, out int[] indices) const {
471     if (numVertices() == 0) return;
472 
473     indices ~= 0;
474     S1Angle clamped_tolerance = max(tolerance, S1Angle.fromRadians(0.0));
475     for (int index = 0; index + 1 < numVertices(); ) {
476       int next_index = findEndVertex(this, clamped_tolerance, index);
477       // Don't create duplicate adjacent vertices.
478       if (vertex(next_index) != vertex(index)) {
479         indices ~= next_index;
480       }
481       index = next_index;
482     }
483   }
484 
485   /// Return true if two polylines are exactly the same.
486   override
487   bool opEquals(in Object o) const {
488     const(S2Polyline) b = cast(S2Polyline) o;
489     if (b is null) return false;
490     if (numVertices() != b.numVertices()) return false;
491     for (int offset = 0; offset < numVertices(); ++offset) {
492       if (vertex(offset) != b.vertex(offset)) return false;
493     }
494     return true;
495   }
496 
497   /**
498    * Return true if two polylines have the same number of vertices, and
499    * corresponding vertex pairs are separated by no more than "max_error".
500    * (For testing purposes.)
501    */
502   bool approxEquals(in S2Polyline b, S1Angle max_error = S1Angle.fromRadians(1e-15)) const {
503     import s2.s2pointutil : approxEquals;
504 
505     if (numVertices() != b.numVertices()) return false;
506     for (int offset = 0; offset < numVertices(); ++offset) {
507       if (!approxEquals(vertex(offset), b.vertex(offset), max_error)) {
508         return false;
509       }
510     }
511     return true;
512   }
513 
514   // Return true if "covered" is within "max_error" of a contiguous subpath of
515   // this polyline over its entire length.  Specifically, this method returns
516   // true if this polyline has parameterization a:[0,1] -> S^2, "covered" has
517   // parameterization b:[0,1] -> S^2, and there is a non-decreasing function
518   // f:[0,1] -> [0,1] such that distance(a(f(t)), b(t)) <= max_error for all t.
519   //
520   // You can think of this as testing whether it is possible to drive a car
521   // along "covered" and a car along some subpath of this polyline such that no
522   // car ever goes backward, and the cars are always within "max_error" of each
523   // other.
524   //
525   // This function is well-defined for empty polylines:
526   //    anything.covers(empty) = true
527   //    empty.covers(nonempty) = false
528   bool nearlyCovers(in S2Polyline covered, S1Angle max_error) const {
529     import s2.s2edge_distances : project;
530 
531     // NOTE: This algorithm is described assuming that adjacent vertices in a
532     // polyline are never at the same point.  That is, the ith and i+1th vertices
533     // of a polyline are never at the same point in space.  The implementation
534     // does not make this assumption.
535 
536     // DEFINITIONS:
537     //   - edge "i" of a polyline is the edge from the ith to i+1th vertex.
538     //   - covered_j is a polyline consisting of edges 0 through j of "covered."
539     //   - this_i is a polyline consisting of edges 0 through i of this polyline.
540     //
541     // A search state is represented as an (int, int, bool) tuple, (i, j,
542     // i_in_progress).  Using the "drive a car" analogy from the header comment, a
543     // search state signifies that you can drive one car along "covered" from its
544     // first vertex through a point on its jth edge, and another car along this
545     // polyline from some point on or before its ith edge to a to a point on its
546     // ith edge, such that no car ever goes backward, and the cars are always
547     // within "max_error" of each other.  If i_in_progress is true, it means that
548     // you can definitely drive along "covered" through the jth vertex (beginning
549     // of the jth edge). Otherwise, you can definitely drive along "covered"
550     // through the point on the jth edge of "covered" closest to the ith vertex of
551     // this polyline.
552     //
553     // The algorithm begins by finding all edges of this polyline that are within
554     // "max_error" of the first vertex of "covered," and adding search states
555     // representing all of these possible starting states to the stack of
556     // "pending" states.
557     //
558     // The algorithm proceeds by popping the next pending state,
559     // (i,j,i_in_progress), off of the stack.  First it checks to see if that
560     // state represents finding a valid covering of "covered" and returns true if
561     // so.  Next, if the state represents reaching the end of this polyline
562     // without finding a successful covering, the algorithm moves on to the next
563     // state in the stack.  Otherwise, if state (i+1,j,false) is valid, it is
564     // added to the stack of pending states.  Same for state (i,j+1,true).
565     //
566     // We need the stack because when "i" and "j" can both be incremented,
567     // sometimes only one choice leads to a solution.  We use a set to keep track
568     // of visited states to avoid duplicating work.  With the set, the worst-case
569     // number of states examined is O(n+m) where n = this->num_vertices() and m =
570     // covered.num_vertices().  Without it, the amount of work could be as high as
571     // O((n*m)^2).  Using set, the running time is O((n*m) log (n*m)).
572     //
573     // TODO(user): Benchmark this, and see if the set is worth it.
574 
575     if (covered.numVertices() == 0) return true;
576     if (numVertices() == 0) return false;
577 
578     SearchState[] pending;
579     bool[SearchState] done;
580 
581     // Find all possible starting states.
582     for (int i = 0, next_i = nextDistinctVertex(this, 0), next_next_i;
583          next_i < numVertices(); i = next_i, next_i = next_next_i) {
584       next_next_i = nextDistinctVertex(this, next_i);
585       S2Point closest_point = project(covered.vertex(0), vertex(i), vertex(next_i));
586 
587       // In order to avoid duplicate starting states, we exclude the end vertex
588       // of each edge *except* for the last non-degenerate edge.
589       if ((next_next_i == numVertices() || closest_point != vertex(next_i))
590           && S1Angle(closest_point, covered.vertex(0)) <= max_error) {
591         pending ~= SearchState(i, 0, true);
592       }
593     }
594 
595     while (!pending.empty()) {
596       const SearchState state = pending.back();
597       pending.popBack();
598 
599       if (state in done) continue;
600       done[state] = true;
601 
602       const int next_i = nextDistinctVertex(this, state.i);
603       const int next_j = nextDistinctVertex(covered, state.j);
604       if (next_j == covered.numVertices()) {
605         return true;
606       } else if (next_i == numVertices()) {
607         continue;
608       }
609 
610       S2Point i_begin, j_begin;
611       if (state.iInProgress) {
612         j_begin = covered.vertex(state.j);
613         i_begin = project(j_begin, vertex(state.i), vertex(next_i));
614       } else {
615         i_begin = vertex(state.i);
616         j_begin = project(i_begin, covered.vertex(state.j), covered.vertex(next_j));
617       }
618 
619       if (isEdgeBNearEdgeA(j_begin, covered.vertex(next_j), i_begin, vertex(next_i), max_error)) {
620         pending ~= SearchState(next_i, state.j, false);
621       }
622       if (isEdgeBNearEdgeA(i_begin, vertex(next_i), j_begin, covered.vertex(next_j), max_error)) {
623         pending ~= SearchState(state.i, next_j, true);
624       }
625     }
626     return false;
627   }
628 
629   // Returns the total number of bytes used by the polyline.
630   size_t spaceUsed() const {
631     return this.classinfo.m_init.length + numVertices() * S2Point.sizeof;
632   }
633 
634   ////////////////////////////////////////////////////////////////////////
635   // S2Region interface (see s2region.h for details):
636 
637   override
638   S2Polyline clone() {
639     return new S2Polyline(this);
640   }
641 
642   override
643   void getCellUnionBound(out S2CellId[] cell_ids) {
644     return getCapBound().getCellUnionBound(cell_ids);
645   }
646 
647   override
648   S2Cap getCapBound() {
649     return getRectBound().getCapBound();
650   }
651 
652   override
653   S2LatLngRect getRectBound() {
654     auto bounder = new S2LatLngRectBounder();
655     for (int i = 0; i < numVertices(); ++i) {
656       bounder.addPoint(vertex(i));
657     }
658     return bounder.getBound();
659   }
660 
661   override
662   bool contains(in S2Cell cell) const {
663     return false;
664   }
665 
666   override
667   bool mayIntersect(in S2Cell cell) const {
668     if (numVertices() == 0) return false;
669 
670     // We only need to check whether the cell contains vertex 0 for correctness,
671     // but these tests are cheap compared to edge crossings so we might as well
672     // check all the vertices.
673     for (int i = 0; i < numVertices(); ++i) {
674       if (cell.contains(vertex(i))) return true;
675     }
676     S2Point[4] cell_vertices;
677     for (int i = 0; i < 4; ++i) {
678       cell_vertices[i] = cell.getVertex(i);
679     }
680     for (int j = 0; j < 4; ++j) {
681       auto crosser = new S2EdgeCrosser(cell_vertices[j], cell_vertices[(j + 1) & 3], vertex(0));
682       for (int i = 1; i < numVertices(); ++i) {
683         if (crosser.crossingSign(vertex(i)) >= 0) {
684           // There is a proper crossing, or two vertices were the same.
685           return true;
686         }
687       }
688     }
689     return false;
690   }
691 
692   /**
693    * Always return false, because "containment" is not numerically
694    * well-defined except at the polyline vertices.
695    */
696   override
697   bool contains(in S2Point p) const {
698     return false;
699   }
700 
701   /**
702    * Appends a serialized representation of the S2Polyline to "encoder".
703    *
704    * REQUIRES: "encoder" uses the default constructor, so that its buffer
705    *           can be enlarged as necessary by calling Ensure(int).
706    */
707   void encode(ORangeT)(Encoder!ORangeT encoder) const
708   out (; encoder.avail() >= 0) {
709     encoder.ensure(numVertices() * S2Point.sizeof + 10);  // sufficient
710 
711     encoder.put8(CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER);
712     encoder.put32(numVertices());
713     encoder.putRaw(_vertices);
714   }
715 
716   /// Decodes an S2Polyline encoded with Encode().  Returns true on success.
717   bool decode(IRangeT)(Decoder!IRangeT decoder) {
718     if (decoder.avail() < ubyte.sizeof + uint.sizeof) return false;
719     ubyte versionNum = decoder.get8();
720     if (versionNum > CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER) return false;
721 
722     uint num_vertices = decoder.get32();
723     if (decoder.avail() < num_vertices * S2Point.sizeof) return false;
724     _vertices = decoder.getRaw!S2Point(num_vertices);
725 
726     if (flagsS2Debug && _s2debugOverride == S2Debug.ALLOW) {
727       enforce(isValid());
728     }
729     return true;
730   }
731 
732   /**
733    * Wrapper class for indexing a polyline (see S2ShapeIndex).  Once this
734    * object is inserted into an S2ShapeIndex it is owned by that index, and
735    * will be automatically deleted when no longer needed by the index.  Note
736    * that this class does not take ownership of the polyline itself (see
737    * OwningShape below).  You can also subtype this class to store additional
738    * data (see S2Shape for details).
739    */
740   static class Shape : S2Shape {
741   public:
742     // Must call Init().
743     this() {
744       _polyline = null;
745     }
746 
747     /**
748      * Initialization.  Does not take ownership of "polyline".
749      *
750      * Note that a polyline with one vertex is defined to have no edges.  Use
751      * S2LaxPolylineShape or S2LaxClosedPolylineShape if you want to define a
752      * polyline consisting of a single degenerate edge.
753      */
754     this(in S2Polyline polyline) {
755       init(polyline);
756     }
757 
758     void init(in S2Polyline polyline) {
759       if (polyline.numVertices() == 1) {
760         logger.logWarn("S2Polyline::Shape with one vertex has no edges");
761       }
762       _polyline = polyline;
763     }
764 
765     const(S2Polyline) polyline() const {
766       return _polyline;
767     }
768 
769     // S2Shape interface:
770 
771     override
772     int numEdges() const {
773       return max(0, _polyline.numVertices() - 1);
774     }
775 
776     override
777     Edge edge(int e) const {
778       return Edge(_polyline.vertex(e), _polyline.vertex(e + 1));
779     }
780 
781     override
782     int dimension() const {
783       return 1;
784     }
785 
786     override
787     S2Shape.ReferencePoint getReferencePoint() const {
788       return S2Shape.ReferencePoint(false);
789     }
790 
791     override
792     int numChains() const {
793       return min(1, numEdges());  // Avoid virtual call.
794     }
795 
796     override
797     Chain chain(int i) const
798     in {
799       assert(i == 0);
800     } do {
801       return Chain(0, numEdges());  // Avoid virtual call.
802     }
803 
804     override
805     Edge chainEdge(int i, int j) const
806     in {
807       assert(i == 0);
808     } do {
809       return Edge(_polyline.vertex(j), _polyline.vertex(j + 1));
810     }
811 
812     override
813     ChainPosition chainPosition(int e) const {
814       return ChainPosition(0, e);
815     }
816 
817    private:
818     Rebindable!(const(S2Polyline)) _polyline;
819   }
820 
821   // Like Shape, except that the S2Polyline is automatically deleted when this
822   // object is deleted by the S2ShapeIndex.  This is useful when an S2Polyline
823   // is constructed solely for the purpose of indexing it.
824   //
825   // class OwningShape : Shape -- Not needed in D with GC.
826 
827  private:
828   // Internal copy constructor used only by Clone() that makes a deep copy of
829   // its argument.
830   this(in S2Polyline o) {
831     _s2debugOverride = o._s2debugOverride;
832     _vertices = o._vertices.dup;
833   }
834 
835   // Allows overriding the automatic validity checking controlled by the
836   // --s2debug flag.
837   S2Debug _s2debugOverride;
838 
839   // We store the vertices in an array rather than a vector because we don't
840   // need any STL methods, and computing the number of vertices using size()
841   // would be relatively expensive (due to division by sizeof(S2Point) == 24).
842   S2Point[] _vertices;
843 
844   // Given a polyline, a tolerance distance, and a start index, this function
845   // returns the maximal end index such that the line segment between these two
846   // vertices passes within "tolerance" of all interior vertices, in order.
847   static int findEndVertex(in S2Polyline polyline, S1Angle tolerance, int index)
848   in {
849     assert(tolerance.radians() >= 0);
850     assert((index + 1) < polyline.numVertices());
851   } do {
852 
853     // The basic idea is to keep track of the "pie wedge" of angles from the
854     // starting vertex such that a ray from the starting vertex at that angle
855     // will pass through the discs of radius "tolerance" centered around all
856     // vertices processed so far.
857 
858     // First we define a "coordinate frame" for the tangent and normal spaces
859     // at the starting vertex.  Essentially this means picking three
860     // orthonormal vectors X,Y,Z such that X and Y span the tangent plane at
861     // the starting vertex, and Z is "up".  We use the coordinate frame to
862     // define a mapping from 3D direction vectors to a one-dimensional "ray
863     // angle" in the range (-Pi, Pi].  The angle of a direction vector is
864     // computed by transforming it into the X,Y,Z basis, and then calculating
865     // atan2(y,x).  This mapping allows us to represent a wedge of angles as a
866     // 1D interval.  Since the interval wraps around, we represent it as an
867     // S1Interval, i.e. an interval on the unit circle.
868     Matrix3x3_d frame;
869     const(S2Point) origin = polyline.vertex(index);
870     getFrame(origin, frame);
871 
872     // As we go along, we keep track of the current wedge of angles and the
873     // distance to the last vertex (which must be non-decreasing).
874     S1Interval current_wedge = S1Interval.full();
875     double last_distance = 0;
876 
877     for (++index; index < polyline.numVertices(); ++index) {
878       const(S2Point) candidate = polyline.vertex(index);
879       double distance = origin.angle(candidate);
880 
881       // We don't allow simplification to create edges longer than 90 degrees,
882       // to avoid numeric instability as lengths approach 180 degrees.  (We do
883       // need to allow for original edges longer than 90 degrees, though.)
884       if (distance > M_PI_2 && last_distance > 0) break;
885 
886       // Vertices must be in increasing order along the ray, except for the
887       // initial disc around the origin.
888       if (distance < last_distance && last_distance > tolerance.radians()) break;
889       last_distance = distance;
890 
891       // Points that are within the tolerance distance of the origin do not
892       // constrain the ray direction, so we can ignore them.
893       if (distance <= tolerance.radians()) continue;
894 
895       // If the current wedge of angles does not contain the angle to this
896       // vertex, then stop right now.  Note that the wedge of possible ray
897       // angles is not necessarily empty yet, but we can't continue unless we
898       // are willing to backtrack to the last vertex that was contained within
899       // the wedge (since we don't create new vertices).  This would be more
900       // complicated and also make the worst-case running time more than linear.
901       S2Point direction = toFrame(frame, candidate);
902       double center = atan2(direction.y(), direction.x());
903       if (!current_wedge.contains(center)) break;
904 
905       // To determine how this vertex constrains the possible ray angles,
906       // consider the triangle ABC where A is the origin, B is the candidate
907       // vertex, and C is one of the two tangent points between A and the
908       // spherical cap of radius "tolerance" centered at B.  Then from the
909       // spherical law of sines, sin(a)/sin(A) = sin(c)/sin(C), where "a" and
910       // "c" are the lengths of the edges opposite A and C.  In our case C is a
911       // 90 degree angle, therefore A = asin(sin(a) / sin(c)).  Angle A is the
912       // half-angle of the allowable wedge.
913 
914       double half_angle = asin(sin(tolerance.radians()) / sin(distance));
915       S1Interval target = S1Interval.fromPoint(center).expanded(half_angle);
916       current_wedge = current_wedge.intersection(target);
917       enforce(!current_wedge.isEmpty());
918     }
919     // We break out of the loop when we reach a vertex index that can't be
920     // included in the line segment, so back up by one vertex.
921     return index - 1;
922   }
923 
924   // Return the first i > "index" such that the ith vertex of "pline" is not at
925   // the same point as the "index"th vertex.  Returns pline.num_vertices() if
926   // there is no such value of i.
927   static int nextDistinctVertex(in S2Polyline pline, int index) {
928     S2Point initial = pline.vertex(index);
929     do {
930       ++index;
931     } while (index < pline.numVertices() && pline.vertex(index) == initial);
932     return index;
933   }
934 
935   // This struct represents a search state in the NearlyCovers algorithm
936   // below.  See the description of the algorithm for details.
937   struct SearchState {
938     int i;
939     int j;
940     bool iInProgress;
941 
942     // This operator is needed for storing SearchStates in a set.  The ordering
943     // chosen has no special meaning.
944     int opCmp(SearchState b) const {
945       if (i != b.i) return i - b.i;
946       if (j != b.j) return j - b.j;
947       return iInProgress - b.iInProgress;
948     }
949   }
950 }
951 
952 private enum CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER = 1;