1 // Copyright 2017 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 // Original Author: ericv@google.com (Eric Veach)
17 // Converted to D:  madric@gmail.com (Vijay Nayar)
18 
19 module s2.s2boolean_operation;
20 
21 import s2.builder.graph;
22 import s2.builder.layer;
23 import s2.builder.util.snap_functions : IdentitySnapFunction;
24 import s2.id_set_lexicon;
25 import s2.logger;
26 import s2.s1angle;
27 import s2.s2builder;
28 import s2.s2contains_point_query;
29 import s2.s2error;
30 import s2.s2point;
31 import s2.s2shape;
32 import s2.s2shape_index;
33 import s2.shapeutil.shape_edge;
34 import s2.shapeutil.shape_edge_id;
35 import s2.util.container.btree_map;
36 import s2.value_lexicon;
37 
38 import std.algorithm;
39 import std.bitmanip;
40 import std.conv;
41 import std.exception;
42 import std.range;
43 import std.stdio;
44 import std.typecons;
45 
46 alias EdgeType = S2Builder.EdgeType;
47 alias SnapFunction = S2Builder.SnapFunction;
48 alias DegenerateEdges = GraphOptions.DegenerateEdges;
49 alias DuplicateEdges = GraphOptions.DuplicateEdges;
50 alias SiblingPairs = GraphOptions.SiblingPairs;
51 
52 alias EdgeId = Graph.EdgeId;
53 alias VertexId = Graph.VertexId;
54 alias InputEdgeId = Graph.InputEdgeId;
55 alias InputEdgeIdSetId = Graph.InputEdgeIdSetId;
56 
57 alias PolygonModel = S2BooleanOperation.PolygonModel;
58 alias PolylineModel = S2BooleanOperation.PolylineModel;
59 
60 // A collection of special InputEdgeIds that allow the GraphEdgeClipper state
61 // modifications to be inserted into the list of edge crossings.
62 private enum InputEdgeId SET_INSIDE = -1;
63 private enum InputEdgeId SET_INVERT_B = -2;
64 private enum InputEdgeId SET_REVERSE_A = -3;
65 
66 /**
67  * This class implements boolean operations (intersection, union, difference,
68  * and symmetric difference) for regions whose boundaries are defined by
69  * geodesic edges.
70  *
71  * S2BooleanOperation operates on exactly two input regions at a time.  Each
72  * region is represented as an S2ShapeIndex and may contain any number of
73  * points, polylines, and polygons.  The region is essentially the union of
74  * these objects, except that polygon interiors must be disjoint from all
75  * other geometry (including other polygon interiors).  If the input geometry
76  * for a region does not meet this condition, it can be normalized by
77  * computing its union first.  Note that points or polylines are allowed to
78  * coincide with the boundaries of polygons.
79  *
80  * Degeneracies are supported.  A polygon loop or polyline may consist of a
81  * single edge from a vertex to itself, and polygons may contain "sibling
82  * pairs" consisting of an edge and its corresponding reverse edge.  Polygons
83  * must not have any duplicate edges (due to the requirement that polygon
84  * interiors are disjoint), but polylines may have duplicate edges or can even
85  * be self-intersecting.
86  *
87  * Points and polyline edges are treated as multisets: if the same point or
88  * polyline edge appears multiple times in the input, it will appear multiple
89  * times in the output.  For example, the union of a point with an identical
90  * point consists of two points.  This feature is useful for modeling large
91  * sets of points or polylines as a single region while maintaining their
92  * distinct identities, even when the points or polylines intersect each
93  * other.  It is also useful for reconstructing polylines that loop back on
94  * themselves.  If duplicate geometry is not desired, it can be merged by
95  * GraphOptions::DuplicateEdges::MERGE in the S2Builder output layer.
96  *
97  * Polylines are always considered to be directed.  Polyline edges between the
98  * same pair of vertices are defined to intersect even if the two edges are in
99  * opposite directions.  (Undirected polylines can be modeled by specifying
100  * GraphOptions::EdgeType::UNDIRECTED in the S2Builder output layer.)
101  *
102  * The output of each operation is sent to an S2Builder::Layer provided by the
103  * client.  This allows clients to build any representation of the geometry
104  * they choose.  It also allows the client to do additional postprocessing of
105  * the output before building data structures; for example, the client can
106  * easily discard degeneracies or convert them to another data type.
107  *
108  * The boundaries of polygons and polylines can be modeled as open, semi-open,
109  * or closed.  Polyline boundaries are controlled by the PolylineModel class,
110  * whose options are as follows:
111  *
112  *  - In the OPEN model, polylines do not contain their first or last vertex.
113  *
114  *  - In the SEMI_OPEN model, polylines contain vertices except the last.
115  *    Therefore if one polyline starts where another polyline stops, the two
116  *    polylines do not intersect.
117  *
118  *  - In the CLOSED model, polylines contain all of their vertices.
119  *
120  * When multiple polylines are present, they are processed independently and
121  * have no effect on each other.  For example, in the OPEN boundary model the
122  * polyline ABC contains the vertex B, while set of polylines {AB, BC} does
123  * not.  (If you want to treat the polylines as a union instead, with
124  * boundaries merged according to the "mod 2" rule, this can be achieved by
125  * reassembling the edges into maximal polylines using S2PolylineVectorLayer
126  * with EdgeType::UNDIRECTED, DuplicateEdges::MERGE, and PolylineType::WALK.)
127  *
128  * Polygon boundaries are controlled by the PolygonModel class, which has the
129  * following options:
130  *
131  *  - In the OPEN model, polygons do not contain their vertices or edges.
132  *    This implies that a polyline that follows the boundary of a polygon will
133  *    not intersect it.
134  *
135  *  - In the SEMI_OPEN model, polygon point containment is defined such that
136  *    if several polygons tile the region around a vertex, then exactly one of
137  *    those polygons contains that vertex.  Similarly polygons contain all of
138  *    their edges, but none of their reversed edges.  This implies that a
139  *    polyline and polygon edge with the same endpoints intersect if and only
140  *    if they are in the same direction.  (This rule ensures that if a
141  *    polyline is intersected with a polygon and its complement, the two
142  *    resulting polylines do not have any edges in common.)
143  *
144  *  - In the CLOSED model, polygons contain all their vertices, edges, and
145  *    reversed edges.  This implies that a polyline that shares an edge (in
146  *    either direction) with a polygon is defined to intersect it.  Similarly,
147  *    this is the only model where polygons that touch at a vertex or along an
148  *    edge intersect.
149  *
150  * Note that PolylineModel and PolygonModel are defined as separate classes in
151  * order to allow for possible future extensions.
152  *
153  * Operations between geometry of different dimensions are defined as follows:
154  *
155  *  - For UNION, the higher-dimensional shape always wins.  For example the
156  *    union of a closed polygon A with a polyline B that coincides with the
157  *    boundary of A consists only of the polygon A.
158  *
159  *  - For INTERSECTION, the lower-dimensional shape always wins.  For example,
160  *    the intersection of a closed polygon A with a point B that coincides
161  *    with a vertex of A consists only of the point B.
162  *
163  *  - For DIFFERENCE, higher-dimensional shapes are not affected by
164  *    subtracting lower-dimensional shapes.  For example, subtracting a point
165  *    or polyline from a polygon A yields the original polygon A.  This rule
166  *    exists because in general, it is impossible to represent the output
167  *    using the specified boundary model(s).  (Consider subtracting one vertex
168  *    from a PolylineModel::CLOSED polyline, or subtracting one edge from a
169  *    PolygonModel::CLOSED polygon.)  If you want to perform operations like
170  *    this, consider representing all boundaries explicitly (topological
171  *    boundaries) using OPEN boundary models.  Another option for polygons is
172  *    to subtract a degenerate loop, which yields a polygon with a degenerate
173  *    hole (see S2LaxPolygonShape).
174  *
175  * Note that in the case of Precision::EXACT operations, the above remarks
176  * only apply to the output before snapping.  Snapping may cause nearby
177  * distinct edges to become coincident, e.g. a polyline may become coincident
178  * with a polygon boundary.  However also note that S2BooleanOperation is
179  * perfectly happy to accept such geometry as input.
180  *
181  * Note the following differences between S2BooleanOperation and the similar
182  * S2MultiBooleanOperation class:
183  *
184  *  - S2BooleanOperation operates on exactly two regions at a time, whereas
185  *    S2MultiBooleanOperation operates on any number of regions.
186  *
187  *  - S2BooleanOperation is potentially much faster when the input is already
188  *    represented as S2ShapeIndexes.  The algorithm is output sensitive and is
189  *    often sublinear in the input size.  This can be a big advantage if, say,
190  *
191  *  - S2BooleanOperation supports exact predicates and the corresponding
192  *    exact operations (i.e., operations that are equivalent to computing the
193  *    exact result and then snap rounding it).
194  *
195  *  - S2MultiBooleanOperation has better error guarantees when there are many
196  *    regions, since it requires only one snapping operation for any number of
197  *    input regions.
198  *
199  * Example usage:
200  *   S2ShapeIndex a, b;  // Input geometry, e.g. containing polygons.
201  *   S2Polygon polygon;  // Output geometry.
202  *   S2BooleanOperation::Options options;
203  *   options.set_snap_function(snap_function);
204  *   S2BooleanOperation op(S2BooleanOperation::OpType::INTERSECTION,
205  *                         absl::make_unique<S2PolygonLayer>(&polygon),
206  *                         options);
207  *   S2Error error;
208  *   if (!op.Build(a, b, &error)) {
209  *     LOG(ERROR) << error;
210  *     ...
211  *   }
212  *
213  * If the output includes objects of different dimensions, they can be
214  * assembled into different layers with code like this:
215  *
216  *   vector<S2Point> points;
217  *   vector<unique_ptr<S2Polyline>> polylines;
218  *   S2Polygon polygon;
219  *   S2BooleanOperation op(
220  *       S2BooleanOperation::OpType::UNION,
221  *       absl::make_unique<s2builderutil::PointVectorLayer>(&points),
222  *       absl::make_unique<s2builderutil::S2PolylineVectorLayer>(&polylines),
223  *       absl::make_unique<S2PolygonLayer>(&polygon));
224  */
225 class S2BooleanOperation {
226 public:
227   /// The supported operation types.
228   enum OpType {
229     UNION,                // Contained by either region.
230     INTERSECTION,         // Contained by both regions.
231     DIFFERENCE,           // Contained by the first region but not the second.
232     SYMMETRIC_DIFFERENCE  // Contained by one region but not the other.
233   }
234 
235   /// Translates OpType to one of the strings above.
236   // Use std.conv.to!string intead.
237   //static const char* OpTypeToString(OpType op_type);
238 
239   /// Defines whether polygons are considered to contain their vertices and/or
240   /// edges (see definitions above).
241   enum PolygonModel { OPEN, SEMI_OPEN, CLOSED }
242 
243   /// Defines whether polylines are considered to contain their endpoints
244   /// (see definitions above).
245   enum PolylineModel { OPEN, SEMI_OPEN, CLOSED }
246 
247   /**
248    * With Precision::EXACT, the operation is evaluated using the exact input
249    * geometry.  Predicates that use this option will produce exact results;
250    * for example, they can distinguish between a polyline that barely
251    * intersects a polygon from one that barely misses it.  Constructive
252    * operations (ones that yield new geometry, as opposed to predicates) are
253    * implemented by computing the exact result and then snap rounding it
254    * according to the given snap_function() (see below).  This is as close as
255    * it is possible to get to the exact result while requiring that vertex
256    * coordinates have type "double".
257    *
258    * With Precision::SNAPPED, the input regions are snapped together *before*
259    * the operation is evaluated.  So for example, two polygons that overlap
260    * slightly will be treated as though they share a common boundary, and
261    * similarly two polygons that are slightly separated from each other will
262    * be treated as though they share a common boundary.  Snapped results are
263    * useful for dealing with points, since in S2 the only points that lie
264    * exactly on a polyline or polygon edge are the endpoints of that edge.
265    *
266    * Conceptually, the difference between these two options is that with
267    * Precision::SNAPPED, the inputs are snap rounded (together), whereas with
268    * Precision::EXACT only the result is snap rounded.
269    */
270   enum Precision { EXACT, SNAPPED }
271 
272   /**
273    * SourceId identifies an edge from one of the two input S2ShapeIndexes.
274    * It consists of a region id (0 or 1), a shape id within that region's
275    * S2ShapeIndex, and an edge id within that shape.
276    */
277   struct SourceId {
278   public:
279     this(int region_id, int shape_id, int edge_id) {
280       _regionId = region_id;
281       _shapeId = shape_id;
282       _edgeId = edge_id;
283     }
284 
285     this(int special_edge_id) {
286       _regionId = 0;
287       _shapeId = 0;
288       _edgeId = special_edge_id;
289     }
290 
291     int regionId() const {
292       return _regionId;
293     }
294 
295     int shapeId() const {
296       return _shapeId;
297     }
298 
299     int edgeId() const {
300       return _edgeId;
301     }
302 
303     bool opEquals(in SourceId other) const {
304       return _regionId == other._regionId
305           && _shapeId == other._shapeId
306           && _edgeId == other._edgeId;
307     }
308 
309     int opCmp(in SourceId other) const {
310       import std.typecons;
311       return tuple(_regionId, _shapeId, _edgeId)
312           .opCmp(tuple(other._regionId, other._shapeId, other._edgeId));
313     }
314 
315     string toString() const {
316       return "SourceId(regionId=" ~ _regionId.to!string ~ ", _shapeId=" ~ _shapeId.to!string
317           ~ ", _edgeId=" ~ edgeId.to!string ~ ")";
318     }
319 
320   private:
321     mixin(bitfields!(
322             uint, "_regionId", 1,
323             uint, "_shapeId", 31));
324     int _edgeId = -1;
325   }
326 
327   final static class Options {
328   public:
329     this() {
330       this(new IdentitySnapFunction(S1Angle.zero()));
331     }
332 
333     /// Convenience constructor that calls set_snap_function().
334     this(in S2Builder.SnapFunction snap_function) {
335       _snapFunction = snap_function.clone();
336       _polygonModel = PolygonModel.SEMI_OPEN;
337       _polylineModel = PolylineModel.CLOSED;
338       _sourceIdLexicon = new ValueLexicon!SourceId();
339     }
340 
341     // Options may be assigned and copied.
342     this(in Options options) {
343       _snapFunction = options._snapFunction.clone();
344       _polygonModel = options._polygonModel;
345       _polylineModel = options._polylineModel;
346       _sourceIdLexicon = new ValueLexicon!SourceId();
347     }
348 
349     /**
350      * Specifies the function to be used for snap rounding.
351      *
352      * DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero())
353      * [This does no snapping and preserves all input vertices exactly unless
354      *  there are crossing edges, in which case the snap radius is increased
355      *  to the maximum intersection point error (S2::kIntersectionError.]
356      */
357     const(S2Builder.SnapFunction) snapFunction() const {
358       return _snapFunction;
359     }
360 
361     void setSnapFunction(in S2Builder.SnapFunction snap_function) {
362       _snapFunction = snap_function.clone();
363     }
364 
365     /**
366      * Defines whether polygons are considered to contain their vertices
367      * and/or edges.
368      *
369      * DEFAULT: PolygonModel::SEMI_OPEN
370      */
371     PolygonModel polygonModel() const {
372       return _polygonModel;
373     }
374 
375     void setPolygonModel(PolygonModel model) {
376       _polygonModel = model;
377     }
378 
379     /**
380      * Defines whether polylines are considered to contain their vertices.
381      *
382      * DEFAULT: PolylineModel::CLOSED
383      */
384     PolylineModel polylineModel() const {
385         return _polylineModel;
386     }
387 
388     void setPolylineModel(PolylineModel model) {
389       _polylineModel = model;
390     }
391 
392     /**
393      * Specifies whether the operation should use the exact input geometry
394      * (Precision::EXACT), or whether the two input regions should be snapped
395      * together first (Precision::SNAPPED).
396      *
397      * DEFAULT: Precision::EXACT
398      */
399     Precision precision() const {
400       return _precision;
401     }
402 
403     void setPrecision(Precision precision) {
404       _precision = precision;
405     }
406 
407     /**
408      * If true, the input geometry is interpreted as representing nearby
409      * geometry that has been snapped or simplified.  It then outputs a
410      * conservative result based on the value of polygon_model() and
411      * polyline_model().  For the most part, this only affects the handling of
412      * degeneracies.
413      *
414      * - If the model is OPEN, the result is as open as possible.  For
415      *   example, the intersection of two identical degenerate shells is empty
416      *   under PolygonModel::OPEN because they could have been disjoint before
417      *   snapping.  Similarly, two identical degenerate polylines have an
418      *   empty intersection under PolylineModel::OPEN.
419      *
420      * - If the model is CLOSED, the result is as closed as possible.  In the
421      *   case of the DIFFERENCE operation, this is equivalent to evaluating
422      *   A - B as Closure(A) - Interior(B).  For other operations, it affects
423      *   only the handling of degeneracies.  For example, the union of two
424      *   identical degenerate holes is empty under PolygonModel::CLOSED
425      *   (i.e., the hole disappears) because the holes could have been
426      *   disjoint before snapping.
427      *
428      * - If the model is SEMI_OPEN, the result is as degenerate as possible.
429      *   New degeneracies will not be created, but all degeneracies that
430      *   coincide with the opposite region's boundary are retained unless this
431      *   would cause a duplicate polygon edge to be created.  This model is
432      *   is very useful for working with input data that has both positive and
433      *   negative degeneracies (i.e., degenerate shells and holes).
434      *
435      * DEFAULT: false
436      */
437     bool conservativeOutput() const {
438       return _conservative;
439     }
440 
441     void setConservativeOutput(bool conservative) {
442       _conservative = conservative;
443     }
444 
445     /**
446      * If specified, then each output edge will be labelled with one or more
447      * SourceIds indicating which input edge(s) it corresponds to.  This
448      * can be useful if your input geometry has additional data that needs to
449      * be propagated from the input to the output (e.g., elevations).
450      *
451      * You can access the labels by using an S2Builder::Layer type that
452      * supports labels, such as S2PolygonLayer.  The layer outputs a
453      * "label_set_lexicon" and an "label_set_id" for each edge.  You can then
454      * look up the source information for each edge like this:
455      *
456      * for (int32 label : label_set_lexicon.id_set(label_set_id)) {
457      *   const SourceId& src = source_id_lexicon.value(label);
458      *   // region_id() specifies which S2ShapeIndex the edge is from (0 or 1).
459      *   DoSomething(src.region_id(), src.shape_id(), src.edge_id());
460      * }
461      *
462      * DEFAULT: nullptr
463      */
464     const(ValueLexicon!SourceId) sourceIdLexicon() const {
465       return _sourceIdLexicon;
466     }
467 
468     void setSourceIdLexicon(ValueLexicon!SourceId source_id_lexicon) {
469       _sourceIdLexicon = source_id_lexicon;
470     }
471 
472   private:
473     S2Builder.SnapFunction _snapFunction;
474     PolygonModel _polygonModel;
475     PolylineModel _polylineModel;
476     Precision _precision;
477     bool _conservative;
478     ValueLexicon!SourceId _sourceIdLexicon;
479   }
480 
481   this(OpType op_type, Layer layer, Options options = null) {
482     _opType = op_type;
483     _options = options is null ? new Options() : options;
484     _resultEmpty = null;
485     _layers ~= layer;
486   }
487 
488   /**
489    * Specifies that the output boundary edges should be sent to three
490    * different layers according to their dimension.  Points (represented by
491    * degenerate edges) are sent to layer 0, polyline edges are sent to
492    * layer 1, and polygon edges are sent to layer 2.
493    *
494    * The dimension of an edge is defined as the minimum dimension of the two
495    * input edges that produced it.  For example, the intersection of two
496    * crossing polyline edges is a considered to be a degenerate polyline
497    * rather than a point, so it is sent to layer 1.  Clients can easily
498    * reclassify such polylines as points if desired, but this rule makes it
499    * easier for clients that want to process point, polyline, and polygon
500    * inputs differently.
501    *
502    * The layers are always built in the order 0, 1, 2, and all arguments to
503    * the Build() calls are guaranteed to be valid until the last call returns.
504    * All Graph objects have the same set of vertices and the same lexicon
505    * objects, in order to make it easier to write classes that process all the
506    * edges in parallel.
507    */
508   this(OpType op_type, Layer[] layers, Options options = null) {
509     _opType = op_type;
510     _options = options is null ? new Options() : options;
511     _layers = layers;
512     _resultEmpty = null;
513   }
514 
515   OpType opType() const {
516     return _opType;
517   }
518 
519   /**
520    * Executes the given operation.  Returns true on success, and otherwise
521    * sets "error" appropriately.  (This class does not generate any errors
522    * itself, but the S2Builder::Layer might.)
523    */
524   bool build(S2ShapeIndex a, S2ShapeIndex b, ref S2Error error) {
525     _regions[0] = a;
526     _regions[1] = b;
527     return new Impl(this).build(error);
528   }
529 
530   /// Convenience method that returns true if the result of the given operation is empty.
531   static bool isEmpty(
532       OpType op_type, S2ShapeIndex a, S2ShapeIndex b, Options options = null) {
533     bool result_empty;
534     auto op = new S2BooleanOperation(
535         op_type, &result_empty, options is null ? new Options() : options);
536     S2Error error;
537     op.build(a, b, error);
538     debug enforce(error.ok());
539     return result_empty;
540   }
541 
542   /// Convenience method that returns true if A intersects B.
543   static bool intersects(S2ShapeIndex a, S2ShapeIndex b, Options options = null) {
544     return !isEmpty(OpType.INTERSECTION, b, a, options is null ? new Options() : options);
545   }
546 
547   /// Convenience method that returns true if A contains B, i.e., if the
548   /// difference (B - A) is empty.
549   static bool contains(S2ShapeIndex a, S2ShapeIndex b, Options options = null) {
550     return isEmpty(OpType.DIFFERENCE, b, a, options is null ? new Options() : options);
551   }
552 
553   /**
554    * Convenience method that returns true if the symmetric difference of A and
555    * B is empty.  (Note that A and B may still not be identical, e.g. A may
556    * contain two copies of a polyline while B contains one.)
557    */
558   static bool equals(S2ShapeIndex a, S2ShapeIndex b, Options options = null) {
559     return isEmpty(OpType.SYMMETRIC_DIFFERENCE, b, a, options is null ? new Options() : options);
560   }
561 
562 private:
563 
564   /// The actual implementation.
565   class Impl {
566   public:
567     this(S2BooleanOperation op) {
568       _op = op;
569       _indexCrossingsFirstRegionId = -1;
570     }
571 
572     bool build(ref S2Error error) {
573       error.clear();
574       if (isBooleanOutput()) {
575         // BuildOpType() returns true if and only if the result is empty.
576         *(_op._resultEmpty) = buildOpType(_op.opType());
577         return true;
578       }
579 
580       // TODO(ericv): Rather than having S2Builder split the edges, it would be
581       // faster to call AddVertex() in this class and have a new S2Builder
582       // option that increases the edge_snap_radius_ to account for errors in
583       // the intersection point (the way that split_crossing_edges does).
584       auto options = new S2Builder.Options(_op._options.snapFunction());
585       options.setSplitCrossingEdges(true);
586 
587       // TODO(ericv): Ideally idempotent() should be true, but existing clients
588       // expect vertices closer than the full "snap_radius" to be snapped.
589       options.setIdempotent(false);
590       _builder = new S2Builder(options);
591       _builder.startLayer(new EdgeClippingLayer(_op._layers, &_inputDimensions, &_inputCrossings));
592 
593       // Polygons with no edges are assumed to be empty.  It is the responsibility
594       // of clients to fix this if desired (e.g. S2Polygon has code for this).
595       //
596       // TODO(ericv): Implement a predicate that can determine whether a
597       // degenerate polygon is empty or full based on the input S2ShapeIndexes.
598       // (It is possible to do this 100% robustly, but tricky.)
599       _builder.addIsFullPolygonPredicate(&isFullPolygonNever);
600       buildOpType(_op.opType());
601       return _builder.build(error);
602     }
603 
604   private:
605     struct CrossingIterator {
606     public:
607       /**
608        * Creates an iterator over crossing edge pairs (a, b) where "b" is an edge
609        * from "b_index".  "crossings_complete" indicates that "crossings" contains
610        * all edge crossings between the two regions (rather than a subset).
611        */
612       this(S2ShapeIndex b_index, IndexCrossings crossings, bool crossings_complete) {
613         _bIndex = b_index;
614         _crossings = crossings;
615         _bShapeId = -1;
616         _crossingsComplete = crossings_complete;
617         update();
618       }
619 
620       void next() {
621         _crossings.popFront();
622         update();
623       }
624 
625       bool done(ShapeEdgeId id) const {
626         return aId() != id;
627       }
628 
629       /// True if all edge crossings are available (see above).
630       bool crossingsComplete() const {
631         return _crossingsComplete;
632       }
633 
634       /// True if this crossing occurs at a point interior to both edges.
635       bool isInteriorCrossing() const {
636         return cast(bool) _crossings[0].isInteriorCrossing;
637       }
638 
639       /// Equal to S2::VertexCrossing(a_edge, b_edge), provided that a_edge and
640       /// b_edge have exactly one vertex in common and neither edge is degenerate.
641       bool isVertexCrossing() const {
642         return cast(bool) _crossings[0].isVertexCrossing;
643       }
644 
645       /// True if a_edge crosses b_edge from left to right (for interior crossings).
646       bool leftToRight() const {
647         return cast(bool) _crossings[0].leftToRight;
648       }
649 
650       ShapeEdgeId aId() const {
651         return _crossings[0].a;
652       }
653 
654       ShapeEdgeId bId() const {
655         return _crossings[0].b;
656       }
657 
658       inout(S2ShapeIndex) bIndex() inout {
659         return _bIndex;
660       }
661 
662       inout(S2Shape) bShape() inout {
663         return _bShape;
664       }
665 
666       int bDimension() const {
667         return _bDimension;
668       }
669 
670       int bShapeId() const {
671         return _bShapeId;
672       }
673 
674       int bEdgeId() const {
675         return bId().edgeId;
676       }
677 
678       S2Shape.Edge bEdge() const {
679         return _bShape.edge(bEdgeId());  // Opportunity to cache this.
680       }
681 
682       /// Information about the chain to which an edge belongs.
683       struct ChainInfo {
684         int chainId;   // chain id
685         int start;     // starting edge id
686         int limit;     // limit edge id
687       }
688 
689       /// Returns a description of the chain to which the current B edge belongs.
690       ChainInfo bChainInfo() {
691         if (_bInfo.chainId < 0) {
692           _bInfo.chainId = bShape().chainPosition(bEdgeId()).chainId;
693           auto chain = bShape().chain(_bInfo.chainId);
694           _bInfo.start = chain.start;
695           _bInfo.limit = chain.start + chain.length;
696         }
697         return _bInfo;
698       }
699 
700     private:
701       /// Updates information about the B shape whenever it changes.
702       void update() {
703         if (aId() != SENTINEL && bId().shapeId != _bShapeId) {
704           _bShapeId = bId().shapeId;
705           _bShape = _bIndex.shape(_bShapeId);
706           _bDimension = _bShape.dimension();
707           _bInfo.chainId = -1;  // Computed on demand.
708         }
709       }
710 
711       S2ShapeIndex _bIndex;
712       IndexCrossings _crossings;
713       S2Shape _bShape;
714       int _bShapeId;
715       int _bDimension;
716       ChainInfo _bInfo;  // Computed on demand.
717       bool _crossingsComplete;
718     }
719 
720     /**
721      * CrossingProcessor is a helper class that processes all the edges from one
722      * region that cross a specific edge of the other region.  It outputs the
723      * appropriate edges to an S2Builder, and outputs other information required
724      * by GraphEdgeClipper to the given vectors.
725      */
726     class CrossingProcessor {
727     public:
728 
729       /**
730        * Prepares to build output for the given polygon and polyline boundary
731        * models.  Edges are emitted to "builder", while other auxiliary data is
732        * appended to the given vectors.
733        *
734        * If a predicate is being evaluated (i.e., we do not need to construct the
735        * actual result), then "builder" and the various output vectors should all
736        * be nullptr.
737        */
738       this(
739           in PolygonModel polygon_model, in PolylineModel polyline_model, S2Builder builder,
740           byte[]* input_dimensions, InputEdgeCrossings* input_crossings) {
741         _polygonModel = polygon_model;
742         _polylineModel = polyline_model;
743         _builder = builder;
744         _inputDimensions = input_dimensions;
745         _inputCrossings = input_crossings;
746         _prevInside = false;
747         _sourceIdMap = new SourceIdMap();
748       }
749 
750       /**
751        * Starts processing edges from the given region.  "invert_a", "invert_b",
752        * and "invert_result" indicate whether region A, region B, and/or the
753        * result should be inverted, which allows operations such as union and
754        * difference to be implemented.  For example, union is ~(~A & ~B).
755        *
756        * This method should be called in pairs, once to process the edges from
757        * region A and once to process the edges from region B.
758        */
759       void startBoundary(int a_region_id, bool invert_a, bool invert_b, bool invert_result) {
760         _aRegionId = a_region_id;
761         _bRegionId = 1 - a_region_id;
762         _invertA = invert_a;
763         _invertB = invert_b;
764         _invertResult = invert_result;
765         _isUnion = invert_b && invert_result;
766 
767         // Specify to GraphEdgeClipper how these edges should be clipped.
768         setClippingState(SET_REVERSE_A, invert_a != invert_result);
769         setClippingState(SET_INVERT_B, invert_b);
770       }
771 
772       /// Starts processing edges from the given shape.
773       void startShape(S2Shape a_shape) {
774         _aShape = a_shape;
775         _aDimension = a_shape.dimension();
776       }
777 
778       /// Starts processing edges from the given chain.
779       void startChain(int chain_id, S2Shape.Chain chain, bool inside) {
780         _chainId = chain_id;
781         _chainStart = chain.start;
782         _chainLimit = chain.start + chain.length;
783         _inside = inside;
784         _v0EmittedMaxEdgeId = chain.start - 1;  // No edges emitted yet.
785         _chainV0Emitted = false;
786       }
787 
788       /**
789        * Processes the given edge "a_id".  "it" should be positioned to the set of
790        * edges from the other region that cross "a_id" (if any).
791        *
792        * Supports "early exit" in the case of boolean results by returning false
793        * as soon as the result is known to be non-empty.
794        */
795       bool processEdge(ShapeEdgeId a_id, ref CrossingIterator it) {
796         // chain_edge() is faster than edge() when there are multiple chains.
797         auto a = _aShape.chainEdge(_chainId, a_id.edgeId - _chainStart);
798         if (_aDimension == 0) {
799           return processEdge0(a_id, a, it);
800         } else if (_aDimension == 1) {
801           return processEdge1(a_id, a, it);
802         } else {
803           debug enforce(_aDimension == 2);
804           return processEdge2(a_id, a, it);
805         }
806       }
807 
808       /**
809        * This method should be called after each pair of calls to StartBoundary.
810        * (The only operation that processes more than one pair of boundaries is
811        * SYMMETRIC_DIFFERENCE, which computes the union of A-B and B-A.)
812        *
813        * Resets the state of the CrossingProcessor.
814        *
815        * Translates the temporary representation of crossing edges (SourceId) into
816        * the format expected by EdgeClippingLayer (InputEdgeId).
817        */
818       void doneBoundaryPair() {
819         // Add entries that translate the "special" crossings.
820         _sourceIdMap[SourceId(SET_INSIDE)] = SET_INSIDE;
821         _sourceIdMap[SourceId(SET_INVERT_B)] = SET_INVERT_B;
822         _sourceIdMap[SourceId(SET_REVERSE_A)] = SET_REVERSE_A;
823         (*_inputCrossings).reserve(_inputCrossings.length + _sourceEdgeCrossings.length);
824         foreach (tmp; _sourceEdgeCrossings) {
825           auto eqRange = _sourceIdMap.equalRange(tmp[1][0]);
826           debug enforce(!eqRange.empty());
827           *_inputCrossings ~= tuple(tmp[0], CrossingInputEdge(eqRange.front.value, tmp[1][1]));
828         }
829         _sourceEdgeCrossings.length = 0;
830         _sourceIdMap.clear();
831       }
832 
833       /**
834        * Indicates whether the point being processed along the current edge chain
835        * is in the polygonal interior of the opposite region, using semi-open
836        * boundaries.  If "invert_b_" is true then this field is inverted.
837        *
838        * This value along with the set of incident edges can be used to compute
839        * whether the opposite region contains this point under any of the
840        * supported boundary models (PolylineModel::CLOSED, etc).
841        */
842       bool inside() const {
843         return _inside;
844       }
845 
846     private:
847       // SourceEdgeCrossing represents an input edge that crosses some other
848       // edge; it crosses the edge from left to right iff the second parameter
849       // is "true".
850       alias SourceEdgeCrossing = Tuple!(SourceId, bool);
851 
852       /**
853        * PointCrossingResult describes the relationship between a point from region A
854        * and a set of crossing edges from region B.  For example, "matches_polygon"
855        * indicates whether a polygon vertex from region B matches the given point.
856        */
857       struct PointCrossingResult {
858         // Note that "matches_polyline" is true only if the point matches a polyline
859         // vertex of B *and* the polyline contains that vertex, whereas
860         // "matches_polygon" is true if the point matches any polygon vertex.
861         bool matchesPoint;     // Matches point.
862         bool matchesPolyline;  // Matches contained polyline vertex.
863         bool matchesPolygon;   // Matches polygon vertex.
864       }
865 
866       /**
867        * EdgeCrossingResult describes the relationship between an edge from region A
868        * ("a_edge") and a set of crossing edges from region B.  For example,
869        * "matches_polygon" indicates whether "a_edge" matches a polygon edge from
870        * region B.
871        */
872       struct EdgeCrossingResult {
873         // These fields indicate that "a_edge" exactly matches an edge of B.
874         bool matchesPolyline;     // Matches polyline edge (either direction).
875         bool matchesPolygon;      // Matches polygon edge (same direction).
876         bool matchesSibling;      // Matches polygon edge (reverse direction).
877 
878         // These fields indicate that a vertex of "a_edge" matches a polyline vertex
879         // of B *and* the polyline contains that vertex.
880         bool a0MatchesPolyline;  // Start vertex matches contained polyline vertex.
881         bool a1MatchesPolyline;  // End vertex matches contained polyline vertex.
882 
883         // These fields indicate that a vertex of "a_edge" matches a polygon vertex
884         // of B.  (Unlike with polylines, the polygon may not contain that vertex.)
885         bool a0MatchesPolygon;   // Start vertex matches polygon vertex.
886         bool a1MatchesPolygon;   // End vertex matches polygon vertex.
887 
888         // These fields count the number of edge crossings at the start vertex, end
889         // vertex, and interior of "a_edge".
890         int a0Crossings;          // Count of polygon crossings at start vertex.
891         int a1Crossings;          // Count of polygon crossings at end vertex.
892         int interiorCrossings;    // Count of polygon crossings in edge interior.
893       }
894 
895       InputEdgeId inputEdgeId() const {
896         return cast(int) _inputDimensions.length;
897       }
898 
899       /**
900        * Returns true if the edges on either side of the first vertex of the
901        * current edge have not been emitted.
902        *
903        * REQUIRES: This method is called just after updating "inside_" for "v0".
904        */
905       bool isV0Isolated(ShapeEdgeId a_id) const {
906         return !_inside && _v0EmittedMaxEdgeId < a_id.edgeId;
907       }
908 
909       /**
910        * Returns true if "a_id" is the last edge of the current chain, and the
911        * edges on either side of the last vertex have not been emitted (including
912        * the possibility that the chain forms a loop).
913        */
914       bool isChainLastVertexIsolated(ShapeEdgeId a_id) const {
915         return (a_id.edgeId == _chainLimit - 1 && !_chainV0Emitted
916             && _v0EmittedMaxEdgeId <= a_id.edgeId);
917       }
918 
919       /// Returns true if the given polyline edge contains "v0", taking into
920       /// account the specified PolylineModel.
921       bool polylineContainsV0(int edge_id, int chain_start) const {
922         return (_polylineModel != PolylineModel.OPEN || edge_id > chain_start);
923       }
924 
925       void addCrossing(SourceEdgeCrossing crossing) {
926         _sourceEdgeCrossings ~= tuple(inputEdgeId(), crossing);
927       }
928 
929       void setClippingState(InputEdgeId parameter, bool state) {
930         addCrossing(SourceEdgeCrossing(SourceId(parameter), state));
931       }
932 
933       /// Supports "early exit" in the case of boolean results by returning false
934       /// as soon as the result is known to be non-empty.
935       bool addEdge(ShapeEdgeId a_id, in S2Shape.Edge a, int dimension, int interior_crossings) {
936         if (_builder is null) return false;  // Boolean output.
937         if (interior_crossings > 0) {
938           // Build a map that translates temporary edge ids (SourceId) to
939           // the representation used by EdgeClippingLayer (InputEdgeId).
940           auto src_id = SourceId(_aRegionId, a_id.shapeId, a_id.edgeId);
941           _sourceIdMap[src_id] = inputEdgeId();
942         }
943         // Set the GraphEdgeClipper's "inside" state to match ours.
944         if (_inside != _prevInside) setClippingState(SET_INSIDE, _inside);
945         *_inputDimensions ~= cast(byte) dimension;
946         _builder.addEdge(a.v0, a.v1);
947         _inside ^= (interior_crossings & 1);
948         _prevInside = _inside;
949         return true;
950       }
951 
952       /// Supports "early exit" in the case of boolean results by returning false
953       /// as soon as the result is known to be non-empty.
954       bool addPointEdge(in S2Point p, int dimension) {
955         if (_builder is null) return false;  // Boolean output.
956         if (!_prevInside) setClippingState(SET_INSIDE, true);
957         *_inputDimensions ~= cast(byte) dimension;
958         _builder.addEdge(p, p);
959         _prevInside = true;
960         return true;
961       }
962 
963       /**
964        * Processes an edge of dimension 0 (i.e., a point) from region A.
965        *
966        * Supports "early exit" in the case of boolean results by returning false
967        * as soon as the result is known to be non-empty.
968        */
969       bool processEdge0(ShapeEdgeId a_id, in S2Shape.Edge a, ref CrossingIterator it)
970       in {
971         assert(a.v0 == a.v1);
972       } do {
973         // When a region is inverted, all points and polylines are discarded.
974         if (_invertA != _invertResult) {
975           skipCrossings(a_id, it);
976           return true;
977         }
978         PointCrossingResult r = processPointCrossings(a_id, a.v0, it);
979 
980         // "contained" indicates whether the current point is inside the polygonal
981         // interior of the opposite region, using semi-open boundaries.
982         bool contained = _inside ^ _invertB;
983         if (r.matchesPolygon && _polygonModel != PolygonModel.SEMI_OPEN) {
984           contained = (_polygonModel == PolygonModel.CLOSED);
985         }
986         if (r.matchesPolyline) {
987           contained = true;
988         }
989 
990         // The output of UNION includes duplicate values, so ensure that points are
991         // not suppressed by other points.
992         if (r.matchesPoint && !_isUnion) {
993           contained = true;
994         }
995 
996         // Test whether the point is contained after region B is inverted.
997         // TODO: Resume here and find why it differs from the C++ version.
998         if (contained == _invertB) {
999           return true;  // Don't exit early.
1000         }
1001         return addPointEdge(a.v0, 0);
1002       }
1003 
1004       /**
1005        * Processes an edge of dimension 1 (i.e., a polyline edge) from region A.
1006        *
1007        * Supports "early exit" in the case of boolean results by returning false
1008        * as soon as the result is known to be non-empty.
1009        */
1010       bool processEdge1(ShapeEdgeId a_id, in S2Shape.Edge a, ref CrossingIterator it) {
1011         // When a region is inverted, all points and polylines are discarded.
1012         if (_invertA != _invertResult) {
1013           skipCrossings(a_id, it);
1014           return true;
1015         }
1016         // Evaluate whether the start vertex should belong to the output, in case it
1017         // needs to be emitted as an isolated vertex.
1018         EdgeCrossingResult r = processEdgeCrossings(a_id, a, it);
1019         bool a0_inside = isPolylineVertexInside(r.a0MatchesPolyline, r.a0MatchesPolygon);
1020 
1021         // Test whether the entire polyline edge should be emitted (or not emitted)
1022         // because it matches a polyline or polygon edge.
1023         _inside ^= (r.a0Crossings & 1);
1024         if (_inside != isPolylineEdgeInside(r)) {
1025           _inside ^= true;   // Invert the inside_ state.
1026           ++r.a1Crossings;  // Restore the correct (semi-open) state later.
1027         }
1028 
1029         // If neither edge adjacent to v0 was emitted, and this polyline contains
1030         // v0, and the other region contains v0, then emit an isolated vertex.
1031         if (isV0Isolated(a_id) && polylineContainsV0(a_id.edgeId, _chainStart) && a0_inside) {
1032           if (!addPointEdge(a.v0, 1)) {
1033             return false;
1034           }
1035         }
1036 
1037         // Test whether the entire edge or any part of it belongs to the output.
1038         if (_inside || r.interiorCrossings > 0) {
1039           // Note: updates "inside_" to correspond to the state just before a1.
1040           if (!addEdge(a_id, a, 1 /*dimension*/, r.interiorCrossings)) {
1041             return false;
1042           }
1043         }
1044         // Remember whether the edge portion just before "a1" was emitted, so that
1045         // we can decide whether "a1" need to be emitted as an isolated vertex.
1046         if (_inside) {
1047           _v0EmittedMaxEdgeId = a_id.edgeId + 1;
1048         }
1049 
1050         // Verify that edge crossings are being counted correctly.
1051         _inside ^= (r.a1Crossings & 1);
1052         if (it.crossingsComplete()) {
1053           debug enforce(
1054               makeS2ContainsPointQuery(it.bIndex()).contains(a.v1) == (_inside ^ _invertB));
1055         }
1056 
1057         // Special case to test whether the last vertex of a polyline should be
1058         // emitted as an isolated vertex.
1059         if (_polylineModel == PolylineModel.CLOSED && it.crossingsComplete()
1060             && isChainLastVertexIsolated(a_id)
1061             && isPolylineVertexInside(r.a1MatchesPolyline, r.a1MatchesPolygon)) {
1062           if (!addPointEdge(a.v1, 1)) return false;
1063         }
1064         return true;
1065       }
1066 
1067       /**
1068        * Processes an edge of dimension 2 (i.e., a polygon edge) from region A.
1069        *
1070        * Supports "early exit" in the case of boolean results by returning false
1071        * as soon as the result is known to be non-empty.
1072        */
1073       bool processEdge2(ShapeEdgeId a_id, in S2Shape.Edge a, ref CrossingIterator it) {
1074 
1075         // In order to keep only one copy of any shared polygon edges, we only
1076         // output shared edges when processing the second region.
1077         bool emit_shared = (_aRegionId == 1);
1078 
1079         // Degeneracies such as isolated vertices and sibling pairs can only be
1080         // created by intersecting CLOSED polygons or unioning OPEN polygons.
1081         bool emit_degenerate =
1082             (_polygonModel == PolygonModel.CLOSED && !_invertA && !_invertB)
1083             || (_polygonModel == PolygonModel.OPEN && _invertA && _invertB);
1084 
1085         EdgeCrossingResult r = processEdgeCrossings(a_id, a, it);
1086         debug enforce(!r.matchesPolyline);
1087         _inside ^= (r.a0Crossings & 1);
1088 
1089         // If only one region is inverted, matching/sibling relations are reversed.
1090         // TODO(ericv): Update the following code to handle degenerate loops.
1091         debug enforce(!r.matchesPolygon || !r.matchesSibling);
1092         if (_invertA != _invertB) swap(r.matchesPolygon, r.matchesSibling);
1093 
1094         // Test whether the entire polygon edge should be emitted (or not emitted)
1095         // because it matches a polygon edge or its sibling.
1096         bool new_inside = _inside;
1097 
1098         // Shared edge are emitted only while processing the second region.
1099         if (r.matchesPolygon) new_inside = emit_shared;
1100 
1101         // Sibling pairs are emitted only when degeneracies are desired.
1102         if (r.matchesSibling) new_inside = emit_degenerate;
1103         if (_inside != new_inside) {
1104           _inside ^= true;   // Invert the inside_ state.
1105           ++r.a1Crossings;  // Restore the correct (semi-open) state later.
1106         }
1107 
1108         // Test whether the first vertex of this edge should be emitted as an
1109         // isolated degenerate vertex.
1110         if (a_id.edgeId == _chainStart) {
1111           _chainV0Emitted = _inside;
1112         } else if (emit_shared && emit_degenerate && r.a0MatchesPolygon && isV0Isolated(a_id)) {
1113           if (!addPointEdge(a.v0, 2)) return false;
1114         }
1115 
1116         // Test whether the entire edge or any part of it belongs to the output.
1117         if (_inside || r.interiorCrossings > 0) {
1118           // Note: updates "inside_" to correspond to the state just before a1.
1119           if (!addEdge(a_id, a, 2 /*dimension*/, r.interiorCrossings)) {
1120             return false;
1121           }
1122         }
1123 
1124         // Remember whether the edge portion just before "a1" was emitted, so that
1125         // we can decide whether "a1" need to be emitted as an isolated vertex.
1126         if (_inside) _v0EmittedMaxEdgeId = a_id.edgeId + 1;
1127         _inside ^= (r.a1Crossings & 1);
1128 
1129         // Verify that edge crossings are being counted correctly.
1130         if (it.crossingsComplete()) {
1131           debug enforce(
1132               makeS2ContainsPointQuery(it.bIndex()).contains(a.v1) == (_inside ^ _invertB));
1133         }
1134 
1135         // Special case to test whether the last vertex of a loop should be emitted
1136         // as an isolated degenerate vertex.
1137         if (emit_shared && emit_degenerate && r.a1MatchesPolygon
1138             && isChainLastVertexIsolated(a_id)) {
1139           if (!addPointEdge(a.v1, 2)) return false;
1140         }
1141         return true;
1142       }
1143 
1144       /// Skip any crossings that were not needed to determine the result.
1145       void skipCrossings(ShapeEdgeId a_id, ref CrossingIterator it) {
1146         while (!it.done(a_id)) it.next();
1147       }
1148 
1149       /// Returns a summary of the relationship between a point from region A and
1150       /// a set of crossing edges from region B (see PointCrossingResult).
1151       PointCrossingResult processPointCrossings(
1152           ShapeEdgeId a_id, in S2Point a0, ref CrossingIterator it) const {
1153         PointCrossingResult r;
1154         for (; !it.done(a_id); it.next()) {
1155           if (it.bDimension() == 0) {
1156             r.matchesPoint = true;
1157           } else if (it.bDimension() == 1) {
1158             if (polylineEdgeContainsVertex(a0, it)) {
1159               r.matchesPolyline = true;
1160             }
1161           } else {
1162             r.matchesPolygon = true;
1163           }
1164         }
1165         return r;
1166       }
1167 
1168       /**
1169        * Returns a summary of the relationship between a test edge from region A and
1170        * a set of crossing edges from region B (see EdgeCrossingResult).
1171        *
1172        * NOTE(ericv): We could save a bit of work when matching polygon vertices by
1173        * passing in a flag saying whether this information is needed.  For example
1174        * if is only needed in ProcessEdge2 when (emit_shared && emit_degenerate).
1175        */
1176       EdgeCrossingResult processEdgeCrossings(
1177           ShapeEdgeId a_id, in S2Shape.Edge a, ref CrossingIterator it) {
1178         // TODO: Resume here, find out why the EdgeCrossingResult is different in D.
1179         EdgeCrossingResult r;
1180         if (it.done(a_id)) {
1181           return r;
1182         }
1183 
1184         // TODO(ericv): bool a_degenerate = (a.v0 == a.v1);
1185         for (; !it.done(a_id); it.next()) {
1186           // Polylines and polygons are not affected by point geometry.
1187           if (it.bDimension() == 0) continue;
1188           S2Shape.Edge b = it.bEdge();
1189           if (it.isInteriorCrossing()) {
1190             // The crossing occurs in the edge interior.  The condition below says
1191             // that (1) polyline crossings don't affect polygon output, and (2)
1192             // subtracting a crossing polyline from a polyline has no effect.
1193             if (_aDimension <= it.bDimension()
1194                 && !(_invertB != _invertResult && it.bDimension() == 1)) {
1195               auto src_id = SourceId(_bRegionId, it.bShapeId(), it.bEdgeId());
1196               addCrossing(tuple(src_id, it.leftToRight()));
1197             }
1198             r.interiorCrossings += (it.bDimension() == 1) ? 2 : 1;
1199           } else if (it.bDimension() == 1) {
1200             // Polygons are not affected by polyline geometry.
1201             if (_aDimension == 2) {
1202               continue;
1203             }
1204             if ((a.v0 == b.v0 && a.v1 == b.v1) || (a.v0 == b.v1 && a.v1 == b.v0)) {
1205               r.matchesPolyline = true;
1206             }
1207             if ((a.v0 == b.v0 || a.v0 == b.v1) && polylineEdgeContainsVertex(a.v0, it)) {
1208               r.a0MatchesPolyline = true;
1209             }
1210             if ((a.v1 == b.v0 || a.v1 == b.v1) && polylineEdgeContainsVertex(a.v1, it)) {
1211               r.a1MatchesPolyline = true;
1212             }
1213           } else {
1214             debug enforce(it.bDimension() == 2);
1215             if (a.v0 == b.v0 && a.v1 == b.v1) {
1216               ++r.a0Crossings;
1217               r.matchesPolygon = true;
1218             } else if (a.v0 == b.v1 && a.v1 == b.v0) {
1219               ++r.a0Crossings;
1220               r.matchesSibling = true;
1221             } else if (it.isVertexCrossing()) {
1222               if (a.v0 == b.v0 || a.v0 == b.v1) {
1223                 ++r.a0Crossings;
1224               } else {
1225                 ++r.a1Crossings;
1226               }
1227             }
1228             if (a.v0 == b.v0 || a.v0 == b.v1) {
1229               r.a0MatchesPolygon = true;
1230             }
1231             if (a.v1 == b.v0 || a.v1 == b.v1) {
1232               r.a1MatchesPolygon = true;
1233             }
1234           }
1235         }
1236         return r;
1237       }
1238 
1239       /**
1240        * Returns true if the current point being processed (which must be a polyline
1241        * vertex) is contained by the opposite region (after inversion if "invert_b_"
1242        * is true).  "matches_polyline" and "matches_polygon" indicate whether the
1243        * vertex matches a polyline/polygon vertex of the opposite region.
1244        */
1245       bool isPolylineVertexInside(bool matches_polyline, bool matches_polygon) const {
1246         // "contained" indicates whether the current point is inside the polygonal
1247         // interior of the opposite region using semi-open boundaries.
1248         bool contained = _inside ^ _invertB;
1249 
1250         // For UNION the output includes duplicate polylines.  The test below
1251         // ensures that isolated polyline vertices are not suppressed by other
1252         // polyline vertices in the output.
1253         if (matches_polyline && !_isUnion) {
1254           contained = true;
1255         } else if (matches_polygon && _polygonModel != PolygonModel.SEMI_OPEN) {
1256           contained = (_polygonModel == PolygonModel.CLOSED);
1257         }
1258         // Finally, invert the result if the opposite region should be inverted.
1259         return contained ^ _invertB;
1260       }
1261 
1262       /**
1263        * Returns true if the current polyline edge is contained by the opposite
1264        * region (after inversion if "invert_b_" is true).
1265        */
1266       bool isPolylineEdgeInside(in EdgeCrossingResult r) const {
1267         // "contained" indicates whether the current point is inside the polygonal
1268         // interior of the opposite region using semi-open boundaries.
1269         bool contained = _inside ^ _invertB;
1270         if (r.matchesPolyline && !_isUnion) {
1271           contained = true;
1272         } else if (r.matchesPolygon) {
1273           // In the SEMI_OPEN model, polygon sibling pairs cancel each other and
1274           // have no effect on point or edge containment.
1275           if (!(r.matchesSibling && _polygonModel == PolygonModel.SEMI_OPEN)) {
1276             contained = (_polygonModel != PolygonModel.OPEN);
1277           }
1278         } else if (r.matchesSibling) {
1279           contained = (_polygonModel == PolygonModel.CLOSED);
1280         }
1281         // Finally, invert the result if the opposite region should be inverted.
1282         return contained ^ _invertB;
1283       }
1284 
1285       /**
1286        * Returns true if the vertex "v" is contained by the polyline edge referred
1287        * to by the CrossingIterator "it", taking into account the PolylineModel.
1288        *
1289        * REQUIRES: it.b_dimension() == 1
1290        * REQUIRES: "v" is an endpoint of it.b_edge()
1291        */
1292       bool polylineEdgeContainsVertex(in S2Point v, ref CrossingIterator it) const
1293       in {
1294         assert(it.bDimension() == 1);
1295         assert(it.bEdge().v0 == v || it.bEdge().v1 == v);
1296       } do {
1297         // Closed polylines contain all their vertices.
1298         if (_polylineModel == PolylineModel.CLOSED) return true;
1299 
1300         // All interior polyline vertices are contained.
1301         // The last polyline vertex is contained iff the model is CLOSED.
1302         // The first polyline vertex is contained iff the model is not OPEN.
1303         // The test below is written such that usually b_edge() is not needed.
1304         const auto b_chain = it.bChainInfo();
1305         int b_edge_id = it.bEdgeId();
1306         return (b_edge_id < b_chain.limit - 1 || it.bEdge().v1 != v)
1307             && (polylineContainsV0(b_edge_id, b_chain.start) || it.bEdge().v0 != v);
1308       }
1309 
1310       // Constructor parameters:
1311 
1312       PolygonModel _polygonModel;
1313       PolylineModel _polylineModel;
1314 
1315       // The output of the CrossingProcessor consists of a subset of the input
1316       // edges that are emitted to "builder_", and some auxiliary information
1317       // that allows GraphEdgeClipper to determine which segments of those input
1318       // edges belong to the output.  The auxiliary information consists of the
1319       // dimension of each input edge, and set of input edges from the other
1320       // region that cross each input input edge.
1321       S2Builder _builder;
1322       byte[]* _inputDimensions;
1323       InputEdgeCrossings* _inputCrossings;
1324 
1325       // Fields set by StartBoundary:
1326 
1327       int _aRegionId, _bRegionId;
1328       bool _invertA, _invertB, _invertResult;
1329       bool _isUnion;  // True if this is a UNION operation.
1330 
1331       // Fields set by StartShape:
1332 
1333       S2Shape _aShape;
1334       int _aDimension;
1335 
1336       // Fields set by StartChain:
1337 
1338       int _chainId;
1339       int _chainStart;
1340       int _chainLimit;
1341 
1342       // Fields updated by ProcessEdge:
1343 
1344       alias SourceEdgeCrossings = Tuple!(InputEdgeId, SourceEdgeCrossing)[];
1345 
1346       /**
1347        * A temporary representation of input_crossings_ that is used internally
1348        * until all necessary edges from *both* polygons have been emitted to the
1349        * S2Builder.  This field is then converted by DoneBoundaryPair() into
1350        * the InputEdgeCrossings format expected by GraphEdgeClipper.
1351        *
1352        * The reason that we can't construct input_crossings_ directly is that it
1353        * uses InputEdgeIds to identify the edges from both polygons, and when we
1354        * are processing edges from the first polygon, InputEdgeIds have not yet
1355        * been assigned to the second polygon.  So instead this field identifies
1356        * edges from the first polygon using an InputEdgeId, and edges from the
1357        * second polygon using a (region_id, shape_id, edge_id) tuple (i.e., a
1358        * SourceId).
1359        *
1360        * All crossings are represented twice, once to indicate that an edge from
1361        * polygon 0 is crossed by an edge from polygon 1, and once to indicate that
1362        * an edge from polygon 1 is crossed by an edge from polygon 0.
1363        */
1364       SourceEdgeCrossings _sourceEdgeCrossings;
1365 
1366       alias SourceIdMap = BTreeMap!(SourceId, InputEdgeId);
1367 
1368       /**
1369        * A map that translates from SourceId (the (region_id, shape_id,
1370        * edge_id) triple that identifies an S2ShapeIndex edge) to InputEdgeId (the
1371        * sequentially increasing numbers assigned to input edges by S2Builder).
1372        */
1373       SourceIdMap _sourceIdMap;
1374 
1375       /**
1376        * Indicates whether the point being processed along the current edge chain
1377        * is in the polygonal interior of the opposite region, using semi-open
1378        * boundaries.  If "invert_b_" is true then this field is inverted.
1379        *
1380        * Equal to: b_index_.Contains(current point) ^ invert_b_
1381        */
1382       bool _inside;
1383 
1384       /**
1385        * The value of that "inside_" would have just before the end of the
1386        * previous edge added to S2Builder.  This value is used to determine
1387        * whether the GraphEdgeClipper state needs to be updated when jumping from
1388        * one edge chain to another.
1389        */
1390       bool _prevInside;
1391 
1392       /**
1393        * The maximum edge id of any edge in the current chain whose v0 vertex has
1394        * already been emitted.  This is used to determine when an isolated vertex
1395        * needs to be emitted, e.g. when two closed polygons share only a vertex.
1396        */
1397       int _v0EmittedMaxEdgeId;
1398 
1399       /**
1400        * True if the first vertex of the current chain has been emitted.  This is
1401        * used when processing loops in order to determine whether the first/last
1402        * vertex of the loop should be emitted as an isolated vertex.
1403        */
1404       bool _chainV0Emitted;
1405     }
1406 
1407     /**
1408      * An IndexCrossing represents a pair of intersecting S2ShapeIndex edges
1409      * ("a_edge" and "b_edge").  We store all such intersections because the
1410      * algorithm needs them twice, once when processing the boundary of region A
1411      * and once when processing the boundary of region B.
1412      */
1413     struct IndexCrossing {
1414       ShapeEdgeId a, b;
1415 
1416       mixin(bitfields!(
1417               /// True if S2::CrossingSign(a_edge, b_edge) > 0.
1418               uint,  "isInteriorCrossing", 1,
1419 
1420               // True if "a_edge" crosses "b_edge" from left to right.  Undefined if
1421               // is_interior_crossing is false.
1422               uint, "leftToRight", 1,
1423 
1424               // Equal to S2::VertexCrossing(a_edge, b_edge).  Undefined if "a_edge" and
1425               // "b_edge" do not share exactly one vertex or either edge is degenerate.
1426               uint, "isVertexCrossing", 1,
1427 
1428               // Unused bits.
1429               uint, "", 5));
1430 
1431       // All flags are "false" by default.
1432       this(ShapeEdgeId a, ShapeEdgeId b) {
1433         this.a = a;
1434         this.b = b;
1435         this.isInteriorCrossing = false;
1436         this.leftToRight = false;
1437         this.isVertexCrossing = false;
1438       }
1439 
1440       bool opEquals(in IndexCrossing x) const {
1441         return a == x.a && b == x.b;
1442       }
1443 
1444       int opCmp(in IndexCrossing x) const {
1445         // The compiler (2017) doesn't optimize the following as well:
1446         // return x.a < y.a || (x.a == y.a && x.b < y.b);
1447         if (a.shapeId != x.a.shapeId) return a.shapeId - x.a.shapeId;
1448         if (a.edgeId != x.a.edgeId) return a.edgeId - x.a.edgeId;
1449         if (b.shapeId != x.b.shapeId) return b.shapeId - x.b.shapeId;
1450         if (b.edgeId != x.b.edgeId) return b.edgeId - x.b.edgeId;
1451         return 0;
1452       }
1453     }
1454 
1455     alias IndexCrossings = IndexCrossing[];
1456 
1457     bool isBooleanOutput() const {
1458       return _op._resultEmpty !is null;
1459     }
1460 
1461     // All of the methods below support "early exit" in the case of boolean
1462     // results by returning "false" as soon as the result is known to be
1463     // non-empty.
1464 
1465     /**
1466      * Clips the boundary of A to the interior of the opposite region B and adds
1467      * the resulting edges to the output.  Optionally, any combination of region
1468      * A, region B, and the result may be inverted, which allows operations such
1469      * as union and difference to be implemented.
1470      *
1471      * Note that when an input region is inverted with respect to the output
1472      * (e.g., invert_a != invert_result), all polygon edges are reversed and all
1473      * points and polylines are discarded, since the complement of such objects
1474      * cannot be represented.  (If you want to compute the complement of points
1475      * or polylines, you can use S2LaxPolygonShape to represent your geometry as
1476      * degenerate polygons instead.)
1477      *
1478      * This method must be called an even number of times (first to clip A to B
1479      * and then to clip B to A), calling DoneBoundaryPair() after each pair.
1480      *
1481      * Supports "early exit" in the case of boolean results by returning false
1482      * as soon as the result is known to be non-empty.
1483      */
1484     bool addBoundary(int a_region_id, bool invert_a, bool invert_b,
1485         bool invert_result,
1486         in ShapeEdgeId[] a_chain_starts,
1487         CrossingProcessor cp) {
1488       S2ShapeIndex a_index = _op._regions[a_region_id];
1489       S2ShapeIndex b_index = _op._regions[1 - a_region_id];
1490       if (!getIndexCrossings(a_region_id)) return false;
1491       cp.startBoundary(a_region_id, invert_a, invert_b, invert_result);
1492 
1493       // Walk the boundary of region A and build a list of all edge crossings.
1494       // We also keep track of whether the current vertex is inside region B.
1495       auto next_start_pos = 0; // a_chain_starts.begin();
1496       auto next_crossing =
1497           CrossingIterator(b_index, _indexCrossings, true /*crossings_complete*/);
1498       ShapeEdgeId next_id = min(a_chain_starts[next_start_pos], next_crossing.aId());
1499       while (next_id != SENTINEL) {
1500         int a_shape_id = next_id.shapeId;
1501         S2Shape a_shape = a_index.shape(a_shape_id);
1502         cp.startShape(a_shape);
1503         while (next_id.shapeId == a_shape_id) {
1504           // TODO(ericv): Special handling of dimension 0?  Can omit most of this
1505           // code, including the loop, since all chains are of length 1.
1506           int edge_id = next_id.edgeId;
1507           S2Shape.ChainPosition chain_position = a_shape.chainPosition(edge_id);
1508           int chain_id = chain_position.chainId;
1509           S2Shape.Chain chain = a_shape.chain(chain_id);
1510           bool start_inside = (next_id == a_chain_starts[next_start_pos]);
1511           if (start_inside) ++next_start_pos;
1512           cp.startChain(chain_id, chain, start_inside);
1513           int chain_limit = chain.start + chain.length;
1514           while (edge_id < chain_limit) {
1515             auto a_id = ShapeEdgeId(a_shape_id, edge_id);
1516             debug enforce(cp.inside() || next_crossing.aId() == a_id);
1517             if (!cp.processEdge(a_id, next_crossing)) {
1518               return false;
1519             }
1520             if (cp.inside()) {
1521               ++edge_id;
1522             } else if (next_crossing.aId().shapeId == a_shape_id
1523                 && next_crossing.aId().edgeId < chain_limit) {
1524               edge_id = next_crossing.aId().edgeId;
1525             } else {
1526               break;
1527             }
1528           }
1529           next_id = min(a_chain_starts[next_start_pos], next_crossing.aId());
1530         }
1531       }
1532       return true;
1533     }
1534 
1535     /**
1536      * Returns the first edge of each edge chain from "a_region_id" whose first
1537      * vertex is contained by opposite region's polygons (using the semi-open
1538      * boundary model).  Each input region and the result region are inverted as
1539      * specified (invert_a, invert_b, and invert_result) before testing for
1540      * containment.  The algorithm uses these "chain starts" in order to clip the
1541      * boundary of A to the interior of B in an output-senstive way.
1542      *
1543      * This method supports "early exit" in the case where a boolean predicate is
1544      * being evaluated and the algorithm discovers that the result region will be
1545      * non-empty.
1546      */
1547     bool getChainStarts(int a_region_id, bool invert_a, bool invert_b,
1548         bool invert_result, CrossingProcessor cp,
1549         ref ShapeEdgeId[] chain_starts) {
1550       S2ShapeIndex a_index = _op._regions[a_region_id];
1551       S2ShapeIndex b_index = _op._regions[1 - a_region_id];
1552 
1553       if (isBooleanOutput()) {
1554         // If boolean output is requested, then we use the CrossingProcessor to
1555         // determine whether the first edge of each chain will be emitted to the
1556         // output region.  This lets us terminate the operation early in many
1557         // cases.
1558         cp.startBoundary(a_region_id, invert_a, invert_b, invert_result);
1559       }
1560 
1561       // If region B has no two-dimensional shapes and is not inverted, then by
1562       // definition no chain starts are contained.  However if boolean output is
1563       // requested then we check for containment anyway, since as a side effect we
1564       // may discover that the result region is non-empty and terminate the entire
1565       // operation early.
1566       bool b_has_interior = hasInterior(b_index);
1567       if (b_has_interior || invert_b || isBooleanOutput()) {
1568         auto query = makeS2ContainsPointQuery(b_index);
1569         int num_shape_ids = a_index.numShapeIds();
1570         for (int shape_id = 0; shape_id < num_shape_ids; ++shape_id) {
1571           S2Shape a_shape = a_index.shape(shape_id);
1572           if (a_shape is null) continue;
1573 
1574           // If region A is being subtracted from region B, points and polylines
1575           // in region A can be ignored since these shapes never contribute to the
1576           // output (they can only remove edges from region B).
1577           if (invert_a != invert_result && a_shape.dimension() < 2) continue;
1578 
1579           if (isBooleanOutput()) cp.startShape(a_shape);
1580           int num_chains = a_shape.numChains();
1581           for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
1582             S2Shape.Chain chain = a_shape.chain(chain_id);
1583             if (chain.length == 0) continue;
1584             auto a = new ShapeEdge(shape_id, chain.start, a_shape.chainEdge(chain_id, 0));
1585             bool inside = (b_has_interior && query.contains(a.v0())) != invert_b;
1586             if (inside) {
1587               chain_starts ~= ShapeEdgeId(shape_id, chain.start);
1588             }
1589             if (isBooleanOutput()) {
1590               cp.startChain(chain_id, chain, inside);
1591               if (!processIncidentEdges(a, query, cp)) return false;
1592             }
1593           }
1594         }
1595       }
1596       chain_starts ~= SENTINEL;
1597       return true;
1598     }
1599 
1600     bool processIncidentEdges(
1601         in ShapeEdge a, S2ContainsPointQuery!S2ShapeIndex query, CrossingProcessor cp) {
1602       _tmpCrossings.length = 0;
1603       query.visitIncidentEdges(a.v0(), (in ShapeEdge b) {
1604             return addIndexCrossing(a, b, false /*is_interior*/, _tmpCrossings);
1605           });
1606       // Fast path for the common case where there are no incident edges.  We
1607       // return false (terminating early) if the first chain edge will be emitted.
1608       if (_tmpCrossings.empty()) {
1609         return !cp.inside();
1610       }
1611       // Otherwise we invoke the full CrossingProcessor logic to determine whether
1612       // the first chain edge will be emitted.
1613       if (_tmpCrossings.length > 1) {
1614         sort(_tmpCrossings);
1615         // VisitIncidentEdges() should not generate any duplicate values.
1616         debug enforce(findAdjacent(_tmpCrossings[]).empty());
1617       }
1618       _tmpCrossings ~= IndexCrossing(SENTINEL, SENTINEL);
1619       auto next_crossing =
1620           CrossingIterator(query.index(), _tmpCrossings, false /*crossings_complete*/);
1621       return cp.processEdge(a.id(), next_crossing);
1622     }
1623 
1624     static bool hasInterior(in S2ShapeIndex index) {
1625       for (int s = index.numShapeIds(); --s >= 0; ) {
1626         const(S2Shape) shape = index.shape(s);
1627         if (shape && shape.hasInterior()) return true;
1628       }
1629       return false;
1630     }
1631 
1632     static bool addIndexCrossing(
1633         in ShapeEdge a, in ShapeEdge b, bool is_interior, ref IndexCrossings crossings) {
1634       import s2.s2predicates : sign;
1635       import s2.s2edge_crossings : vertexCrossing;
1636 
1637       crossings ~= IndexCrossing(a.id(), b.id());
1638       IndexCrossing* crossing = &crossings.back();
1639       if (is_interior) {
1640         crossing.isInteriorCrossing = true;
1641         if (sign(a.v0(), a.v1(), b.v0()) > 0) {
1642           crossing.leftToRight = true;
1643         }
1644       } else {
1645         // TODO(ericv): This field isn't used unless one shape is a polygon and
1646         // the other is a polyline or polygon, but we don't have the shape
1647         // dimension information readily available here.
1648         if (vertexCrossing(a.v0(), a.v1(), b.v0(), b.v1())) {
1649           crossing.isVertexCrossing = true;
1650         }
1651       }
1652       return true;  // Continue visiting.
1653     }
1654 
1655     /**
1656      * Initialize index_crossings_ to the set of crossing edge pairs such that the
1657      * first element of each pair is an edge from "region_id".
1658      *
1659      * Supports "early exit" in the case of boolean results by returning false
1660      * as soon as the result is known to be non-empty.
1661      */
1662     bool getIndexCrossings(int region_id) {
1663       import s2.shapeutil.visit_crossing_edge_pairs;
1664       import s2.s2crossing_edge_query : CrossingType;
1665 
1666       if (region_id == _indexCrossingsFirstRegionId) return true;
1667       if (_indexCrossingsFirstRegionId < 0) {
1668         debug enforce(region_id == 0);  // For efficiency, not correctness.
1669         if (!visitCrossingEdgePairs(
1670                 _op._regions[0], _op._regions[1],
1671                 CrossingType.ALL,
1672                 (in ShapeEdge a, in ShapeEdge b, bool is_interior) {
1673                   // For all supported operations (union, intersection, and
1674                   // difference), if the input edges have an interior crossing
1675                   // then the output is guaranteed to have at least one edge.
1676                   if (is_interior && isBooleanOutput()) return false;
1677                   return addIndexCrossing(a, b, is_interior, _indexCrossings);
1678                 })) {
1679           return false;
1680         }
1681         if (_indexCrossings.length > 1) {
1682           sort(_indexCrossings);
1683           _indexCrossings = uniq(_indexCrossings).array;
1684         }
1685         // Add a sentinel value to simplify the loop logic.
1686         _indexCrossings ~= IndexCrossing(SENTINEL, SENTINEL);
1687         _indexCrossingsFirstRegionId = 0;
1688       }
1689       if (region_id != _indexCrossingsFirstRegionId) {
1690         foreach (ref crossing; _indexCrossings) {
1691           swap(crossing.a, crossing.b);
1692           // The following predicates get inverted when the edges are swapped.
1693           crossing.leftToRight(crossing.leftToRight ^ true);
1694           crossing.isVertexCrossing(crossing.isVertexCrossing ^ true);
1695         }
1696         sort(_indexCrossings);
1697         _indexCrossingsFirstRegionId = region_id;
1698       }
1699       return true;
1700     }
1701 
1702     /// Supports "early exit" in the case of boolean results by returning false
1703     /// as soon as the result is known to be non-empty.
1704     bool addBoundaryPair(bool invert_a, bool invert_b, bool invert_result, CrossingProcessor cp) {
1705       // Optimization: if the operation is DIFFERENCE or SYMMETRIC_DIFFERENCE,
1706       // it is worthwhile checking whether the two regions are identical (in which
1707       // case the output is empty).
1708       //
1709       // TODO(ericv): When boolean output is requested there are other quick
1710       // checks that could be done here, such as checking whether a full cell from
1711       // one S2ShapeIndex intersects a non-empty cell of the other S2ShapeIndex.
1712       auto type = _op.opType();
1713       if (type == OpType.DIFFERENCE || type == OpType.SYMMETRIC_DIFFERENCE) {
1714         if (areRegionsIdentical()) {
1715           return true;
1716         }
1717       } else if (!isBooleanOutput()) {
1718       }
1719       ShapeEdgeId[] a_starts, b_starts;
1720       if (!getChainStarts(0, invert_a, invert_b, invert_result, cp, a_starts)
1721           || !getChainStarts(1, invert_b, invert_a, invert_result, cp, b_starts)
1722           || !addBoundary(0, invert_a, invert_b, invert_result, a_starts, cp)
1723           || !addBoundary(1, invert_b, invert_a, invert_result, b_starts, cp)) {
1724         return false;
1725       }
1726       if (!isBooleanOutput()) {
1727         cp.doneBoundaryPair();
1728       }
1729       return true;
1730     }
1731 
1732     bool areRegionsIdentical() const {
1733       const(S2ShapeIndex) a = _op._regions[0];
1734       const(S2ShapeIndex) b = _op._regions[1];
1735       if (a == b) return true;
1736       int num_shape_ids = a.numShapeIds();
1737       if (num_shape_ids != b.numShapeIds()) return false;
1738       for (int s = 0; s < num_shape_ids; ++s) {
1739         const(S2Shape) a_shape = a.shape(s);
1740         const(S2Shape) b_shape = b.shape(s);
1741         if (a_shape.dimension() != b_shape.dimension()) return false;
1742         if (a_shape.dimension() == 2) {
1743           auto a_ref = a_shape.getReferencePoint();
1744           auto b_ref = b_shape.getReferencePoint();
1745           if (a_ref.point != b_ref.point) return false;
1746           if (a_ref.contained != b_ref.contained) return false;
1747         }
1748         int num_chains = a_shape.numChains();
1749         if (num_chains != b_shape.numChains()) return false;
1750         for (int c = 0; c < num_chains; ++c) {
1751           S2Shape.Chain a_chain = a_shape.chain(c);
1752           S2Shape.Chain b_chain = b_shape.chain(c);
1753           debug enforce(a_chain.start == b_chain.start);
1754           if (a_chain.length != b_chain.length) return false;
1755           for (int i = 0; i < a_chain.length; ++i) {
1756             S2Shape.Edge a_edge = a_shape.chainEdge(c, i);
1757             S2Shape.Edge b_edge = b_shape.chainEdge(c, i);
1758             if (a_edge.v0 != b_edge.v0) return false;
1759             if (a_edge.v1 != b_edge.v1) return false;
1760           }
1761         }
1762       }
1763       return true;
1764     }
1765 
1766     /// Supports "early exit" in the case of boolean results by returning false
1767     /// as soon as the result is known to be non-empty.
1768     bool buildOpType(OpType op_type) {
1769       // CrossingProcessor does the real work of emitting the output edges.
1770       auto cp = new CrossingProcessor(_op._options.polygonModel(),
1771           _op._options.polylineModel(),
1772           _builder, &_inputDimensions, &_inputCrossings);
1773       final switch (op_type) {
1774         case OpType.UNION:
1775           // A | B == ~(~A & ~B)
1776           return addBoundaryPair(true, true, true, cp);
1777 
1778         case OpType.INTERSECTION:
1779           // A & B
1780           return addBoundaryPair(false, false, false, cp);
1781 
1782         case OpType.DIFFERENCE:
1783           // A - B = A & ~B
1784           return addBoundaryPair(false, true, false, cp);
1785 
1786         case OpType.SYMMETRIC_DIFFERENCE:
1787           // Compute the union of (A - B) and (B - A).
1788           return (addBoundaryPair(false, true, false, cp)
1789               && addBoundaryPair(true, false, false, cp));
1790       }
1791     }
1792 
1793     S2BooleanOperation _op;
1794 
1795     /// The S2Builder used to construct the output.
1796     S2Builder _builder;
1797 
1798     /// A vector specifying the dimension of each edge added to S2Builder.
1799     byte[] _inputDimensions;
1800 
1801     /// The set of all input edge crossings, which is used by EdgeClippingLayer
1802     /// to construct the clipped output polygon.
1803     InputEdgeCrossings _inputCrossings;
1804 
1805     /// kSentinel is a sentinel value used to mark the end of vectors.
1806     enum ShapeEdgeId SENTINEL = ShapeEdgeId(int.max, 0);
1807 
1808     /**
1809      * A vector containing all pairs of crossing edges from the two input
1810      * regions (including edge pairs that share a common vertex).  The first
1811      * element of each pair is an edge from "index_crossings_first_region_id_",
1812      * while the second element of each pair is an edge from the other region.
1813      */
1814     IndexCrossings _indexCrossings;
1815 
1816     /**
1817      * Indicates that the first element of each crossing edge pair in
1818      * "index_crossings_" corresponds to an edge from the given region.
1819      * This field is negative if index_crossings_ has not been computed yet.
1820      */
1821     int _indexCrossingsFirstRegionId;
1822 
1823     /// Temporary storage used in GetChainStarts(), declared here to avoid
1824     /// repeatedly allocating memory.
1825     IndexCrossings _tmpCrossings;
1826   }
1827 
1828   // TODO: Resume here.
1829 
1830   /// Internal constructor to reduce code duplication.
1831   this(OpType op_type, Options options) {
1832     _opType = op_type;
1833     _options = options;
1834     _resultEmpty = null;
1835   }
1836 
1837   /**
1838    * Specifies that "result_empty" should be set to indicate whether the exact
1839    * result of the operation is empty (contains no edges).  This constructor
1840    * is used to efficiently test boolean relationships (see IsEmpty above).
1841    */
1842   this(OpType op_type, bool* result_empty, Options options = null) {
1843     _opType = op_type;
1844     _options = options is null ? new Options() : options;
1845     _resultEmpty = result_empty;
1846   }
1847 
1848   OpType _opType;
1849   Options _options;
1850 
1851   /// The input regions.
1852   S2ShapeIndex[2] _regions;
1853 
1854   /// The output consists either of zero layers, one layer, or three layers.
1855   Layer[] _layers;
1856 
1857   /// The following field is set if and only if there are no output layers.
1858   bool* _resultEmpty;
1859 }
1860 
1861 /**
1862  * CrossingInputEdge represents an input edge B that crosses some other input
1863  * edge A.  It stores the input edge id of edge B and also whether it crosses
1864  * edge A from left to right (or vice versa).
1865  */
1866 struct CrossingInputEdge {
1867 public:
1868   /// Indicates that input edge "input_id" crosses another edge (from left to
1869   /// right if "left_to_right" is true).
1870   this(InputEdgeId input_id, bool left_to_right) {
1871     _leftToRight = left_to_right;
1872     _inputId = input_id;
1873   }
1874 
1875   InputEdgeId inputId() const {
1876     return _inputId;
1877   }
1878 
1879   bool leftToRight() const {
1880     return _leftToRight;
1881   }
1882 
1883   int opCmp(in CrossingInputEdge other) const {
1884     return _inputId - other._inputId;
1885   }
1886 
1887   int opCmp(in InputEdgeId other) const {
1888     return _inputId - other;
1889   }
1890 
1891   string toString() const {
1892     return "CrossingInputEdge(leftToRight=" ~ _leftToRight.to!string
1893         ~ ", inputId=" ~ _inputId.to!string ~ ")";
1894   }
1895 
1896 private:
1897   mixin(bitfields!(
1898           bool,  "_leftToRight", 1,
1899           InputEdgeId, "_inputId", 31));
1900 }
1901 
1902 /// InputEdgeCrossings represents all pairs of intersecting input edges.
1903 /// It is sorted in lexicographic order.
1904 alias InputEdgeCrossings = Tuple!(InputEdgeId, CrossingInputEdge)[];
1905 
1906 /**
1907  * Given two input edges A and B that intersect, suppose that A maps to a
1908  * chain of snapped edges A_0, A_1, ..., A_m and B maps to a chain of snapped
1909  * edges B_0, B_1, ..., B_n.  CrossingGraphEdge represents an edge from chain
1910  * B that shares a vertex with chain A.  It is used as a temporary data
1911  * representation while processing chain A.  The arguments are:
1912  *
1913  *   "id" - the Graph::EdgeId of an edge from chain B.
1914  *   "a_index" - the index of the vertex (A_i) that is shared with chain A.
1915  *   "outgoing" - true if the shared vertex is the first vertex of the B edge.
1916  *   "dst" - the Graph::VertexId of the vertex that is not shared with chain A.
1917  *
1918  * Note that if an edge from the B chain shares both vertices with the A
1919  * chain, there will be two entries: an outgoing edge that treats its first
1920  * vertex as being shared, and an incoming edge that treats its second vertex
1921  * as being shared.
1922  */
1923 struct CrossingGraphEdge {
1924   EdgeId id;
1925   int aIndex;
1926   bool outgoing;
1927   VertexId dst;
1928 }
1929 alias CrossingGraphEdgeVector = CrossingGraphEdge[];
1930 
1931 /**
1932  * Returns a vector of EdgeIds sorted by input edge id.  When more than one
1933  * output edge has the same input edge id (i.e., the input edge snapped to a
1934  * chain of edges), the edges are sorted so that they form a directed edge
1935  * chain.
1936  *
1937  * This function could possibily be moved to S2Builder::Graph, but note that
1938  * it has special requirements.  Namely, duplicate edges and sibling pairs
1939  * must be kept in order to ensure that every output edge corresponds to
1940  * exactly one input edge.  (See also S2Builder::Graph::GetInputEdgeOrder.)
1941  */
1942 private EdgeId[] getInputEdgeChainOrder(in Graph g, in InputEdgeId[] input_ids)
1943 in {
1944   assert(g.options().edgeType() == EdgeType.DIRECTED);
1945   assert(g.options().duplicateEdges() == DuplicateEdges.KEEP);
1946   assert(g.options().siblingPairs() == SiblingPairs.KEEP);
1947 } do {
1948   // First, sort the edges so that the edges corresponding to each input edge
1949   // are consecutive.  (Each input edge was snapped to a chain of output
1950   // edges, or two chains in the case of undirected input edges.)
1951   EdgeId[] order = g.getInputEdgeOrder(input_ids);
1952 
1953   // Now sort the group of edges corresponding to each input edge in edge
1954   // chain order (e.g.  AB, BC, CD).
1955   Tuple!(VertexId, EdgeId)[] vmap;     // Map from source vertex to edge id.
1956   int[] indegree = new int[](g.numVertices());  // Restricted to current input edge.
1957   for (int end, begin = 0; begin < order.length; begin = end) {
1958     // Gather the edges that came from a single input edge.
1959     InputEdgeId input_id = input_ids[order[begin]];
1960     for (end = begin; end < order.length; ++end) {
1961       if (input_ids[order[end]] != input_id) break;
1962     }
1963     if (end - begin == 1) continue;
1964 
1965     // Build a map from the source vertex of each edge to its edge id,
1966     // and also compute the indegree at each vertex considering only the edges
1967     // that came from the current input edge.
1968     for (int i = begin; i < end; ++i) {
1969       EdgeId e = order[i];
1970       vmap ~= tuple(g.edge(e)[0], e);
1971       indegree[g.edge(e)[1]] += 1;
1972     }
1973     sort(vmap);
1974 
1975     // Find the starting edge for building the edge chain.
1976     EdgeId next = g.numEdges();
1977     for (int i = begin; i < end; ++i) {
1978       EdgeId e = order[i];
1979       if (indegree[g.edge(e)[0]] == 0) next = e;
1980     }
1981     // Build the edge chain.
1982     for (int i = begin; ;) {
1983       order[i] = next;
1984       VertexId v = g.edge(next)[1];
1985       indegree[v] = 0;  // Clear as we go along.
1986       if (++i == end) break;
1987       auto triRanges = assumeSorted(vmap).trisect(tuple(v, 0));
1988       auto output = chain(triRanges[1], triRanges[2]);
1989       debug enforce(!output.empty() && output.front[0] == v);
1990       next = output.front[1];
1991     }
1992     vmap.length = 0;
1993   }
1994   return order;
1995 }
1996 
1997 /**
1998  * Given a set of clipping instructions encoded as a set of InputEdgeCrossings,
1999  * GraphEdgeClipper determines which graph edges correspond to clipped
2000  * portions of input edges and removes them.
2001  *
2002  * The clipping model is as follows.  The input consists of edge chains.  The
2003  * clipper maintains an "inside" boolean state as it clips each chain, and
2004  * toggles this state whenever an input edge is crossed.  Any edges that are
2005  * deemed to be "outside" after clipping are removed.
2006  *
2007  * The "inside" state can be reset when necessary (e.g., when jumping to the
2008  * start of a new chain) by adding a special crossing marked kSetInside.
2009  * There are also two other special "crossings" that modify the clipping
2010  * parameters: kSetInvertB specifies that edges should be clipped to the
2011  * exterior of the other region, and kSetReverseA specifies that edges should
2012  * be reversed before emitting them (which is needed to implement difference
2013  * operations).
2014  */
2015 class GraphEdgeClipper {
2016 public:
2017   // "input_dimensions" is a vector specifying the dimension of each input
2018   // edge (0, 1, or 2).  "input_crossings" is the set of all crossings to be
2019   // used when clipping the edges of "g", sorted in lexicographic order.
2020   //
2021   // The clipped set of edges and their corresponding set of input edge ids
2022   // are returned in "new_edges" and "new_input_edge_ids".  (These can be used
2023   // to construct a new S2Builder::Graph.)
2024   this(
2025       in Graph g, in byte[] input_dimensions,
2026       in InputEdgeCrossings input_crossings,
2027       Graph.Edge[]* new_edges,
2028       InputEdgeIdSetId[]* new_input_edge_ids) {
2029     _g = g;
2030     _in = new Graph.VertexInMap(g);
2031     _out = new Graph.VertexOutMap(g);
2032     _inputDimensions = input_dimensions;
2033     _inputCrossings = input_crossings;
2034     _newEdges = new_edges;
2035     _newInputEdgeIds = new_input_edge_ids;
2036     _inputIds = g.inputEdgeIdSetIds();
2037     _order = getInputEdgeChainOrder(_g, _inputIds);
2038     _rank.length = _order.length;
2039     for (int i = 0; i < _order.length; ++i) {
2040       _rank[_order[i]] = i;
2041     }
2042   }
2043 
2044   void run() {
2045     // Declare vectors here and reuse them to avoid reallocation.
2046     VertexId[] a_vertices;
2047     int[] a_num_crossings;
2048     bool[] a_isolated;
2049     CrossingInputEdge[] b_input_edges;
2050     CrossingGraphEdgeVector[] b_edges;
2051 
2052     bool inside = false;
2053     bool invert_b = false;
2054     bool reverse_a = false;
2055     auto inputCrossingRange = _inputCrossings[];
2056     for (int i = 0; i < _order.length; ++i) {
2057       // For each input edge (the "A" input edge), gather all the input edges
2058       // that cross it (the "B" input edges).
2059       InputEdgeId a_input_id = _inputIds[_order[i]];
2060       const(Graph.Edge) edge0 = _g.edge(_order[i]);
2061       b_input_edges.length = 0;
2062       for (; !inputCrossingRange.empty(); inputCrossingRange.popFront()) {
2063         auto next = inputCrossingRange.front();
2064         if (next[0] != a_input_id) {
2065           break;
2066         }
2067         if (next[1].inputId() >= 0) {
2068           b_input_edges ~= next[1];
2069         } else if (next[1].inputId() == SET_INSIDE) {
2070           inside = next[1].leftToRight();
2071         } else if (next[1].inputId() == SET_INVERT_B) {
2072           invert_b = next[1].leftToRight();
2073         } else {
2074           debug enforce(next[1].inputId() == SET_REVERSE_A);
2075           reverse_a = next[1].leftToRight();
2076         }
2077       }
2078       // Optimization for degenerate edges.
2079       // TODO(ericv): If the output layer for this edge dimension specifies
2080       // DegenerateEdges::DISCARD, then remove the edge here.
2081       if (edge0[0] == edge0[1]) {
2082         inside ^= (b_input_edges.length & 1);
2083         addEdge(edge0, a_input_id);
2084         continue;
2085       }
2086       // Optimization for the case where there are no crossings.
2087       if (b_input_edges.empty()) {
2088         // In general the caller only passes edges that are part of the output
2089         // (i.e., we could DCHECK(inside) here).  The one exception is for
2090         // polyline/polygon operations, where the polygon edges are needed to
2091         // compute the polyline output but are not emitted themselves.
2092         if (inside) {
2093           addEdge(reverse_a ? Graph.reverse(edge0) : edge0, a_input_id);
2094         }
2095         continue;
2096       }
2097       // Walk along the chain of snapped edges for input edge A, and at each
2098       // vertex collect all the incident edges that belong to one of the
2099       // crossing edge chains (the "B" input edges).
2100       a_vertices.length = 0;
2101       a_vertices ~= edge0[0];
2102       b_edges.length = 0;
2103       b_edges.length = b_input_edges.length;
2104 
2105       gatherIncidentEdges(a_vertices, 0, b_input_edges, b_edges);
2106       for (; i < _order.length && _inputIds[_order[i]] == a_input_id; ++i) {
2107         a_vertices ~= _g.edge(_order[i])[1];
2108         gatherIncidentEdges(a_vertices, cast(int) a_vertices.length - 1, b_input_edges, b_edges);
2109       }
2110       --i;
2111       if (s2builderVerbose) {
2112         writeln("input edge ", a_input_id, " (inside=", inside, "):");
2113         foreach (VertexId id; a_vertices) writeln(" ", id);
2114       }
2115       // Now for each B edge chain, decide which vertex of the A chain it
2116       // crosses, and keep track of the number of signed crossings at each A
2117       // vertex.  The sign of a crossing depends on whether the other edge
2118       // crosses from left to right or right to left.
2119       //
2120       // This would not be necessary if all calculations were done in exact
2121       // arithmetic, because crossings would have strictly alternating signs.
2122       // But because we have already snapped the result, some crossing locations
2123       // are ambiguous, and GetCrossedVertexIndex() handles this by choosing a
2124       // candidate vertex arbitrarily.  The end result is that rarely, we may
2125       // see two crossings in a row with the same sign.  We correct for this by
2126       // adding extra output edges that essentially link up the crossings in the
2127       // correct (alternating sign) order.  Compared to the "correct" behavior,
2128       // the only difference is that we have added some extra sibling pairs
2129       // (consisting of an edge and its corresponding reverse edge) which do not
2130       // affect the result.
2131       a_num_crossings = new int[a_vertices.length];
2132       a_isolated.length = 0;
2133       a_isolated.length = a_vertices.length;
2134       for (int bi = 0; bi < b_input_edges.length; ++bi) {
2135         bool left_to_right = b_input_edges[bi].leftToRight();
2136         int a_index = getCrossedVertexIndex(a_vertices, b_edges[bi], left_to_right);
2137         if (s2builderVerbose) {
2138           write("  b input edge ", b_input_edges[bi].inputId(), " (l2r=", left_to_right,
2139               ", crossing=", a_vertices[a_index], ")");
2140           foreach (x; b_edges[bi]) {
2141             const Graph.Edge e = _g.edge(x.id);
2142             write(" (", e[0], ", ", e[1], ")");
2143           }
2144           writeln("");
2145         }
2146         // Keep track of the number of signed crossings (see above).
2147         bool is_line = _inputDimensions[b_input_edges[bi].inputId()] == 1;
2148         int sign = is_line ? 0 : (left_to_right == invert_b) ? -1 : 1;
2149         a_num_crossings[a_index] += sign;
2150 
2151         // Any polyline or polygon vertex that has at least one crossing but no
2152         // adjacent emitted edge may be emitted as an isolated vertex.
2153         a_isolated[a_index] = true;
2154       }
2155       if (s2builderVerbose) writeln();
2156 
2157       // Finally, we iterate through the A edge chain, keeping track of the
2158       // number of signed crossings as we go along.  The "multiplicity" is
2159       // defined as the cumulative number of signed crossings, and indicates how
2160       // many edges should be output (and in which direction) in order to link
2161       // up the edge crossings in the correct order.  (The multiplicity is
2162       // almost always either 0 or 1 except in very rare cases.)
2163       int multiplicity = inside + a_num_crossings[0];
2164       for (int ai = 1; ai < a_vertices.length; ++ai) {
2165         if (multiplicity != 0) {
2166           a_isolated[ai - 1] = a_isolated[ai] = false;
2167         }
2168         int edge_count = reverse_a ? -multiplicity : multiplicity;
2169         // Output any forward edges required.
2170         for (int j = 0; j < edge_count; ++j) {
2171           addEdge(Graph.Edge(a_vertices[ai - 1], a_vertices[ai]), a_input_id);
2172         }
2173         // Output any reverse edges required.
2174         for (int j = edge_count; j < 0; ++j) {
2175           addEdge(Graph.Edge(a_vertices[ai], a_vertices[ai - 1]), a_input_id);
2176         }
2177         multiplicity += a_num_crossings[ai];
2178       }
2179       // Multiplicities other than 0 or 1 can only occur in the edge interior.
2180       debug enforce(multiplicity == 0 || multiplicity == 1);
2181       inside = (multiplicity != 0);
2182 
2183       // Output any isolated polyline vertices.
2184       // TODO(ericv): Only do this if an output layer wants degenerate edges.
2185       if (_inputDimensions[a_input_id] != 0) {
2186         for (int ai = 0; ai < a_vertices.length; ++ai) {
2187           if (a_isolated[ai]) {
2188             addEdge(Graph.Edge(a_vertices[ai], a_vertices[ai]), a_input_id);
2189           }
2190         }
2191       }
2192     }
2193   }
2194 
2195 private:
2196   void addEdge(Graph.Edge edge, InputEdgeId input_edge_id) {
2197     *_newEdges ~= edge;
2198     *_newInputEdgeIds ~= input_edge_id;
2199   }
2200 
2201   /**
2202    * Given the vertices of the snapped edge chain for an input edge A and the
2203    * set of input edges B that cross input edge A, this method gathers all of
2204    * the snapped edges of B that are incident to a given snapped vertex of A.
2205    * The incident edges for each input edge of B are appended to a separate
2206    * output vector.  (A and B can refer to either the input edge or the
2207    * corresponding snapped edge chain.)
2208    */
2209   void gatherIncidentEdges(
2210       in VertexId[] a, int ai, in CrossingInputEdge[] b_input_edges,
2211       ref CrossingGraphEdgeVector[] b_edges) const
2212   in {
2213     assert(b_input_edges.length == b_edges.length);
2214   } do {
2215     // Examine all of the edges incident to the given vertex of A.  If any edge
2216     // comes from a B input edge, append it to the appropriate vector.
2217     foreach (EdgeId e; _in.edgeIds(a[ai])) {
2218       InputEdgeId id = _inputIds[e];
2219       auto trisectRanges = b_input_edges.assumeSorted().trisect(id);
2220       if (!trisectRanges[1].empty()) {
2221         auto edges = &b_edges[trisectRanges[0].length];
2222         *edges ~= CrossingGraphEdge(e, ai, false, _g.edge(e)[0]);
2223       }
2224     }
2225     foreach (EdgeId e; _out.edgeIds(a[ai])) {
2226       InputEdgeId id = _inputIds[e];
2227       auto trisectRanges = b_input_edges.assumeSorted().trisect(id);
2228       if (!trisectRanges[1].empty()) {
2229         auto edges = &b_edges[trisectRanges[0].length];
2230         *edges ~= CrossingGraphEdge(e, ai, true, _g.edge(e)[1]);
2231       }
2232     }
2233   }
2234 
2235   /**
2236    * Given an edge chain A that is crossed by another edge chain B (where
2237    * "left_to_right" indicates whether B crosses A from left to right), this
2238    * method decides which vertex of A the crossing takes place at.  The
2239    * parameters are the vertices of the A chain ("a") and the set of edges in
2240    * the B chain ("b") that are incident to vertices of A.  The B chain edges
2241    * are sorted in increasing order of (a_index, outgoing) tuple.
2242    */
2243   int getCrossedVertexIndex(
2244       in VertexId[] a, in CrossingGraphEdgeVector b, bool left_to_right) const
2245   in {
2246     assert(!a.empty());
2247     assert(!b.empty());
2248   } do {
2249     import s2.s2predicates : orderedCCW;
2250 
2251     // The reason this calculation is tricky is that after snapping, the A and B
2252     // chains may meet and separate several times.  For example, if B crosses A
2253     // from left to right, then B may touch A, make an excursion to the left of
2254     // A, come back to A, then make an excursion to the right of A and come back
2255     // to A again, like this:
2256     //
2257     //  *--B--*-\             /-*-\
2258     //           B-\       /-B     B-\      6     7     8     9
2259     //  *--A--*--A--*-A,B-*--A--*--A--*-A,B-*--A--*--A--*-A,B-*
2260     //  0     1     2     3     4     5      \-B     B-/
2261     //                                          \-*-/
2262     //
2263     // (where "*" is a vertex, and "A" and "B" are edge labels).  Note that B
2264     // may also follow A for one or more edges whenever they touch (e.g. between
2265     // vertices 2 and 3 ).  In this case the only vertices of A where the
2266     // crossing could take place are 5 and 6, i.e. after all excursions of B to
2267     // the left of A, and before all excursions of B to the right of A.
2268     //
2269     // Other factors to consider are that the portion of B before and/or after
2270     // the crossing may be degenerate, and some or all of the B edges may be
2271     // reversed relative to the A edges.
2272 
2273     // First, check whether edge A is degenerate.
2274     int n = cast(int) a.length;
2275     if (n == 1) return 0;
2276 
2277     // If edge chain B is incident to only one vertex of A, we're done.
2278     if (b[0].aIndex == b.back().aIndex) return b[0].aIndex;
2279 
2280     // Determine whether the B chain visits the first and last vertices that it
2281     // shares with the A chain in the same order or the reverse order.  This is
2282     // only needed to implement one special case (see below).
2283     bool b_reversed = getVertexRank(b[0]) > getVertexRank(b.back());
2284 
2285     // Examine each incident B edge and use it to narrow the range of positions
2286     // where the crossing could occur in the B chain.  Vertex positions are
2287     // represented as a range [lo, hi] of vertex ranks in the B chain (see
2288     // GetVertexRank).
2289     //
2290     // Note that if an edge of B is incident to the first or last vertex of A,
2291     // we can't test which side of the A chain it is on.  There can be up to 4
2292     // such edges (one incoming and one outgoing edge at each vertex).  Two of
2293     // these edges logically extend past the end of the A chain and place no
2294     // restrictions on the crossing vertex.  The other two edges define the ends
2295     // of the subchain where B shares vertices with A.  We save these edges in
2296     // order to handle a special case (see below).
2297     int lo = -1, hi = cast(int) _order.length;   // Vertex ranks of acceptable crossings
2298     EdgeId b_first = -1, b_last = -1;  // "b" subchain connecting "a" endpoints
2299     foreach (e; b) {
2300       int ai = e.aIndex;
2301       if (ai == 0) {
2302         if (e.outgoing != b_reversed && e.dst != a[1]) b_first = e.id;
2303       } else if (ai == n - 1) {
2304         if (e.outgoing == b_reversed && e.dst != a[n - 2]) b_last = e.id;
2305       } else {
2306         // This B edge is incident to an interior vertex of the A chain.  First
2307         // check whether this edge is identical (or reversed) to an edge in the
2308         // A chain, in which case it does not create any restrictions.
2309         if (e.dst == a[ai - 1] || e.dst == a[ai + 1]) continue;
2310 
2311         // Otherwise we can test which side of the A chain the edge lies on.
2312         bool on_left = orderedCCW(
2313             _g.vertex(a[ai + 1]), _g.vertex(e.dst), _g.vertex(a[ai - 1]), _g.vertex(a[ai]));
2314 
2315         // Every B edge that is incident to an interior vertex of the A chain
2316         // places some restriction on where the crossing vertex could be.
2317         if (left_to_right == on_left) {
2318           // This is a pre-crossing edge, so the crossing cannot be before the
2319           // destination vertex of this edge.  (For example, the input B edge
2320           // crosses the input A edge from left to right and this edge of the B
2321           // chain is to the left of the A chain.)
2322           lo = max(lo, _rank[e.id] + 1);
2323         } else {
2324           // This is a post-crossing edge, so the crossing cannot be after the
2325           // source vertex of this edge.
2326           hi = min(hi, _rank[e.id]);
2327         }
2328       }
2329     }
2330     // There is one special case.  If there were no B edges incident to interior
2331     // vertices of A, then we can't reliably test which side of A the B edges
2332     // are on.  (An s2pred::Sign test doesn't work, since an edge of B can snap to
2333     // the "wrong" side of A while maintaining topological guarantees.)  So
2334     // instead we construct a loop consisting of the A edge chain plus the
2335     // portion of the B chain that connects the endpoints of A.  We can then
2336     // test the orientation of this loop.
2337     //
2338     // Note that it would be possible to avoid this in some situations by
2339     // testing whether either endpoint of the A chain has two incident B edges,
2340     // in which case we could check which side of the B chain the A edge is on
2341     // and use this to limit the possible crossing locations.
2342     if (lo < 0 && hi >= _order.length && b_first >= 0 && b_last >= 0) {
2343       // Swap the edges if necessary so that they are in B chain order.
2344       if (b_reversed) swap(b_first, b_last);
2345       bool on_left = edgeChainOnLeft(a, b_first, b_last);
2346       if (left_to_right == on_left) {
2347         lo = max(lo, _rank[b_last] + 1);
2348       } else {
2349         hi = min(hi, _rank[b_first]);
2350       }
2351     }
2352 
2353     // Otherwise we choose the smallest shared VertexId in the acceptable range,
2354     // in order to ensure that both chains choose the same crossing vertex.
2355     int best = -1;
2356     debug enforce(lo <= hi);
2357     foreach (e; b) {
2358       int ai = e.aIndex;
2359       int vrank = getVertexRank(e);
2360       if (vrank >= lo && vrank <= hi && (best < 0 || a[ai] < a[best])) {
2361         best = ai;
2362       }
2363     }
2364     return best;
2365   }
2366 
2367   /**
2368    * Returns the "vertex rank" of the shared vertex associated with the given
2369    * CrossingGraphEdge.  Recall that graph edges are sorted in input edge order,
2370    * and that the rank of an edge is its position in this order (rank_[e]).
2371    * VertexRank(e) is defined such that VertexRank(e.src) == rank_[e] and
2372    * VertexRank(e.dst) == rank_[e] + 1.  Note that the concept of "vertex rank"
2373    * is only defined within a single edge chain (since different edge chains can
2374    * have overlapping vertex ranks).
2375    */
2376   int getVertexRank(in CrossingGraphEdge e) const {
2377     return _rank[e.id] + !e.outgoing;
2378   }
2379 
2380   /**
2381    * Given edge chains A and B that form a loop (after possibly reversing the
2382    * direction of chain B), returns true if chain B is to the left of chain A.
2383    * Chain A is given as a sequence of vertices, while chain B is specified as
2384    * the first and last edges of the chain.
2385    */
2386   bool edgeChainOnLeft(in VertexId[] a, EdgeId b_first, EdgeId b_last) const {
2387     import s2.s2measures : turnAngle;
2388 
2389     // Gather all the interior vertices of the B subchain.
2390     VertexId[] loop;
2391     for (int i = _rank[b_first]; i < _rank[b_last]; ++i) {
2392       loop ~= _g.edge(_order[i])[1];
2393     }
2394     // Possibly reverse the chain so that it forms a loop when "a" is appended.
2395     if (_g.edge(b_last)[1] != a[0]) reverse(loop);
2396     loop ~= a;
2397     // Duplicate the first two vertices to simplify vertex indexing.
2398     loop ~= loop[0 .. 2];
2399     // Now B is to the left of A if and only if the loop is counterclockwise.
2400     double sum = 0;
2401     for (int i = 2; i < loop.length; ++i) {
2402       sum += turnAngle(_g.vertex(loop[i - 2]), _g.vertex(loop[i - 1]), _g.vertex(loop[i]));
2403     }
2404     return sum > 0;
2405   }
2406 
2407   const(Graph) _g;
2408   Graph.VertexInMap _in;
2409   Graph.VertexOutMap _out;
2410   const(byte[]) _inputDimensions;
2411   const(InputEdgeCrossings) _inputCrossings;
2412   Graph.Edge[]* _newEdges;
2413   InputEdgeIdSetId[]* _newInputEdgeIds;
2414 
2415   // Every graph edge is associated with exactly one input edge in our case,
2416   // which means that we can declare g_.input_edge_id_set_ids() as a vector of
2417   // InputEdgeIds rather than a vector of InputEdgeIdSetIds.  (This also takes
2418   // advantage of the fact that IdSetLexicon represents a singleton set as the
2419   // value of its single element.)
2420   const(InputEdgeId[]) _inputIds;
2421 
2422   EdgeId[] _order;  // Graph edges sorted in input edge id order.
2423   int[] _rank;      // The rank of each graph edge within order_.
2424 }
2425 
2426 /**
2427  * Given a set of clipping instructions encoded as a set of intersections
2428  * between input edges, EdgeClippingLayer determines which graph edges
2429  * correspond to clipped portions of input edges and removes them.  It
2430  * assembles the remaining edges into a new S2Builder::Graph and passes the
2431  * result to the given output layer for assembly.
2432  */
2433 class EdgeClippingLayer : Layer {
2434 public:
2435   this(Layer[] layers, in byte[]* input_dimensions, in InputEdgeCrossings* input_crossings) {
2436     _layers = layers;
2437     _inputDimensions = input_dimensions;
2438     _inputCrossings = input_crossings;
2439   }
2440 
2441   /// Layer interface:
2442   override
2443   GraphOptions graphOptions() const {
2444     // We keep all edges, including degenerate ones, so that we can figure out
2445     // the correspondence between input edge crossings and output edge
2446     // crossings.
2447     return new GraphOptions(
2448         EdgeType.DIRECTED, DegenerateEdges.KEEP,
2449         DuplicateEdges.KEEP, SiblingPairs.KEEP);
2450   }
2451 
2452   override
2453   void build(Graph g, ref S2Error error) {
2454     // The bulk of the work is handled by GraphEdgeClipper.
2455     Graph.Edge[] new_edges;
2456     InputEdgeIdSetId[] new_input_edge_ids;
2457     // Destroy the GraphEdgeClipper immediately to save memory.
2458     new GraphEdgeClipper(g, *_inputDimensions, *_inputCrossings, &new_edges, &new_input_edge_ids)
2459         .run();
2460     if (s2builderVerbose) {
2461       writeln("Edges after clipping: ");
2462       for (int i = 0; i < new_edges.length; ++i) {
2463         writeln("  ", new_input_edge_ids[i], " (", new_edges[i][0], ", ", new_edges[i][1], ")");
2464       }
2465     }
2466     // Construct one or more graphs from the clipped edges and pass them to the
2467     // given output layer(s).
2468     auto new_input_edge_id_set_lexicon = new IdSetLexicon();
2469     if (_layers.length == 1) {
2470       GraphOptions options = _layers[0].graphOptions();
2471       Graph new_graph = makeGraph(
2472           g, options, new_edges, new_input_edge_ids, new_input_edge_id_set_lexicon, error);
2473       _layers[0].build(new_graph, error);
2474     } else {
2475       // The Graph objects must be valid until the last Build() call completes,
2476       // so we store all of the graph data in arrays with 3 elements.
2477       debug enforce(_layers.length == 3);
2478       Graph.Edge[][3] layer_edges;
2479       InputEdgeIdSetId[][3] layer_input_edge_ids;
2480       GraphOptions[3] layer_options;
2481       Graph[] layer_graphs;  // No default constructor.
2482       layer_graphs.reserve(3);
2483       // Separate the edges according to their dimension.
2484       for (int i = 0; i < new_edges.length; ++i) {
2485         int d = (*_inputDimensions)[new_input_edge_ids[i]];
2486         layer_edges[d] ~= new_edges[i];
2487         layer_input_edge_ids[d] ~= new_input_edge_ids[i];
2488       }
2489       // Clear variables to save space.
2490       new_edges.length = 0;
2491       new_input_edge_ids.length = 0;
2492       for (int d = 0; d < 3; ++d) {
2493         layer_options[d] = _layers[d].graphOptions();
2494         layer_graphs ~= makeGraph(
2495             g, layer_options[d], layer_edges[d], layer_input_edge_ids[d],
2496             new_input_edge_id_set_lexicon, error);
2497         _layers[d].build(layer_graphs[d], error);
2498       }
2499     }
2500   }
2501 
2502   override
2503   string toString() const {
2504     import std.conv;
2505     return "EdgeClippingLayer(_layers=" ~ _layers.to!string
2506         ~ ", _inputDimensions=" ~ (*_inputDimensions).to!string
2507         ~ ", _inputCrossings=" ~ (*_inputCrossings).to!string ~ ")";
2508   }
2509 
2510 private:
2511   // Helper function (in anonymous namespace) to create an S2Builder::Graph from
2512   // a vector of edges.
2513   static Graph makeGraph(
2514       in Graph g, GraphOptions options, ref Graph.Edge[] new_edges,
2515       ref InputEdgeIdSetId[] new_input_edge_ids,
2516       ref IdSetLexicon new_input_edge_id_set_lexicon, ref S2Error error) {
2517     if (options.edgeType() == EdgeType.UNDIRECTED) {
2518       // Create a reversed edge for every edge.
2519       size_t n = new_edges.length;
2520       new_edges.reserve(2 * n);
2521       new_input_edge_ids.reserve(2 * n);
2522       for (int i = 0; i < n; ++i) {
2523         new_edges ~= Graph.reverse(new_edges[i]);
2524         new_input_edge_ids ~= IdSetLexicon.emptySetId();
2525       }
2526     }
2527     Graph.processEdges(
2528         options, new_edges, new_input_edge_ids, new_input_edge_id_set_lexicon, error);
2529     return new Graph(
2530         options, g.vertices(), new_edges, new_input_edge_ids,
2531         new_input_edge_id_set_lexicon, g.labelSetIds(),
2532         g.labelSetLexicon(), g.isFullPolygonPredicate());
2533   }
2534 
2535   Layer[] _layers;
2536   const(byte[])* _inputDimensions;
2537   const(InputEdgeCrossings)* _inputCrossings;
2538 }
2539 
2540 /**
2541  * Given a polygon edge graph containing only degenerate edges and sibling
2542  * edge pairs, the purpose of this function is to decide whether the polygon
2543  * is empty or full except for the degeneracies, i.e. whether the degeneracies
2544  * represent shells or holes.
2545  *
2546  * This function always returns false, meaning that the polygon is empty and
2547  * the degeneracies represent shells.  The main side effect of this is that
2548  * operations whose result should be the full polygon will instead be the
2549  * empty polygon.  (Classes such as S2Polygon already have code to correct for
2550  * this, but if that functionality were moved here then it would be useful for
2551  * other polygon representations such as S2LaxPolygonShape.)
2552  */
2553 private bool isFullPolygonNever(in Graph g, ref S2Error error) {
2554   return false;  // Assumes the polygon is empty.
2555 }