1 /** 2 An S2Loop represents a simple spherical polygon. 3 4 Copyright: 2005 Google Inc. All Rights Reserved. 5 6 License: 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 $(LINK http://www.apache.org/licenses/LICENSE-2.0) 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS-IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 19 Authors: ericv@google.com (Eric Veach), madric@gmail.com (Vijay Nayar) 20 */ 21 module s2.s2loop; 22 23 import s2.logger; 24 import s2.mutable_s2shape_index; 25 import s2.r1interval; 26 import s2.r2point; 27 import s2.r2rect; 28 import s2.s1angle; 29 import s2.s1chord_angle; 30 import s2.s1interval; 31 import s2.s2cap; 32 import s2.s2cell; 33 import s2.s2cell_id; 34 import s2.s2centroids : trueCentroid; 35 import s2.s2closest_edge_query; 36 import s2.s2coords : XYZtoFaceSiTi; 37 import s2.s2crossing_edge_query; 38 import s2.s2debug; 39 import s2.s2edge_crosser; 40 import s2.s2edge_distances : getDistance; 41 import s2.s2error; 42 import s2.s2latlng_rect; 43 import s2.s2latlng_rect_bounder; 44 import s2.s2measures : signedArea, turnAngle; 45 import s2.s2padded_cell; 46 import s2.s2point; 47 import s2.s2point_compression : S2XYZFaceSiTi; 48 import s2.s2pointutil : approxEquals, fromFrame, getFrame, origin, robustCrossProd; 49 import s2.s2predicates : orderedCCW; 50 import s2.s2region; 51 import s2.s2shape; 52 import s2.s2shape_index; 53 import s2.s2wedge_relations : wedgeContains, wedgeIntersects; 54 import s2.shapeutil.visit_crossing_edge_pairs : findSelfIntersection; 55 import s2.util.coding.coder; 56 import s2.util.math.matrix3x3; 57 import s2.util.math.vector; 58 59 import std.algorithm : copy, min, max, reverse, swap; 60 import std.array : appender; 61 import std.conv : to; 62 import std.exception; 63 import std.math; 64 import std.range : empty, back, isInputRange, popBack; 65 import core.atomic; 66 67 // Build the S2ShapeIndex only when it is first needed. This can save 68 // significant amounts of memory and time when geometry is constructed but 69 // never queried, for example when loops are passed directly to S2Polygon, 70 // or when geometry is being converted from one format to another. 71 enum bool LAZY_INDEXING = true; 72 73 /** 74 * An S2Loop represents a simple spherical polygon. It consists of a single 75 * chain of vertices where the first vertex is implicitly connected to the 76 * last. All loops are defined to have a CCW orientation, i.e. the interior of 77 * the loop is on the left side of the edges. This implies that a clockwise 78 * loop enclosing a small area is interpreted to be a CCW loop enclosing a 79 * very large area. 80 * 81 * Loops are not allowed to have any duplicate vertices (whether adjacent or 82 * not). Non-adjacent edges are not allowed to intersect, and furthermore edges 83 * of length 180 degrees are not allowed (i.e., adjacent vertices cannot be 84 * antipodal). Loops must have at least 3 vertices (except for the "empty" and 85 * "full" loops discussed below). Although these restrictions are not enforced 86 * in optimized code, you may get unexpected results if they are violated. 87 * 88 * There are two special loops: the "empty" loop contains no points, while the 89 * "full" loop contains all points. These loops do not have any edges, but to 90 * preserve the invariant that every loop can be represented as a vertex 91 * chain, they are defined as having exactly one vertex each (see kEmpty and 92 * kFull). 93 * 94 * Point containment of loops is defined such that if the sphere is subdivided 95 * into faces (loops), every point is contained by exactly one face. This 96 * implies that loops do not necessarily contain their vertices. 97 * 98 * Note: The reason that duplicate vertices and intersecting edges are not 99 * allowed is that they make it harder to define and implement loop 100 * relationships, e.g. whether one loop contains another. If your data does 101 * not satisfy these restrictions, you can use S2Builder to normalize it. 102 * 103 * TODO: Convert logic to use a ForwardRange rather than making a copy of its vertices. 104 */ 105 class S2Loop : S2Region { 106 public: 107 /// Default constructor. The loop must be initialized by calling Init() or 108 /// Decode() before it is used. 109 this() { 110 _bound = new S2LatLngRect(); 111 _subregionBound = new S2LatLngRect(); 112 _index = new MutableS2ShapeIndex(); 113 } 114 115 this(in S2Point[] vertices) { 116 this(vertices, S2Debug.ALLOW); 117 } 118 119 /// Convenience constructor that calls Init() with the given vertices. 120 this(in S2Point[] vertices, S2Debug s2DebugOverride) { 121 this(); 122 _s2DebugOverride = s2DebugOverride; 123 initialize(vertices); 124 } 125 126 /** 127 Initialize a loop with given vertices. The last vertex is implicitly 128 connected to the first. All points should be unit length. Loops must 129 have at least 3 vertices (except for the "empty" and "full" loops, see 130 kEmpty and kFull). This method may be called multiple times. 131 */ 132 void initialize(RangeT)(RangeT vertexRange) 133 if (isInputRange!RangeT /*&& is(ElementType!RangeT == Vector!(double, 3))*/) { 134 clearIndex(); 135 _vertices.length = 0; 136 _vertices ~= vertexRange; 137 //copy(vertexRange, appender(_vertices)); 138 initOriginAndBound(); 139 } 140 141 /** 142 A special vertex chain of length 1 that creates an empty loop (i.e., a 143 loop with no edges that contains no points). Example usage: 144 145 S2Loop emptyLoop = new S2Loop(S2Loop.empty()); 146 147 The loop may be safely encoded lossily (e.g. by snapping it to an S2Cell 148 center) as long as its position does not move by 90 degrees or more. 149 */ 150 static S2Point[] empty() { 151 return [S2Loop.emptyVertex()]; 152 } 153 154 /// A special vertex chain of length 1 that creates a full loop (i.e., a loop 155 /// with no edges that contains all points). See kEmpty() for details. 156 static S2Point[] full() { 157 return [S2Loop.fullVertex()]; 158 } 159 160 /** 161 Construct a loop corresponding to the given cell. 162 163 Note that the loop and cell *do not* contain exactly the same set of 164 points, because S2Loop and S2Cell have slightly different definitions of 165 point containment. For example, an S2Cell vertex is contained by all 166 four neighboring S2Cells, but it is contained by exactly one of four 167 S2Loops constructed from those cells. As another example, the S2Cell 168 coverings of "cell" and "S2Loop(cell)" will be different, because the 169 loop contains points on its boundary that actually belong to other cells 170 (i.e., the covering will include a layer of neighboring cells). 171 */ 172 this(in S2Cell cell) { 173 this(); 174 _depth = 0; 175 _vertices.length = 4; 176 _s2DebugOverride = S2Debug.ALLOW; 177 _unindexedContainsCalls = 0; 178 foreach (i; 0 .. 4) { 179 _vertices[i] = cell.getVertex(i); 180 } 181 // We recompute the bounding rectangle ourselves, since S2Cell uses a 182 // different method and we need all the bounds to be consistent. 183 initOriginAndBound(); 184 } 185 186 /** 187 Allows overriding the automatic validity checks controlled by the 188 --s2debug flag. If this flag is true, then loops are automatically 189 checked for validity as they are initialized. The main reason to disable 190 this flag is if you intend to call IsValid() explicitly, like this: 191 --- 192 S2Loop loop; 193 loop.set_s2debug_override(S2Debug::DISABLE); 194 loop.Init(...); 195 if (!loop.IsValid()) { ... } 196 --- 197 Without the call to set_s2debug_override(), invalid data would cause a 198 fatal error in Init() whenever the --s2debug flag is enabled. 199 200 This setting is preserved across calls to Init() and Decode(). 201 */ 202 @property 203 void s2DebugOverride(S2Debug s2DebugOverride) { 204 _s2DebugOverride = s2DebugOverride; 205 } 206 207 @property 208 S2Debug s2DebugOverride() const { 209 return _s2DebugOverride; 210 } 211 212 /** 213 Returns true if this is a valid loop. Note that validity is checked 214 automatically during initialization when --s2debug is enabled (true by 215 default in debug binaries). 216 */ 217 bool isValid() { 218 S2Error error; 219 if (findValidationError(error)) { 220 if (_s2DebugOverride == S2Debug.ALLOW) logger.logError(error); 221 return false; 222 } 223 return true; 224 } 225 226 /** 227 Returns true if this is *not* a valid loop and sets "error" 228 appropriately. Otherwise returns false and leaves "error" unchanged. 229 */ 230 bool findValidationError(ref S2Error error) { 231 return (findValidationErrorNoIndex(error) || findSelfIntersection(_index, error)); 232 } 233 234 /** 235 Like FindValidationError(), but skips any checks that would require 236 building the S2ShapeIndex (i.e., self-intersection tests). This is used 237 by the S2Polygon implementation, which uses its own index to check for 238 loop self-intersections. 239 */ 240 bool findValidationErrorNoIndex(ref S2Error error) const 241 in { 242 // subregion_bound_ must be at least as large as bound_. (This is an 243 // internal consistency check rather than a test of client data.) 244 assert(_subregionBound.contains(_bound)); 245 } do { 246 import s2.s2pointutil : isUnitLength; 247 // All vertices must be unit length. (Unfortunately this check happens too 248 // late in debug mode, because S2Loop construction calls s2pred::Sign which 249 // expects vertices to be unit length. But it is still a useful check in 250 // optimized builds.) 251 for (int i = 0; i < numVertices(); ++i) { 252 if (!isUnitLength(vertex(i))) { 253 error.initialize(S2Error.Code.NOT_UNIT_LENGTH, "Vertex %d is not unit length", i); 254 return true; 255 } 256 } 257 // Loops must have at least 3 vertices (except for "empty" and "full"). 258 if (numVertices() < 3) { 259 if (isEmptyOrFull()) { 260 return false; // Skip remaining tests. 261 } 262 error.initialize(S2Error.Code.LOOP_NOT_ENOUGH_VERTICES, 263 "Non-empty, non-full loops must have at least 3 vertices"); 264 return true; 265 } 266 // Loops are not allowed to have any duplicate vertices or edge crossings. 267 // We split this check into two parts. First we check that no edge is 268 // degenerate (identical endpoints). Then we check that there are no 269 // intersections between non-adjacent edges (including at vertices). The 270 // second part needs the S2ShapeIndex, so it does not fall within the scope 271 // of this method. 272 for (int i = 0; i < numVertices(); ++i) { 273 if (vertex(i) == vertex(i+1)) { 274 error.initialize( 275 S2Error.Code.DUPLICATE_VERTICES, "Edge %d is degenerate (duplicate vertex)", i); 276 return true; 277 } 278 if (vertex(i) == -vertex(i + 1)) { 279 error.initialize(S2Error.Code.ANTIPODAL_VERTICES, 280 "Vertices %d and %d are antipodal", i, (i + 1) % numVertices()); 281 return true; 282 } 283 } 284 return false; 285 } 286 287 int numVertices() const { 288 return cast(int) _vertices.length; 289 } 290 291 /** 292 For convenience, we make two entire copies of the vertex list available: 293 vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == num_vertices(). 294 295 REQUIRES: 0 <= i < 2 * num_vertices() 296 */ 297 S2Point vertex(int i) const 298 in { 299 assert(i >= 0); 300 assert(i < 2 * numVertices()); 301 } do { 302 int j = i - numVertices(); 303 return _vertices[j < 0 ? i : j]; 304 } 305 306 const(S2Point[]) vertices() const { 307 return _vertices; 308 } 309 310 /** 311 Like vertex(), but this method returns vertices in reverse order if the 312 loop represents a polygon hole. For example, arguments 0, 1, 2 are 313 mapped to vertices n-1, n-2, n-3, where n == num_vertices(). This 314 ensures that the interior of the polygon is always to the left of the 315 vertex chain. 316 317 REQUIRES: 0 <= i < 2 * num_vertices() 318 */ 319 S2Point orientedVertex(int i) const 320 in { 321 assert(i >= 0); 322 assert(i < 2 * numVertices()); 323 } do { 324 int j = i - numVertices(); 325 if (j < 0) j = i; 326 if (isHole()) j = numVertices() - 1 - j; 327 return _vertices[j]; 328 } 329 330 /// Returns true if this is the special "empty" loop that contains no points. 331 bool isEmpty() const { 332 return isEmptyOrFull() && !containsOrigin(); 333 } 334 335 /// Returns true if this is the special "full" loop that contains all points. 336 bool isFull() const { 337 return isEmptyOrFull() && containsOrigin(); 338 } 339 340 /// Returns true if this loop is either "empty" or "full". 341 bool isEmptyOrFull() const { 342 return numVertices() == 1; 343 } 344 345 /** 346 The depth of a loop is defined as its nesting level within its containing 347 polygon. "Outer shell" loops have depth 0, holes within those loops have 348 depth 1, shells within those holes have depth 2, etc. This field is only 349 used by the S2Polygon implementation. 350 */ 351 int depth() const { 352 return _depth; 353 } 354 355 void setDepth(int depth) { 356 _depth = depth; 357 } 358 359 /// Returns true if this loop represents a hole in its containing polygon. 360 bool isHole() const { 361 return (_depth & 1) != 0; 362 } 363 364 /** 365 The sign of a loop is -1 if the loop represents a hole in its containing 366 polygon, and +1 otherwise. 367 */ 368 int sign() const { 369 return isHole() ? -1 : 1; 370 } 371 372 /** 373 Return true if the loop area is at most 2*Pi. Degenerate loops are 374 handled consistently with s2pred::Sign(), i.e., if a loop can be 375 expressed as the union of degenerate or nearly-degenerate CCW triangles, 376 then it will always be considered normalized. 377 */ 378 bool isNormalized() const { 379 // Optimization: if the longitude span is less than 180 degrees, then the 380 // loop covers less than half the sphere and is therefore normalized. 381 if (_bound.lng().getLength() < M_PI) return true; 382 383 // We allow some error so that hemispheres are always considered normalized. 384 // TODO(ericv): This is no longer required by the S2Polygon implementation, 385 // so alternatively we could create the invariant that a loop is normalized 386 // if and only if its complement is not normalized. 387 return getTurningAngle() >= -getTurningAngleMaxError(); 388 } 389 390 /// Invert the loop if necessary so that the area enclosed by the loop is at most 2*Pi. 391 void normalize() 392 out { 393 assert(isNormalized()); 394 } do { 395 if (!isNormalized()) invert(); 396 } 397 398 /** 399 Reverse the order of the loop vertices, effectively complementing the 400 region represented by the loop. For example, the loop ABCD (with edges 401 AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD). 402 Notice that the last edge is the same in both cases except that its 403 direction has been reversed. 404 */ 405 void invert() { 406 clearIndex(); 407 if (isEmptyOrFull()) { 408 _vertices[0] = isFull() ? emptyVertex() : fullVertex(); 409 } else { 410 reverse(_vertices); 411 } 412 // origin_inside_ must be set correctly before building the S2ShapeIndex. 413 _originInside ^= true; 414 if (_bound.lat().lo() > -M_PI_2 && _bound.lat().hi() < M_PI_2) { 415 // The complement of this loop contains both poles. 416 _subregionBound = _bound = S2LatLngRect.full(); 417 } else { 418 initBound(); 419 } 420 initIndex(); 421 } 422 423 /** 424 Return the area of the loop interior, i.e. the region on the left side of 425 the loop. The return value is between 0 and 4*Pi. (Note that the return 426 value is not affected by whether this loop is a "hole" or a "shell".) 427 */ 428 double getArea() const { 429 // It is suprisingly difficult to compute the area of a loop robustly. The 430 // main issues are (1) whether degenerate loops are considered to be CCW or 431 // not (i.e., whether their area is close to 0 or 4*Pi), and (2) computing 432 // the areas of small loops with good relative accuracy. 433 // 434 // With respect to degeneracies, we would like GetArea() to be consistent 435 // with S2Loop::Contains(S2Point) in that loops that contain many points 436 // should have large areas, and loops that contain few points should have 437 // small areas. For example, if a degenerate triangle is considered CCW 438 // according to s2pred::Sign(), then it will contain very few points and 439 // its area should be approximately zero. On the other hand if it is 440 // considered clockwise, then it will contain virtually all points and so 441 // its area should be approximately 4*Pi. 442 443 // More precisely, let U be the set of S2Points for which S2::IsUnitLength() 444 // is true, let P(U) be the projection of those points onto the mathematical 445 // unit sphere, and let V(P(U)) be the Voronoi diagram of the projected 446 // points. Then for every loop x, we would like GetArea() to approximately 447 // equal the sum of the areas of the Voronoi regions of the points p for 448 // which x.Contains(p) is true. 449 // 450 // The second issue is that we want to compute the area of small loops 451 // accurately. This requires having good relative precision rather than 452 // good absolute precision. For example, if the area of a loop is 1e-12 and 453 // the error is 1e-15, then the area only has 3 digits of accuracy. (For 454 // reference, 1e-12 is about 40 square meters on the surface of the earth.) 455 // We would like to have good relative accuracy even for small loops. 456 // 457 // To achieve these goals, we combine two different methods of computing the 458 // area. This first method is based on the Gauss-Bonnet theorem, which says 459 // that the area enclosed by the loop equals 2*Pi minus the total geodesic 460 // curvature of the loop (i.e., the sum of the "turning angles" at all the 461 // loop vertices). The big advantage of this method is that as long as we 462 // use s2pred::Sign() to compute the turning angle at each vertex, then 463 // degeneracies are always handled correctly. In other words, if a 464 // degenerate loop is CCW according to the symbolic perturbations used by 465 // s2pred::Sign(), then its turning angle will be approximately 2*Pi. 466 // 467 // The disadvantage of the Gauss-Bonnet method is that its absolute error is 468 // about 2e-15 times the number of vertices (see GetTurningAngleMaxError). 469 // So, it cannot compute the area of small loops accurately. 470 // 471 // The second method is based on splitting the loop into triangles and 472 // summing the area of each triangle. To avoid the difficulty and expense 473 // of decomposing the loop into a union of non-overlapping triangles, 474 // instead we compute a signed sum over triangles that may overlap (see the 475 // comments for S2Loop::GetSurfaceIntegral). The advantage of this method 476 // is that the area of each triangle can be computed with much better 477 // relative accuracy (using l'Huilier's theorem). The disadvantage is that 478 // the result is a signed area: CCW loops may yield a small positive value, 479 // while CW loops may yield a small negative value (which is converted to a 480 // positive area by adding 4*Pi). This means that small errors in computing 481 // the signed area may translate into a very large error in the result (if 482 // the sign of the sum is incorrect). 483 // 484 // So, our strategy is to combine these two methods as follows. First we 485 // compute the area using the "signed sum over triangles" approach (since it 486 // is generally more accurate). We also estimate the maximum error in this 487 // result. If the signed area is too close to zero (i.e., zero is within 488 // the error bounds), then we double-check the sign of the result using the 489 // Gauss-Bonnet method. (In fact we just call IsNormalized(), which is 490 // based on this method.) If the two methods disagree, we return either 0 491 // or 4*Pi based on the result of IsNormalized(). Otherwise we return the 492 // area that we computed originally. 493 494 if (isEmptyOrFull()) { 495 return containsOrigin() ? (4 * M_PI) : 0; 496 } 497 double area = getSurfaceIntegral(&signedArea); 498 499 // TODO(ericv): This error estimate is very approximate. There are two 500 // issues: (1) SignedArea needs some improvements to ensure that its error 501 // is actually never higher than GirardArea, and (2) although the number of 502 // triangles in the sum is typically N-2, in theory it could be as high as 503 // 2*N for pathological inputs. But in other respects this error bound is 504 // very conservative since it assumes that the maximum error is achieved on 505 // every triangle. 506 double max_error = getTurningAngleMaxError(); 507 508 // The signed area should be between approximately -4*Pi and 4*Pi. 509 enforce(fabs(area) <= 4 * M_PI + max_error); 510 if (area < 0) { 511 // We have computed the negative of the area of the loop exterior. 512 area += 4 * M_PI; 513 } 514 area = max(0.0, min(4 * M_PI, area)); 515 516 // If the area is close enough to zero or 4*Pi so that the loop orientation 517 // is ambiguous, then we compute the loop orientation explicitly. 518 if (area < max_error && !isNormalized()) { 519 return 4 * M_PI; 520 } else if (area > (4 * M_PI - max_error) && isNormalized()) { 521 return 0.0; 522 } else { 523 return area; 524 } 525 526 } 527 528 /** 529 Returns the true centroid of the loop multiplied by the area of the loop 530 (see s2centroids.h for details on centroids). The result is not unit 531 length, so you may want to normalize it. Also note that in general, the 532 centroid may not be contained by the loop. 533 534 We prescale by the loop area for two reasons: (1) it is cheaper to 535 compute this way, and (2) it makes it easier to compute the centroid of 536 more complicated shapes (by splitting them into disjoint regions and 537 adding their centroids). 538 539 Note that the return value is not affected by whether this loop is a 540 "hole" or a "shell". 541 */ 542 S2Point getCentroid() const { 543 // GetSurfaceIntegral() returns either the integral of position over loop 544 // interior, or the negative of the integral of position over the loop 545 // exterior. But these two values are the same (!), because the integral of 546 // position over the entire sphere is (0, 0, 0). 547 return getSurfaceIntegral(&trueCentroid); 548 } 549 550 /** 551 Returns the sum of the turning angles at each vertex. The return value is 552 positive if the loop is counter-clockwise, negative if the loop is 553 clockwise, and zero if the loop is a great circle. Degenerate and 554 nearly-degenerate loops are handled consistently with s2pred::Sign(). 555 So for example, if a loop has zero area (i.e., it is a very small CCW 556 loop) then the turning angle will always be negative. 557 558 This quantity is also called the "geodesic curvature" of the loop. 559 */ 560 double getTurningAngle() const { 561 // For empty and full loops, we return the limit value as the loop area 562 // approaches 0 or 4*Pi respectively. 563 if (isEmptyOrFull()) { 564 return containsOrigin() ? (-2 * M_PI) : (2 * M_PI); 565 } 566 // Don't crash even if the loop is not well-defined. 567 if (numVertices() < 3) return 0; 568 569 // To ensure that we get the same result when the vertex order is rotated, 570 // and that the result is negated when the vertex order is reversed, we need 571 // to add up the individual turn angles in a consistent order. (In general, 572 // adding up a set of numbers in a different order can change the sum due to 573 // rounding errors.) 574 // 575 // Furthermore, if we just accumulate an ordinary sum then the worst-case 576 // error is quadratic in the number of vertices. (This can happen with 577 // spiral shapes, where the partial sum of the turning angles can be linear 578 // in the number of vertices.) To avoid this we use the Kahan summation 579 // algorithm (http://en.wikipedia.org/wiki/Kahan_summation_algorithm). 580 581 int n = numVertices(); 582 int dir, i = getCanonicalFirstVertex(dir); 583 double sum = turnAngle(vertex((i + n - dir) % n), vertex(i), vertex((i + dir) % n)); 584 double compensation = 0; // Kahan summation algorithm 585 while (--n > 0) { 586 i += dir; 587 double angle = turnAngle(vertex(i - dir), vertex(i), vertex(i + dir)); 588 double old_sum = sum; 589 angle += compensation; 590 sum += angle; 591 compensation = (old_sum - sum) + angle; 592 } 593 return dir * (sum + compensation); 594 } 595 596 /** 597 Return the maximum error in GetTurningAngle(). The return value is not 598 constant; it depends on the loop. 599 */ 600 double getTurningAngleMaxError() const { 601 // The maximum error can be bounded as follows: 602 // 2.24 * DBL_EPSILON for RobustCrossProd(b, a) 603 // 2.24 * DBL_EPSILON for RobustCrossProd(c, b) 604 // 3.25 * DBL_EPSILON for Angle() 605 // 2.00 * DBL_EPSILON for each addition in the Kahan summation 606 // ------------------ 607 // 9.73 * DBL_EPSILON 608 const double kMaxErrorPerVertex = 9.73 * double.epsilon; 609 return kMaxErrorPerVertex * numVertices(); 610 } 611 612 /** 613 Returns the distance from the given point to the loop interior. If the 614 loop is empty, return S1Angle::Infinity(). "x" should be unit length. 615 */ 616 S1Angle getDistance(in S2Point x) { 617 // Note that S2Loop::Contains(S2Point) is slightly more efficient than the 618 // generic version used by S2ClosestEdgeQuery. 619 if (contains(x)) return S1Angle.zero(); 620 return getDistanceToBoundary(x); 621 } 622 623 /** 624 Returns the distance from the given point to the loop boundary. If the 625 loop is empty or full, return S1Angle::Infinity() (since the loop has no 626 boundary). "x" should be unit length. 627 */ 628 S1Angle getDistanceToBoundary(in S2Point x) { 629 auto options = new S2ClosestEdgeQuery.Options(); 630 options.setIncludeInteriors(false); 631 auto t = new S2ClosestEdgeQuery.PointTarget(x); 632 return new S2ClosestEdgeQuery(_index, options).getDistance(t).toS1Angle(); 633 } 634 635 /** 636 If the given point is contained by the loop, return it. Otherwise return 637 the closest point on the loop boundary. If the loop is empty, return the 638 input argument. Note that the result may or may not be contained by the 639 loop. "x" should be unit length. 640 */ 641 S2Point project(in S2Point x) { 642 if (contains(x)) return x; 643 return projectToBoundary(x); 644 } 645 646 /** 647 Return the closest point on the loop boundary to the given point. If the 648 loop is empty or full, return the input argument (since the loop has no 649 boundary). "x" should be unit length. 650 */ 651 S2Point projectToBoundary(in S2Point x) { 652 auto options = new S2ClosestEdgeQuery.Options(); 653 options.setIncludeInteriors(false); 654 auto q = new S2ClosestEdgeQuery(_index, options); 655 auto target = new S2ClosestEdgeQuery.PointTarget(x); 656 S2ClosestEdgeQuery.Result edge = q.findClosestEdge(target); 657 return q.project(x, edge); 658 } 659 660 /** 661 Returns true if the region contained by this loop is a superset of the 662 region contained by the given other loop. 663 */ 664 bool contains(S2Loop b) { 665 // For this loop A to contains the given loop B, all of the following must 666 // be true: 667 // 668 // (1) There are no edge crossings between A and B except at vertices. 669 // 670 // (2) At every vertex that is shared between A and B, the local edge 671 // ordering implies that A contains B. 672 // 673 // (3) If there are no shared vertices, then A must contain a vertex of B 674 // and B must not contain a vertex of A. (An arbitrary vertex may be 675 // chosen in each case.) 676 // 677 // The second part of (3) is necessary to detect the case of two loops whose 678 // union is the entire sphere, i.e. two loops that contains each other's 679 // boundaries but not each other's interiors. 680 if (!_subregionBound.contains(b._bound)) return false; 681 682 // Special cases to handle either loop being empty or full. 683 if (isEmptyOrFull() || b.isEmptyOrFull()) { 684 return isFull() || b.isEmpty(); 685 } 686 687 // Check whether there are any edge crossings, and also check the loop 688 // relationship at any shared vertices. 689 auto relation = new ContainsRelation(); 690 if (hasCrossingRelation(this, b, relation)) return false; 691 692 // There are no crossings, and if there are any shared vertices then A 693 // contains B locally at each shared vertex. 694 if (relation.foundSharedVertex()) return true; 695 696 // Since there are no edge intersections or shared vertices, we just need to 697 // test condition (3) above. We can skip this test if we discovered that A 698 // contains at least one point of B while checking for edge crossings. 699 if (!contains(b.vertex(0))) return false; 700 701 // We still need to check whether (A union B) is the entire sphere. 702 // Normally this check is very cheap due to the bounding box precondition. 703 if ((b._subregionBound.contains(_bound) || b._bound.unite(_bound).isFull()) 704 && b.contains(vertex(0))) { 705 return false; 706 } 707 return true; 708 } 709 710 /** 711 Returns true if the region contained by this loop intersects the region 712 contained by the given other loop. 713 */ 714 bool intersects(S2Loop b) { 715 // a->Intersects(b) if and only if !a->Complement()->Contains(b). 716 // This code is similar to Contains(), but is optimized for the case 717 // where both loops enclose less than half of the sphere. 718 if (!_bound.intersects(b._bound)) return false; 719 720 // Check whether there are any edge crossings, and also check the loop 721 // relationship at any shared vertices. 722 auto relation = new IntersectsRelation(); 723 if (hasCrossingRelation(this, b, relation)) return true; 724 if (relation.foundSharedVertex()) return false; 725 726 // Since there are no edge intersections or shared vertices, the loops 727 // intersect only if A contains B, B contains A, or the two loops contain 728 // each other's boundaries. These checks are usually cheap because of the 729 // bounding box preconditions. Note that neither loop is empty (because of 730 // the bounding box check above), so it is safe to access vertex(0). 731 732 // Check whether A contains B, or A and B contain each other's boundaries. 733 // (Note that A contains all the vertices of B in either case.) 734 if (_subregionBound.contains(b._bound) || _bound.unite(b._bound).isFull()) { 735 if (contains(b.vertex(0))) return true; 736 } 737 // Check whether B contains A. 738 if (b._subregionBound.contains(_bound)) { 739 if (b.contains(vertex(0))) return true; 740 } 741 return false; 742 } 743 744 /** 745 Returns true if two loops have the same vertices in the same linear order 746 (i.e., cyclic rotations are not allowed). 747 */ 748 bool equals(in S2Loop b) const { 749 if (numVertices() != b.numVertices()) return false; 750 for (int i = 0; i < numVertices(); ++i) { 751 if (vertex(i) != b.vertex(i)) return false; 752 } 753 return true; 754 } 755 756 /** 757 Returns true if two loops have the same boundary. This is true if and 758 only if the loops have the same vertices in the same cyclic order (i.e., 759 the vertices may be cyclically rotated). The empty and full loops are 760 considered to have different boundaries. 761 */ 762 bool boundaryEquals(in S2Loop b) const { 763 if (numVertices() != b.numVertices()) return false; 764 765 // Special case to handle empty or full loops. Since they have the same 766 // number of vertices, if one loop is empty/full then so is the other. 767 if (isEmptyOrFull()) return isEmpty() == b.isEmpty(); 768 769 for (int offset = 0; offset < numVertices(); ++offset) { 770 if (vertex(offset) == b.vertex(0)) { 771 // There is at most one starting offset since loop vertices are unique. 772 for (int i = 0; i < numVertices(); ++i) { 773 if (vertex(i + offset) != b.vertex(i)) return false; 774 } 775 return true; 776 } 777 } 778 return false; 779 } 780 781 /** 782 Returns true if two loops have the same boundary except for vertex 783 perturbations. More precisely, the vertices in the two loops must be in 784 the same cyclic order, and corresponding vertex pairs must be separated 785 by no more than "max_error". 786 */ 787 bool boundaryApproxEquals(in S2Loop b, S1Angle max_error = S1Angle.fromRadians(1e-15)) const { 788 if (numVertices() != b.numVertices()) return false; 789 790 // Special case to handle empty or full loops. Since they have the same 791 // number of vertices, if one loop is empty/full then so is the other. 792 if (isEmptyOrFull()) return isEmpty() == b.isEmpty(); 793 794 for (int offset = 0; offset < numVertices(); ++offset) { 795 if (approxEquals(vertex(offset), b.vertex(0), max_error)) { 796 bool success = true; 797 for (int i = 0; i < numVertices(); ++i) { 798 if (!approxEquals(vertex(i + offset), b.vertex(i), max_error)) { 799 success = false; 800 break; 801 } 802 } 803 if (success) return true; 804 // Otherwise continue looping. There may be more than one candidate 805 // starting offset since vertices are only matched approximately. 806 } 807 } 808 return false; 809 } 810 811 /** 812 Returns true if the two loop boundaries are within "max_error" of each 813 other along their entire lengths. The two loops may have different 814 numbers of vertices. More precisely, this method returns true if the two 815 loops have parameterizations a:[0,1] -> S^2, b:[0,1] -> S^2 such that 816 distance(a(t), b(t)) <= max_error for all t. You can think of this as 817 testing whether it is possible to drive two cars all the way around the 818 two loops such that no car ever goes backward and the cars are always 819 within "max_error" of each other. 820 */ 821 bool boundaryNear(in S2Loop b, S1Angle max_error = S1Angle.fromRadians(1e-15)) const { 822 // Special case to handle empty or full loops. 823 if (isEmptyOrFull() || b.isEmptyOrFull()) { 824 return (isEmpty() && b.isEmpty()) || (isFull() && b.isFull()); 825 } 826 827 for (int a_offset = 0; a_offset < numVertices(); ++a_offset) { 828 if (matchBoundaries(this, b, a_offset, max_error)) return true; 829 } 830 return false; 831 } 832 833 /** 834 This method computes the oriented surface integral of some quantity f(x) 835 over the loop interior, given a function f_tri(A,B,C) that returns the 836 corresponding integral over the spherical triangle ABC. Here "oriented 837 surface integral" means: 838 839 (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise, 840 and the integral of -f if ABC is clockwise. 841 842 (2) The result of this function is *either* the integral of f over the 843 loop interior, or the integral of (-f) over the loop exterior. 844 845 Note that there are at least two common situations where it easy to work 846 around property (2) above: 847 848 - If the integral of f over the entire sphere is zero, then it doesn't 849 matter which case is returned because they are always equal. 850 851 - If f is non-negative, then it is easy to detect when the integral over 852 the loop exterior has been returned, and the integral over the loop 853 interior can be obtained by adding the integral of f over the entire 854 unit sphere (a constant) to the result. 855 856 Also requires that the default constructor for T must initialize the 857 value to zero. (This is true for built-in types such as "double".) 858 */ 859 T getSurfaceIntegral(T)( 860 T function(in Vector!(double, 3), in Vector!(double, 3), in Vector!(double, 3)) fTri) const { 861 // We sum "f_tri" over a collection T of oriented triangles, possibly 862 // overlapping. Let the sign of a triangle be +1 if it is CCW and -1 863 // otherwise, and let the sign of a point "x" be the sum of the signs of the 864 // triangles containing "x". Then the collection of triangles T is chosen 865 // such that either: 866 // 867 // (1) Each point in the loop interior has sign +1, and sign 0 otherwise; or 868 // (2) Each point in the loop exterior has sign -1, and sign 0 otherwise. 869 // 870 // The triangles basically consist of a "fan" from vertex 0 to every loop 871 // edge that does not include vertex 0. These triangles will always satisfy 872 // either (1) or (2). However, what makes this a bit tricky is that 873 // spherical edges become numerically unstable as their length approaches 874 // 180 degrees. Of course there is not much we can do if the loop itself 875 // contains such edges, but we would like to make sure that all the triangle 876 // edges under our control (i.e., the non-loop edges) are stable. For 877 // example, consider a loop around the equator consisting of four equally 878 // spaced points. This is a well-defined loop, but we cannot just split it 879 // into two triangles by connecting vertex 0 to vertex 2. 880 // 881 // We handle this type of situation by moving the origin of the triangle fan 882 // whenever we are about to create an unstable edge. We choose a new 883 // location for the origin such that all relevant edges are stable. We also 884 // create extra triangles with the appropriate orientation so that the sum 885 // of the triangle signs is still correct at every point. 886 887 // The maximum length of an edge for it to be considered numerically stable. 888 // The exact value is fairly arbitrary since it depends on the stability of 889 // the "f_tri" function. The value below is quite conservative but could be 890 // reduced further if desired. 891 const auto kMaxLength = S1ChordAngle.fromRadians(M_PI - 1e-5); 892 893 // The default constructor for T must initialize the value to zero. 894 // (This is true for built-in types such as "double".) 895 T sum = to!T(0); 896 S2Point origin = vertex(0); 897 for (int i = 1; i + 1 < numVertices(); ++i) { 898 // Let V_i be vertex(i), let O be the current origin, and let length(A,B) 899 // be the length of edge (A,B). At the start of each loop iteration, the 900 // "leading edge" of the triangle fan is (O,V_i), and we want to extend 901 // the triangle fan so that the leading edge is (O,V_i+1). 902 // 903 // Invariants: 904 // 1. length(O,V_i) < kMaxLength for all (i > 1). 905 // 2. Either O == V_0, or O is approximately perpendicular to V_0. 906 // 3. "sum" is the oriented integral of f over the area defined by 907 // (O, V_0, V_1, ..., V_i). 908 enforce(i == 1 || S1ChordAngle(origin, vertex(i)) < kMaxLength); 909 enforce(origin == vertex(0) || fabs(origin.dotProd(vertex(0))) < 1e-15); 910 911 if (S1ChordAngle(vertex(i + 1), origin) > kMaxLength) { 912 // We are about to create an unstable edge, so choose a new origin O' 913 // for the triangle fan. 914 S2Point old_origin = origin; 915 if (origin == vertex(0)) { 916 // The following point is well-separated from V_i and V_0 (and 917 // therefore V_i+1 as well). 918 origin = robustCrossProd(vertex(0), vertex(i)).normalize(); 919 } else if (S1ChordAngle(vertex(i), vertex(0)) < kMaxLength) { 920 // All edges of the triangle (O, V_0, V_i) are stable, so we can 921 // revert to using V_0 as the origin. 922 origin = vertex(0); 923 } else { 924 // (O, V_i+1) and (V_0, V_i) are antipodal pairs, and O and V_0 are 925 // perpendicular. Therefore V_0.CrossProd(O) is approximately 926 // perpendicular to all of {O, V_0, V_i, V_i+1}, and we can choose 927 // this point O' as the new origin. 928 origin = vertex(0).crossProd(old_origin); 929 930 // Advance the edge (V_0,O) to (V_0,O'). 931 sum += fTri(vertex(0), old_origin, origin); 932 } 933 // Advance the edge (O,V_i) to (O',V_i). 934 sum += fTri(old_origin, vertex(i), origin); 935 } 936 // Advance the edge (O,V_i) to (O,V_i+1). 937 sum += fTri(origin, vertex(i), vertex(i+1)); 938 } 939 // If the origin is not V_0, we need to sum one more triangle. 940 if (origin != vertex(0)) { 941 // Advance the edge (O,V_n-1) to (O,V_0). 942 sum += fTri(origin, vertex(numVertices() - 1), vertex(0)); 943 } 944 return sum; 945 } 946 947 /** 948 Constructs a regular polygon with the given number of vertices, all 949 located on a circle of the specified radius around "center". The radius 950 is the actual distance from "center" to each vertex. 951 */ 952 static S2Loop makeRegularLoop(in S2Point center, S1Angle radius, int num_vertices) { 953 Matrix3x3_d m; 954 getFrame(center, m); // TODO(ericv): Return by value 955 return makeRegularLoop(m, radius, num_vertices); 956 } 957 958 /** 959 Like the function above, but this version constructs a loop centered 960 around the z-axis of the given coordinate frame, with the first vertex in 961 the direction of the positive x-axis. (This allows the loop to be 962 rotated for testing purposes.) 963 */ 964 static S2Loop makeRegularLoop(in Matrix3x3_d frame, S1Angle radius, int num_vertices) { 965 // We construct the loop in the given frame coordinates, with the center at 966 // (0, 0, 1). For a loop of radius "r", the loop vertices have the form 967 // (x, y, z) where x^2 + y^2 = sin(r) and z = cos(r). The distance on the 968 // sphere (arc length) from each vertex to the center is acos(cos(r)) = r. 969 double z = cos(radius.radians()); 970 double r = sin(radius.radians()); 971 double radian_step = 2 * M_PI / num_vertices; 972 S2Point[] vertices; 973 for (int i = 0; i < num_vertices; ++i) { 974 double angle = i * radian_step; 975 auto p = S2Point(r * cos(angle), r * sin(angle), z); 976 vertices ~= fromFrame(frame, p).normalize(); 977 } 978 return new S2Loop(vertices); 979 } 980 981 /// Returns the total number of bytes used by the loop. 982 size_t spaceUsed() const { 983 size_t size = this.classinfo.m_init.sizeof; 984 size += numVertices() * S2Point.sizeof; 985 // index_ itself is already included in sizeof(*this). 986 size += _index.spaceUsed() - _index.sizeof; 987 return size; 988 } 989 990 //////////////////////////////////////////////////////////////////////// 991 // S2Region interface (see s2region.h for details): 992 993 override 994 S2Loop clone() const { 995 return new S2Loop(this); 996 } 997 998 /** 999 GetRectBound() returns essentially tight results, while GetCapBound() 1000 might have a lot of extra padding. Both bounds are conservative in that 1001 if the loop contains a point P, then the bound contains P also. 1002 */ 1003 override 1004 S2Cap getCapBound() const { 1005 return _bound.getCapBound(); 1006 } 1007 1008 override 1009 S2LatLngRect getRectBound() { 1010 return _bound; 1011 } 1012 1013 override 1014 void getCellUnionBound(out S2CellId[] cell_ids) const { 1015 return getCapBound().getCellUnionBound(cell_ids); 1016 } 1017 1018 override 1019 bool contains(in S2Cell target) { 1020 auto it = new MutableS2ShapeIndex.Iterator(_index); 1021 S2ShapeIndex.CellRelation relation = it.locate(target.id()); 1022 1023 // If "target" is disjoint from all index cells, it is not contained. 1024 // Similarly, if "target" is subdivided into one or more index cells then it 1025 // is not contained, since index cells are subdivided only if they (nearly) 1026 // intersect a sufficient number of edges. (But note that if "target" itself 1027 // is an index cell then it may be contained, since it could be a cell with 1028 // no edges in the loop interior.) 1029 if (relation != S2ShapeIndex.CellRelation.INDEXED) return false; 1030 1031 // Otherwise check if any edges intersect "target". 1032 if (boundaryApproxIntersects(it, target)) return false; 1033 1034 // Otherwise check if the loop contains the center of "target". 1035 return contains(it, target.getCenter()); 1036 } 1037 1038 override 1039 bool mayIntersect(in S2Cell target) { 1040 auto it = new MutableS2ShapeIndex.Iterator(_index); 1041 S2ShapeIndex.CellRelation relation = it.locate(target.id()); 1042 1043 // If "target" does not overlap any index cell, there is no intersection. 1044 if (relation == S2ShapeIndex.CellRelation.DISJOINT) return false; 1045 1046 // If "target" is subdivided into one or more index cells, there is an 1047 // intersection to within the S2ShapeIndex error bound (see Contains). 1048 if (relation == S2ShapeIndex.CellRelation.SUBDIVIDED) return true; 1049 1050 // If "target" is an index cell, there is an intersection because index cells 1051 // are created only if they have at least one edge or they are entirely 1052 // contained by the loop. 1053 if (it.id() == target.id()) return true; 1054 1055 // Otherwise check if any edges intersect "target". 1056 if (boundaryApproxIntersects(it, target)) return true; 1057 1058 // Otherwise check if the loop contains the center of "target". 1059 return contains(it, target.getCenter()); 1060 } 1061 1062 // The point 'p' does not need to be normalized. 1063 override 1064 bool contains(in S2Point p) { 1065 // NOTE(ericv): A bounds check slows down this function by about 50%. It is 1066 // worthwhile only when it might allow us to delay building the index. 1067 if (!_index.isFresh() && !_bound.contains(p)) return false; 1068 1069 // For small loops it is faster to just check all the crossings. We also 1070 // use this method during loop initialization because InitOriginAndBound() 1071 // calls Contains() before InitIndex(). Otherwise, we keep track of the 1072 // number of calls to Contains() and only build the index when enough calls 1073 // have been made so that we think it is worth the effort. Note that the 1074 // code below is structured so that if many calls are made in parallel only 1075 // one thread builds the index, while the rest continue using brute force 1076 // until the index is actually available. 1077 // 1078 // The constants below were tuned using the benchmarks. It turns out that 1079 // building the index costs roughly 50x as much as Contains(). (The ratio 1080 // increases slowly from 46x with 64 points to 61x with 256k points.) The 1081 // textbook approach to this problem would be to wait until the cumulative 1082 // time we would have saved with an index approximately equals the cost of 1083 // building the index, and then build it. (This gives the optimal 1084 // competitive ratio of 2; look up "competitive algorithms" for details.) 1085 // We set the limit somewhat lower than this (20 rather than 50) because 1086 // building the index may be forced anyway by other API calls, and so we 1087 // want to err on the side of building it too early. 1088 1089 enum int kMaxBruteForceVertices = 32; 1090 enum int kMaxUnindexedContainsCalls = 20; // See notes above. 1091 if (_index.numShapeIds() == 0 // InitIndex() not called yet 1092 || numVertices() <= kMaxBruteForceVertices 1093 || (!_index.isFresh() && atomicOp!"+="(_unindexedContainsCalls, 1) != kMaxUnindexedContainsCalls)) { 1094 return bruteForceContains(p); 1095 } 1096 // Otherwise we look up the S2ShapeIndex cell containing this point. Note 1097 // the index is built automatically the first time an iterator is created. 1098 auto it = new MutableS2ShapeIndex.Iterator(_index); 1099 if (!it.locate(p)) return false; 1100 return contains(it, p); 1101 } 1102 1103 /** 1104 Appends a serialized representation of the S2Loop to "encoder". 1105 1106 Generally clients should not use S2Loop::Encode(). Instead they should 1107 encode an S2Polygon, which unlike this method supports (lossless) 1108 compression. 1109 1110 REQUIRES: "encoder" uses the default constructor, so that its buffer 1111 can be enlarged as necessary by calling Ensure(int). 1112 */ 1113 void encode(ORangeT)(Encoder!ORangeT encoder) const 1114 out (; encoder.avail >= 0) { 1115 encoder.ensure(numVertices() * S2Point.sizeof + 20); // sufficient 1116 1117 encoder.put8(CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER); 1118 encoder.put32(numVertices()); 1119 encoder.putRaw(_vertices); 1120 encoder.put8(_originInside); 1121 encoder.put32(_depth); 1122 1123 _bound.encode(encoder); 1124 } 1125 1126 /** 1127 Decodes a loop encoded with Encode() or the private method 1128 EncodeCompressed() (used by the S2Polygon encoder). Returns true on 1129 success. 1130 1131 This method may be called with loops that have already been initialized. 1132 */ 1133 bool decode(IRangeT)(Decoder!IRangeT decoder) { 1134 if (decoder.avail() < ubyte.sizeof) return false; 1135 ubyte versionNum = decoder.get8(); 1136 switch (versionNum) { 1137 case CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER: 1138 return decodeInternal(decoder); 1139 default: 1140 return false; 1141 } 1142 } 1143 1144 // Provides the same functionality as Decode, except that decoded regions 1145 // are allowed to point directly into the Decoder's memory buffer rather 1146 // than copying the data. This can be much faster, but the decoded loop is 1147 // only valid within the scope (lifetime) of the Decoder's memory buffer. 1148 // bool DecodeWithinScope(Decoder* const decoder); 1149 1150 //////////////////////////////////////////////////////////////////////// 1151 // Methods intended primarily for use by the S2Polygon implementation: 1152 1153 /** 1154 Given two loops of a polygon, return true if A contains B. This version 1155 of Contains() is cheap because it does not test for edge intersections. 1156 The loops must meet all the S2Polygon requirements; for example this 1157 implies that their boundaries may not cross or have any shared edges 1158 (although they may have shared vertices). 1159 */ 1160 bool containsNested(S2Loop b) { 1161 if (!_subregionBound.contains(b._bound)) return false; 1162 1163 // Special cases to handle either loop being empty or full. Also bail out 1164 // when B has no vertices to avoid heap overflow on the vertex(1) call 1165 // below. (This method is called during polygon initialization before the 1166 // client has an opportunity to call IsValid().) 1167 if (isEmptyOrFull() || b.numVertices() < 2) { 1168 return isFull() || b.isEmpty(); 1169 } 1170 1171 // We are given that A and B do not share any edges, and that either one 1172 // loop contains the other or they do not intersect. 1173 int m = findVertex(b.vertex(1)); 1174 if (m < 0) { 1175 // Since b->vertex(1) is not shared, we can check whether A contains it. 1176 return contains(b.vertex(1)); 1177 } 1178 // Check whether the edge order around b->vertex(1) is compatible with 1179 // A containing B. 1180 return wedgeContains(vertex(m-1), vertex(m), vertex(m+1), b.vertex(0), b.vertex(2)); 1181 } 1182 1183 /** 1184 Return +1 if A contains the boundary of B, -1 if A excludes the boundary 1185 of B, and 0 if the boundaries of A and B cross. Shared edges are handled 1186 as follows: If XY is a shared edge, define Reversed(XY) to be true if XY 1187 appears in opposite directions in A and B. Then A contains XY if and 1188 only if Reversed(XY) == B->is_hole(). (Intuitively, this checks whether 1189 A contains a vanishingly small region extending from the boundary of B 1190 toward the interior of the polygon to which loop B belongs.) 1191 1192 This method is used for testing containment and intersection of 1193 multi-loop polygons. Note that this method is not symmetric, since the 1194 result depends on the direction of loop A but not on the direction of 1195 loop B (in the absence of shared edges). 1196 1197 REQUIRES: neither loop is empty. 1198 REQUIRES: if b->is_full(), then !b->is_hole(). 1199 */ 1200 int compareBoundary(S2Loop b) 1201 in { 1202 assert(!isEmpty() && !b.isEmpty()); 1203 assert(!b.isFull() || !b.isHole()); 1204 } do { 1205 // The bounds must intersect for containment or crossing. 1206 if (!_bound.intersects(b._bound)) return -1; 1207 1208 // Full loops are handled as though the loop surrounded the entire sphere. 1209 if (isFull()) return 1; 1210 if (b.isFull()) return -1; 1211 1212 // Check whether there are any edge crossings, and also check the loop 1213 // relationship at any shared vertices. 1214 auto relation = new CompareBoundaryRelation(b.isHole()); 1215 if (hasCrossingRelation(this, b, relation)) return 0; 1216 if (relation.foundSharedVertex()) { 1217 return relation.containsEdge() ? 1 : -1; 1218 } 1219 1220 // There are no edge intersections or shared vertices, so we can check 1221 // whether A contains an arbitrary vertex of B. 1222 return contains(b.vertex(0)) ? 1 : -1; 1223 } 1224 1225 /** 1226 Given two loops whose boundaries do not cross (see CompareBoundary), 1227 return true if A contains the boundary of B. If "reverse_b" is true, the 1228 boundary of B is reversed first (which only affects the result when there 1229 are shared edges). This method is cheaper than CompareBoundary() because 1230 it does not test for edge intersections. 1231 1232 REQUIRES: neither loop is empty. 1233 REQUIRES: if b->is_full(), then reverse_b == false. 1234 */ 1235 package bool containsNonCrossingBoundary(in S2Loop b, bool reverse_b) 1236 in { 1237 assert(!isEmpty() && !b.isEmpty()); 1238 assert(!b.isFull() || !reverse_b); 1239 } do { 1240 // The bounds must intersect for containment. 1241 if (!_bound.intersects(b._bound)) return false; 1242 1243 // Full loops are handled as though the loop surrounded the entire sphere. 1244 if (isFull()) return true; 1245 if (b.isFull()) return false; 1246 1247 int m = findVertex(b.vertex(0)); 1248 if (m < 0) { 1249 // Since vertex b0 is not shared, we can check whether A contains it. 1250 return contains(b.vertex(0)); 1251 } 1252 // Otherwise check whether the edge (b0, b1) is contained by A. 1253 return wedgeContainsSemiwedge(vertex(m-1), vertex(m), vertex(m+1), b.vertex(1), reverse_b); 1254 } 1255 1256 /** 1257 Wrapper class for indexing a loop (see S2ShapeIndex). Once this object 1258 is inserted into an S2ShapeIndex it is owned by that index, and will be 1259 automatically deleted when no longer needed by the index. Note that this 1260 class does not take ownership of the loop itself (see OwningShape below). 1261 You can also subtype this class to store additional data (see S2Shape for 1262 details). 1263 */ 1264 static class Shape : S2Shape { 1265 public: 1266 /// Must call Init(). 1267 this() { 1268 _loop = null; 1269 } 1270 1271 /// Initialize the shape. Does not take ownership of "loop". 1272 this(S2Loop loop) { 1273 initialize(loop); 1274 } 1275 1276 void initialize(S2Loop loop) { 1277 _loop = loop; 1278 } 1279 1280 inout(S2Loop) loop() inout { 1281 return _loop; 1282 } 1283 1284 // S2Shape interface: 1285 final override 1286 int numEdges() const { 1287 return _loop.isEmptyOrFull() ? 0 : _loop.numVertices(); 1288 } 1289 1290 final override 1291 Edge edge(int e) const { 1292 return S2Shape.Edge(_loop.vertex(e), _loop.vertex(e + 1)); 1293 } 1294 1295 final override 1296 int dimension() const { 1297 return 2; 1298 } 1299 1300 final override 1301 S2Shape.ReferencePoint getReferencePoint() const { 1302 return S2Shape.ReferencePoint(origin(), _loop.containsOrigin()); 1303 } 1304 1305 final override 1306 int numChains() const { 1307 return _loop.isEmptyOrFull() ? 0 : 1; 1308 } 1309 1310 final override 1311 Chain chain(int i) const 1312 in { 1313 assert(i == 0); 1314 } do { 1315 return Chain(0, numEdges()); 1316 } 1317 1318 final override 1319 Edge chainEdge(int i, int j) const 1320 in { 1321 assert(i == 0); 1322 } do { 1323 return Edge(_loop.vertex(j), _loop.vertex(j + 1)); 1324 } 1325 1326 final override 1327 ChainPosition chainPosition(int e) const { 1328 return ChainPosition(0, e); 1329 } 1330 1331 private: 1332 S2Loop _loop; 1333 } 1334 1335 // Like Shape, except that the S2Loop is automatically deleted when this 1336 // object is deleted by the S2ShapeIndex. This is useful when an S2Loop 1337 // is constructed solely for the purpose of indexing it. 1338 // 1339 // Not needed in GC languages. 1340 // class OwningShape : public Shape { ... } 1341 1342 int opCmp(in S2Loop other) const { 1343 import std.algorithm : cmp; 1344 return cmp(_vertices, other._vertices); 1345 } 1346 1347 package: 1348 /// Returns true if this loop contains S2.origin(). 1349 bool containsOrigin() const { 1350 return _originInside; 1351 } 1352 1353 package: 1354 /// Internal copy constructor used only by Clone() that makes a deep copy of its argument. 1355 this(in S2Loop src) { 1356 this(); 1357 _depth = src._depth; 1358 _vertices = src._vertices.dup; 1359 _s2DebugOverride = src._s2DebugOverride; 1360 _originInside = src._originInside; 1361 _unindexedContainsCalls = 0; 1362 _bound = src._bound.clone(); 1363 _subregionBound = src._subregionBound.clone(); 1364 initIndex(); 1365 } 1366 1367 private: 1368 // Any single-vertex loop is interpreted as being either the empty loop or the 1369 // full loop, depending on whether the vertex is in the northern or southern 1370 // hemisphere respectively. 1371 1372 /// The single vertex in the "empty loop" vertex chain. 1373 static S2Point emptyVertex() { 1374 return S2Point(0, 0, 1); 1375 } 1376 1377 /// The single vertex in the "full loop" vertex chain. 1378 static S2Point fullVertex() { 1379 return S2Point(0, 0, -1); 1380 } 1381 1382 void initOriginAndBound() { 1383 import s2.s2predicates : orderedCCW; 1384 import s2.s2pointutil : ortho; 1385 if (numVertices() < 3) { 1386 // Check for the special "empty" and "full" loops (which have one vertex). 1387 if (!isEmptyOrFull()) { 1388 _originInside = false; 1389 return; // Bail out without trying to access non-existent vertices. 1390 } 1391 // If the vertex is in the southern hemisphere then the loop is full, 1392 // otherwise it is empty. 1393 _originInside = (vertex(0).z() < 0); 1394 } else { 1395 // Point containment testing is done by counting edge crossings starting 1396 // at a fixed point on the sphere (S2::Origin()). Historically this was 1397 // important, but it is now no longer necessary, and it may be worthwhile 1398 // experimenting with using a loop vertex as the reference point. In any 1399 // case, we need to know whether the reference point (S2::Origin) is 1400 // inside or outside the loop before we can construct the S2ShapeIndex. 1401 // We do this by first guessing that it is outside, and then seeing 1402 // whether we get the correct containment result for vertex 1. If the 1403 // result is incorrect, the origin must be inside the loop. 1404 // 1405 // A loop with consecutive vertices A,B,C contains vertex B if and only if 1406 // the fixed vector R = S2::Ortho(B) is contained by the wedge ABC. The 1407 // wedge is closed at A and open at C, i.e. the point B is inside the loop 1408 // if A=R but not if C=R. This convention is required for compatibility 1409 // with S2::VertexCrossing. (Note that we can't use S2::Origin() 1410 // as the fixed vector because of the possibility that B == S2::Origin().) 1411 // 1412 // TODO(ericv): Investigate using vertex(0) as the reference point. 1413 1414 _originInside = false; // Initialize before calling Contains(). 1415 bool v1_inside = orderedCCW(ortho(vertex(1)), vertex(0), vertex(2), vertex(1)); 1416 // Note that Contains(S2Point) only does a bounds check once InitIndex() 1417 // has been called, so it doesn't matter that bound_ is undefined here. 1418 if (v1_inside != contains(vertex(1))) { 1419 _originInside = true; 1420 } 1421 } 1422 // We *must* call InitBound() before InitIndex(), because InitBound() calls 1423 // Contains(S2Point), and Contains(S2Point) does a bounds check whenever the 1424 // index is not fresh (i.e., the loop has been added to the index but the 1425 // index has not been updated yet). 1426 // 1427 // TODO(ericv): When fewer S2Loop methods depend on internal bounds checks, 1428 // consider computing the bound on demand as well. 1429 initBound(); 1430 initIndex(); 1431 } 1432 1433 void initBound() { 1434 // Check for the special "empty" and "full" loops. 1435 if (isEmptyOrFull()) { 1436 if (isEmpty()) { 1437 _subregionBound = _bound = S2LatLngRect.empty(); 1438 } else { 1439 _subregionBound = _bound = S2LatLngRect.full(); 1440 } 1441 return; 1442 } 1443 1444 // The bounding rectangle of a loop is not necessarily the same as the 1445 // bounding rectangle of its vertices. First, the maximal latitude may be 1446 // attained along the interior of an edge. Second, the loop may wrap 1447 // entirely around the sphere (e.g. a loop that defines two revolutions of a 1448 // candy-cane stripe). Third, the loop may include one or both poles. 1449 // Note that a small clockwise loop near the equator contains both poles. 1450 1451 auto bounder = new S2LatLngRectBounder(); 1452 for (int i = 0; i <= numVertices(); ++i) { 1453 bounder.addPoint(vertex(i)); 1454 } 1455 S2LatLngRect b = bounder.getBound(); 1456 if (contains(S2Point(0, 0, 1))) { 1457 b = new S2LatLngRect(R1Interval(b.lat().lo(), M_PI_2), S1Interval.full()); 1458 } 1459 // If a loop contains the south pole, then either it wraps entirely 1460 // around the sphere (full longitude range), or it also contains the 1461 // north pole in which case b.lng().is_full() due to the test above. 1462 // Either way, we only need to do the south pole containment test if 1463 // b.lng().is_full(). 1464 if (b.lng().isFull() && contains(S2Point(0, 0, -1))) { 1465 b.mutableLat().setLo(-M_PI_2); 1466 } 1467 _bound = b; 1468 _subregionBound = S2LatLngRectBounder.expandForSubregions(_bound); 1469 } 1470 1471 void initIndex() { 1472 import s2.s2debug : flagsS2Debug; 1473 _index.add(new Shape(this)); 1474 if (!LAZY_INDEXING) { 1475 _index.forceBuild(); 1476 } 1477 if (flagsS2Debug && _s2DebugOverride == S2Debug.ALLOW) { 1478 // Note that FLAGS_s2debug is false in optimized builds (by default). 1479 enforce(isValid()); 1480 } 1481 } 1482 1483 // A version of Contains(S2Point) that does not use the S2ShapeIndex. 1484 // Used by the S2Polygon implementation. 1485 package bool bruteForceContains(in S2Point p) const { 1486 // Empty and full loops don't need a special case, but invalid loops with 1487 // zero vertices do, so we might as well handle them all at once. 1488 if (numVertices() < 3) return _originInside; 1489 1490 S2Point origin = origin(); 1491 auto crosser = new S2CopyingEdgeCrosser(origin, p, vertex(0)); 1492 bool inside = _originInside; 1493 for (int i = 1; i <= numVertices(); ++i) { 1494 inside ^= crosser.edgeOrVertexCrossing(vertex(i)); 1495 } 1496 return inside; 1497 } 1498 1499 /** 1500 Internal implementation of the Decode and DecodeWithinScope methods above. 1501 If within_scope is true, memory is allocated for vertices_ and data 1502 is copied from the decoder using std::copy. If it is false, vertices_ 1503 will point to the memory area inside the decoder, and the field 1504 owns_vertices_ is set to false. 1505 */ 1506 bool decodeInternal(IRangeT)(Decoder!IRangeT decoder) { 1507 // Perform all checks before modifying vertex state. Empty loops are 1508 // explicitly allowed here: a newly created loop has zero vertices 1509 // and such loops encode and decode properly. 1510 if (decoder.avail() < uint.sizeof) return false; 1511 uint num_vertices = decoder.get32(); 1512 if (num_vertices > DECODE_MAX_NUM_VERTICES) { 1513 return false; 1514 } 1515 if (decoder.avail() < (num_vertices * S2Point.sizeof + ubyte.sizeof + uint.sizeof)) { 1516 return false; 1517 } 1518 clearIndex(); 1519 _vertices = decoder.getRaw!S2Point(num_vertices); 1520 1521 _originInside = cast(bool) decoder.get8(); 1522 _depth = decoder.get32(); 1523 if (!_bound.decode(decoder)) return false; 1524 _subregionBound = S2LatLngRectBounder.expandForSubregions(_bound); 1525 1526 // An initialized loop will have some non-zero count of vertices. A default 1527 // (uninitialized) has zero vertices. This code supports encoding and 1528 // decoding of uninitialized loops, but we only want to call InitIndex for 1529 // initialized loops. Otherwise we defer InitIndex until the call to Init(). 1530 if (numVertices() > 0) { 1531 initIndex(); 1532 } 1533 1534 return true; 1535 } 1536 1537 /** 1538 * Converts the loop vertices to the S2XYZFaceSiTi format and store the result 1539 * in the given array, which must be large enough to store all the vertices. 1540 */ 1541 package void getXYZFaceSiTiVertices(ref S2XYZFaceSiTi[] vertices, size_t current_index) const { 1542 for (int i = 0; i < numVertices(); ++i) { 1543 size_t index = current_index + i; 1544 vertices[index].xyz = vertex(i); 1545 vertices[index].cellLevel = XYZtoFaceSiTi( 1546 vertices[index].xyz, vertices[index].face, vertices[index].si, vertices[index].ti); 1547 } 1548 } 1549 1550 // Encode the loop's vertices using S2EncodePointsCompressed. Uses 1551 // approximately 8 bytes for the first vertex, going down to less than 4 bytes 1552 // per vertex on Google's geographic repository, plus 24 bytes per vertex that 1553 // does not correspond to the center of a cell at level 'snap_level'. The loop 1554 // vertices must first be converted to the S2XYZFaceSiTi format with 1555 // GetXYZFaceSiTiVertices. 1556 // 1557 // REQUIRES: the loop is initialized and valid. 1558 // void EncodeCompressed(Encoder* encoder, const S2XYZFaceSiTi* vertices, 1559 // int snap_level) const; 1560 1561 // Decode a loop encoded with EncodeCompressed. The parameters must be the 1562 // same as the one used when EncodeCompressed was called. 1563 // bool DecodeCompressed(Decoder* decoder, int snap_level); 1564 1565 // Returns a bitset of properties used by EncodeCompressed 1566 // to efficiently encode boolean values. Properties are 1567 // origin_inside and whether the bound was encoded. 1568 // std::bitset<2> GetCompressedEncodingProperties() const; 1569 1570 /** 1571 Given an iterator that is already positioned at the S2ShapeIndexCell 1572 containing "p", returns Contains(p). 1573 */ 1574 bool contains(in MutableS2ShapeIndex.Iterator it, in S2Point p) const { 1575 // Test containment by drawing a line segment from the cell center to the 1576 // given point and counting edge crossings. 1577 const(S2ClippedShape) a_clipped = it.cell().clipped(0); 1578 bool inside = a_clipped.containsCenter(); 1579 int a_num_edges = a_clipped.numEdges(); 1580 if (a_num_edges > 0) { 1581 S2Point center = it.center(); 1582 auto crosser = new S2CopyingEdgeCrosser(center, p); 1583 int ai_prev = -2; 1584 for (int i = 0; i < a_num_edges; ++i) { 1585 int ai = a_clipped.edge(i); 1586 if (ai != ai_prev + 1) crosser.restartAt(vertex(ai)); 1587 ai_prev = ai; 1588 inside ^= crosser.edgeOrVertexCrossing(vertex(ai+1)); 1589 } 1590 } 1591 return inside; 1592 } 1593 1594 /** 1595 Returns true if the loop boundary intersects "target". It may also 1596 return true when the loop boundary does not intersect "target" but 1597 some edge comes within the worst-case error tolerance. 1598 1599 REQUIRES: it.id().contains(target.id()) 1600 [This condition is true whenever it.Locate(target) returns INDEXED.] 1601 */ 1602 bool boundaryApproxIntersects(in MutableS2ShapeIndex.Iterator it, in S2Cell target) const 1603 in { 1604 assert(it.id().contains(target.id())); 1605 } do { 1606 import s2.s2edge_clipping : 1607 clipToPaddedFace, intersectsRect, FACE_CLIP_ERROR_UV_COORD, INTERSECTS_RECT_ERROR_UV_DIST; 1608 1609 const S2ClippedShape a_clipped = it.cell().clipped(0); 1610 int a_num_edges = a_clipped.numEdges(); 1611 1612 // If there are no edges, there is no intersection. 1613 if (a_num_edges == 0) return false; 1614 1615 // We can save some work if "target" is the index cell itself. 1616 if (it.id() == target.id()) return true; 1617 1618 // Otherwise check whether any of the edges intersect "target". 1619 static const double kMaxError = FACE_CLIP_ERROR_UV_COORD + INTERSECTS_RECT_ERROR_UV_DIST; 1620 R2Rect bound = target.getBoundUV().expanded(kMaxError); 1621 for (int i = 0; i < a_num_edges; ++i) { 1622 int ai = a_clipped.edge(i); 1623 R2Point v0, v1; 1624 if (clipToPaddedFace(vertex(ai), vertex(ai+1), target.face(), kMaxError, v0, v1) 1625 && intersectsRect(v0, v1, bound)) { 1626 return true; 1627 } 1628 } 1629 return false; 1630 } 1631 1632 /** 1633 Returns an index "first" and a direction "dir" (either +1 or -1) such that 1634 the vertex sequence (first, first+dir, ..., first+(n-1)*dir) does not 1635 change when the loop vertex order is rotated or inverted. This allows 1636 the loop vertices to be traversed in a canonical order. The return 1637 values are chosen such that (first, ..., first+n*dir) are in the range 1638 [0, 2*n-1] as expected by the vertex() method. 1639 */ 1640 package int getCanonicalFirstVertex(out int dir) const { 1641 int first = 0; 1642 int n = numVertices(); 1643 for (int i = 1; i < n; ++i) { 1644 if (vertex(i) < vertex(first)) first = i; 1645 } 1646 if (vertex(first + 1) < vertex(first + n - 1)) { 1647 dir = 1; 1648 // 0 <= first <= n-1, so (first+n*dir) <= 2*n-1. 1649 } else { 1650 dir = -1; 1651 first += n; 1652 // n <= first <= 2*n-1, so (first+n*dir) >= 0. 1653 } 1654 return first; 1655 } 1656 1657 /** 1658 Returns the index of a vertex at point "p", or -1 if not found. 1659 The return value is in the range 1..num_vertices_ if found. 1660 */ 1661 int findVertex(in S2Point p) { 1662 if (numVertices() < 10) { 1663 // Exhaustive search. Return value must be in the range [1..N]. 1664 for (int i = 1; i <= numVertices(); ++i) { 1665 if (vertex(i) == p) return i; 1666 } 1667 return -1; 1668 } 1669 auto it = new MutableS2ShapeIndex.Iterator(_index); 1670 if (!it.locate(p)) return -1; 1671 1672 const(S2ClippedShape) a_clipped = it.cell().clipped(0); 1673 for (int i = a_clipped.numEdges() - 1; i >= 0; --i) { 1674 int ai = a_clipped.edge(i); 1675 // Return value must be in the range [1..N]. 1676 if (vertex(ai) == p) return (ai == 0) ? numVertices() : ai; 1677 if (vertex(ai+1) == p) return ai+1; 1678 } 1679 return -1; 1680 } 1681 1682 /** 1683 This method checks all edges of loop A for intersection against all edges 1684 of loop B. If there is any shared vertex, the wedges centered at this 1685 vertex are sent to "relation". 1686 1687 If the two loop boundaries cross, this method is guaranteed to return 1688 true. It also returns true in certain cases if the loop relationship is 1689 equivalent to crossing. For example, if the relation is Contains() and a 1690 point P is found such that B contains P but A does not contain P, this 1691 method will return true to indicate that the result is the same as though 1692 a pair of crossing edges were found (since Contains() returns false in 1693 both cases). 1694 1695 See Contains(), Intersects() and CompareBoundary() for the three uses of 1696 this function. 1697 */ 1698 static bool hasCrossingRelation(S2Loop a, S2Loop b, LoopRelation relation) { 1699 // We look for S2CellId ranges where the indexes of A and B overlap, and 1700 // then test those edges for crossings. 1701 auto ai = new RangeIterator(a._index); 1702 auto bi = new RangeIterator(b._index); 1703 auto ab = new LoopCrosser(a, b, relation, false); // Tests edges of A against B 1704 auto ba = new LoopCrosser(b, a, relation, true); // Tests edges of B against A 1705 int cnt = 0; 1706 while (!ai.done() || !bi.done()) { 1707 if (ai.rangeMax() < bi.rangeMin()) { 1708 // The A and B cells don't overlap, and A precedes B. 1709 ai.seekTo(bi); 1710 } else if (bi.rangeMax() < ai.rangeMin()) { 1711 // The A and B cells don't overlap, and B precedes A. 1712 bi.seekTo(ai); 1713 } else { 1714 // One cell contains the other. Determine which cell is larger. 1715 long ab_relation = ai.id().lsb() - bi.id().lsb(); 1716 if (ab_relation > 0) { 1717 // A's index cell is larger. 1718 if (ab.hasCrossingRelation(ai, bi)) return true; 1719 } else if (ab_relation < 0) { 1720 // B's index cell is larger. 1721 if (ba.hasCrossingRelation(bi, ai)) return true; 1722 } else { 1723 // The A and B cells are the same. Since the two cells have the same 1724 // center point P, check whether P satisfies the crossing targets. 1725 if (ai.containsCenter() == ab.aCrossingTarget() 1726 && bi.containsCenter() == ab.bCrossingTarget()) { 1727 return true; 1728 } 1729 // Otherwise test all the edge crossings directly. 1730 if (ai.numEdges() > 0 && bi.numEdges() > 0 1731 && ab.cellCrossesCell(ai.clipped(), bi.clipped())) { 1732 return true; 1733 } 1734 ai.next(); 1735 bi.next(); 1736 } 1737 } 1738 } 1739 return false; 1740 } 1741 1742 /** 1743 When the loop is modified (Invert(), or Init() called again) then the 1744 indexing structures need to be cleared since they become invalid. 1745 */ 1746 void clearIndex() { 1747 atomicStore!(MemoryOrder.raw)(_unindexedContainsCalls, 0); 1748 _index.clear(); 1749 } 1750 1751 /** 1752 The nesting depth, if this field belongs to an S2Polygon. We define it 1753 here to optimize field packing. 1754 */ 1755 int _depth = 0; 1756 1757 /** 1758 We store the vertices in an array rather than a vector because we don't 1759 need any STL methods, and computing the number of vertices using size() 1760 would be relatively expensive (due to division by sizeof(S2Point) == 24). 1761 When DecodeWithinScope is used to initialize the loop, we do not 1762 take ownership of the memory for vertices_, and the owns_vertices_ field 1763 is used to prevent deallocation and overwriting. 1764 */ 1765 S2Point[] _vertices; 1766 1767 S2Debug _s2DebugOverride = S2Debug.ALLOW; 1768 bool _originInside = false; // Does the loop contain S2::Origin()? 1769 1770 /** 1771 In general we build the index the first time it is needed, but we make an 1772 exception for Contains(S2Point) because this method has a simple brute 1773 force implementation that is also relatively cheap. For this one method 1774 we keep track of the number of calls made and only build the index once 1775 enough calls have been made that we think an index would be worthwhile. 1776 */ 1777 shared int _unindexedContainsCalls; 1778 1779 /** 1780 "bound_" is a conservative bound on all points contained by this loop: 1781 if A.Contains(P), then A.bound_.Contains(S2LatLng(P)). 1782 */ 1783 S2LatLngRect _bound; 1784 1785 /** 1786 Since "bound_" is not exact, it is possible that a loop A contains 1787 another loop B whose bounds are slightly larger. "subregion_bound_" 1788 has been expanded sufficiently to account for this error, i.e. 1789 if A.Contains(B), then A.subregion_bound_.Contains(B.bound_). 1790 */ 1791 S2LatLngRect _subregionBound; 1792 1793 /// Spatial index for this loop. 1794 MutableS2ShapeIndex _index; 1795 } 1796 1797 /// Loop relation for Contains(). 1798 class ContainsRelation : LoopRelation { 1799 public: 1800 this() { 1801 _foundSharedVertex = false; 1802 } 1803 1804 bool foundSharedVertex() const { 1805 return _foundSharedVertex; 1806 } 1807 1808 // If A.Contains(P) == false && B.Contains(P) == true, it is equivalent to 1809 // having an edge crossing (i.e., Contains returns false). 1810 override 1811 int aCrossingTarget() const { 1812 return false; 1813 } 1814 1815 override 1816 int bCrossingTarget() const { 1817 return true; 1818 } 1819 1820 override 1821 bool wedgesCross( 1822 in S2Point a0, in S2Point ab1, in S2Point a2, 1823 in S2Point b0, in S2Point b2) { 1824 _foundSharedVertex = true; 1825 return !wedgeContains(a0, ab1, a2, b0, b2); 1826 } 1827 1828 private: 1829 bool _foundSharedVertex; 1830 } 1831 1832 /// Loop relation for Intersects(). 1833 class IntersectsRelation : LoopRelation { 1834 public: 1835 this() { 1836 _foundSharedVertex = false; 1837 } 1838 1839 bool foundSharedVertex() const { 1840 return _foundSharedVertex; 1841 } 1842 1843 // If A.Contains(P) == true && B.Contains(P) == true, it is equivalent to 1844 // having an edge crossing (i.e., Intersects returns true). 1845 final override 1846 int aCrossingTarget() const { 1847 return true; 1848 } 1849 1850 final override 1851 int bCrossingTarget() const { 1852 return true; 1853 } 1854 1855 override 1856 bool wedgesCross( 1857 in S2Point a0, in S2Point ab1, in S2Point a2, 1858 in S2Point b0, in S2Point b2) { 1859 _foundSharedVertex = true; 1860 return wedgeIntersects(a0, ab1, a2, b0, b2); 1861 } 1862 1863 private: 1864 bool _foundSharedVertex; 1865 } 1866 1867 // Returns true if the wedge (a0, ab1, a2) contains the "semiwedge" defined as 1868 // any non-empty open set of rays immediately CCW from the edge (ab1, b2). If 1869 // "reverse_b" is true, then substitute "clockwise" for "CCW"; this simulates 1870 // what would happen if the direction of loop B was reversed. 1871 private bool wedgeContainsSemiwedge( 1872 in S2Point a0, in S2Point ab1, in S2Point a2, in S2Point b2, bool reverse_b) { 1873 if (b2 == a0 || b2 == a2) { 1874 // We have a shared or reversed edge. 1875 return (b2 == a0) == reverse_b; 1876 } else { 1877 return orderedCCW(a0, a2, b2, ab1); 1878 } 1879 } 1880 1881 /// Loop relation for CompareBoundary(). 1882 class CompareBoundaryRelation : LoopRelation { 1883 public: 1884 this(bool reverse_b) { 1885 _reverseB = reverse_b; 1886 _foundSharedVertex = false; 1887 _containsEdge = false; 1888 _excludesEdge = false; 1889 } 1890 1891 bool foundSharedVertex() const { 1892 return _foundSharedVertex; 1893 } 1894 1895 bool containsEdge() const { 1896 return _containsEdge; 1897 } 1898 1899 // The CompareBoundary relation does not have a useful early-exit condition, 1900 // so we return -1 for both crossing targets. 1901 // 1902 // Aside: A possible early exit condition could be based on the following. 1903 // If A contains a point of both B and ~B, then A intersects Boundary(B). 1904 // If ~A contains a point of both B and ~B, then ~A intersects Boundary(B). 1905 // So if the intersections of {A, ~A} with {B, ~B} are all non-empty, 1906 // the return value is 0, i.e., Boundary(A) intersects Boundary(B). 1907 // Unfortunately it isn't worth detecting this situation because by the 1908 // time we have seen a point in all four intersection regions, we are also 1909 // guaranteed to have seen at least one pair of crossing edges. 1910 override 1911 int aCrossingTarget() const { 1912 return -1; 1913 } 1914 1915 override 1916 int bCrossingTarget() const { 1917 return -1; 1918 } 1919 1920 override 1921 bool wedgesCross( 1922 in S2Point a0, in S2Point ab1, in S2Point a2, 1923 in S2Point b0, in S2Point b2) { 1924 // Because we don't care about the interior of B, only its boundary, it is 1925 // sufficient to check whether A contains the semiwedge (ab1, b2). 1926 _foundSharedVertex = true; 1927 if (wedgeContainsSemiwedge(a0, ab1, a2, b2, _reverseB)) { 1928 _containsEdge = true; 1929 } else { 1930 _excludesEdge = true; 1931 } 1932 return _containsEdge & _excludesEdge; 1933 } 1934 1935 protected: 1936 const bool _reverseB; // True if loop B should be reversed. 1937 bool _foundSharedVertex; // True if any wedge was processed. 1938 bool _containsEdge; // True if any edge of B is contained by A. 1939 bool _excludesEdge; // True if any edge of B is excluded by A. 1940 } 1941 1942 /// LoopRelation is an abstract class that defines a relationship between two 1943 /// loops (Contains, Intersects, or CompareBoundary). 1944 abstract class LoopRelation { 1945 public: 1946 this() {} 1947 1948 /** 1949 Optionally, a_target() and b_target() can specify an early-exit condition 1950 for the loop relation. If any point P is found such that 1951 1952 A.Contains(P) == a_crossing_target() && 1953 B.Contains(P) == b_crossing_target() 1954 1955 then the loop relation is assumed to be the same as if a pair of crossing 1956 edges were found. For example, the Contains() relation has 1957 1958 a_crossing_target() == 0 1959 b_crossing_target() == 1 1960 1961 because if A.Contains(P) == 0 (false) and B.Contains(P) == 1 (true) for 1962 any point P, then it is equivalent to finding an edge crossing (i.e., 1963 since Contains() returns false in both cases). 1964 1965 Loop relations that do not have an early-exit condition of this form 1966 should return -1 for both crossing targets. 1967 */ 1968 int aCrossingTarget() const; 1969 int bCrossingTarget() const; 1970 1971 /** 1972 Given a vertex "ab1" that is shared between the two loops, return true if 1973 the two associated wedges (a0, ab1, b2) and (b0, ab1, b2) are equivalent 1974 to an edge crossing. The loop relation is also allowed to maintain its 1975 own internal state, and can return true if it observes any sequence of 1976 wedges that are equivalent to an edge crossing. 1977 */ 1978 bool wedgesCross( 1979 in S2Point a0, in S2Point ab1, 1980 in S2Point a2, in S2Point b0, 1981 in S2Point b2); 1982 } 1983 1984 /** 1985 RangeIterator is a wrapper over MutableS2ShapeIndex::Iterator with extra 1986 methods that are useful for merging the contents of two or more 1987 S2ShapeIndexes. 1988 */ 1989 class RangeIterator { 1990 public: 1991 /// Construct a new RangeIterator positioned at the first cell of the index. 1992 this(MutableS2ShapeIndex index) { 1993 _it = new MutableS2ShapeIndex.Iterator(index, S2ShapeIndex.InitialPosition.BEGIN); 1994 refresh(); 1995 } 1996 1997 /// The current S2CellId and cell contents. 1998 S2CellId id() const { 1999 return _it.id(); 2000 } 2001 2002 const(S2ShapeIndexCell) cell() const { 2003 return _it.cell(); 2004 } 2005 2006 /// The min and max leaf cell ids covered by the current cell. If Done() is 2007 /// true, these methods return a value larger than any valid cell id. 2008 S2CellId rangeMin() const { 2009 return _rangeMin; 2010 } 2011 2012 S2CellId rangeMax() const { 2013 return _rangeMax; 2014 } 2015 2016 /// Various other convenience methods for the current cell. 2017 const(S2ClippedShape) clipped() const { 2018 return cell().clipped(0); 2019 } 2020 2021 int numEdges() const { 2022 return clipped().numEdges(); 2023 } 2024 2025 bool containsCenter() const { 2026 return clipped().containsCenter(); 2027 } 2028 2029 void next() { 2030 _it.next(); 2031 refresh(); 2032 } 2033 2034 bool done() { 2035 return _it.done(); 2036 } 2037 2038 /// Positions the iterator at the first cell that overlaps or follows 2039 /// "target", i.e. such that range_max() >= target.range_min(). 2040 void seekTo(in RangeIterator target) { 2041 _it.seek(target.rangeMin()); 2042 // If the current cell does not overlap "target", it is possible that the 2043 // previous cell is the one we are looking for. This can only happen when 2044 // the previous cell contains "target" but has a smaller S2CellId. 2045 if (_it.done() || _it.id().rangeMin() > target.rangeMax()) { 2046 if (_it.prev() && _it.id().rangeMax() < target.id()) _it.next(); 2047 } 2048 refresh(); 2049 } 2050 2051 /// Positions the iterator at the first cell that follows "target", i.e. the 2052 /// first cell such that range_min() > target.range_max(). 2053 void seekBeyond(in RangeIterator target) { 2054 _it.seek(target.rangeMax().next()); 2055 if (!_it.done() && _it.id().rangeMin() <= target.rangeMax()) { 2056 _it.next(); 2057 } 2058 refresh(); 2059 } 2060 2061 private: 2062 /// Updates internal state after the iterator has been repositioned. 2063 void refresh() { 2064 _rangeMin = id().rangeMin(); 2065 _rangeMax = id().rangeMax(); 2066 } 2067 2068 MutableS2ShapeIndex.Iterator _it; 2069 S2CellId _rangeMin, _rangeMax; 2070 S2ClippedShape _clipped; 2071 } 2072 2073 /** 2074 LoopCrosser is a helper class for determining whether two loops cross. 2075 It is instantiated twice for each pair of loops to be tested, once for the 2076 pair (A,B) and once for the pair (B,A), in order to be able to process 2077 edges in either loop nesting order. 2078 */ 2079 class LoopCrosser { 2080 public: 2081 /** 2082 If "swapped" is true, the loops A and B have been swapped. This affects 2083 how arguments are passed to the given loop relation, since for example 2084 A.Contains(B) is not the same as B.Contains(A). 2085 */ 2086 this(S2Loop a, S2Loop b, LoopRelation relation, bool swapped) { 2087 _a = a; 2088 _b = b; 2089 _relation = relation; 2090 _swapped = swapped; 2091 _aCrossingTarget = relation.aCrossingTarget(); 2092 _bCrossingTarget = relation.bCrossingTarget(); 2093 _bQuery = new S2CrossingEdgeQuery(b._index); 2094 if (swapped) swap(_aCrossingTarget, _bCrossingTarget); 2095 _crosser = new S2CopyingEdgeCrosser(); 2096 } 2097 2098 /** 2099 Returns the crossing targets for the loop relation, taking into account 2100 whether the loops have been swapped. 2101 */ 2102 int aCrossingTarget() const { 2103 return _aCrossingTarget; 2104 } 2105 2106 int bCrossingTarget() const { 2107 return _bCrossingTarget; 2108 } 2109 2110 /** 2111 Given two iterators positioned such that ai->id().Contains(bi->id()), 2112 return true if there is a crossing relationship anywhere within ai->id(). 2113 Specifically, this method returns true if there is an edge crossing, a 2114 wedge crossing, or a point P that matches both "crossing targets". 2115 Advances both iterators past ai->id(). 2116 */ 2117 bool hasCrossingRelation(RangeIterator ai, RangeIterator bi) 2118 in { 2119 assert(ai.id().contains(bi.id())); 2120 } do { 2121 if (ai.numEdges() == 0) { 2122 if (ai.containsCenter() == _aCrossingTarget) { 2123 // All points within ai->id() satisfy the crossing target for A, so it's 2124 // worth iterating through the cells of B to see whether any cell 2125 // centers also satisfy the crossing target for B. 2126 do { 2127 if (bi.containsCenter() == _bCrossingTarget) return true; 2128 bi.next(); 2129 } while (bi.id() <= ai.rangeMax()); 2130 } else { 2131 // The crossing target for A is not satisfied, so we skip over the cells 2132 // of B using binary search. 2133 bi.seekBeyond(ai); 2134 } 2135 } else { 2136 // The current cell of A has at least one edge, so check for crossings. 2137 if (hasCrossing(ai, bi)) return true; 2138 } 2139 ai.next(); 2140 return false; 2141 } 2142 2143 /** 2144 Given two index cells, return true if there are any edge crossings or 2145 wedge crossings within those cells. 2146 */ 2147 bool cellCrossesCell(in S2ClippedShape a_clipped, in S2ClippedShape b_clipped) { 2148 // Test all edges of "a_clipped" against all edges of "b_clipped". 2149 int a_num_edges = a_clipped.numEdges(); 2150 for (int i = 0; i < a_num_edges; ++i) { 2151 startEdge(a_clipped.edge(i)); 2152 if (edgeCrossesCell(b_clipped)) return true; 2153 } 2154 return false; 2155 } 2156 2157 private: 2158 /** 2159 Given two iterators positioned such that ai->id().Contains(bi->id()), 2160 return true if there is an edge crossing or wedge crosssing anywhere 2161 within ai->id(). Advances "bi" (only) past ai->id(). 2162 */ 2163 bool hasCrossing(RangeIterator ai, RangeIterator bi) 2164 in { 2165 assert(ai.id().contains(bi.id())); 2166 } do { 2167 // If ai->id() intersects many edges of B, then it is faster to use 2168 // S2CrossingEdgeQuery to narrow down the candidates. But if it intersects 2169 // only a few edges, it is faster to check all the crossings directly. 2170 // We handle this by advancing "bi" and keeping track of how many edges we 2171 // would need to test. 2172 2173 enum int kEdgeQueryMinEdges = 20; // Tuned using benchmarks. 2174 int total_edges = 0; 2175 _bCells.length = 0; 2176 do { 2177 if (bi.numEdges() > 0) { 2178 total_edges += bi.numEdges(); 2179 if (total_edges >= kEdgeQueryMinEdges) { 2180 // There are too many edges to test them directly, so use 2181 // S2CrossingEdgeQuery. 2182 if (cellCrossesAnySubcell(ai.clipped(), ai.id())) return true; 2183 bi.seekBeyond(ai); 2184 return false; 2185 } 2186 _bCells ~= bi.cell(); 2187 } 2188 bi.next(); 2189 } while (bi.id() <= ai.rangeMax()); 2190 2191 // Test all the edge crossings directly. 2192 for (int c = 0; c < _bCells.length; ++c) { 2193 if (cellCrossesCell(ai.clipped(), _bCells[c].clipped(0))) { 2194 return true; 2195 } 2196 } 2197 return false; 2198 } 2199 2200 /** 2201 Given an index cell of A, return true if there are any edge or wedge 2202 crossings with any index cell of B contained within "b_id". 2203 */ 2204 bool cellCrossesAnySubcell(in S2ClippedShape a_clipped, S2CellId b_id) { 2205 // Test all edges of "a_clipped" against all edges of B. The relevant B 2206 // edges are guaranteed to be children of "b_id", which lets us find the 2207 // correct index cells more efficiently. 2208 auto b_root = new S2PaddedCell(b_id, 0); 2209 int a_num_edges = a_clipped.numEdges(); 2210 for (int i = 0; i < a_num_edges; ++i) { 2211 int aj = a_clipped.edge(i); 2212 // Use an S2CrossingEdgeQuery starting at "b_root" to find the index cells 2213 // of B that might contain crossing edges. 2214 _bQuery.getCells(_a.vertex(aj), _a.vertex(aj+1), b_root, _bCells); 2215 if (_bCells.empty()) continue; 2216 startEdge(aj); 2217 for (int c = 0; c < _bCells.length; ++c) { 2218 if (edgeCrossesCell(_bCells[c].clipped(0))) return true; 2219 } 2220 } 2221 return false; 2222 } 2223 2224 /// Prepare to check the given edge of loop A for crossings. 2225 void startEdge(int aj) { 2226 // Start testing the given edge of A for crossings. 2227 _crosser.initialize(_a.vertex(aj), _a.vertex(aj+1)); 2228 _aj = aj; 2229 _bjPrev = -2; 2230 } 2231 2232 /** 2233 Check the current edge of loop A for crossings with all edges of the 2234 given index cell of loop B. 2235 */ 2236 bool edgeCrossesCell(in S2ClippedShape b_clipped) { 2237 // Test the current edge of A against all edges of "b_clipped". 2238 int b_num_edges = b_clipped.numEdges(); 2239 for (int j = 0; j < b_num_edges; ++j) { 2240 int bj = b_clipped.edge(j); 2241 if (bj != _bjPrev + 1) _crosser.restartAt(_b.vertex(bj)); 2242 _bjPrev = bj; 2243 int crossing = _crosser.crossingSign(_b.vertex(bj + 1)); 2244 if (crossing < 0) continue; 2245 if (crossing > 0) return true; 2246 // We only need to check each shared vertex once, so we only 2247 // consider the case where a_vertex(aj_+1) == b_.vertex(bj+1). 2248 if (_a.vertex(_aj+1) == _b.vertex(bj+1)) { 2249 if (_swapped 2250 ? _relation.wedgesCross( 2251 _b.vertex(bj), _b.vertex(bj+1), _b.vertex(bj+2), 2252 _a.vertex(_aj), _a.vertex(_aj+2)) 2253 : _relation.wedgesCross( 2254 _a.vertex(_aj), _a.vertex(_aj+1), _a.vertex(_aj+2), 2255 _b.vertex(bj), _b.vertex(bj+2))) { 2256 return true; 2257 } 2258 } 2259 } 2260 return false; 2261 } 2262 2263 const(S2Loop) _a; 2264 const(S2Loop) _b; 2265 LoopRelation _relation; 2266 const(bool) _swapped; 2267 int _aCrossingTarget, _bCrossingTarget; 2268 2269 // State maintained by StartEdge() and EdgeCrossesCell(). 2270 S2CopyingEdgeCrosser _crosser; 2271 int _aj, _bjPrev; 2272 2273 // Temporary data declared here to avoid repeated memory allocations. 2274 S2CrossingEdgeQuery _bQuery; 2275 const(S2ShapeIndexCell)[] _bCells; 2276 } 2277 2278 private bool matchBoundaries(in S2Loop a, in S2Loop b, int a_offset, S1Angle max_error) { 2279 // The state consists of a pair (i,j). A state transition consists of 2280 // incrementing either "i" or "j". "i" can be incremented only if 2281 // a(i+1+a_offset) is near the edge from b(j) to b(j+1), and a similar rule 2282 // applies to "j". The function returns true iff we can proceed all the way 2283 // around both loops in this way. 2284 // 2285 // Note that when "i" and "j" can both be incremented, sometimes only one 2286 // choice leads to a solution. We handle this using a stack and 2287 // backtracking. We also keep track of which states have already been 2288 // explored to avoid duplicating work. 2289 2290 struct Boundary { 2291 int first; 2292 int second; 2293 } 2294 Boundary[] pending; 2295 bool[Boundary] done; 2296 pending ~= Boundary(0, 0); 2297 while (!pending.empty()) { 2298 int i = pending.back().first; 2299 int j = pending.back().second; 2300 pending.popBack(); 2301 if (i == a.numVertices() && j == b.numVertices()) { 2302 return true; 2303 } 2304 done[Boundary(i, j)] = true; 2305 2306 // If (i == na && offset == na-1) where na == a->num_vertices(), then 2307 // then (i+1+offset) overflows the [0, 2*na-1] range allowed by vertex(). 2308 // So we reduce the range if necessary. 2309 int io = i + a_offset; 2310 if (io >= a.numVertices()) { 2311 io -= a.numVertices(); 2312 } 2313 2314 if (i < a.numVertices() && Boundary(i + 1, j) !in done 2315 && getDistance(a.vertex(io + 1), b.vertex(j), b.vertex(j + 1)) <= max_error) { 2316 pending ~= Boundary(i + 1, j); 2317 } 2318 if (j < b.numVertices() && Boundary(i, j + 1) !in done 2319 && getDistance(b.vertex(j + 1), a.vertex(io), a.vertex(io + 1)) <= max_error) { 2320 pending ~= Boundary(i, j + 1); 2321 } 2322 } 2323 return false; 2324 } 2325 2326 private enum ubyte CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER = 1; 2327 private enum uint DECODE_MAX_NUM_VERTICES = 50000000;