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