1 /**
2    S2Builder is a tool for assembling polygonal geometry from edges
3 
4    This class is a replacement for S2PolygonBuilder.  Once all clients have
5    been updated to use this class, S2PolygonBuilder will be removed.
6 
7    Copyright: 2016 Google Inc. All Rights Reserved.
8 
9    License:
10    Licensed under the Apache License, Version 2.0 (the "License");
11    you may not use this file except in compliance with the License.
12    You may obtain a copy of the License at
13 
14    $(LINK http://www.apache.org/licenses/LICENSE-2.0)
15 
16    Unless required by applicable law or agreed to in writing, software
17    distributed under the License is distributed on an "AS-IS" BASIS,
18    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19    See the License for the specific language governing permissions and
20    limitations under the License.
21 
22    Authors: ericv@google.com (Eric Veach), madric@gmail.com (Vijay Nayar)
23 */
24 module s2.s2builder;
25 
26 import s2.builder.graph;
27 import s2.builder.layer;
28 import s2.builder.util.snap_functions;
29 import s2.id_set_lexicon;
30 import s2.logger;
31 import s2.mutable_s2shape_index;
32 import s2.s1angle;
33 import s2.s1chord_angle;
34 import s2.s2cell_id;
35 import s2.s2closest_edge_query;
36 import s2.s2closest_point_query;
37 import s2.s2edge_crossings : INTERSECTION_ERROR;
38 import s2.s2edge_distances : getUpdateMinDistanceMaxError;
39 import s2.s2error;
40 import s2.s2loop;
41 import s2.s2point;
42 import s2.s2point_index;
43 import s2.s2polygon;
44 import s2.s2polyline;
45 import s2.s2polyline_simplifier;
46 import s2.s2predicates;
47 import s2.s2shape;
48 import s2.s2shape_index;
49 import s2.s2text_format;
50 import s2.util.math.vector;
51 
52 import std.algorithm;
53 import std.exception;
54 import std.math;
55 import std.range;
56 import std.stdio;
57 import std.typecons;
58 
59 /// Internal flag intended to be set from within a debugger.
60 bool s2builderVerbose = false;
61 
62 /**
63  * S2Builder is a tool for assembling polygonal geometry from edges.  Here are
64  * some of the things it is designed for:
65  *
66  * 1. Building polygons, polylines, and polygon meshes from unsorted
67  *    collections of edges.
68  *
69  * 2. Snapping geometry to discrete representations (such as S2CellId centers
70  *    or E7 lat/lng coordinates) while preserving the input topology and with
71  *    guaranteed error bounds.
72  *
73  * 3. Simplifying geometry (e.g. for indexing, display, or storage).
74  *
75  * 4. Importing geometry from other formats, including repairing geometry
76  *    that has errors.
77  *
78  * 5. As a tool for implementing more complex operations such as polygon
79  *    intersections and unions.
80  *
81  * The implementation is based on the framework of "snap rounding".  Unlike
82  * most snap rounding implementations, S2Builder defines edges as geodesics on
83  * the sphere (straight lines) and uses the topology of the sphere (i.e.,
84  * there are no "seams" at the poles or 180th meridian).  The algorithm is
85  * designed to be 100% robust for arbitrary input geometry.  It offers the
86  * following properties:
87  *
88  *   - Guaranteed bounds on how far input vertices and edges can move during
89  *     the snapping process (i.e., at most the given "snap_radius").
90  *
91  *   - Guaranteed minimum separation between edges and vertices other than
92  *     their endpoints (similar to the goals of Iterated Snap Rounding).  In
93  *     other words, edges that do not intersect in the output are guaranteed
94  *     to have a minimum separation between them.
95  *
96  *   - Idempotency (similar to the goals of Stable Snap Rounding), i.e. if the
97  *     input already meets the output criteria then it will not be modified.
98  *
99  *   - Preservation of the input topology (up to the creation of
100  *     degeneracies).  This means that there exists a continuous deformation
101  *     from the input to the output such that no vertex crosses an edge.  In
102  *     other words, self-intersections won't be created, loops won't change
103  *     orientation, etc.
104  *
105  *   - The ability to snap to arbitrary discrete point sets (such as S2CellId
106  *     centers, E7 lat/lng points on the sphere, or simply a subset of the
107  *     input vertices), rather than being limited to an integer grid.
108  *
109  * Here are some of its other features:
110  *
111  *  - It can handle both directed and undirected edges.  Undirected edges can
112  *    be useful for importing data from other formats, e.g. where loops have
113  *    unspecified orientations.
114  *
115  *  - It can eliminate self-intersections by finding all edge pairs that cross
116  *    and adding a new vertex at each intersection point.
117  *
118  *  - It can simplify polygons to within a specified tolerance.  For example,
119  *    if two vertices are close enough they will be merged, and if an edge
120  *    passes nearby a vertex then it will be rerouted through that vertex.
121  *    Optionally, it can also detect nearly straight chains of short edges and
122  *    replace them with a single long edge, while maintaining the same
123  *    accuracy, separation, and topology guarantees ("simplify_edge_chains").
124  *
125  *  - It supports many different output types through the concept of "layers"
126  *    (polylines, polygons, polygon meshes, etc).  You can build multiple
127  *    layers at once in order to ensure that snapping does not create
128  *    intersections between different objects (for example, you can simplify a
129  *    set of contour lines without the risk of having them cross each other).
130  *
131  *  - It supports edge labels, which allow you to attach arbitrary information
132  *    to edges and have it preserved during the snapping process.  (This can
133  *    also be achieved using layers, at a coarser level of granularity.)
134  *
135  * Caveats:
136  *
137  *  - Because S2Builder only works with edges, it cannot distinguish between
138  *    the empty and full polygons.  If your application can generate both the
139  *    empty and full polygons, you must implement logic outside of this class.
140  *
141  * Example showing how to snap a polygon to E7 coordinates:
142  * ---
143  *  S2Builder builder(S2Builder.Options(new IntLatLngSnapFunction(7)));
144  *  auto output = new S2Polygon();
145  *  builder.startLayer(new S2PolygonLayer(output));
146  *  builder.addPolygon(input);
147  *  S2Error error;
148  *  if (!builder.build(&error)) {
149  *    logger.logError(error);
150  *    ...
151  *  }
152  * ---
153  */
154 class S2Builder {
155 public:
156   /**
157    * Indicates whether the input edges are undirected.  Typically this is
158    * specified for each output layer (e.g., s2builderutil::S2PolygonLayer).
159    *
160    * Directed edges are preferred, since otherwise the output is ambiguous.
161    * For example, output polygons may be the *inverse* of the intended result
162    * (e.g., a polygon intended to represent the world's oceans may instead
163    * represent the world's land masses).  Directed edges are also somewhat
164    * more efficient.
165    *
166    * However even with undirected edges, most S2Builder layer types try to
167    * preserve the input edge direction whenever possible.  Generally, edges
168    * are reversed only when it would yield a simpler output.  For example,
169    * S2PolygonLayer assumes that polygons created from undirected edges should
170    * cover at most half of the sphere.  Similarly, S2PolylineVectorLayer
171    * assembles edges into as few polylines as possible, even if this means
172    * reversing some of the "undirected" input edges.
173    *
174    * For shapes with interiors, directed edges should be oriented so that the
175    * interior is to the left of all edges.  This means that for a polygon with
176    * holes, the outer loops ("shells") should be directed counter-clockwise
177    * while the inner loops ("holes") should be directed clockwise.  Note that
178    * S2Builder::AddPolygon() follows this convention automatically.
179    */
180   enum EdgeType { DIRECTED, UNDIRECTED }
181 
182   /**
183    * A SnapFunction restricts the locations of the output vertices.  For
184    * example, there are predefined snap functions that require vertices to be
185    * located at S2CellId centers or at E5/E6/E7 coordinates.  The SnapFunction
186    * can also specify a minimum spacing between vertices (the "snap radius").
187    *
188    * A SnapFunction defines the following methods:
189    *
190    * 1. The SnapPoint() method, which snaps a point P to a nearby point (the
191    *    "candidate snap site").  Any point may be returned, including P
192    *    itself (this is the "identity snap function").
193    *
194    * 2. "snap_radius", the maximum distance that vertices can move when
195    *    snapped.  The snap_radius must be at least as large as the maximum
196    *    distance between P and SnapPoint(P) for any point P.
197    *
198    * 3. "max_edge_deviation", the maximum distance that edges can move when
199    *    snapped.  It is slightly larger than "snap_radius" because when a
200    *    geodesic edge is snapped, the center of the edge moves further than
201    *    its endpoints.  This value is computed automatically by S2Builder.
202    *
203    * 4. "min_vertex_separation", the guaranteed minimum distance between
204    *    vertices in the output.  This is generally a fraction of
205    *    "snap_radius" where the fraction depends on the snap function.
206    *
207    * 5. A "min_edge_vertex_separation", the guaranteed minimum distance
208    *    between edges and non-incident vertices in the output.  This is
209    *    generally a fraction of "snap_radius" where the fraction depends on
210    *    the snap function.
211    *
212    * It is important to note that SnapPoint() does not define the actual
213    * mapping from input vertices to output vertices, since the points it
214    * returns (the candidate snap sites) are further filtered to ensure that
215    * they are separated by at least the snap radius.  For example, if you
216    * specify E7 coordinates (2cm resolution) and a snap radius of 10m, then a
217    * subset of points returned by SnapPoint will be chosen (the "snap sites"),
218    * and each input vertex will be mapped to the closest site.  Therefore you
219    * cannot assume that P is necessarily snapped to SnapPoint(P).
220    *
221    * S2Builder makes the following guarantees:
222    *
223    * 1. Every vertex is at a location returned by SnapPoint().
224    *
225    * 2. Vertices are within "snap_radius" of the corresponding input vertex.
226    *
227    * 3. Edges are within "max_edge_deviation" of the corresponding input edge
228    *    (a distance slightly larger than "snap_radius").
229    *
230    * 4. Vertices are separated by at least "min_vertex_separation"
231    *    (a fraction of "snap_radius" that depends on the snap function).
232    *
233    * 5. Edges and non-incident vertices are separated by at least
234    *    "min_edge_vertex_separation" (a fraction of "snap_radius").
235    *
236    * 6. Vertex and edge locations do not change unless one of the conditions
237    *    above is not already met (idempotency / stability).
238    *
239    * 7. The topology of the input geometry is preserved (up to the creation
240    *    of degeneracies).  This means that there exists a continuous
241    *    deformation from the input to the output such that no vertex
242    *    crosses an edge.
243    */
244   abstract static class SnapFunction {
245   public:
246 
247     /**
248      * The maximum distance that vertices can move when snapped.
249      *
250      * If the snap radius is zero, then vertices are snapped together only if
251      * they are identical.  Edges will not be snapped to any vertices other
252      * than their endpoints, even if there are vertices whose distance to the
253      * edge is zero, unless split_crossing_edges() is true.
254      *
255      * REQUIRES: snap_radius() <= kMaxSnapRadius
256      */
257     abstract S1Angle snapRadius() const;
258 
259     /**
260      * The maximum snap radius is just large enough to support snapping to
261      * S2CellId level 0.  It is equivalent to 7800km on the Earth's surface.
262      */
263     static S1Angle kMaxSnapRadius() {
264       // This value can't be larger than 85.7 degrees without changing the code
265       // related to min_edge_length_to_split_ca_, and increasing it to 90 degrees
266       // or more would most likely require significant changes to the algorithm.
267       return S1Angle.fromDegrees(70.0);
268     }
269 
270     /**
271      * The maximum distance that the center of an edge can move when snapped.
272      * This is slightly larger than "snap_radius" because when a geodesic edge
273      * is snapped, the center of the edge moves further than its endpoints.
274      */
275     S1Angle maxEdgeDeviation() const
276     in {
277       assert(snapRadius() <= kMaxSnapRadius());
278     } do {
279       // We want max_edge_deviation() to be large enough compared to snap_radius()
280       // such that edge splitting is rare.
281       //
282       // Using spherical trigonometry, if the endpoints of an edge of length L
283       // move by at most a distance R, the center of the edge moves by at most
284       // asin(sin(R) / cos(L / 2)).  Thus the (max_edge_deviation / snap_radius)
285       // ratio increases with both the snap radius R and the edge length L.
286       //
287       // We arbitrarily limit the edge deviation to be at most 10% more than the
288       // snap radius.  With the maximum allowed snap radius of 70 degrees, this
289       // means that edges up to 30.6 degrees long are never split.  For smaller
290       // snap radii, edges up to 49 degrees long are never split.  (Edges of any
291       // length are not split unless their endpoints move far enough so that the
292       // actual edge deviation exceeds the limit; in practice, splitting is rare
293       // even with long edges.)  Note that it is always possible to split edges
294       // when max_edge_deviation() is exceeded; see MaybeAddExtraSites().
295       enum double kMaxEdgeDeviationRatio = 1.1;
296       return kMaxEdgeDeviationRatio * snapRadius();
297     }
298 
299     /**
300      * The guaranteed minimum distance between vertices in the output.
301      * This is generally some fraction of "snap_radius".
302      */
303     abstract S1Angle minVertexSeparation() const;
304 
305     /**
306      * The guaranteed minimum spacing between edges and non-incident vertices
307      * in the output.  This is generally some fraction of "snap_radius".
308      */
309     abstract S1Angle minEdgeVertexSeparation() const;
310 
311     /**
312      * Returns a candidate snap site for the given point.  The final vertex
313      * locations are a subset of the snap sites returned by this function
314      * (spaced at least "min_vertex_separation" apart).
315      *
316      * The only requirement is that SnapPoint(x) must return a point whose
317      * distance from "x" is no greater than "snap_radius".
318      */
319     abstract S2Point snapPoint(in S2Point point) const;
320 
321     // Returns a deep copy of this SnapFunction.
322     abstract SnapFunction clone() const;
323 
324     override
325     string toString() const {
326       import std.conv;
327       return "SnapFunction[]";
328     }
329   }
330 
331   static class Options {
332   public:
333     this() {
334       _snapFunction = new IdentitySnapFunction(S1Angle.zero());
335     }
336 
337     /// Convenience constructor that calls set_snap_function().
338     this(in SnapFunction snap_function) {
339       _snapFunction = snap_function.clone();
340     }
341 
342     this(in Options options) {
343       _snapFunction = options._snapFunction.clone();
344       _splitCrossingEdges = options._splitCrossingEdges;
345       _simplifyEdgeChains = options._simplifyEdgeChains;
346       _idempotent = options._idempotent;
347     }
348 
349     /**
350      * Sets the desired snap function.  The snap function is copied
351      * internally, so you can safely pass a temporary object.
352      *
353      * Note that if your input data includes vertices that were created using
354      * S2::GetIntersection(), then you should use a "snap_radius" of
355      * at least S2::kIntersectionSnapRadius, e.g. by calling
356      *
357      *  options.set_snap_function(s2builderutil::IdentitySnapFunction(
358      *      S2::kIntersectionSnapRadius));
359      *
360      * DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero())
361      * [This does no snapping and preserves all input vertices exactly.]
362      */
363     const(SnapFunction) snapFunction() const {
364       return _snapFunction;
365     }
366 
367     void setSnapFunction(in SnapFunction snap_function) {
368       _snapFunction = snap_function.clone();
369     }
370 
371     /**
372      * If true, then detect all pairs of crossing edges and eliminate them by
373      * adding a new vertex at their intersection point.
374      *
375      * When this option is true, the effective snap_radius() for edges is
376      * increased by S2::kIntersectionError to take into account the
377      * additional error when computing intersection points.  In other words,
378      * edges may move by up to snap_radius() + S2::kIntersectionError.
379      *
380      * Undirected edges should always be used when the output is a polygon,
381      * since splitting a directed loop at a self-intersection converts it into
382      * two loops that don't define a consistent interior according to the
383      * "interior is on the left" rule.  (On the other hand, it is fine to use
384      * directed edges when defining a polygon *mesh* because in that case the
385      * input consists of sibling edge pairs.)
386      *
387      * Self-intersections can also arise when importing data from a 2D
388      * projection.  You can minimize this problem by subdividing the input
389      * edges so that the S2 edges (which are geodesics) stay close to the
390      * original projected edges (which are curves on the sphere).  This can
391      * be done using s2builderutil::EdgeSplitter(), for example.
392      *
393      * DEFAULT: false
394      */
395     bool splitCrossingEdges() const {
396       return _splitCrossingEdges;
397     }
398 
399     void setSplitCrossingEdges(bool split_crossing_edges) {
400       _splitCrossingEdges = split_crossing_edges;
401     }
402 
403     /**
404      * If true, then simplify the output geometry by replacing nearly straight
405      * chains of short edges with a single long edge.
406      *
407      * The combined effect of snapping and simplifying will not change the
408      * input by more than the guaranteed tolerances (see the list documented
409      * with the SnapFunction class).  For example, simplified edges are
410      * guaranteed to pass within snap_radius() of the *original* positions of
411      * all vertices that were removed from that edge.  This is a much tighter
412      * guarantee than can be achieved by snapping and simplifying separately.
413      *
414      * However, note that this option does not guarantee idempotency.  In
415      * other words, simplifying geometry that has already been simplified once
416      * may simplify it further.  (This is unavoidable, since tolerances are
417      * measured with respect to the original geometry, which is no longer
418      * available when the geometry is simplified a second time.)
419      *
420      * When the output consists of multiple layers, simplification is
421      * guaranteed to be consistent: for example, edge chains are simplified in
422      * the same way across layers, and simplification preserves topological
423      * relationships between layers (e.g., no crossing edges will be created).
424      * Note that edge chains in different layers do not need to be identical
425      * (or even have the same number of vertices, etc) in order to be
426      * simplified together.  All that is required is that they are close
427      * enough together so that the same simplified edge can meet all of their
428      * individual snapping guarantees.
429      *
430      * Note that edge chains are approximated as parametric curves rather than
431      * point sets.  This means that if an edge chain backtracks on itself (for
432      * example, ABCDEFEDCDEFGH) then such backtracking will be preserved to
433      * within snap_radius() (for example, if the preceding point were all in a
434      * straight line then the edge chain would be simplified to ACFCFH, noting
435      * that C and F have degree > 2 and therefore can't be simplified away).
436      *
437      * Simplified edges are assigned all labels associated with the edges of
438      * the simplified chain.
439      *
440      * For this option to have any effect, a SnapFunction with a non-zero
441      * snap_radius() must be specified.  Also note that vertices specified
442      * using ForceVertex are never simplified away.
443      *
444      * DEFAULT: false
445      */
446     bool simplifyEdgeChains() const {
447       return _simplifyEdgeChains;
448     }
449 
450     void setSimplifyEdgeChains(bool simplify_edge_chains) {
451       _simplifyEdgeChains = simplify_edge_chains;
452 
453       // Simplification requires a non-zero snap radius, and while it might be
454       // possible to do some simplifying without snapping, it is much simpler to
455       // always snap (even if the input geometry already meets the other output
456       // requirements).  We need to compute edge_sites_ in order to avoid
457       // approaching non-incident vertices too closely, for example.
458       setIdempotent(false);
459     }
460 
461     /**
462      * If true, then snapping occurs only when the input geometry does not
463      * already meet the S2Builder output guarantees (see the SnapFunction
464      * class description for details).  This means that if all input vertices
465      * are at snapped locations, all vertex pairs are separated by at least
466      * min_vertex_separation(), and all edge-vertex pairs are separated by at
467      * least min_edge_vertex_separation(), then no snapping is done.
468      *
469      * If false, then all vertex pairs and edge-vertex pairs closer than
470      * "snap_radius" will be considered for snapping.  This can be useful, for
471      * example, if you know that your geometry contains errors and you want to
472      * make sure that features closer together than "snap_radius" are merged.
473      *
474      * This option is automatically turned off by simplify_edge_chains(),
475      * since simplifying edge chains is never guaranteed to be idempotent.
476      *
477      * DEFAULT: true
478      */
479     bool idempotent() const {
480       return _idempotent;
481     }
482 
483     void setIdempotent(bool idempotent) {
484       _idempotent = idempotent;
485     }
486 
487   private:
488     SnapFunction _snapFunction;
489     bool _splitCrossingEdges;
490     bool _simplifyEdgeChains;
491     bool _idempotent = true;
492   }
493 
494   /**
495    * For output layers that represent polygons, there is an ambiguity inherent
496    * in spherical geometry that does not exist in planar geometry.  Namely, if
497    * a polygon has no edges, does it represent the empty polygon (containing
498    * no points) or the full polygon (containing all points)?  This ambiguity
499    * also occurs for polygons that consist only of degeneracies, e.g. a
500    * degenerate loop with only two edges could be either a degenerate shell in
501    * the empty polygon or a degenerate hole in the full polygon.
502    *
503    * To resolve this ambiguity, an IsFullPolygonPredicate may be specified for
504    * each input layer (see AddIsFullPolygonPredicate below).  If the layer
505    * consists only of polygon degeneracies, the layer implementation may call
506    * this method to determine whether the polygon is empty or full except for
507    * the given degeneracies.  (Note that under the semi-open boundary model,
508    * degeneracies do not affect point containment.)
509    *
510    * This predicate is only required for layers that will be assembled into
511    * polygons.  It is not used by other layer types.
512    */
513   alias IsFullPolygonPredicate = bool function(in Graph g, ref S2Error error);
514 
515   /// Default constructor; requires Init() to be called.
516   this() {
517     _labelSetLexicon = new IdSetLexicon();
518   }
519 
520   /**
521    * Convenience constructor that calls Init().  Note that to use the default
522    * options, C++ syntax requires an extra layer of parentheses:
523    *
524    *   S2Builder builder((S2Builder::Options()));
525    */
526   this(Options options) {
527     this();
528     initialize(options);
529   }
530 
531   /// Initializes an S2Builder with the given options.
532   void initialize(Options options) {
533     import std.stdio;
534     import core.stdc.stdio;
535 
536     _options = options;
537     const(SnapFunction) snap_function = options.snapFunction();
538     S1Angle snap_radius = snap_function.snapRadius();
539     debug enforce(snap_radius <= SnapFunction.kMaxSnapRadius());
540 
541     // Convert the snap radius to an S1ChordAngle.  This is the "true snap
542     // radius" used when evaluating exact predicates (s2predicates.h).
543     _siteSnapRadiusCa = S1ChordAngle(snap_radius);
544 
545     // When split_crossing_edges() is true, we need to use a larger snap radius
546     // for edges than for vertices to ensure that both edges are snapped to the
547     // edge intersection location.  This is because the computed intersection
548     // point is not exact; it may be up to kIntersectionError away from its true
549     // position.  The computed intersection point might then be snapped to some
550     // other vertex up to snap_radius away.  So to ensure that both edges are
551     // snapped to a common vertex, we need to increase the snap radius for edges
552     // to at least the sum of these two values (calculated conservatively).
553     S1Angle edge_snap_radius = snap_radius;
554     if (!options.splitCrossingEdges()) {
555       _edgeSnapRadiusCa = _siteSnapRadiusCa;
556     } else {
557       edge_snap_radius += INTERSECTION_ERROR;
558       _edgeSnapRadiusCa = roundUp(edge_snap_radius);
559     }
560     _snappingRequested = edge_snap_radius > S1Angle.zero();
561 
562     // Compute the maximum distance that a vertex can be separated from an
563     // edge while still affecting how that edge is snapped.
564     _maxEdgeDeviation = snap_function.maxEdgeDeviation();
565     _edgeSiteQueryRadiusCa =
566         S1ChordAngle(_maxEdgeDeviation + snap_function.minEdgeVertexSeparation());
567 
568     // Compute the maximum edge length such that even if both endpoints move by
569     // the maximum distance allowed (i.e., snap_radius), the center of the edge
570     // will still move by less than max_edge_deviation().  This saves us a lot
571     // of work since then we don't need to check the actual deviation.
572     _minEdgeLengthToSplitCa =
573         S1ChordAngle.fromRadians(2 * acos(sin(snap_radius) / sin(_maxEdgeDeviation)));
574 
575     // If the condition below is violated, then AddExtraSites() needs to be
576     // modified to check that snapped edges pass on the same side of each "site
577     // to avoid" as the input edge.  Currently it doesn't need to do this
578     // because the condition below guarantees that if the snapped edge passes on
579     // the wrong side of the site then it is also too close, which will cause a
580     // separation site to be added.
581     //
582     // Currently max_edge_deviation() is at most 1.1 * snap_radius(), whereas
583     // min_edge_vertex_separation() is at least 0.219 * snap_radius() (based on
584     // S2CellIdSnapFunction, which is currently the worst case).
585     enforce(snap_function.maxEdgeDeviation()
586         <= snap_function.snapRadius() + snap_function.minEdgeVertexSeparation());
587 
588     // To implement idempotency, we check whether the input geometry could
589     // possibly be the output of a previous S2Builder invocation.  This involves
590     // testing whether any site/site or edge/site pairs are too close together.
591     // This is done using exact predicates, which require converting the minimum
592     // separation values to an S1ChordAngle.
593     _minSiteSeparation = snap_function.minVertexSeparation();
594     _minSiteSeparationCa = S1ChordAngle(_minSiteSeparation);
595     _minEdgeSiteSeparationCa = S1ChordAngle(snap_function.minEdgeVertexSeparation());
596 
597     // This is an upper bound on the distance computed by S2ClosestPointQuery
598     // where the true distance might be less than min_edge_site_separation_ca_.
599     _minEdgeSiteSeparationCaLimit = addPointToEdgeError(_minEdgeSiteSeparationCa);
600 
601     // Compute the maximum possible distance between two sites whose Voronoi
602     // regions touch.  (The maximum radius of each Voronoi region is
603     // edge_snap_radius_.)  Then increase this bound to account for errors.
604     _maxAdjacentSiteSeparationCa = addPointToPointError(roundUp(2 * edge_snap_radius));
605 
606     // Finally, we also precompute sin^2(edge_snap_radius), which is simply the
607     // squared distance between a vertex and an edge measured perpendicular to
608     // the plane containing the edge, and increase this value by the maximum
609     // error in the calculation to compare this distance against the bound.
610     double d = sin(edge_snap_radius);
611     _edgeSnapRadiusSin2 = d * d;
612     _edgeSnapRadiusSin2 +=
613         ((9.5 * d + 2.5 + 2 * sqrt(3.0)) * d + 9 * double.epsilon) * double.epsilon;
614 
615     // Initialize the current label set.
616     _labelSetId = _labelSetLexicon.emptySetId();
617     _labelSetModified = false;
618 
619     // If snapping was requested, we try to determine whether the input geometry
620     // already meets the output requirements.  This is necessary for
621     // idempotency, and can also save work.  If we discover any reason that the
622     // input geometry needs to be modified, snapping_needed_ is set to true.
623     _snappingNeeded = false;
624   }
625 
626   const(Options) options() const {
627     return _options;
628   }
629 
630   /**
631    * Starts a new output layer.  This method must be called before adding any
632    * edges to the S2Builder.  You may call this method multiple times to build
633    * multiple geometric objects that are snapped to the same set of sites.
634    *
635    * For example, if you have a set of contour lines, then you could put each
636    * contour line in a separate layer.  This keeps the contour lines separate
637    * from each other, while also ensuring that no crossing edges are created
638    * when they are snapped and/or simplified.  \(This is not true if the
639    * contour lines are snapped or simplified independently.\)
640    *
641    * Similarly, if you have a set of polygons that share common boundaries
642    * \(e.g., countries\), you can snap and/or simplify them at the same time by
643    * putting them in different layers, while ensuring that their boundaries
644    * remain consistent \(i.e., no crossing edges or T-vertices are introduced\).
645    *
646    * Ownership of the layer is transferred to the S2Builder.  Example usage:
647    *
648    * ---
649    * auto line1 = new S2Polyline();
650    * auto line2 = new S2Polyline();
651    * builder.startLayer(new S2PolylineLayer(line1));
652    * // Add edges using builder.addEdge(), etc ...
653    * builder.startLayer(new S2PolylineLayer(line2));
654    * // Add edges using builder.addEdge(), etc ...
655    * S2Error error;
656    * enforce(builder.build(error), error.toString());  // Builds "line1" & "line2"
657    * ---
658    */
659   void startLayer(Layer layer) {
660     _layerOptions ~= layer.graphOptions();
661     _layerBegins ~= cast(int) _inputEdges.length;
662     _layerIsFullPolygonPredicates ~= &isFullPolygonUnspecified;
663     _layers ~= layer;
664   }
665 
666   /// Adds a degenerate edge (representing a point) to the current layer.
667   void addPoint(in S2Point v) {
668     addEdge(v, v);
669   }
670 
671   /// Adds the given edge to the current layer.
672   void addEdge(in S2Point v0, in S2Point v1)
673   in {
674     assert(!_layers.empty(), "Call StartLayer before adding any edges");
675   } do {
676     if (v0 == v1
677         && _layerOptions.back().degenerateEdges() == GraphOptions.DegenerateEdges.DISCARD) {
678       return;
679     }
680     InputVertexId j0 = addVertex(v0);
681     InputVertexId j1 = addVertex(v1);
682     _inputEdges ~= [j0, j1];
683 
684     // If there are any labels, then attach them to this input edge.
685     if (_labelSetModified) {
686       if (_labelSetIds.empty()) {
687         // Populate the missing entries with empty label sets.
688         _labelSetIds.length = _inputEdges.length - 1;
689         _labelSetIds.fill(_labelSetId);
690       }
691       _labelSetId = _labelSetLexicon.add(_labelSet);
692       _labelSetIds ~= _labelSetId;
693       _labelSetModified = false;
694     } else if (!_labelSetIds.empty()) {
695       _labelSetIds ~= _labelSetId;
696     }
697   }
698 
699   /**
700    * Adds the edges in the given polyline.  (Note that if the polyline
701    * consists of 0 or 1 vertices, this method does nothing.)
702    */
703   void addPolyline(in S2Polyline polyline) {
704     const int n = polyline.numVertices();
705     for (int i = 1; i < n; ++i) {
706       addEdge(polyline.vertex(i - 1), polyline.vertex(i));
707     }
708   }
709 
710   /**
711    * Adds the edges in the given loop.  If the sign() of the loop is negative
712    * (i.e. this loop represents a hole within a polygon), the edge directions
713    * are automatically reversed to ensure that the polygon interior is always
714    * to the left of every edge.
715    */
716   void addLoop(in S2Loop loop) {
717     // Ignore loops that do not have a boundary.
718     if (loop.isEmptyOrFull()) return;
719 
720     // For loops that represent holes, we add the edge from vertex n-1 to vertex
721     // n-2 first.  This is because these edges will be assembled into a
722     // clockwise loop, which will eventually be normalized in S2Polygon by
723     // calling S2Loop::Invert().  S2Loop::Invert() reverses the order of the
724     // vertices, so to end up with the original vertex order (0, 1, ..., n-1) we
725     // need to build a clockwise loop with vertex order (n-1, n-2, ..., 0).
726     // This is done by adding the edge (n-1, n-2) first, and then ensuring that
727     // Build() assembles loops starting from edges in the order they were added.
728     const int n = loop.numVertices();
729     for (int i = 0; i < n; ++i) {
730       addEdge(loop.orientedVertex(i), loop.orientedVertex(i + 1));
731     }
732   }
733 
734   /**
735    * Adds the loops in the given polygon.  Loops representing holes have their
736    * edge directions automatically reversed as described for AddLoop().  Note
737    * that this method does not distinguish between the empty and full polygons,
738    * i.e. adding a full polygon has the same effect as adding an empty one.
739    */
740   void addPolygon(in S2Polygon polygon) {
741     for (int i = 0; i < polygon.numLoops(); ++i) {
742       addLoop(polygon.loop(i));
743     }
744   }
745 
746   /// Adds the edges of the given shape to the current layer.
747   void addShape(in S2Shape shape) {
748     for (int e = 0, n = shape.numEdges(); e < n; ++e) {
749       S2Shape.Edge edge = shape.edge(e);
750       addEdge(edge.v0, edge.v1);
751     }
752   }
753 
754   /**
755    * For layers that will be assembled into polygons, this method specifies a
756    * predicate that will be called to determine whether the polygon is empty
757    * or full except for the given degeneracies.  (See IsFullPolygonPredicate
758    * above.)
759    *
760    * This method should be called at most once per layer; additional calls
761    * simply overwrite the previous value for the current layer.
762    *
763    * The default implementation sets an appropriate error and returns false
764    * (i.e., degenerate polygons are assumed to be empty).
765    */
766   void addIsFullPolygonPredicate(IsFullPolygonPredicate predicate) {
767     _layerIsFullPolygonPredicates[$ - 1] = predicate;
768   }
769 
770   /**
771    * Forces a vertex to be located at the given position.  This can be used to
772    * prevent certain input vertices from moving.  However if you are trying to
773    * preserve part of the input boundary, be aware that this option does not
774    * prevent edges from being split by new vertices.
775    *
776    * Forced vertices are never snapped; if this is desired then you need to
777    * call options().snap_function().SnapPoint() explicitly.  Forced vertices
778    * are also never simplified away (if simplify_edge_chains() is used).
779    *
780    * Caveat: Since this method can place vertices arbitrarily close together,
781    * S2Builder makes no minimum separation guaranteees with forced vertices.
782    */
783   void forceVertex(in S2Point vertex) {
784     _sites ~= vertex;
785   }
786 
787   /**
788    * Every edge can have a set of non-negative integer labels attached to it.
789    * When used with an appropriate layer type, you can then retrieve the
790    * labels associated with each output edge.  This can be useful when merging
791    * or combining data from several sources.  (Note that in many cases it is
792    * easier to use separate output layers rather than labels.)
793    *
794    * Labels are 32-bit non-negative integers.  To support other label types,
795    * you can use ValueLexicon to store the set of unique labels seen so far:
796    *
797    *   ValueLexicon<MyLabel> my_label_lexicon;
798    *   builder.set_label(my_label_lexicon.Add(label));
799    *
800    * The current set of labels is represented as a stack.  This makes it easy
801    * to add and remove labels hierarchically (e.g., polygon 5, loop 2).  Use
802    * set_label() and clear_labels() if you need at most one label per edge.
803    */
804   alias Label = int;
805 
806   /// Clear the stack of labels.
807   void clearLabels() {
808     _labelSet.length = 0;
809     _labelSetModified = true;
810   }
811 
812   /**
813    * Add a label to the stack.
814    * REQUIRES: label >= 0.
815    */
816   void pushLabel(Label label)
817   in {
818     assert(label >= 0);
819   } do {
820     _labelSet ~= label;
821     _labelSetModified = true;
822   }
823 
824   /// Remove a label from the stack.
825   void popLabel() {
826     _labelSet.popBack();
827     _labelSetModified = true;
828   }
829 
830   /**
831    * Convenience function that clears the stack and adds a single label.
832    * REQUIRES: label >= 0.
833    */
834   void setLabel(Label label)
835   in {
836     assert(label >= 0);
837   } do {
838     _labelSet.length = 1;
839     _labelSet[0] = label;
840     _labelSetModified = true;
841   }
842 
843   /**
844    * Performs the requested edge splitting, snapping, simplification, etc, and
845    * then assembles the resulting edges into the requested output layers.
846    *
847    * Returns true if all edges were assembled; otherwise sets "error"
848    * appropriately.  Depending on the error, some or all output layers may
849    * have been created.  Automatically resets the S2Builder state so that it
850    * can be reused.
851    */
852   bool build(ref S2Error error) {
853     error.clear();
854     _error = &error;
855 
856     // Mark the end of the last layer.
857     _layerBegins ~= cast(int) _inputEdges.length;
858 
859     // See the algorithm overview at the top of this file.
860     if (_snappingRequested && !_options.idempotent()) {
861       _snappingNeeded = true;
862     }
863     chooseSites();
864     buildLayers();
865     reset();
866     return _error.ok();
867   }
868 
869   /**
870    * Clears all input data and resets the builder state.  Any options
871    * specified are preserved.
872    */
873   void reset() {
874     _inputVertices.length = 0;
875     _inputEdges.length = 0;
876     _layers.length = 0;
877     _layerOptions.length = 0;
878     _layerBegins.length = 0;
879     _labelSetIds.length = 0;
880     _labelSetLexicon.clear();
881     _labelSet.length = 0;
882     _labelSetModified = false;
883     _sites.length = 0;
884     _edgeSites.length = 0;
885     _snappingNeeded = false;
886   }
887 
888   /// Identifies an input vertex.
889   alias InputVertexId = int;
890 
891   /// Identifies an input edge.
892   alias InputEdgeId = int;
893 
894   /// Identifies the set of input edge ids that were snapped to a given edge.
895   alias InputEdgeIdSetId = int;
896 
897   alias LabelSetId = int;
898 
899 private:
900   //////////////////////  Input Types  /////////////////////////
901   // All types associated with the S2Builder inputs are prefixed with "Input".
902 
903   /// Defines an input edge.
904   alias InputEdge = InputVertexId[2];
905 
906   /**
907    * Sort key for prioritizing input vertices.  (Note that keys are *not*
908    * compared using std::less; see SortInputVertices for details.)
909    */
910   struct InputVertexKey {
911     S2CellId first;
912     InputVertexId second;
913   }
914 
915   //////////////////////  Output Types  /////////////////////////
916   // These types define the output vertices and edges.
917 
918   /**
919    * Identifies a snapped vertex ("snap site").  If there is only one layer,
920    * than SiteId is the same as Graph::VertexId, but if there are many layers
921    * then each Graph may contain only a subset of the sites.  Also see
922    * GraphOptions::allow_vertex_filtering().
923    */
924   alias SiteId = int;
925 
926   /// Defines an output edge.
927   alias Edge = Tuple!(SiteId, SiteId);
928 
929   /// Identifies an output edge.
930   alias EdgeId = int;
931 
932   /// Identifies an output edge in a particular layer.
933   struct LayerEdgeId {
934     int first;
935     EdgeId second;
936 
937     int opCmp(LayerEdgeId other) const {
938       return (first != other.first)
939           ? first - other.first
940           : second - other.second;
941     }
942   }
943 
944   /**
945    * Input vertices are stored in a vector, with some removal of duplicates.
946    * Edges are represented as (VertexId, VertexId) pairs.  All edges are stored
947    * in a single vector; each layer corresponds to a contiguous range.
948    */
949   InputVertexId addVertex(in S2Point v) {
950     // Remove duplicate vertices that follow the pattern AB, BC, CD.  If we want
951     // to do anything more sophisticated, either use a ValueLexicon, or sort the
952     // vertices once they have all been added, remove duplicates, and update the
953     // edges.
954     if (_inputVertices.empty() || v != _inputVertices.back()) {
955       _inputVertices ~= v;
956     }
957     return cast(InputVertexId) _inputVertices.length - 1;
958   }
959 
960   void chooseSites() {
961     if (_inputVertices.empty()) return;
962 
963     auto input_edge_index = new MutableS2ShapeIndex();
964     input_edge_index.add(new VertexIdEdgeVectorShape(_inputEdges, _inputVertices));
965     if (_options.splitCrossingEdges()) {
966       addEdgeCrossings(input_edge_index);
967     }
968     if (_snappingRequested) {
969       auto site_index = new S2PointIndex!SiteId();
970       addForcedSites(site_index);
971       chooseInitialSites(site_index);
972       collectSiteEdges(site_index);
973     }
974     if (_snappingNeeded) {
975       addExtraSites(input_edge_index);
976     } else {
977       copyInputEdges();
978     }
979   }
980 
981   /**
982    * Sort the input vertices, discard duplicates, and update the input edges
983    * to refer to the pruned vertex list.  (We sort in the same order used by
984    * ChooseInitialSites() to avoid inconsistencies in tests.)
985    */
986   void copyInputEdges() {
987     InputVertexKey[] sorted = sortInputVertices();
988     auto vmap = new InputVertexId[_inputVertices.length];
989     _sites.length = 0;
990     _sites.reserve(_inputVertices.length);
991     for (int ind = 0; ind < sorted.length; ) {
992       const S2Point site = _inputVertices[sorted[ind].second];
993       vmap[sorted[ind].second] = cast(InputVertexId) _sites.length;
994       while (++ind < sorted.length && _inputVertices[sorted[ind].second] == site) {
995         vmap[sorted[ind].second] = cast(InputVertexId) _sites.length;
996       }
997       _sites ~= site;
998     }
999     _inputVertices = _sites;
1000     for (int i = 0; i < _inputEdges.length; ++i) {
1001       InputEdge* e = &(_inputEdges[i]);
1002       (*e)[0] = vmap[(*e)[0]];
1003       (*e)[1] = vmap[(*e)[1]];
1004     }
1005   }
1006 
1007   InputVertexKey[] sortInputVertices() {
1008     // Sort all the input vertices in the order that we wish to consider them as
1009     // candidate Voronoi sites.  Any sort order will produce correct output, so
1010     // we have complete flexibility in choosing the sort key.  We could even
1011     // leave them unsorted, although this would have the disadvantage that
1012     // changing the order of the input edges could cause S2Builder to snap to a
1013     // different set of Voronoi sites.
1014     //
1015     // We have chosen to sort them primarily by S2CellId since this improves the
1016     // performance of many S2Builder phases (due to better spatial locality).
1017     // It also allows the possibility of replacing the current S2PointIndex
1018     // approach with a more efficient recursive divide-and-conquer algorithm.
1019     //
1020     // However, sorting by leaf S2CellId alone has two small disadvantages in
1021     // the case where the candidate sites are densely spaced relative to the
1022     // snap radius (e.g., when using the IdentitySnapFunction, or when snapping
1023     // to E6/E7 near the poles, or snapping to S2CellId/E6/E7 using a snap
1024     // radius larger than the minimum value required):
1025     //
1026     //  - First, it tends to bias the Voronoi site locations towards points that
1027     //    are earlier on the S2CellId Hilbert curve.  For example, suppose that
1028     //    there are two parallel rows of input vertices on opposite sides of the
1029     //    edge between two large S2Cells, and the rows are separated by less
1030     //    than the snap radius.  Then only vertices from the cell with the
1031     //    smaller S2CellId are selected, because they are considered first and
1032     //    prevent us from selecting the sites from the other cell (because they
1033     //    are closer than "snap_radius" to an existing site).
1034     //
1035     //  - Second, it tends to choose more Voronoi sites than necessary, because
1036     //    at each step we choose the first site along the Hilbert curve that is
1037     //    at least "snap_radius" away from all previously selected sites.  This
1038     //    tends to yield sites whose "coverage discs" overlap quite a bit,
1039     //    whereas it would be better to cover all the input vertices with a
1040     //    smaller set of coverage discs that don't overlap as much.  (This is
1041     //    the "geometric set cover problem", which is NP-hard.)
1042     //
1043     // It is not worth going to much trouble to fix these problems, because they
1044     // really aren't that important (and don't affect the guarantees made by the
1045     // algorithm), but here are a couple of heuristics that might help:
1046     //
1047     // 1. Sort the input vertices by S2CellId at a coarse level (down to cells
1048     // that are O(snap_radius) in size), and then sort by a fingerprint of the
1049     // S2Point coordinates (i.e., quasi-randomly).  This would retain most of
1050     // the advantages of S2CellId sorting, but makes it more likely that we will
1051     // select sites that are further apart.
1052     //
1053     // 2. Rather than choosing the first uncovered input vertex and snapping it
1054     // to obtain the next Voronoi site, instead look ahead through following
1055     // candidates in S2CellId order and choose the furthest candidate whose
1056     // snapped location covers all previous uncovered input vertices.
1057     //
1058     // TODO(ericv): Experiment with these approaches.
1059     import std.algorithm : sort;
1060 
1061     InputVertexKey[] keys;
1062     keys.reserve(_inputVertices.length);
1063     for (InputVertexId i = 0; i < _inputVertices.length; ++i) {
1064       keys ~= InputVertexKey(S2CellId(_inputVertices[i]), i);
1065     }
1066     sort!((a, b) {
1067           if (a.first < b.first) return true;
1068           if (b.first < a.first) return false;
1069           return _inputVertices[a.second] < _inputVertices[b.second];
1070         })(keys);
1071     return keys;
1072   }
1073 
1074   /**
1075    * Check all edge pairs for crossings, and add the corresponding intersection
1076    * points to input_vertices_.  (The intersection points will be snapped and
1077    * merged with the other vertices during site selection.)
1078    */
1079   void addEdgeCrossings(MutableS2ShapeIndex input_edge_index) {
1080     import s2.s2crossing_edge_query : CrossingType;
1081     import s2.s2edge_crossings : getIntersection;
1082     import s2.shapeutil.shape_edge : ShapeEdge;
1083     import s2.shapeutil.visit_crossing_edge_pairs;
1084 
1085     // We need to build a list of intersections and add them afterwards so that
1086     // we don't reallocate vertices_ during the VisitCrossings() call.
1087     S2Point[] new_vertices;
1088     visitCrossingEdgePairs(
1089         input_edge_index, CrossingType.INTERIOR,
1090         (in ShapeEdge a, in ShapeEdge b, bool) {
1091           new_vertices ~= getIntersection(a.v0(), a.v1(), b.v0(), b.v1());
1092           return true;  // Continue visiting.
1093         });
1094     if (!new_vertices.empty()) {
1095       _snappingNeeded = true;
1096       foreach (const vertex; new_vertices) addVertex(vertex);
1097     }
1098   }
1099 
1100   void addForcedSites(S2PointIndex!SiteId site_index) {
1101     import std.array, std.algorithm;
1102 
1103     // Sort the forced sites and remove duplicates.
1104     _sites = _sites.sort.uniq.array;
1105     // Add the forced sites to the index.
1106     for (SiteId id = 0; id < _sites.length; ++id) {
1107       site_index.add(_sites[id], id);
1108     }
1109     _numForcedSites = cast(SiteId) _sites.length;
1110   }
1111 
1112   bool isForced(SiteId v) const {
1113     return v < _numForcedSites;
1114   }
1115 
1116   void chooseInitialSites(S2PointIndex!SiteId site_index) {
1117     import s2.s2predicates : compareDistance;
1118     // Find all points whose distance is <= min_site_separation_ca_.
1119     auto options = new S2ClosestPointQueryOptions();
1120     options.setConservativeMaxDistance(_minSiteSeparationCa);
1121     auto site_query = new S2ClosestPointQuery!SiteId(site_index, options);
1122     S2ClosestPointQuery!(SiteId).Result[] results;
1123 
1124     // Apply the snap_function() to each input vertex, then check whether any
1125     // existing site is closer than min_vertex_separation().  If not, then add a
1126     // new site.
1127     //
1128     // NOTE(ericv): There are actually two reasonable algorithms, which we call
1129     // "snap first" (the one above) and "snap last".  The latter checks for each
1130     // input vertex whether any existing site is closer than snap_radius(), and
1131     // only then applies the snap_function() and adds a new site.  "Snap last"
1132     // can yield slightly fewer sites in some cases, but it is also more
1133     // expensive and can produce surprising results.  For example, if you snap
1134     // the polyline "0:0, 0:0.7" using IntLatLngSnapFunction(0), the result is
1135     // "0:0, 0:0" rather than the expected "0:0, 0:1", because the snap radius
1136     // is approximately sqrt(2) degrees and therefore it is legal to snap both
1137     // input points to "0:0".  "Snap first" produces "0:0, 0:1" as expected.
1138     InputVertexKey[] sorted = sortInputVertices();
1139     for (int i = 0; i < sorted.length; ++i) {
1140       const S2Point vertex = _inputVertices[sorted[i].second];
1141       S2Point site = snapSite(vertex);
1142       // If any vertex moves when snapped, the output cannot be idempotent.
1143       _snappingNeeded = _snappingNeeded || site != vertex;
1144 
1145       // FindClosestPoints() measures distances conservatively, so we need to
1146       // recheck the distances using exact predicates.
1147       //
1148       // NOTE(ericv): When the snap radius is large compared to the average
1149       // vertex spacing, we could possibly avoid the call the FindClosestPoints
1150       // by checking whether sites_.back() is close enough.
1151       auto target = new S2ClosestPointQueryPointTarget(site);
1152       site_query.findClosestPoints(target, results);
1153       bool add_site = true;
1154       foreach (const result; results) {
1155         if (compareDistance(site, result.point(), _minSiteSeparationCa) <= 0) {
1156           add_site = false;
1157           // This pair of sites is too close.  If the sites are distinct, then
1158           // the output cannot be idempotent.
1159           _snappingNeeded = _snappingNeeded || site != result.point();
1160         }
1161       }
1162       if (add_site) {
1163         site_index.add(site, cast(SiteId) _sites.length);
1164         _sites ~= site;
1165         site_query.reInitialize();
1166       }
1167     }
1168   }
1169 
1170   S2Point snapSite(in S2Point point) {
1171     if (!_snappingRequested) return point;
1172     S2Point site = _options.snapFunction().snapPoint(point);
1173     auto dist_moved = S1ChordAngle(site, point);
1174     if (dist_moved > _siteSnapRadiusCa) {
1175       _error.initialize(
1176           S2Error.Code.BUILDER_SNAP_RADIUS_TOO_SMALL,
1177           "Snap function moved vertex (%.15g, %.15g, %.15g) "
1178               ~ "by %.15g, which is more than the specified snap "
1179               ~ "radius of %.15g",
1180           point.x(), point.y(), point.z(),
1181           dist_moved.toS1Angle().radians(),
1182           _siteSnapRadiusCa.toS1Angle().radians());
1183     }
1184     return site;
1185   }
1186 
1187   /**
1188    * For each edge, find all sites within min_edge_site_query_radius_ca_ and
1189    * store them in edge_sites_.  Also, to implement idempotency this method also
1190    * checks whether the input vertices and edges may already satisfy the output
1191    * criteria.  If any problems are found then snapping_needed_ is set to true.
1192    */
1193   void collectSiteEdges(S2PointIndex!SiteId site_index) {
1194     import s2.s2predicates : compareEdgeDistance;
1195     // Find all points whose distance is <= edge_site_query_radius_ca_.
1196     auto options = new S2ClosestPointQueryOptions();
1197     options.setConservativeMaxDistance(_edgeSiteQueryRadiusCa);
1198     auto site_query = new S2ClosestPointQuery!SiteId(site_index, options);
1199     S2ClosestPointQuery!(SiteId).Result[] results;
1200     _edgeSites.length = _inputEdges.length;
1201     for (InputEdgeId e = 0; e < _inputEdges.length; ++e) {
1202       const InputEdge edge = _inputEdges[e];
1203       const S2Point v0 = _inputVertices[edge[0]];
1204       const S2Point v1 = _inputVertices[edge[1]];
1205       if (s2builderVerbose) {
1206         writeln("S2Polyline: ", v0, ", ", v1, "\n");
1207       }
1208       auto target = new S2ClosestPointQueryEdgeTarget(v0, v1);
1209       site_query.findClosestPoints(target, results);
1210       auto sites = &_edgeSites[e];
1211       (*sites).reserve(results.length);
1212       foreach (const result; results) {
1213         *sites ~= result.data();
1214         if (!_snappingNeeded
1215             && result.distance() < _minEdgeSiteSeparationCaLimit
1216             && result.point() != v0
1217             && result.point() != v1
1218             && compareEdgeDistance(result.point(), v0, v1, _minEdgeSiteSeparationCa) < 0) {
1219           _snappingNeeded = true;
1220         }
1221       }
1222       sortSitesByDistance(v0, *sites);
1223     }
1224   }
1225 
1226   void sortSitesByDistance(in S2Point x, ref SiteId[] sites) const {
1227     import s2.s2predicates : compareDistances;
1228     // Sort sites in increasing order of distance to X.
1229     sort!((SiteId i, SiteId j) => compareDistances(x, _sites[i], _sites[j]) < 0)(sites);
1230   }
1231 
1232   /**
1233    * There are two situatons where we need to add extra Voronoi sites in order
1234    * to ensure that the snapped edges meet the output requirements:
1235    *
1236    *  (1) If a snapped edge deviates from its input edge by more than
1237    *      max_edge_deviation(), we add a new site on the input edge near the
1238    *      middle of the snapped edge.  This causes the snapped edge to split
1239    *      into two pieces, so that it follows the input edge more closely.
1240    *
1241    *  (2) If a snapped edge is closer than min_edge_vertex_separation() to any
1242    *      nearby site (the "site to avoid"), then we add a new site (the
1243    *      "separation site") on the input edge near the site to avoid.  This
1244    *      causes the snapped edge to follow the input edge more closely and is
1245    *      guaranteed to increase the separation to the required distance.
1246    *
1247    * We check these conditions by snapping all the input edges to a chain of
1248    * Voronoi sites and then testing each edge in the chain.  If a site needs to
1249    * be added, we mark all nearby edges for re-snapping.
1250    */
1251   void addExtraSites(MutableS2ShapeIndex input_edge_index) {
1252     // When options_.split_crossing_edges() is true, this function may be called
1253     // even when site_snap_radius_ca_ == 0 (because edge_snap_radius_ca_ > 0).
1254     // However neither of the conditions above apply in that case.
1255     if (_siteSnapRadiusCa == S1ChordAngle.zero()) return;
1256 
1257     SiteId[] chain;  // Temporary
1258     InputEdgeId[] snap_queue;
1259     for (InputEdgeId max_e = 0; max_e < _inputEdges.length; ++max_e) {
1260       snap_queue ~= max_e;
1261       while (!snap_queue.empty()) {
1262         InputEdgeId e = snap_queue.back();
1263         snap_queue.popBack();
1264         snapEdge(e, chain);
1265         // We could save the snapped chain here in a snapped_chains_ vector, to
1266         // avoid resnapping it in AddSnappedEdges() below, however currently
1267         // SnapEdge only accounts for less than 5% of the runtime.
1268         maybeAddExtraSites(e, max_e, chain, input_edge_index, snap_queue);
1269       }
1270     }
1271   }
1272 
1273   void maybeAddExtraSites(
1274       in InputEdgeId edge_id,
1275       in InputEdgeId max_edge_id,
1276       in SiteId[] chain,
1277       MutableS2ShapeIndex input_edge_index,
1278       ref InputEdgeId[] snap_queue) {
1279     import s2.s2edge_distances : isEdgeBNearEdgeA, project;
1280     import s2.s2predicates : compareEdgeDistance;
1281     // The snapped chain is always a *subsequence* of the nearby sites
1282     // (edge_sites_), so we walk through the two arrays in parallel looking for
1283     // sites that weren't snapped.  We also keep track of the current snapped
1284     // edge, since it is the only edge that can be too close.
1285     int i = 0;
1286     foreach (SiteId id; _edgeSites[edge_id]) {
1287       if (id == chain[i]) {
1288         if (++i == chain.length) break;
1289         // Check whether this snapped edge deviates too far from its original
1290         // position.  If so, we split the edge by adding an extra site.
1291         const S2Point v0 = _sites[chain[i - 1]];
1292         const S2Point v1 = _sites[chain[i]];
1293         if (S1ChordAngle(v0, v1) < _minEdgeLengthToSplitCa) continue;
1294 
1295         const InputEdge edge = _inputEdges[edge_id];
1296         const S2Point a0 = _inputVertices[edge[0]];
1297         const S2Point a1 = _inputVertices[edge[1]];
1298         if (!isEdgeBNearEdgeA(a0, a1, v0, v1, _maxEdgeDeviation)) {
1299           // Add a new site on the input edge, positioned so that it splits the
1300           // snapped edge into two approximately equal pieces.  Then we find all
1301           // the edges near the new site (including this one) and add them to
1302           // the snap queue.
1303           //
1304           // Note that with large snap radii, it is possible that the snapped
1305           // edge wraps around the sphere the "wrong way".  To handle this we
1306           // find the preferred split location by projecting both endpoints onto
1307           // the input edge and taking their midpoint.
1308           S2Point mid = (project(v0, a0, a1) + project(v1, a0, a1)).normalize();
1309           S2Point new_site = getSeparationSite(mid, v0, v1, edge_id);
1310           addExtraSite(new_site, max_edge_id, input_edge_index, snap_queue);
1311           return;
1312         }
1313       } else if (i > 0 && id >= _numForcedSites) {
1314         // Check whether this "site to avoid" is closer to the snapped edge than
1315         // min_edge_vertex_separation().  Note that this is the only edge of the
1316         // chain that can be too close because its vertices must span the point
1317         // where "site_to_avoid" projects onto the input edge XY (this claim
1318         // relies on the fact that all sites are separated by at least the snap
1319         // radius).  We don't try to avoid sites added using ForceVertex()
1320         // because we don't guarantee any minimum separation from such sites.
1321         const S2Point site_to_avoid = _sites[id];
1322         const S2Point v0 = _sites[chain[i - 1]];
1323         const S2Point v1 = _sites[chain[i]];
1324         if (compareEdgeDistance(site_to_avoid, v0, v1, _minEdgeSiteSeparationCa) < 0) {
1325           // A snapped edge can only approach a site too closely when there are
1326           // no sites near the input edge near that point.  We fix that by
1327           // adding a new site along the input edge (a "separation site"), then
1328           // we find all the edges near the new site (including this one) and
1329           // add them to the snap queue.
1330           S2Point new_site = getSeparationSite(site_to_avoid, v0, v1, edge_id);
1331           debug enforce(site_to_avoid != new_site);
1332           addExtraSite(new_site, max_edge_id, input_edge_index, snap_queue);
1333           return;
1334         }
1335       }
1336     }
1337   }
1338 
1339   /**
1340    * Adds a new site, then updates "edge_sites"_ for all edges near the new site
1341    * and adds them to "snap_queue" for resnapping (unless their edge id exceeds
1342    * "max_edge_id", since those edges have not been snapped the first time yet).
1343    */
1344   void addExtraSite(
1345       in S2Point new_site, InputEdgeId max_edge_id,
1346       MutableS2ShapeIndex input_edge_index, ref InputEdgeId[] snap_queue) {
1347     SiteId new_site_id = cast(SiteId) _sites.length;
1348     _sites ~= new_site;
1349     // Find all edges whose distance is <= edge_site_query_radius_ca_.
1350     auto options = new S2ClosestEdgeQuery.Options();
1351     options.setConservativeMaxDistance(_edgeSiteQueryRadiusCa);
1352     options.setIncludeInteriors(false);
1353     auto query = new S2ClosestEdgeQuery(input_edge_index, options);
1354     auto target = new S2ClosestEdgeQuery.PointTarget(new_site);
1355     foreach (const result; query.findClosestEdges(target)) {
1356       InputEdgeId e = result.edgeId;
1357       SiteId[]* site_ids = &_edgeSites[e];  // Use a pointer so we can modify the original.
1358       *site_ids ~= new_site_id;
1359       sortSitesByDistance(_inputVertices[_inputEdges[e][0]], *site_ids);
1360       if (e <= max_edge_id) snap_queue ~= e;
1361     }
1362   }
1363 
1364   S2Point getSeparationSite(
1365       in S2Point site_to_avoid, in S2Point v0, in S2Point v1, InputEdgeId input_edge_id) {
1366     import s2.s2pointutil : robustCrossProd;
1367     import s2.s2edge_distances : project;
1368     // Define the "coverage disc" of a site S to be the disc centered at S with
1369     // radius "snap_radius".  Similarly, define the "coverage interval" of S for
1370     // an edge XY to be the intersection of XY with the coverage disc of S.  The
1371     // SnapFunction implementations guarantee that the only way that a snapped
1372     // edge can be closer than min_edge_vertex_separation() to a non-snapped
1373     // site (i.e., site_to_avoid) if is there is a gap in the coverage of XY
1374     // near this site.  We can fix this problem simply by adding a new site to
1375     // fill this gap, located as closely as possible to the site to avoid.
1376     //
1377     // To calculate the coverage gap, we look at the two snapped sites on
1378     // either side of site_to_avoid, and find the endpoints of their coverage
1379     // intervals.  The we place a new site in the gap, located as closely as
1380     // possible to the site to avoid.  Note that the new site may move when it
1381     // is snapped by the snap_function, but it is guaranteed not to move by
1382     // more than snap_radius and therefore its coverage interval will still
1383     // intersect the gap.
1384     const InputEdge edge = _inputEdges[input_edge_id];
1385     const S2Point x = _inputVertices[edge[0]];
1386     const S2Point y = _inputVertices[edge[1]];
1387     Vector3_d xy_dir = y - x;
1388     S2Point n = robustCrossProd(x, y);
1389     S2Point new_site = project(site_to_avoid, x, y, n);
1390     S2Point gap_min = getCoverageEndpoint(v0, x, y, n);
1391     S2Point gap_max = getCoverageEndpoint(v1, y, x, -n);
1392     if ((new_site - gap_min).dotProd(xy_dir) < 0) {
1393       new_site = gap_min;
1394     } else if ((gap_max - new_site).dotProd(xy_dir) < 0) {
1395       new_site = gap_max;
1396     }
1397     new_site = snapSite(new_site);
1398     debug enforce(v0 != new_site);
1399     debug enforce(v1 != new_site);
1400     return new_site;
1401   }
1402 
1403   /**
1404    * Given a site P and an edge XY with normal N, intersect XY with the disc of
1405    * radius snap_radius() around P, and return the intersection point that is
1406    * further along the edge XY toward Y.
1407    */
1408   S2Point getCoverageEndpoint(in S2Point p, in S2Point x, in S2Point y, in S2Point n) const {
1409     // Consider the plane perpendicular to P that cuts off a spherical cap of
1410     // radius snap_radius().  This plane intersects the plane through the edge
1411     // XY (perpendicular to N) along a line, and that line intersects the unit
1412     // sphere at two points Q and R, and we want to return the point R that is
1413     // further along the edge XY toward Y.
1414     //
1415     // Let M be the midpoint of QR.  This is the point along QR that is closest
1416     // to P.  We can now express R as the sum of two perpendicular vectors OM
1417     // and MR in the plane XY.  Vector MR is in the direction N x P, while
1418     // vector OM is in the direction (N x P) x N, where N = X x Y.
1419     //
1420     // The length of OM can be found using the Pythagorean theorem on triangle
1421     // OPM, and the length of MR can be found using the Pythagorean theorem on
1422     // triangle OMR.
1423     //
1424     // In the calculations below, we save some work by scaling all the vectors
1425     // by n.CrossProd(p).Norm2(), and normalizing at the end.
1426     double n2 = n.norm2();
1427     double nDp = n.dotProd(p);
1428     S2Point nXp = n.crossProd(p);
1429     S2Point nXpXn = n2 * p - nDp * n;
1430     Vector3_d om = sqrt(1 - _edgeSnapRadiusSin2) * nXpXn;
1431     double mr2 = _edgeSnapRadiusSin2 * n2 - nDp * nDp;
1432 
1433     // MR is constructed so that it points toward Y (rather than X).
1434     Vector3_d mr = sqrt(max(0.0, mr2)) * nXp;
1435     return (om + mr).normalize();
1436   }
1437 
1438   void snapEdge(InputEdgeId e, out SiteId[] chain) const {
1439     const InputEdge edge = _inputEdges[e];
1440     if (!_snappingNeeded) {
1441       chain ~= edge[0];
1442       chain ~= edge[1];
1443       return;
1444     }
1445 
1446     const S2Point x = _inputVertices[edge[0]];
1447     const S2Point y = _inputVertices[edge[1]];
1448 
1449     // Optimization: if there is only one nearby site, return.
1450     // Optimization: if there are exactly two nearby sites, and one is close
1451     // enough to each vertex, then return.
1452 
1453     // Now iterate through the sites.  We keep track of the sequence of sites
1454     // that are visited.
1455     const candidates = _edgeSites[e];
1456     for (int i = 0; i < candidates.length; ++i) {
1457       const S2Point c = _sites[candidates[i]];
1458       // Skip any sites that are too far away.  (There will be some of these,
1459       // because we also keep track of "sites to avoid".)  Note that some sites
1460       // may be close enough to the line containing the edge, but not to the
1461       // edge itself, so we can just use the dot product with the edge normal.
1462       if (compareEdgeDistance(c, x, y, _edgeSnapRadiusCa) > 0) {
1463         continue;
1464       }
1465       // Check whether the new site C excludes the previous site B.  If so,
1466       // repeat with the previous site, and so on.
1467       bool add_site_c = true;
1468       for (; !chain.empty(); chain.popBack()) {
1469         S2Point b = _sites[chain.back()];
1470 
1471         // First, check whether B and C are so far apart that their clipped
1472         // Voronoi regions can't intersect.
1473         auto bc = S1ChordAngle(b, c);
1474         if (bc >= _maxAdjacentSiteSeparationCa) break;
1475 
1476         // Otherwise, we want to check whether site C prevents the Voronoi
1477         // region of B from intersecting XY, or vice versa.  This can be
1478         // determined by computing the "coverage interval" (the segment of XY
1479         // intersected by the coverage disc of radius snap_radius) for each
1480         // site.  If the coverage interval of one site contains the coverage
1481         // interval of the other, then the contained site can be excluded.
1482         Excluded result = getVoronoiSiteExclusion(b, c, x, y, _edgeSnapRadiusCa);
1483         if (result == Excluded.FIRST) continue;  // Site B excluded by C
1484         if (result == Excluded.SECOND) {
1485           add_site_c = false;  // Site C is excluded by B.
1486           break;
1487         }
1488         debug enforce(Excluded.NEITHER == result);
1489 
1490         // Otherwise check whether the previous site A is close enough to B and
1491         // C that it might further clip the Voronoi region of B.
1492         if (chain.length < 2) break;
1493         S2Point a = _sites[chain[$ - 2]];
1494         auto ac = S1ChordAngle(a, c);
1495         if (ac >= _maxAdjacentSiteSeparationCa) break;
1496 
1497         // If triangles ABC and XYB have the same orientation, the circumcenter
1498         // Z of ABC is guaranteed to be on the same side of XY as B.
1499         int xyb = sign(x, y, b);
1500         if (sign(a, b, c) == xyb) {
1501           break;  // The circumcenter is on the same side as B but further away.
1502         }
1503         // Other possible optimizations:
1504         //  - if AB > max_adjacent_site_separation_ca_ then keep B.
1505         //  - if d(B, XY) < 0.5 * min(AB, BC) then keep B.
1506 
1507         // If the circumcenter of ABC is on the same side of XY as B, then B is
1508         // excluded by A and C combined.  Otherwise B is needed and we can exit.
1509         if (edgeCircumcenterSign(x, y, a, b, c) != xyb) break;
1510       }
1511       if (add_site_c) {
1512         chain ~= candidates[i];
1513       }
1514     }
1515     debug enforce(!chain.empty());
1516     debug {
1517       for (int i = 0; i < candidates.length; ++i) {
1518         if (compareDistances(y, _sites[chain.back()], _sites[candidates[i]]) > 0) {
1519           logger.logError("Snapping invariant broken!");
1520         }
1521       }
1522     }
1523     if (s2builderVerbose) {
1524       writeln("(", edge[0], ",", edge[1], "): ");
1525       foreach (SiteId id; chain) write(id, " ");
1526       writeln();
1527     }
1528   }
1529 
1530   void buildLayers() {
1531     // Each output edge has an "input edge id set id" (an int32) representing
1532     // the set of input edge ids that were snapped to this edge.  The actual
1533     // InputEdgeIds can be retrieved using "input_edge_id_set_lexicon".
1534     Edge[][] layer_edges;
1535     InputEdgeIdSetId[][] layer_input_edge_ids;
1536     IdSetLexicon input_edge_id_set_lexicon = new IdSetLexicon();
1537     buildLayerEdges(layer_edges, layer_input_edge_ids, input_edge_id_set_lexicon);
1538 
1539     // At this point we have no further need for the input geometry or nearby
1540     // site data, so we clear those fields to save space.
1541     _inputVertices.length = 0;
1542     _inputEdges.length = 0;
1543     _edgeSites.length = 0;
1544 
1545     // If there are a large number of layers, then we build a minimal subset of
1546     // vertices for each layer.  This ensures that layer types that iterate over
1547     // vertices will run in time proportional to the size of that layer rather
1548     // than the size of all layers combined.
1549     S2Point[][] layer_vertices;
1550     enum int kMinLayersForVertexFiltering = 10;
1551     if (_layers.length >= kMinLayersForVertexFiltering) {
1552       // Disable vertex filtering if it is disallowed by any layer.  (This could
1553       // be optimized, but in current applications either all layers allow
1554       // filtering or none of them do.)
1555       bool allow_vertex_filtering = false;
1556       foreach (const options; _layerOptions) {
1557         allow_vertex_filtering &= options.allowVertexFiltering();
1558       }
1559       if (allow_vertex_filtering) {
1560         Graph.VertexId[] filter_tmp;  // Temporary used by FilterVertices.
1561         layer_vertices.length = _layers.length;
1562         for (int i = 0; i < _layers.length; ++i) {
1563           layer_vertices[i] = Graph.filterVertices(_sites, layer_edges[i], filter_tmp);
1564         }
1565         _sites.length = 0;  // Release memory
1566       }
1567     }
1568     for (int i = 0; i < _layers.length; ++i) {
1569       const S2Point[] vertices = layer_vertices.empty() ? _sites : layer_vertices[i];
1570       auto graph = new Graph(_layerOptions[i], vertices, layer_edges[i],
1571           layer_input_edge_ids[i], input_edge_id_set_lexicon,
1572           _labelSetIds, _labelSetLexicon,
1573           _layerIsFullPolygonPredicates[i]);
1574       _layers[i].build(graph, *_error);
1575       // Don't free the layer data until all layers have been built, in order to
1576       // support building multiple layers at once (e.g. ClosedSetNormalizer).
1577     }
1578   }
1579 
1580   /**
1581    * Snaps and possibly simplifies the edges for each layer, populating the
1582    * given output arguments.  The resulting edges can be used to construct an
1583    * S2Builder::Graph directly (no further processing is necessary).
1584    *
1585    * This method is not "const" because Graph::ProcessEdges can modify
1586    * layer_options_ in some cases (changing undirected edges to directed ones).
1587    */
1588   void buildLayerEdges(ref Edge[][] layer_edges, ref InputEdgeIdSetId[][] layer_input_edge_ids,
1589       IdSetLexicon input_edge_id_set_lexicon) {
1590     // Edge chains are simplified only when a non-zero snap radius is specified.
1591     // If so, we build a map from each site to the set of input vertices that
1592     // snapped to that site.
1593     InputVertexId[][] site_vertices;
1594     bool simplify = _snappingNeeded && _options.simplifyEdgeChains();
1595     if (simplify) {
1596       site_vertices.length = _sites.length;
1597     }
1598 
1599     layer_edges.length = _layers.length;
1600     layer_input_edge_ids.length = _layers.length;
1601     for (int i = 0; i < _layers.length; ++i) {
1602       addSnappedEdges(_layerBegins[i], _layerBegins[i + 1], _layerOptions[i],
1603           layer_edges[i], layer_input_edge_ids[i],
1604           input_edge_id_set_lexicon, site_vertices);
1605     }
1606     if (simplify) {
1607       simplifyEdgeChains(
1608           site_vertices, layer_edges, layer_input_edge_ids, input_edge_id_set_lexicon);
1609     }
1610     // We simplify edge chains before processing the per-layer GraphOptions
1611     // because simplification can create duplicate edges and/or sibling edge
1612     // pairs which may need to be removed.
1613     for (int i = 0; i < _layers.length; ++i) {
1614       // The errors generated by ProcessEdges are really warnings, so we simply
1615       // record them and continue.
1616       Graph.processEdges(_layerOptions[i], layer_edges[i],
1617           layer_input_edge_ids[i], input_edge_id_set_lexicon, *_error);
1618     }
1619   }
1620 
1621   /**
1622    * Snaps all the input edges for a given layer, populating the given output
1623    * arguments.  If (*site_vertices) is non-empty then it is updated so that
1624    * (*site_vertices)[site] contains a list of all input vertices that were
1625    * snapped to that site.
1626    */
1627   void addSnappedEdges(
1628       InputEdgeId begin, InputEdgeId end, in GraphOptions options,
1629       ref Edge[] edges, ref InputEdgeIdSetId[] input_edge_ids,
1630       IdSetLexicon input_edge_id_set_lexicon,
1631       ref InputVertexId[][] site_vertices) const {
1632     bool discard_degenerate_edges =
1633         options.degenerateEdges() == GraphOptions.DegenerateEdges.DISCARD;
1634     SiteId[] chain;
1635     for (InputEdgeId e = begin; e < end; ++e) {
1636       InputEdgeIdSetId id = input_edge_id_set_lexicon.addSingleton(e);
1637       snapEdge(e, chain);
1638       maybeAddInputVertex(_inputEdges[e][0], chain[0], site_vertices);
1639       if (chain.length == 1) {
1640         if (discard_degenerate_edges) continue;
1641         addSnappedEdge(chain[0], chain[0], id, options.edgeType(), edges, input_edge_ids);
1642       } else {
1643         maybeAddInputVertex(_inputEdges[e][1], chain.back(), site_vertices);
1644         for (int i = 1; i < chain.length; ++i) {
1645           addSnappedEdge(chain[i - 1], chain[i], id, options.edgeType(), edges, input_edge_ids);
1646         }
1647       }
1648     }
1649     if (s2builderVerbose) dumpEdges(edges, _sites);
1650   }
1651 
1652   /**
1653    * If "site_vertices" is non-empty, ensures that (*site_vertices)[id] contains
1654    * "v".  Duplicate entries are allowed.
1655    */
1656   void maybeAddInputVertex(InputVertexId v, SiteId id, ref InputVertexId[][] site_vertices) const {
1657     if (site_vertices.empty) return;
1658 
1659     // Optimization: check if we just added this vertex.  This is worthwhile
1660     // because the input edges usually form a continuous chain, i.e. the
1661     // destination of one edge is the same as the source of the next edge.
1662     InputVertexId[]* vertices = &site_vertices[id];
1663     if ((*vertices).empty() || (*vertices).back() != v) {
1664       (*vertices) ~= v;
1665     }
1666   }
1667 
1668   /**
1669    * Adds the given edge to "edges" and "input_edge_ids".  If undirected edges
1670    * are being used, also adds an edge in the opposite direction.
1671    */
1672   void addSnappedEdge(SiteId src, SiteId dst, InputEdgeIdSetId id,
1673       EdgeType edge_type, ref Edge[] edges, ref InputEdgeIdSetId[] input_edge_ids) const {
1674     edges ~= Edge(src, dst);
1675     input_edge_ids ~= id;
1676     if (edge_type == EdgeType.UNDIRECTED) {
1677       edges ~= Edge(dst, src);
1678       input_edge_ids ~= IdSetLexicon.emptySetId();
1679     }
1680   }
1681 
1682   void simplifyEdgeChains(
1683       in InputVertexId[][] site_vertices,
1684       ref Edge[][] layer_edges,
1685       ref InputEdgeIdSetId[][] layer_input_edge_ids,
1686       IdSetLexicon input_edge_id_set_lexicon) const {
1687     if (_layers.empty()) return;
1688 
1689     // Merge the edges from all layers (in order to build a single graph).
1690     Edge[] merged_edges;
1691     InputEdgeIdSetId[] merged_input_edge_ids;
1692     int[] merged_edge_layers;
1693     mergeLayerEdges(layer_edges, layer_input_edge_ids,
1694         merged_edges, merged_input_edge_ids, merged_edge_layers);
1695 
1696     // The following fields will be reconstructed by EdgeChainSimplifier.
1697     foreach (ref edges; layer_edges) edges.length = 0;
1698     foreach (ref input_edge_ids; layer_input_edge_ids) input_edge_ids.length = 0;
1699 
1700     // The graph options are irrelevant for edge chain simplification, but we
1701     // try to set them appropriately anyway.
1702     auto graph_options = new GraphOptions(
1703         EdgeType.DIRECTED,
1704         GraphOptions.DegenerateEdges.KEEP,
1705         GraphOptions.DuplicateEdges.KEEP,
1706         GraphOptions.SiblingPairs.KEEP);
1707     auto graph = new Graph(graph_options, _sites, merged_edges, merged_input_edge_ids,
1708         input_edge_id_set_lexicon, null, null, IsFullPolygonPredicate());
1709     auto simplifier = new EdgeChainSimplifier(
1710         this, graph, merged_edge_layers, site_vertices,
1711         &layer_edges, &layer_input_edge_ids, input_edge_id_set_lexicon);
1712     simplifier.run();
1713   }
1714 
1715   /**
1716    * Merges the edges from all layers and sorts them in lexicographic order so
1717    * that we can construct a single graph.  The sort is stable, which means that
1718    * any duplicate edges within each layer will still be sorted by InputEdgeId.
1719    */
1720   void mergeLayerEdges(
1721       in Edge[][] layer_edges,
1722       in InputEdgeIdSetId[][] layer_input_edge_ids,
1723       ref Edge[] edges,
1724       ref InputEdgeIdSetId[] input_edge_ids,
1725       ref int[] edge_layers) const {
1726     LayerEdgeId[] order;
1727     for (int i = 0; i < layer_edges.length; ++i) {
1728       for (int e = 0; e < layer_edges[i].length; ++e) {
1729         order ~= LayerEdgeId(i, e);
1730       }
1731     }
1732     .sort!((a, b) => stableLessThan(layer_edges[a.first][a.second], layer_edges[b.first][b.second], a, b))(
1733             order);
1734     edges.reserve(order.length);
1735     input_edge_ids.reserve(order.length);
1736     edge_layers.reserve(order.length);
1737     for (int i = 0; i < order.length; ++i) {
1738       const LayerEdgeId id = order[i];
1739       edges ~= layer_edges[id.first][id.second];
1740       input_edge_ids ~= layer_input_edge_ids[id.first][id.second];
1741       edge_layers ~= id.first;
1742     }
1743   }
1744 
1745   /**
1746    * A comparision function that allows stable sorting with std::sort (which is
1747    * fast but not stable).  It breaks ties between equal edges by comparing
1748    * their LayerEdgeIds.
1749    */
1750   static bool stableLessThan(in Edge a, in Edge b, in LayerEdgeId ai, in LayerEdgeId bi) {
1751     // The compiler doesn't optimize this as well as it should:
1752     //   return make_pair(a, ai) < make_pair(b, bi);
1753     if (a[0] < b[0]) return true;
1754     if (b[0] < a[0]) return false;
1755     if (a[1] < b[1]) return true;
1756     if (b[1] < a[1]) return false;
1757     return ai < bi;  // Stable sort.
1758   }
1759 
1760   //////////// Parameters /////////////
1761 
1762   /// S2Builder options.
1763   Options _options;
1764 
1765   /// The maximum distance (inclusive) that a vertex can move when snapped,
1766   /// equal to S1ChordAngle(options_.snap_function().snap_radius()).
1767   S1ChordAngle _siteSnapRadiusCa;
1768 
1769   /**
1770    * The maximum distance (inclusive) that an edge can move when snapping to a
1771    * snap site.  It can be slightly larger than the site snap radius when
1772    * edges are being split at crossings.
1773    */
1774   S1ChordAngle _edgeSnapRadiusCa;
1775 
1776   S1Angle _maxEdgeDeviation;
1777   S1ChordAngle _edgeSiteQueryRadiusCa;
1778   S1ChordAngle _minEdgeLengthToSplitCa;
1779 
1780   S1Angle _minSiteSeparation;
1781   S1ChordAngle _minSiteSeparationCa;
1782   S1ChordAngle _minEdgeSiteSeparationCa;
1783   S1ChordAngle _minEdgeSiteSeparationCaLimit;
1784 
1785   S1ChordAngle _maxAdjacentSiteSeparationCa;
1786 
1787   /**
1788    * The squared sine of the edge snap radius.  This is equivalent to the snap
1789    * radius (squared) for distances measured through the interior of the
1790    * sphere to the plane containing an edge.  This value is used only when
1791    * interpolating new points along edges (see GetSeparationSite).
1792    */
1793   double _edgeSnapRadiusSin2;
1794 
1795   /// A copy of the argument to Build().
1796   S2Error* _error;
1797 
1798   /**
1799    * True if snapping was requested.  This is true if either snap_radius() is
1800    * positive, or split_crossing_edges() is true (which implicitly requests
1801    * snapping to ensure that both crossing edges are snapped to the
1802    * intersection point).
1803    */
1804   bool _snappingRequested;
1805 
1806   /**
1807    * Initially false, and set to true when it is discovered that at least one
1808    * input vertex or edge does not meet the output guarantees (e.g., that
1809    * vertices are separated by at least snap_function.min_vertex_separation).
1810    */
1811   bool _snappingNeeded;
1812 
1813   //////////// Input Data /////////////
1814 
1815   /// A flag indicating whether label_set_ has been modified since the last
1816   /// time label_set_id_ was computed.
1817   bool _labelSetModified;
1818 
1819   S2Point[] _inputVertices;
1820   InputEdge[] _inputEdges;
1821 
1822   Layer[] _layers;
1823   GraphOptions[] _layerOptions;
1824   InputEdgeId[] _layerBegins;
1825   IsFullPolygonPredicate[] _layerIsFullPolygonPredicates;
1826 
1827   /**
1828    * Each input edge has "label set id" (an int32) representing the set of
1829    * labels attached to that edge.  This vector is populated only if at least
1830    * one label is used.
1831    */
1832   LabelSetId[] _labelSetIds;
1833   IdSetLexicon _labelSetLexicon;
1834 
1835   /// The current set of labels (represented as a stack).
1836   Label[] _labelSet;
1837 
1838   /// The LabelSetId corresponding to the current label set, computed on demand
1839   /// (by adding it to label_set_lexicon()).
1840   LabelSetId _labelSetId;
1841 
1842   ////////////// Data for Snapping and Simplifying //////////////
1843 
1844   /// The number of sites specified using ForceVertex().  These sites are
1845   /// always at the beginning of the sites_ vector.
1846   SiteId _numForcedSites;
1847 
1848   // The set of snapped vertex locations ("sites").
1849   S2Point[] _sites;
1850 
1851   /**
1852    * A map from each input edge to the set of sites "nearby" that edge,
1853    * defined as the set of sites that are candidates for snapping and/or
1854    * avoidance.  Note that compact_array will inline up to two sites, which
1855    * usually takes care of the vast majority of edges.  Sites are kept sorted
1856    * by increasing distance from the origin of the input edge.
1857    *
1858    * Once snapping is finished, this field is discarded unless edge chain
1859    * simplification was requested, in which case instead the sites are
1860    * filtered by removing the ones that each edge was snapped to, leaving only
1861    * the "sites to avoid" (needed for simplification).
1862    */
1863   SiteId[][] _edgeSites;
1864 }
1865 
1866 /**
1867  * This class is only needed by S2Builder::Layer implementations.  A layer is
1868  * responsible for assembling an S2Builder::Graph of snapped edges into the
1869  * desired output format (e.g., an S2Polygon).  The GraphOptions class allows
1870  * each Layer type to specify requirements on its input graph: for example, if
1871  * DegenerateEdges::DISCARD is specified, then S2Builder will ensure that all
1872  * degenerate edges are removed before passing the graph to the S2Layer::Build
1873  * method.
1874  */
1875 class GraphOptions {
1876 public:
1877   alias EdgeType = S2Builder.EdgeType;
1878 
1879   /**
1880    * All S2Builder::Layer subtypes should specify GraphOptions explicitly
1881    * using this constructor, rather than relying on default values.
1882    */
1883   this(EdgeType edge_type, DegenerateEdges degenerate_edges,
1884       DuplicateEdges duplicate_edges, SiblingPairs sibling_pairs) {
1885     _edgeType = edge_type;
1886     _degenerateEdges = degenerate_edges;
1887     _duplicateEdges = duplicate_edges;
1888     _siblingPairs = sibling_pairs;
1889     _allowVertexFiltering = true;
1890   }
1891 
1892   /**
1893    * The default options specify that all edges should be kept, since this
1894    * produces the least surprising output and makes it easier to diagnose the
1895    * problem when an option is left unspecified.
1896    */
1897   this() {
1898     _edgeType = EdgeType.DIRECTED;
1899     _degenerateEdges = DegenerateEdges.KEEP;
1900     _duplicateEdges = DuplicateEdges.KEEP;
1901     _siblingPairs = SiblingPairs.KEEP;
1902     _allowVertexFiltering = true;
1903   }
1904 
1905   /**
1906    * Specifies whether the S2Builder input edges should be treated as
1907    * undirected.  If true, then all input edges are duplicated into pairs
1908    * consisting of an edge and a sibling (reverse) edge.  The layer
1909    * implementation is responsible for ensuring that exactly one edge from
1910    * each pair is used in the output, i.e. *only half* of the graph edges will
1911    * be used.  (Note that some values of the sibling_pairs() option
1912    * automatically take care of this issue by removing half of the edges and
1913    * changing edge_type() to DIRECTED.)
1914    */
1915   EdgeType edgeType() const {
1916     return _edgeType;
1917   }
1918 
1919   void setEdgeType(EdgeType edge_type) {
1920     _edgeType = edge_type;
1921   }
1922 
1923   /**
1924    * Controls how degenerate edges (i.e., an edge from a vertex to itself) are
1925    * handled.  Such edges may be present in the input, or they may be created
1926    * when both endpoints of an edge are snapped to the same output vertex.
1927    * The options available are:
1928    *
1929    * DISCARD: Discards all degenerate edges.  This is useful for layers that
1930    *          do not support degeneracies, such as S2PolygonLayer.
1931    *
1932    * DISCARD_EXCESS: Discards all degenerate edges that are connected to
1933    *                 non-degenerate edges.  (Any remaining duplicate edges can
1934    *                 be merged using DuplicateEdges::MERGE.)  This is useful
1935    *                 for simplifying polygons while ensuring that loops that
1936    *                 collapse to a single point do not disappear.
1937    *
1938    * KEEP: Keeps all degenerate edges.  Be aware that this may create many
1939    *       redundant edges when simplifying geometry (e.g., a polyline of the
1940    *       form AABBBBBCCCCCCDDDD).  DegenerateEdges::KEEP is mainly useful
1941    *       for algorithms that require an output edge for every input edge.
1942    */
1943   enum DegenerateEdges { DISCARD, DISCARD_EXCESS, KEEP }
1944 
1945   DegenerateEdges degenerateEdges() const {
1946     return _degenerateEdges;
1947   }
1948 
1949   void setDegenerateEdges(DegenerateEdges degenerate_edges) {
1950     _degenerateEdges = degenerate_edges;
1951   }
1952 
1953   /**
1954    * Controls how duplicate edges (i.e., edges that are present multiple
1955    * times) are handled.  Such edges may be present in the input, or they can
1956    * be created when vertices are snapped together.  When several edges are
1957    * merged, the result is a single edge labelled with all of the original
1958    * input edge ids.
1959    */
1960   enum DuplicateEdges { MERGE, KEEP }
1961 
1962   DuplicateEdges duplicateEdges() const {
1963     return _duplicateEdges;
1964   }
1965 
1966   void setDuplicateEdges(DuplicateEdges duplicate_edges) {
1967     _duplicateEdges = duplicate_edges;
1968   }
1969 
1970   /**
1971    * Controls how sibling edge pairs (i.e., pairs consisting of an edge and
1972    * its reverse edge) are handled.  Layer types that define an interior
1973    * (e.g., polygons) normally discard such edge pairs since they do not
1974    * affect the result (i.e., they define a "loop" with no interior).  The
1975    * various options include:
1976    *
1977    * DISCARD: Discards all sibling edge pairs.
1978    *
1979    * DISCARD_EXCESS: Like DISCARD, except that a single sibling pair is kept
1980    *                 if the result would otherwise be empty.  This is useful
1981    *                 for polygons with degeneracies (S2LaxPolygonShape), and
1982    *                 for simplifying polylines while ensuring that they are
1983    *                 not split into multiple disconnected pieces.
1984    *
1985    * KEEP: Keeps sibling pairs.  This can be used to create polylines that
1986    *       double back on themselves, or degenerate loops (with a layer type
1987    *       such as S2LaxPolygonShape).
1988    *
1989    * REQUIRE: Requires that all edges have a sibling (and returns an error
1990    *          otherwise).  This is useful with layer types that create a
1991    *          collection of adjacent polygons (a polygon mesh).
1992    *
1993    * CREATE: Ensures that all edges have a sibling edge by creating them if
1994    *         necessary.  This is useful with polygon meshes where the input
1995    *         polygons do not cover the entire sphere.  Such edges always
1996    *         have an empty set of labels.
1997    *
1998    * If edge_type() is EdgeType::UNDIRECTED, a sibling edge pair is considered
1999    * to consist of four edges (two duplicate edges and their siblings), since
2000    * only two of these four edges will be used in the final output.
2001    *
2002    * Furthermore, since the options REQUIRE and CREATE guarantee that all
2003    * edges will have siblings, S2Builder implements these options for
2004    * undirected edges by discarding half of the edges in each direction and
2005    * changing the edge_type() to EdgeType::DIRECTED.  For example, two
2006    * undirected input edges between vertices A and B would first be converted
2007    * into two directed edges in each direction, and then one edge of each pair
2008    * would be discarded leaving only one edge in each direction.
2009    *
2010    * Degenerate edges are considered not to have siblings.  If such edges are
2011    * present, they are passed through unchanged by SiblingPairs::DISCARD.  For
2012    * SiblingPairs::REQUIRE or SiblingPairs::CREATE with undirected edges, the
2013    * number of copies of each degenerate edge is reduced by a factor of two.
2014    *
2015    * Any of the options that discard edges (DISCARD, DISCARD_EXCESS, and
2016    * REQUIRE/CREATE in the case of undirected edges) have the side effect that
2017    * when duplicate edges are present, all of the corresponding edge labels
2018    * are merged together and assigned to the remaining edges.  (This avoids
2019    * the problem of having to decide which edges are discarded.)  Note that
2020    * this merging takes place even when all copies of an edge are kept, and
2021    * that even labels attached to duplicate degenerate edges are merged.  For
2022    * example, consider the graph {AB1, AB2, BA3, CD4, CD5} (where XYn denotes
2023    * an edge from X to Y with label "n").  With SiblingPairs::DISCARD, we need
2024    * to discard one of the copies of AB.  But which one?  Rather than choosing
2025    * arbitrarily, instead we merge the labels of all duplicate edges (even
2026    * ones where no sibling pairs were discarded), yielding {AB12, CD45, CD45}
2027    * (assuming that duplicate edges are being kept).
2028    */
2029   enum SiblingPairs { DISCARD, DISCARD_EXCESS, KEEP, REQUIRE, CREATE }
2030 
2031   SiblingPairs siblingPairs() const {
2032     return _siblingPairs;
2033   }
2034 
2035   void setSiblingPairs(SiblingPairs sibling_pairs) {
2036     _siblingPairs = sibling_pairs;
2037   }
2038 
2039   /**
2040    * This is a specialized option that is only needed by clients want to work
2041    * with the graphs for multiple layers at the same time (e.g., in order to
2042    * check whether the same edge is present in two different graphs).  [Note
2043    * that if you need to do this, usually it is easier just to build a single
2044    * graph with suitable edge labels.]
2045    *
2046    * When there are a large number of layers, then by default S2Builder builds
2047    * a minimal subgraph for each layer containing only the vertices needed by
2048    * the edges in that layer.  This ensures that layer types that iterate over
2049    * the vertices run in time proportional to the size of that layer rather
2050    * than the size of all layers combined.  (For example, if there are a
2051    * million layers with one edge each, then each layer would be passed a
2052    * graph with 2 vertices rather than 2 million vertices.)
2053    *
2054    * If this option is set to false, this optimization is disabled.  Instead
2055    * the graph passed to this layer will contain the full set of vertices.
2056    * (This is not recommended when the number of layers could be large.)
2057    *
2058    * DEFAULT: true
2059    */
2060   bool allowVertexFiltering() const {
2061     return _allowVertexFiltering;
2062   }
2063 
2064   void setAllowVertexFiltering(bool allow_vertex_filtering) {
2065     _allowVertexFiltering = allow_vertex_filtering;
2066   }
2067 
2068   override
2069   bool opEquals(Object other) const {
2070     GraphOptions y = cast(GraphOptions) other;
2071     if (y is null) return false;
2072     return (edgeType() == y.edgeType()
2073         && degenerateEdges() == y.degenerateEdges()
2074         && duplicateEdges() == y.duplicateEdges()
2075         && siblingPairs() == y.siblingPairs()
2076         && allowVertexFiltering() == y.allowVertexFiltering());
2077   }
2078 
2079 private:
2080   EdgeType _edgeType;
2081   DegenerateEdges _degenerateEdges;
2082   DuplicateEdges _duplicateEdges;
2083   SiblingPairs _siblingPairs;
2084   bool _allowVertexFiltering;
2085 }
2086 
2087 /**
2088  * An S2Shape used to represent the entire collection of S2Builder input edges.
2089  * Vertices are specified as indices into a vertex vector to save space.
2090  */
2091 class VertexIdEdgeVectorShape : S2Shape {
2092 public:
2093   /// Requires that "edges" is constant for the lifetime of this object.
2094   this(in int[2][] edges, in S2Point[] vertices) {
2095     _edges = edges;
2096     _vertices = vertices;
2097   }
2098 
2099   const(S2Point) vertex0(int e) const {
2100     return vertex(_edges[e][0]);
2101   }
2102 
2103   const(S2Point) vertex1(int e) const {
2104     return vertex(_edges[e][1]);
2105   }
2106 
2107   // S2Shape interface:
2108   final override
2109   int numEdges() const {
2110     return cast(int) _edges.length;
2111   }
2112 
2113   final override
2114   Edge edge(int e) const {
2115     return Edge(_vertices[_edges[e][0]], _vertices[_edges[e][1]]);
2116   }
2117 
2118   final override
2119   int dimension() const {
2120     return 1;
2121   }
2122 
2123   final override
2124   ReferencePoint getReferencePoint() const {
2125     return ReferencePoint(false);
2126   }
2127 
2128   final override
2129   int numChains() const {
2130     return cast(int) _edges.length;
2131   }
2132 
2133   final override
2134   Chain chain(int i) const {
2135     return Chain(i, 1);
2136   }
2137 
2138   final override
2139   Edge chainEdge(int i, int j) const {
2140     return edge(i);
2141   }
2142 
2143   final override
2144   ChainPosition chainPosition(int e) const {
2145     return ChainPosition(e, 0);
2146   }
2147 
2148 private:
2149   const(S2Point) vertex(int i) const {
2150     return _vertices[i];
2151   }
2152 
2153   const(int[2][]) _edges;
2154   const(S2Point[]) _vertices;
2155 }
2156 
2157 // A class that encapsulates the state needed for simplifying edge chains.
2158 class EdgeChainSimplifier {
2159  public:
2160   // The graph "g" contains all edges from all layers.  "edge_layers"
2161   // indicates the original layer for each edge.  "site_vertices" is a map
2162   // from SiteId to the set of InputVertexIds that were snapped to that site.
2163   // "layer_edges" and "layer_input_edge_ids" are output arguments where the
2164   // simplified edge chains will be placed.  The input and output edges are
2165   // not sorted.
2166   this(
2167       in S2Builder builder,
2168       in Graph g,
2169       in int[] edge_layers,
2170       in InputVertexId[][] site_vertices,
2171       Edge[][]* layer_edges,
2172       InputEdgeIdSetId[][]* layer_input_edge_ids,
2173       IdSetLexicon input_edge_id_set_lexicon) {
2174     _builder = builder;
2175     _g = g;
2176     _in = new VertexInMap(g);
2177     _out = new VertexOutMap(g);
2178     _edgeLayers = edge_layers;
2179     _siteVertices = site_vertices;
2180     _layerEdges = layer_edges;
2181     _layerInputEdgeIds = layer_input_edge_ids;
2182     _inputEdgeIdSetLexicon = input_edge_id_set_lexicon;
2183     _layerBegins = _builder._layerBegins;
2184     _isInterior = new bool[](g.numVertices());
2185     _used = new bool[](g.numEdges());
2186     _newEdges.reserve(g.numEdges());
2187     _newInputEdgeIds.reserve(g.numEdges());
2188     _newEdgeLayers.reserve(g.numEdges());
2189   }
2190 
2191   void run() {
2192     // Determine which vertices can be interior vertices of an edge chain.
2193     for (VertexId v = 0; v < _g.numVertices(); ++v) {
2194       _isInterior[v] = isInterior(v);
2195     }
2196     // Attempt to simplify all edge chains that start from a non-interior
2197     // vertex.  (This takes care of all chains except loops.)
2198     for (EdgeId e = 0; e < _g.numEdges(); ++e) {
2199       if (_used[e]) continue;
2200       Edge edge = _g.edge(e);
2201       if (_isInterior[edge[0]]) continue;
2202       if (!_isInterior[edge[1]]) {
2203         outputEdge(e);  // An edge between two non-interior vertices.
2204       } else {
2205         simplifyChain(edge[0], edge[1]);
2206       }
2207     }
2208     // If there are any edges left, they form one or more disjoint loops where
2209     // all vertices are interior vertices.
2210     //
2211     // TODO(ericv): It would be better to start from the edge with the smallest
2212     // min_input_edge_id(), since that would make the output more predictable
2213     // for testing purposes.  It also means that we won't create an edge that
2214     // spans the start and end of a polyline if the polyline is snapped into a
2215     // loop.  (Unfortunately there are pathological examples that prevent us
2216     // from guaranteeing this in general, e.g. there could be two polylines in
2217     // different layers that snap to the same loop but start at different
2218     // positions.  In general we only consider input edge ids to be a hint
2219     // towards the preferred output ordering.)
2220     for (EdgeId e = 0; e < _g.numEdges(); ++e) {
2221       if (_used[e]) continue;
2222       Edge edge = _g.edge(e);
2223       if (edge[0] == edge[1]) {
2224         // Note that it is safe to output degenerate edges as we go along,
2225         // because this vertex has at least one non-degenerate outgoing edge and
2226         // therefore we will (or just did) start an edge chain here.
2227         outputEdge(e);
2228       } else {
2229         simplifyChain(edge[0], edge[1]);
2230       }
2231     }
2232 
2233     // Finally, copy the output edges into the appropriate layers.  They don't
2234     // need to be sorted because the input edges were also unsorted.
2235     for (int e = 0; e < _newEdges.length; ++e) {
2236       int layer = _newEdgeLayers[e];
2237       (*_layerEdges)[layer] ~= _newEdges[e];
2238       (*_layerInputEdgeIds)[layer] ~= _newInputEdgeIds[e];
2239     }
2240   }
2241 
2242 private:
2243   alias VertexId = Graph.VertexId;
2244   alias VertexInMap = Graph.VertexInMap;
2245   alias VertexOutMap = Graph.VertexOutMap;
2246 
2247   alias InputVertexId = S2Builder.InputVertexId;
2248   alias Edge = S2Builder.Edge;
2249   alias EdgeId = S2Builder.EdgeId;
2250   alias InputEdgeId = S2Builder.InputEdgeId;
2251   alias InputEdgeIdSetId = S2Builder.InputEdgeIdSetId;
2252 
2253   /**
2254    * A helper class for determining whether a vertex can be an interior vertex
2255    * of a simplified edge chain.  Such a vertex must be adjacent to exactly two
2256    * vertices (across all layers combined), and in each layer the number of
2257    * incoming edges from one vertex must equal the number of outgoing edges to
2258    * the other vertex (in both directions).  Furthermore the vertex cannot have
2259    * any degenerate edges in a given layer unless it has at least one
2260    * non-degenerate edge in that layer as well.  (Note that usually there will
2261    * not be any degenerate edges at all, since most layer types discard them.)
2262    *
2263    * The last condition is necessary to prevent the following: suppose that one
2264    * layer has a chain ABC and another layer has a degenerate edge BB (with no
2265    * other edges incident to B).  Then we can't simplify ABC to AC because there
2266    * would be no suitable replacement for the edge BB (since the input edge that
2267    * mapped to BB can't be replaced by any of the edges AA, AC, or CC without
2268    * moving further than snap_radius).
2269    */
2270   static class InteriorVertexMatcher {
2271   public:
2272     /// Checks whether "v0" can be an interior vertex of an edge chain.
2273     this(VertexId v0) {
2274       _v0 = v0;
2275       _v1 = -1;
2276       _v2 = -1;
2277       _n0 = 0;
2278       _n1 = 0;
2279       _n2 = 0;
2280       _excessOut = 0;
2281       _tooManyEndpoints = false;
2282     }
2283 
2284     /// Starts analyzing the edges of a new layer.
2285     void startLayer() {
2286       _excessOut = _n0 = _n1 = _n2 = 0;
2287     }
2288 
2289     /// This method should be called for each edge incident to "v0" in a given
2290     /// layer.  (For degenerate edges, it should be called twice.)
2291     void tally(VertexId v, bool outgoing) {
2292       _excessOut += outgoing ? 1 : -1;  // outdegree - indegree
2293       if (v == _v0) {
2294         ++_n0;  // Counts both endpoints of each degenerate edge.
2295       } else {
2296         // We keep track of the total number of edges (incoming or outgoing)
2297         // connecting v0 to up to two adjacent vertices.
2298         if (_v1 < 0) _v1 = v;
2299         if (_v1 == v) {
2300           ++_n1;
2301         } else {
2302           if (_v2 < 0) _v2 = v;
2303           if (_v2 == v) {
2304             ++_n2;
2305           } else {
2306             _tooManyEndpoints = true;
2307           }
2308         }
2309       }
2310     }
2311 
2312     /// This method should be called after processing the edges for each layer.
2313     /// It returns true if "v0" is an interior vertex based on the edges so far.
2314     bool matches() const {
2315       // We check that there are the same number of incoming and outgoing edges
2316       // in each direction by verifying that (1) indegree(v0) == outdegree(v0)
2317       // and (2) the total number of edges (incoming and outgoing) to "v1" and
2318       // "v2" are equal.  We also check the condition on degenerate edges that
2319       // is documented above.
2320       return (!_tooManyEndpoints && _excessOut == 0 && _n1 == _n2 && (_n0 == 0 || _n1 > 0));
2321     }
2322 
2323   private:
2324     VertexId _v0, _v1, _v2;
2325     int _n0, _n1, _n2;
2326     int _excessOut;           // outdegree(v0) - indegree(v0)
2327     bool _tooManyEndpoints;  // Have we seen more than two adjacent vertices?
2328   }
2329 
2330   /// Copies the given edge to the output and marks it as used.
2331   void outputEdge(EdgeId e) {
2332     _newEdges ~= _g.edge(e);
2333     _newInputEdgeIds ~= _g.inputEdgeIdSetId(e);
2334     _newEdgeLayers ~= _edgeLayers[e];
2335     _used[e] = true;
2336   }
2337 
2338   /// Returns the layer that a given graph edge belongs to.
2339   int graphEdgeLayer(EdgeId e) const {
2340     return _edgeLayers[e];
2341   }
2342 
2343   /// Returns the layer than a given input edge belongs to.
2344   int inputEdgeLayer(InputEdgeId id) const
2345   in {
2346     // NOTE(ericv): If this method shows up in profiling, the result could be
2347     // stored with each edge (i.e., edge_layers_ and new_edge_layers_).
2348     assert(id >= 0);
2349   } do {
2350     auto triResults = _layerBegins.assumeSorted().trisect(id);
2351     return cast(int) (triResults[0].length + triResults[1].length) - 1;
2352   }
2353 
2354   /// Returns true if VertexId "v" can be an interior vertex of a simplified edge
2355   /// chain.  (See the InteriorVertexMatcher class for what this implies.)
2356   bool isInterior(VertexId v) {
2357     // Check a few simple prerequisites.
2358     if (_out.degree(v) == 0) return false;
2359     if (_out.degree(v) != _in.degree(v)) return false;
2360     if (v < _builder._numForcedSites) return false;  // Keep forced vertices.
2361 
2362     // Sort the edges so that they are grouped by layer.
2363     EdgeId[] edges = _tmpEdges;  // Avoid allocating each time.
2364     edges.length = 0;
2365     foreach (EdgeId e; _out.edgeIds(v)) edges ~= e;
2366     foreach (EdgeId e; _in.edgeIds(v)) edges ~= e;
2367     sort!((EdgeId x, EdgeId y) {
2368           return graphEdgeLayer(x) < graphEdgeLayer(y);
2369         })(edges);
2370     // Now feed the edges in each layer to the InteriorVertexMatcher.
2371     auto matcher = new InteriorVertexMatcher(v);
2372     for (auto i = 0; i < edges.length; ) {
2373       int layer = graphEdgeLayer(edges[i]);
2374       matcher.startLayer();
2375       for (; i < edges.length && graphEdgeLayer(edges[i]) == layer; ++i) {
2376         Edge edge = _g.edge(edges[i]);
2377         if (edge[0] == v) matcher.tally(edge[1], true /*outgoing*/);
2378         if (edge[1] == v) matcher.tally(edge[0], false /*outgoing*/);
2379       }
2380       if (!matcher.matches()) {
2381         return false;
2382       }
2383     }
2384     return true;
2385   }
2386 
2387   /**
2388    * Follows the edge chain starting with (v0, v1) until either we find a
2389    * non-interior vertex or we return to the original vertex v0.  At each vertex
2390    * we simplify a subchain of edges that is as long as possible.
2391    */
2392   void simplifyChain(VertexId v0, VertexId v1) {
2393     // Avoid allocating "chain" each time by reusing it.
2394     VertexId[] chain = _tmpVertices;
2395     auto simplifier = new S2PolylineSimplifier();
2396     VertexId vstart = v0;
2397     bool done = false;
2398     do {
2399       // Simplify a subchain of edges starting (v0, v1).
2400       simplifier.initialize(_g.vertex(v0));
2401       avoidSites(v0, v0, v1, simplifier);
2402       chain ~= v0;
2403       do {
2404         chain ~= v1;
2405         done = !_isInterior[v1] || v1 == vstart;
2406         if (done) break;
2407 
2408         // Attempt to extend the chain to the next vertex.
2409         VertexId vprev = v0;
2410         v0 = v1;
2411         v1 = followChain(vprev, v0);
2412       } while (targetInputVertices(v0, simplifier)
2413           && avoidSites(chain[0], v0, v1, simplifier)
2414           && simplifier.extend(_g.vertex(v1)));
2415 
2416       if (chain.length == 2) {
2417         outputAllEdges(chain[0], chain[1]);  // Could not simplify.
2418       } else {
2419         mergeChain(chain);
2420       }
2421       // Note that any degenerate edges that were not merged into a chain are
2422       // output by EdgeChainSimplifier::Run().
2423       chain.length = 0;
2424     } while (!done);
2425   }
2426 
2427   /// Given an edge (v0, v1) where v1 is an interior vertex, returns the (unique)
2428   /// next vertex in the edge chain.
2429   VertexId followChain(VertexId v0, VertexId v1) const
2430   in {
2431     assert(_isInterior[v1]);
2432   } do {
2433     foreach (EdgeId e; _out.edgeIds(v1)) {
2434       VertexId v = _g.edge(e)[1];
2435       if (v != v0 && v != v1) return v;
2436     }
2437     logger.logFatal("Could not find next edge in edge chain");
2438     return -1;
2439   }
2440 
2441   /// Copies all input edges between v0 and v1 (in both directions) to the output.
2442   void outputAllEdges(VertexId v0, VertexId v1) {
2443     foreach (EdgeId e; _out.edgeIds(v0, v1)) outputEdge(e);
2444     foreach (EdgeId e; _out.edgeIds(v1, v0)) outputEdge(e);
2445   }
2446 
2447   /// Ensures that the simplified edge passes within "edge_snap_radius" of all
2448   /// the *input* vertices that snapped to the given vertex "v".
2449   bool targetInputVertices(VertexId v, S2PolylineSimplifier simplifier) const {
2450     foreach (InputVertexId i; _siteVertices[v]) {
2451       if (!simplifier.targetDisc(_builder._inputVertices[i], _builder._edgeSnapRadiusCa)) {
2452         return false;
2453       }
2454     }
2455     return true;
2456   }
2457 
2458   /**
2459    * Given the starting vertex v0 and last edge (v1, v2) of an edge chain,
2460    * restricts the allowable range of angles in order to ensure that all sites
2461    * near the edge (v1, v2) are avoided by at least min_edge_vertex_separation.
2462    */
2463   bool avoidSites(VertexId v0, VertexId v1, VertexId v2, S2PolylineSimplifier simplifier) const {
2464     import s2.s2predicates : orderedCCW, sign;
2465     const S2Point p0 = _g.vertex(v0);
2466     const S2Point p1 = _g.vertex(v1);
2467     const S2Point p2 = _g.vertex(v2);
2468     auto r1 = S1ChordAngle(p0, p1);
2469     auto r2 = S1ChordAngle(p0, p2);
2470 
2471     // The distance from the start of the edge chain must increase monotonically
2472     // for each vertex, since we don't want to simplify chains that backtrack on
2473     // themselves (we want a parametric approximation, not a geometric one).
2474     if (r2 < r1) return false;
2475 
2476     // We also limit the maximum edge length in order to guarantee that the
2477     // simplified edge stays with max_edge_deviation() of all the input edges
2478     // that snap to it.
2479     if (r2 >= _builder._minEdgeLengthToSplitCa) return false;
2480 
2481     // Otherwise it is sufficient to consider the nearby sites (edge_sites_) for
2482     // a single input edge that snapped to (v1, v2) or (v2, v1).  This is
2483     // because each edge has a list of all sites within (max_edge_deviation +
2484     // min_edge_vertex_separation), and since the output edge is within
2485     // max_edge_deviation of all input edges, this list includes all sites
2486     // within min_edge_vertex_separation of the output edge.
2487     //
2488     // Usually there is only one edge to choose from, but it's not much more
2489     // effort to choose the edge with the shortest list of edge_sites_.
2490     InputEdgeId best = -1;
2491     const edge_sites = _builder._edgeSites;
2492     foreach (EdgeId e; _out.edgeIds(v1, v2)) {
2493       foreach (InputEdgeId id; _g.inputEdgeIds(e)) {
2494         if (best < 0 || edge_sites[id].length < edge_sites[best].length)
2495           best = id;
2496       }
2497     }
2498     foreach (EdgeId e; _out.edgeIds(v2, v1)) {
2499       foreach (InputEdgeId id; _g.inputEdgeIds(e)) {
2500         if (best < 0 || edge_sites[id].length < edge_sites[best].length)
2501           best = id;
2502       }
2503     }
2504     debug enforce(best >= 0);  // Because there is at least one outgoing edge.
2505 
2506     foreach (VertexId v; edge_sites[best]) {
2507       // This test is optional since these sites are excluded below anyway.
2508       if (v == v0 || v == v1 || v == v2) continue;
2509 
2510       // We are only interested in sites whose distance from "p0" is in the
2511       // range (r1, r2).  Sites closer than "r1" have already been processed,
2512       // and sites further than "r2" aren't relevant yet.
2513       S2Point p = _g.vertex(v);
2514       auto r = S1ChordAngle(p0, p);
2515       if (r <= r1 || r >= r2) continue;
2516 
2517       // We need to figure out whether this site is to the left or right of the
2518       // edge chain.  For the first edge this is easy.  Otherwise, since we are
2519       // only considering sites in the radius range (r1, r2), we can do this by
2520       // checking whether the site is to the left of the wedge (p0, p1, p2).
2521       bool disc_on_left = (v1 == v0) ? (sign(p1, p2, p) > 0) : orderedCCW(p0, p2, p, p1);
2522       if (!simplifier.avoidDisc(p, _builder._minEdgeSiteSeparationCa, disc_on_left)) {
2523         return false;
2524       }
2525     }
2526     return true;
2527   }
2528 
2529   /**
2530    * Given the vertices in a simplified edge chain, adds the corresponding
2531    * simplified edge(s) to the output.  Note that (1) the edge chain may exist
2532    * in multiple layers, (2) the edge chain may exist in both directions, (3)
2533    * there may be more than one copy of an edge chain (in either direction)
2534    * within a single layer.
2535    */
2536   void mergeChain(in VertexId[] vertices) {
2537     // Suppose that all interior vertices have M outgoing edges and N incoming
2538     // edges.  Our goal is to group the edges into M outgoing chains and N
2539     // incoming chains, and then replace each chain by a single edge.
2540     S2Builder.InputEdgeId[][] merged_input_ids;
2541     InputEdgeId[] degenerate_ids;
2542     int num_out;  // Edge count in the outgoing direction.
2543     for (int i = 1; i < vertices.length; ++i) {
2544       VertexId v0 = vertices[i - 1];
2545       VertexId v1 = vertices[i];
2546       auto out_edges = _out.edgeIds(v0, v1);
2547       auto in_edges = _out.edgeIds(v1, v0);
2548       if (i == 1) {
2549         // Allocate space to store the input edge ids associated with each edge.
2550         num_out = cast(int) out_edges.length;
2551         merged_input_ids.length = num_out + in_edges.length;
2552         foreach (InputEdgeId[] ids; merged_input_ids) {
2553           ids.reserve(vertices.length - 1);
2554         }
2555       } else {
2556         // For each interior vertex, we build a list of input edge ids
2557         // associated with degenerate edges.  Each input edge ids will be
2558         // assigned to one of the output edges later.  (Normally there are no
2559         // degenerate edges at all since most layer types don't want them.)
2560         debug enforce(_isInterior[v0]);
2561         foreach (EdgeId e; _out.edgeIds(v0, v0)) {
2562           foreach (InputEdgeId id; _g.inputEdgeIds(e)) {
2563             degenerate_ids ~= id;
2564           }
2565           _used[e] = true;
2566         }
2567       }
2568       // Because the edges were created in layer order, and all sorts used are
2569       // stable, the edges are still in layer order.  Therefore we can simply
2570       // merge together all the edges in the same relative position.
2571       int j = 0;
2572       foreach (EdgeId e; out_edges) {
2573         foreach (InputEdgeId id; _g.inputEdgeIds(e)) {
2574           merged_input_ids[j] ~= id;
2575         }
2576         _used[e] = true;
2577         ++j;
2578       }
2579       foreach (EdgeId e; in_edges) {
2580         foreach (InputEdgeId id; _g.inputEdgeIds(e)) {
2581           merged_input_ids[j] ~= id;
2582         }
2583         _used[e] = true;
2584         ++j;
2585       }
2586       debug enforce(merged_input_ids.length == j);
2587     }
2588     if (!degenerate_ids.empty()) {
2589       sort(degenerate_ids);
2590       assignDegenerateEdges(degenerate_ids, merged_input_ids);
2591     }
2592     // Output the merged edges.
2593     VertexId v0 = vertices[0], v1 = vertices[1], vb = vertices.back();
2594     foreach (EdgeId e; _out.edgeIds(v0, v1)) {
2595       _newEdges ~= Edge(v0, vb);
2596       _newEdgeLayers ~= graphEdgeLayer(e);
2597     }
2598     foreach (EdgeId e; _out.edgeIds(v1, v0)) {
2599       _newEdges ~= Edge(vb, v0);
2600       _newEdgeLayers ~= graphEdgeLayer(e);
2601     }
2602     foreach (ids; merged_input_ids) {
2603       _newInputEdgeIds ~= _inputEdgeIdSetLexicon.add(ids);
2604     }
2605   }
2606 
2607   /**
2608    * Given a list of the input edge ids associated with degenerate edges in the
2609    * interior of an edge chain, assigns each input edge id to one of the output
2610    * edges.
2611    */
2612   void assignDegenerateEdges(
2613       in S2Builder.InputEdgeId[] degenerate_ids, ref S2Builder.InputEdgeId[][] merged_ids) const {
2614     // Each degenerate edge is assigned to an output edge in the appropriate
2615     // layer.  If there is more than one candidate, we use heuristics so that if
2616     // the input consists of a chain of edges provided in consecutive order
2617     // (some of which became degenerate), then all those input edges are
2618     // assigned to the same output edge.  For example, suppose that one output
2619     // edge is labeled with input edges 3,4,7,8, while another output edge is
2620     // labeled with input edges 50,51,54,57.  Then if we encounter degenerate
2621     // edges labeled with input edges 5 and 6, we would prefer to assign them to
2622     // the first edge (yielding the continuous range 3,4,5,6,7,8).
2623     //
2624     // The heuristic below is only smart enough to handle the case where the
2625     // candidate output edges have non-overlapping ranges of input edges.
2626     // (Otherwise there is probably not a good heuristic for assigning the
2627     // degenerate edges in any case.)
2628 
2629     // Duplicate edge ids don't affect the heuristic below, so we don't bother
2630     // removing them.  (They will be removed by IdSetLexicon::Add.)
2631     foreach (ref ids; merged_ids) sort(ids);
2632 
2633     // Sort the output edges by their minimum input edge id.  This is sufficient
2634     // for the purpose of determining which layer they belong to.  With
2635     // EdgeType::UNDIRECTED, some edges might not have any input edge ids (i.e.,
2636     // if they consist entirely of siblings of input edges).  We simply remove
2637     // such edges from the lists of candidates.
2638     uint[] order;
2639     order.reserve(merged_ids.length);
2640     for (int i = 0; i < merged_ids.length; ++i) {
2641       if (!merged_ids[i].empty()) order ~= i;
2642     }
2643     sort!((int i, int j) {
2644           return merged_ids[i][0] < merged_ids[j][0];
2645         })(order);
2646 
2647     // Now determine where each degenerate edge should be assigned.
2648     for (int i = 0; i < degenerate_ids.length; ++i) {
2649       InputEdgeId degenerate_id = degenerate_ids[i];
2650       int layer = inputEdgeLayer(degenerate_id);
2651 
2652       // Find the first output edge whose range of input edge ids starts after
2653       // "degenerate_id".  If the previous edge belongs to the correct layer,
2654       // then we assign the degenerate edge to it.
2655       auto it = countUntil!((uint y) {
2656             return degenerate_id < merged_ids[y][0];
2657           })(order);
2658       if (it == -1) it = order.length;
2659       if (it != 0) {
2660         if (merged_ids[order[it - 1]][0] >= _layerBegins[layer]) --it;
2661       }
2662       debug enforce(inputEdgeLayer(merged_ids[order[it]][0]) == layer);
2663       merged_ids[order[it]] ~= degenerate_id;
2664     }
2665   }
2666 
2667   const(S2Builder) _builder;
2668   const(Graph) _g;
2669   const(Graph.VertexInMap) _in;
2670   const(Graph.VertexOutMap) _out;
2671   const(int[]) _edgeLayers;
2672   const(S2Builder.InputVertexId[][]) _siteVertices;
2673   S2Builder.Edge[][]* _layerEdges;
2674   S2Builder.InputEdgeIdSetId[][]* _layerInputEdgeIds;
2675   IdSetLexicon _inputEdgeIdSetLexicon;
2676 
2677   /// Convenience member copied from builder_.
2678   const(S2Builder.InputEdgeId[]) _layerBegins;
2679 
2680   /**
2681    * is_interior_[v] indicates that VertexId "v" is eligible to be an interior
2682    * vertex of a simplified edge chain.  You can think of it as vertex whose
2683    * indegree and outdegree are both 1 (although the actual definition is a
2684    * bit more complicated because of duplicate edges and layers).
2685    */
2686   bool[] _isInterior;
2687 
2688   /// used_[e] indicates that EdgeId "e" has already been processed.
2689   bool[] _used;
2690 
2691   // Temporary vectors, declared here to avoid repeated allocation.
2692   VertexId[] _tmpVertices;
2693   S2Builder.EdgeId[] _tmpEdges;
2694 
2695   // The output edges after simplification.
2696   S2Builder.Edge[] _newEdges;
2697   S2Builder.InputEdgeIdSetId[] _newInputEdgeIds;
2698   int[] _newEdgeLayers;
2699 }
2700 
2701 // Helper functions for computing error bounds:
2702 
2703 private S1ChordAngle roundUp(S1Angle a) {
2704   auto ca = S1ChordAngle(a);
2705   return ca.plusError(ca.getS1AngleConstructorMaxError());
2706 }
2707 
2708 private S1ChordAngle addPointToPointError(S1ChordAngle ca) {
2709   return ca.plusError(ca.getS2PointConstructorMaxError());
2710 }
2711 
2712 private S1ChordAngle addPointToEdgeError(S1ChordAngle ca) {
2713   return ca.plusError(getUpdateMinDistanceMaxError(ca));
2714 }
2715 
2716 private bool isFullPolygonUnspecified(in Graph g, ref S2Error error) {
2717   error.initialize(S2Error.Code.BUILDER_IS_FULL_PREDICATE_NOT_SPECIFIED,
2718       "A degenerate polygon was found, but no predicate was specified "
2719       ~ "to determine whether the polygon is empty or full.  Call "
2720       ~ "S2Builder::AddIsFullPolygonPredicate() to fix this problem.");
2721   return false;  // Assumes the polygon is empty.
2722 }
2723 
2724 private void dumpEdges(in Graph.Edge[] edges, in S2Point[] vertices) {
2725   foreach (e; edges) {
2726     S2Point[] v;
2727     v ~= vertices[e[0]];
2728     v ~= vertices[e[1]];
2729     writeln("S2Polyline: ", toString(v), "(", e[0], ",", e[1], ")");
2730   }
2731 }