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 }