1 // Copyright 2005 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 16 // Original Author: ericv@google.com (Eric Veach) 17 // Converted to D: madric@gmail.com (Vijay Nayar) 18 19 module s2.s2polyline; 20 21 import s2.logger; 22 import s2.s1angle; 23 import s2.s1interval; 24 import s2.s2cap; 25 import s2.s2cell; 26 import s2.s2cell_id; 27 import s2.s2debug; 28 import s2.s2edge_crosser; 29 import s2.s2edge_distances : getDistance, interpolateAtDistance, isEdgeBNearEdgeA; 30 import s2.s2error; 31 import s2.s2latlng; 32 import s2.s2latlng_rect; 33 import s2.s2latlng_rect_bounder; 34 import s2.s2point; 35 import s2.s2pointutil : approxEquals, getFrame, isUnitLength, toFrame; 36 import s2.s2predicates : orderedCCW, sign; 37 import s2.s2region; 38 import s2.s2shape; 39 import s2.util.coding.coder; 40 import s2.util.math.matrix3x3; 41 42 import std.math; 43 import std.algorithm : copy, max, min; 44 import std.exception : enforce; 45 import std.range : back, empty, popBack; 46 import std.typecons : Rebindable; 47 48 /** 49 * An S2Polyline represents a sequence of zero or more vertices connected by 50 * straight edges (geodesics). Edges of length 0 and 180 degrees are not 51 * allowed, i.e. adjacent vertices should not be identical or antipodal. 52 */ 53 class S2Polyline : S2Region { 54 public: 55 /// Creates an empty S2Polyline that should be initialized by calling Init() or Decode(). 56 this() { 57 _s2debugOverride = S2Debug.ALLOW; 58 } 59 60 /// Convenience constructors that call Init() with the given vertices. 61 this(S2Point[] vertices) { 62 this(vertices, S2Debug.ALLOW); 63 } 64 65 this(S2LatLng[] vertices) { 66 this(vertices, S2Debug.ALLOW); 67 } 68 69 /** 70 * Convenience constructors to disable the automatic validity checking 71 * controlled by the --s2debug flag. Example: 72 * 73 * S2Polyline* line = new S2Polyline(vertices, S2Debug::DISABLE); 74 * 75 * This is equivalent to: 76 * 77 * S2Polyline* line = new S2Polyline; 78 * line->set_s2debug_override(S2Debug::DISABLE); 79 * line->Init(vertices); 80 * 81 * The main reason to use this constructors is if you intend to call 82 * IsValid() explicitly. See set_s2debug_override() for details. 83 */ 84 this(S2Point[] vertices, S2Debug s2debugOverride) { 85 _s2debugOverride = s2debugOverride; 86 initialize(vertices); 87 } 88 89 this(S2LatLng[] vertices, S2Debug s2debugOverride) { 90 _s2debugOverride = s2debugOverride; 91 initialize(vertices); 92 } 93 94 /** 95 * Initialize a polyline that connects the given vertices. Empty polylines are 96 * allowed. Adjacent vertices should not be identical or antipodal. All 97 * vertices should be unit length. 98 */ 99 void initialize(S2Point[] vertices) { 100 _vertices.length = vertices.length; 101 copy(vertices, _vertices); 102 if (flagsS2Debug && _s2debugOverride == S2Debug.ALLOW) { 103 enforce(isValid()); 104 } 105 } 106 107 /** 108 * Convenience initialization function that accepts latitude-longitude 109 * coordinates rather than S2Points. 110 */ 111 void initialize(S2LatLng[] vertices) { 112 _vertices.length = vertices.length; 113 for (int i = 0; i < _vertices.length; ++i) { 114 _vertices[i] = vertices[i].toS2Point(); 115 } 116 if (flagsS2Debug && _s2debugOverride == S2Debug.ALLOW) { 117 enforce(isValid()); 118 } 119 } 120 121 /** 122 * Allows overriding the automatic validity checks controlled by the 123 * --s2debug flag. If this flag is true, then polylines are automatically 124 * checked for validity as they are initialized. The main reason to disable 125 * this flag is if you intend to call IsValid() explicitly, like this: 126 * 127 * S2Polyline line; 128 * line.set_s2debug_override(S2Debug::DISABLE); 129 * line.Init(...); 130 * if (!line.IsValid()) { ... } 131 * 132 * Without the call to set_s2debug_override(), invalid data would cause a 133 * fatal error in Init() whenever the --s2debug flag is enabled. 134 */ 135 void setS2debugOverride(S2Debug s2debugOverride) { 136 _s2debugOverride = s2debugOverride; 137 } 138 139 S2Debug s2debugOverride() const { 140 return _s2debugOverride; 141 } 142 143 /// Return true if the given vertices form a valid polyline. 144 bool isValid() const { 145 S2Error error; 146 if (findValidationError(error)) { 147 if (flagsS2Debug) { 148 logger.logError(error); 149 } 150 return false; 151 } 152 return true; 153 } 154 155 /** 156 * Returns true if this is *not* a valid polyline and sets "error" 157 * appropriately. Otherwise returns false and leaves "error" unchanged. 158 * 159 * REQUIRES: error != nullptr 160 */ 161 bool findValidationError(out S2Error error) const { 162 // All vertices must be unit length. 163 for (int i = 0; i < numVertices(); ++i) { 164 if (!isUnitLength(vertex(i))) { 165 error.initialize( 166 S2Error.Code.NOT_UNIT_LENGTH, "Vertex %d is not unit length", i); 167 return true; 168 } 169 } 170 // Adjacent vertices must not be identical or antipodal. 171 for (int i = 1; i < numVertices(); ++i) { 172 if (vertex(i - 1) == vertex(i)) { 173 error.initialize( 174 S2Error.Code.DUPLICATE_VERTICES, "Vertices %d and %d are identical", i - 1, i); 175 return true; 176 } 177 if (vertex(i - 1) == -vertex(i)) { 178 error.initialize( 179 S2Error.Code.ANTIPODAL_VERTICES, "Vertices %d and %d are antipodal", i - 1, i); 180 return true; 181 } 182 } 183 return false; 184 } 185 186 int numVertices() const { 187 return cast(int) _vertices.length; 188 } 189 190 ref const(S2Point) vertex(int k) const 191 in { 192 assert(k >= 0); 193 assert(k < _vertices.length); 194 } do { 195 return _vertices[k]; 196 } 197 198 const(S2Point[]) vertices() const { 199 return _vertices; 200 } 201 202 /// Return the length of the polyline. 203 S1Angle getLength() const { 204 S1Angle length; 205 for (int i = 1; i < numVertices(); ++i) { 206 length += S1Angle(vertex(i-1), vertex(i)); 207 } 208 return length; 209 } 210 211 /** 212 * Return the true centroid of the polyline multiplied by the length of the 213 * polyline (see s2centroids.h for details on centroids). The result is not 214 * unit length, so you may want to normalize it. 215 * 216 * Prescaling by the polyline length makes it easy to compute the centroid 217 * of several polylines (by simply adding up their centroids). 218 */ 219 S2Point getCentroid() const { 220 S2Point centroid; 221 for (int i = 1; i < numVertices(); ++i) { 222 // The centroid (multiplied by length) is a vector toward the midpoint 223 // of the edge, whose length is twice the sin of half the angle between 224 // the two vertices. Defining theta to be this angle, we have: 225 S2Point vsum = vertex(i-1) + vertex(i); // Length == 2*cos(theta) 226 S2Point vdiff = vertex(i-1) - vertex(i); // Length == 2*sin(theta) 227 double cos2 = vsum.norm2(); 228 double sin2 = vdiff.norm2(); 229 enforce(cos2 > 0.0); // Otherwise edge is undefined, and result is NaN. 230 centroid += sqrt(sin2 / cos2) * vsum; // Length == 2*sin(theta) 231 } 232 return centroid; 233 } 234 235 /** 236 * Return the point whose distance from vertex 0 along the polyline is the 237 * given fraction of the polyline's total length. Fractions less than zero 238 * or greater than one are clamped. The return value is unit length. This 239 * cost of this function is currently linear in the number of vertices. 240 * The polyline must not be empty. 241 */ 242 S2Point interpolate(double fraction) const { 243 int next_vertex; 244 return getSuffix(fraction, next_vertex); 245 } 246 247 /** 248 * Like Interpolate(), but also return the index of the next polyline 249 * vertex after the interpolated point P. This allows the caller to easily 250 * construct a given suffix of the polyline by concatenating P with the 251 * polyline vertices starting at "next_vertex". Note that P is guaranteed 252 * to be different than vertex(*next_vertex), so this will never result in 253 * a duplicate vertex. 254 * 255 * The polyline must not be empty. Note that if "fraction" >= 1.0, then 256 * "next_vertex" will be set to num_vertices() (indicating that no vertices 257 * from the polyline need to be appended). The value of "next_vertex" is 258 * always between 1 and num_vertices(). 259 * 260 * This method can also be used to construct a prefix of the polyline, by 261 * taking the polyline vertices up to "next_vertex - 1" and appending the 262 * returned point P if it is different from the last vertex (since in this 263 * case there is no guarantee of distinctness). 264 */ 265 S2Point getSuffix(double fraction, out int next_vertex) const 266 in { 267 assert(numVertices() > 0); 268 } do { 269 // We intentionally let the (fraction >= 1) case fall through, since 270 // we need to handle it in the loop below in any case because of 271 // possible roundoff errors. 272 if (fraction <= 0) { 273 next_vertex = 1; 274 return vertex(0); 275 } 276 S1Angle length_sum; 277 for (int i = 1; i < numVertices(); ++i) { 278 length_sum += S1Angle(vertex(i-1), vertex(i)); 279 } 280 S1Angle target = fraction * length_sum; 281 for (int i = 1; i < numVertices(); ++i) { 282 auto length = S1Angle(vertex(i-1), vertex(i)); 283 if (target < length) { 284 // This interpolates with respect to arc length rather than 285 // straight-line distance, and produces a unit-length result. 286 S2Point result = interpolateAtDistance(target, vertex(i-1), vertex(i)); 287 // It is possible that (result == vertex(i)) due to rounding errors. 288 next_vertex = (result == vertex(i)) ? (i + 1) : i; 289 return result; 290 } 291 target -= length; 292 } 293 next_vertex = numVertices(); 294 return vertex(numVertices() - 1); 295 } 296 297 /** 298 * The inverse operation of GetSuffix/Interpolate. Given a point on the 299 * polyline, returns the ratio of the distance to the point from the 300 * beginning of the polyline over the length of the polyline. The return 301 * value is always betwen 0 and 1 inclusive. See GetSuffix() for the 302 * meaning of "next_vertex". 303 * 304 * The polyline should not be empty. If it has fewer than 2 vertices, the 305 * return value is zero. 306 */ 307 double unInterpolate(in S2Point point, int next_vertex) const 308 in { 309 assert(numVertices() > 0); 310 } do { 311 if (numVertices() < 2) { 312 return 0; 313 } 314 S1Angle length_sum; 315 for (int i = 1; i < next_vertex; ++i) { 316 length_sum += S1Angle(vertex(i-1), vertex(i)); 317 } 318 S1Angle length_to_point = length_sum + S1Angle(vertex(next_vertex-1), point); 319 for (int i = next_vertex; i < numVertices(); ++i) { 320 length_sum += S1Angle(vertex(i-1), vertex(i)); 321 } 322 // The ratio can be greater than 1.0 due to rounding errors or because the 323 // point is not exactly on the polyline. 324 return min(1.0, (length_to_point / length_sum).radians()); 325 } 326 327 /** 328 * Given a point, returns a point on the polyline that is closest to the given 329 * point. See GetSuffix() for the meaning of "next_vertex", which is chosen 330 * here w.r.t. the projected point as opposed to the interpolated point in 331 * GetSuffix(). 332 * 333 * The polyline must be non-empty. 334 */ 335 S2Point project(in S2Point point, out int next_vertex) const 336 in { 337 assert(numVertices() > 0); 338 } do { 339 import s2.s2edge_distances : project; 340 341 if (numVertices() == 1) { 342 // If there is only one vertex, it is always closest to any given point. 343 next_vertex = 1; 344 return vertex(0); 345 } 346 347 // Initial value larger than any possible distance on the unit sphere. 348 S1Angle min_distance = S1Angle.fromRadians(10.0); 349 int min_index = -1; 350 351 // Find the line segment in the polyline that is closest to the point given. 352 for (int i = 1; i < numVertices(); ++i) { 353 S1Angle distance_to_segment = getDistance(point, vertex(i-1), vertex(i)); 354 if (distance_to_segment < min_distance) { 355 min_distance = distance_to_segment; 356 min_index = i; 357 } 358 } 359 enforce(min_index != -1); 360 361 // Compute the point on the segment found that is closest to the point given. 362 S2Point closest_point = project(point, vertex(min_index - 1), vertex(min_index)); 363 364 next_vertex = min_index + (closest_point == vertex(min_index) ? 1 : 0); 365 return closest_point; 366 } 367 368 /** 369 * Returns true if the point given is on the right hand side of the polyline, 370 * using a naive definition of "right-hand-sideness" where the point is on 371 * the RHS of the polyline iff the point is on the RHS of the line segment in 372 * the polyline which it is closest to. 373 * 374 * The polyline must have at least 2 vertices. 375 */ 376 bool isOnRight(in S2Point point) const 377 in { 378 assert(numVertices() >= 2); 379 } do { 380 int next_vertex; 381 S2Point closest_point = project(point, next_vertex); 382 383 enforce(next_vertex >= 1); 384 enforce(next_vertex <= numVertices()); 385 386 // If the closest point C is an interior vertex of the polyline, let B and D 387 // be the previous and next vertices. The given point P is on the right of 388 // the polyline (locally) if B, P, D are ordered CCW around vertex C. 389 if (closest_point == vertex(next_vertex - 1) && next_vertex > 1 390 && next_vertex < numVertices()) { 391 if (point == vertex(next_vertex-1)) 392 return false; // Polyline vertices are not on the RHS. 393 return orderedCCW(vertex(next_vertex-2), point, vertex(next_vertex), vertex(next_vertex-1)); 394 } 395 396 // Otherwise, the closest point C is incident to exactly one polyline edge. 397 // We test the point P against that edge. 398 if (next_vertex == numVertices()) 399 --next_vertex; 400 401 return sign(point, vertex(next_vertex), vertex(next_vertex - 1)) > 0; 402 } 403 404 /** 405 * Return true if this polyline intersects the given polyline. If the 406 * polylines share a vertex they are considered to be intersecting. When a 407 * polyline endpoint is the only intersection with the other polyline, the 408 * function may return true or false arbitrarily. 409 * 410 * The running time is quadratic in the number of vertices. (To intersect 411 * polylines more efficiently, or compute the actual intersection geometry, 412 * use S2BooleanOperation.) 413 */ 414 bool intersects(S2Polyline line) { 415 if (numVertices() <= 0 || line.numVertices() <= 0) { 416 return false; 417 } 418 419 if (!getRectBound().intersects(line.getRectBound())) { 420 return false; 421 } 422 423 // TODO(ericv): Use S2ShapeIndex here. 424 for (int i = 1; i < numVertices(); ++i) { 425 auto crosser = new S2EdgeCrosser(vertex(i - 1), vertex(i), line.vertex(0)); 426 for (int j = 1; j < line.numVertices(); ++j) { 427 if (crosser.crossingSign(line.vertex(j)) >= 0) { 428 return true; 429 } 430 } 431 } 432 return false; 433 } 434 435 /// Reverse the order of the polyline vertices. 436 void reverse() { 437 import std.algorithm : reverse; 438 reverse(_vertices); 439 } 440 441 /** 442 * Return a subsequence of vertex indices such that the polyline connecting 443 * these vertices is never further than "tolerance" from the original 444 * polyline. Provided the first and last vertices are distinct, they are 445 * always preserved; if they are not, the subsequence may contain only a 446 * single index. 447 * 448 * Some useful properties of the algorithm: 449 * 450 * - It runs in linear time. 451 * 452 * - The output is always a valid polyline. In particular, adjacent 453 * output vertices are never identical or antipodal. 454 * 455 * - The method is not optimal, but it tends to produce 2-3% fewer 456 * vertices than the Douglas-Peucker algorithm with the same tolerance. 457 * 458 * - The output is *parametrically* equivalent to the original polyline to 459 * within the given tolerance. For example, if a polyline backtracks on 460 * itself and then proceeds onwards, the backtracking will be preserved 461 * (to within the given tolerance). This is different than the 462 * Douglas-Peucker algorithm, which only guarantees geometric equivalence. 463 * 464 * See also S2PolylineSimplifier, which uses the same algorithm but is more 465 * efficient and supports more features, and also S2Builder, which can 466 * simplify polylines and polygons, supports snapping (e.g. to E7 lat/lng 467 * coordinates or S2CellId centers), and can split polylines at intersection 468 * points. 469 */ 470 void subsampleVertices(S1Angle tolerance, out int[] indices) const { 471 if (numVertices() == 0) return; 472 473 indices ~= 0; 474 S1Angle clamped_tolerance = max(tolerance, S1Angle.fromRadians(0.0)); 475 for (int index = 0; index + 1 < numVertices(); ) { 476 int next_index = findEndVertex(this, clamped_tolerance, index); 477 // Don't create duplicate adjacent vertices. 478 if (vertex(next_index) != vertex(index)) { 479 indices ~= next_index; 480 } 481 index = next_index; 482 } 483 } 484 485 /// Return true if two polylines are exactly the same. 486 override 487 bool opEquals(in Object o) const { 488 const(S2Polyline) b = cast(S2Polyline) o; 489 if (b is null) return false; 490 if (numVertices() != b.numVertices()) return false; 491 for (int offset = 0; offset < numVertices(); ++offset) { 492 if (vertex(offset) != b.vertex(offset)) return false; 493 } 494 return true; 495 } 496 497 /** 498 * Return true if two polylines have the same number of vertices, and 499 * corresponding vertex pairs are separated by no more than "max_error". 500 * (For testing purposes.) 501 */ 502 bool approxEquals(in S2Polyline b, S1Angle max_error = S1Angle.fromRadians(1e-15)) const { 503 import s2.s2pointutil : approxEquals; 504 505 if (numVertices() != b.numVertices()) return false; 506 for (int offset = 0; offset < numVertices(); ++offset) { 507 if (!approxEquals(vertex(offset), b.vertex(offset), max_error)) { 508 return false; 509 } 510 } 511 return true; 512 } 513 514 // Return true if "covered" is within "max_error" of a contiguous subpath of 515 // this polyline over its entire length. Specifically, this method returns 516 // true if this polyline has parameterization a:[0,1] -> S^2, "covered" has 517 // parameterization b:[0,1] -> S^2, and there is a non-decreasing function 518 // f:[0,1] -> [0,1] such that distance(a(f(t)), b(t)) <= max_error for all t. 519 // 520 // You can think of this as testing whether it is possible to drive a car 521 // along "covered" and a car along some subpath of this polyline such that no 522 // car ever goes backward, and the cars are always within "max_error" of each 523 // other. 524 // 525 // This function is well-defined for empty polylines: 526 // anything.covers(empty) = true 527 // empty.covers(nonempty) = false 528 bool nearlyCovers(in S2Polyline covered, S1Angle max_error) const { 529 import s2.s2edge_distances : project; 530 531 // NOTE: This algorithm is described assuming that adjacent vertices in a 532 // polyline are never at the same point. That is, the ith and i+1th vertices 533 // of a polyline are never at the same point in space. The implementation 534 // does not make this assumption. 535 536 // DEFINITIONS: 537 // - edge "i" of a polyline is the edge from the ith to i+1th vertex. 538 // - covered_j is a polyline consisting of edges 0 through j of "covered." 539 // - this_i is a polyline consisting of edges 0 through i of this polyline. 540 // 541 // A search state is represented as an (int, int, bool) tuple, (i, j, 542 // i_in_progress). Using the "drive a car" analogy from the header comment, a 543 // search state signifies that you can drive one car along "covered" from its 544 // first vertex through a point on its jth edge, and another car along this 545 // polyline from some point on or before its ith edge to a to a point on its 546 // ith edge, such that no car ever goes backward, and the cars are always 547 // within "max_error" of each other. If i_in_progress is true, it means that 548 // you can definitely drive along "covered" through the jth vertex (beginning 549 // of the jth edge). Otherwise, you can definitely drive along "covered" 550 // through the point on the jth edge of "covered" closest to the ith vertex of 551 // this polyline. 552 // 553 // The algorithm begins by finding all edges of this polyline that are within 554 // "max_error" of the first vertex of "covered," and adding search states 555 // representing all of these possible starting states to the stack of 556 // "pending" states. 557 // 558 // The algorithm proceeds by popping the next pending state, 559 // (i,j,i_in_progress), off of the stack. First it checks to see if that 560 // state represents finding a valid covering of "covered" and returns true if 561 // so. Next, if the state represents reaching the end of this polyline 562 // without finding a successful covering, the algorithm moves on to the next 563 // state in the stack. Otherwise, if state (i+1,j,false) is valid, it is 564 // added to the stack of pending states. Same for state (i,j+1,true). 565 // 566 // We need the stack because when "i" and "j" can both be incremented, 567 // sometimes only one choice leads to a solution. We use a set to keep track 568 // of visited states to avoid duplicating work. With the set, the worst-case 569 // number of states examined is O(n+m) where n = this->num_vertices() and m = 570 // covered.num_vertices(). Without it, the amount of work could be as high as 571 // O((n*m)^2). Using set, the running time is O((n*m) log (n*m)). 572 // 573 // TODO(user): Benchmark this, and see if the set is worth it. 574 575 if (covered.numVertices() == 0) return true; 576 if (numVertices() == 0) return false; 577 578 SearchState[] pending; 579 bool[SearchState] done; 580 581 // Find all possible starting states. 582 for (int i = 0, next_i = nextDistinctVertex(this, 0), next_next_i; 583 next_i < numVertices(); i = next_i, next_i = next_next_i) { 584 next_next_i = nextDistinctVertex(this, next_i); 585 S2Point closest_point = project(covered.vertex(0), vertex(i), vertex(next_i)); 586 587 // In order to avoid duplicate starting states, we exclude the end vertex 588 // of each edge *except* for the last non-degenerate edge. 589 if ((next_next_i == numVertices() || closest_point != vertex(next_i)) 590 && S1Angle(closest_point, covered.vertex(0)) <= max_error) { 591 pending ~= SearchState(i, 0, true); 592 } 593 } 594 595 while (!pending.empty()) { 596 const SearchState state = pending.back(); 597 pending.popBack(); 598 599 if (state in done) continue; 600 done[state] = true; 601 602 const int next_i = nextDistinctVertex(this, state.i); 603 const int next_j = nextDistinctVertex(covered, state.j); 604 if (next_j == covered.numVertices()) { 605 return true; 606 } else if (next_i == numVertices()) { 607 continue; 608 } 609 610 S2Point i_begin, j_begin; 611 if (state.iInProgress) { 612 j_begin = covered.vertex(state.j); 613 i_begin = project(j_begin, vertex(state.i), vertex(next_i)); 614 } else { 615 i_begin = vertex(state.i); 616 j_begin = project(i_begin, covered.vertex(state.j), covered.vertex(next_j)); 617 } 618 619 if (isEdgeBNearEdgeA(j_begin, covered.vertex(next_j), i_begin, vertex(next_i), max_error)) { 620 pending ~= SearchState(next_i, state.j, false); 621 } 622 if (isEdgeBNearEdgeA(i_begin, vertex(next_i), j_begin, covered.vertex(next_j), max_error)) { 623 pending ~= SearchState(state.i, next_j, true); 624 } 625 } 626 return false; 627 } 628 629 // Returns the total number of bytes used by the polyline. 630 size_t spaceUsed() const { 631 return this.classinfo.m_init.length + numVertices() * S2Point.sizeof; 632 } 633 634 //////////////////////////////////////////////////////////////////////// 635 // S2Region interface (see s2region.h for details): 636 637 override 638 S2Polyline clone() { 639 return new S2Polyline(this); 640 } 641 642 override 643 void getCellUnionBound(out S2CellId[] cell_ids) { 644 return getCapBound().getCellUnionBound(cell_ids); 645 } 646 647 override 648 S2Cap getCapBound() { 649 return getRectBound().getCapBound(); 650 } 651 652 override 653 S2LatLngRect getRectBound() { 654 auto bounder = new S2LatLngRectBounder(); 655 for (int i = 0; i < numVertices(); ++i) { 656 bounder.addPoint(vertex(i)); 657 } 658 return bounder.getBound(); 659 } 660 661 override 662 bool contains(in S2Cell cell) const { 663 return false; 664 } 665 666 override 667 bool mayIntersect(in S2Cell cell) const { 668 if (numVertices() == 0) return false; 669 670 // We only need to check whether the cell contains vertex 0 for correctness, 671 // but these tests are cheap compared to edge crossings so we might as well 672 // check all the vertices. 673 for (int i = 0; i < numVertices(); ++i) { 674 if (cell.contains(vertex(i))) return true; 675 } 676 S2Point[4] cell_vertices; 677 for (int i = 0; i < 4; ++i) { 678 cell_vertices[i] = cell.getVertex(i); 679 } 680 for (int j = 0; j < 4; ++j) { 681 auto crosser = new S2EdgeCrosser(cell_vertices[j], cell_vertices[(j + 1) & 3], vertex(0)); 682 for (int i = 1; i < numVertices(); ++i) { 683 if (crosser.crossingSign(vertex(i)) >= 0) { 684 // There is a proper crossing, or two vertices were the same. 685 return true; 686 } 687 } 688 } 689 return false; 690 } 691 692 /** 693 * Always return false, because "containment" is not numerically 694 * well-defined except at the polyline vertices. 695 */ 696 override 697 bool contains(in S2Point p) const { 698 return false; 699 } 700 701 /** 702 * Appends a serialized representation of the S2Polyline to "encoder". 703 * 704 * REQUIRES: "encoder" uses the default constructor, so that its buffer 705 * can be enlarged as necessary by calling Ensure(int). 706 */ 707 void encode(ORangeT)(Encoder!ORangeT encoder) const 708 out (; encoder.avail() >= 0) { 709 encoder.ensure(numVertices() * S2Point.sizeof + 10); // sufficient 710 711 encoder.put8(CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER); 712 encoder.put32(numVertices()); 713 encoder.putRaw(_vertices); 714 } 715 716 /// Decodes an S2Polyline encoded with Encode(). Returns true on success. 717 bool decode(IRangeT)(Decoder!IRangeT decoder) { 718 if (decoder.avail() < ubyte.sizeof + uint.sizeof) return false; 719 ubyte versionNum = decoder.get8(); 720 if (versionNum > CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER) return false; 721 722 uint num_vertices = decoder.get32(); 723 if (decoder.avail() < num_vertices * S2Point.sizeof) return false; 724 _vertices = decoder.getRaw!S2Point(num_vertices); 725 726 if (flagsS2Debug && _s2debugOverride == S2Debug.ALLOW) { 727 enforce(isValid()); 728 } 729 return true; 730 } 731 732 /** 733 * Wrapper class for indexing a polyline (see S2ShapeIndex). Once this 734 * object is inserted into an S2ShapeIndex it is owned by that index, and 735 * will be automatically deleted when no longer needed by the index. Note 736 * that this class does not take ownership of the polyline itself (see 737 * OwningShape below). You can also subtype this class to store additional 738 * data (see S2Shape for details). 739 */ 740 static class Shape : S2Shape { 741 public: 742 // Must call Init(). 743 this() { 744 _polyline = null; 745 } 746 747 /** 748 * Initialization. Does not take ownership of "polyline". 749 * 750 * Note that a polyline with one vertex is defined to have no edges. Use 751 * S2LaxPolylineShape or S2LaxClosedPolylineShape if you want to define a 752 * polyline consisting of a single degenerate edge. 753 */ 754 this(in S2Polyline polyline) { 755 init(polyline); 756 } 757 758 void init(in S2Polyline polyline) { 759 if (polyline.numVertices() == 1) { 760 logger.logWarn("S2Polyline::Shape with one vertex has no edges"); 761 } 762 _polyline = polyline; 763 } 764 765 const(S2Polyline) polyline() const { 766 return _polyline; 767 } 768 769 // S2Shape interface: 770 771 override 772 int numEdges() const { 773 return max(0, _polyline.numVertices() - 1); 774 } 775 776 override 777 Edge edge(int e) const { 778 return Edge(_polyline.vertex(e), _polyline.vertex(e + 1)); 779 } 780 781 override 782 int dimension() const { 783 return 1; 784 } 785 786 override 787 S2Shape.ReferencePoint getReferencePoint() const { 788 return S2Shape.ReferencePoint(false); 789 } 790 791 override 792 int numChains() const { 793 return min(1, numEdges()); // Avoid virtual call. 794 } 795 796 override 797 Chain chain(int i) const 798 in { 799 assert(i == 0); 800 } do { 801 return Chain(0, numEdges()); // Avoid virtual call. 802 } 803 804 override 805 Edge chainEdge(int i, int j) const 806 in { 807 assert(i == 0); 808 } do { 809 return Edge(_polyline.vertex(j), _polyline.vertex(j + 1)); 810 } 811 812 override 813 ChainPosition chainPosition(int e) const { 814 return ChainPosition(0, e); 815 } 816 817 private: 818 Rebindable!(const(S2Polyline)) _polyline; 819 } 820 821 // Like Shape, except that the S2Polyline is automatically deleted when this 822 // object is deleted by the S2ShapeIndex. This is useful when an S2Polyline 823 // is constructed solely for the purpose of indexing it. 824 // 825 // class OwningShape : Shape -- Not needed in D with GC. 826 827 private: 828 // Internal copy constructor used only by Clone() that makes a deep copy of 829 // its argument. 830 this(in S2Polyline o) { 831 _s2debugOverride = o._s2debugOverride; 832 _vertices = o._vertices.dup; 833 } 834 835 // Allows overriding the automatic validity checking controlled by the 836 // --s2debug flag. 837 S2Debug _s2debugOverride; 838 839 // We store the vertices in an array rather than a vector because we don't 840 // need any STL methods, and computing the number of vertices using size() 841 // would be relatively expensive (due to division by sizeof(S2Point) == 24). 842 S2Point[] _vertices; 843 844 // Given a polyline, a tolerance distance, and a start index, this function 845 // returns the maximal end index such that the line segment between these two 846 // vertices passes within "tolerance" of all interior vertices, in order. 847 static int findEndVertex(in S2Polyline polyline, S1Angle tolerance, int index) 848 in { 849 assert(tolerance.radians() >= 0); 850 assert((index + 1) < polyline.numVertices()); 851 } do { 852 853 // The basic idea is to keep track of the "pie wedge" of angles from the 854 // starting vertex such that a ray from the starting vertex at that angle 855 // will pass through the discs of radius "tolerance" centered around all 856 // vertices processed so far. 857 858 // First we define a "coordinate frame" for the tangent and normal spaces 859 // at the starting vertex. Essentially this means picking three 860 // orthonormal vectors X,Y,Z such that X and Y span the tangent plane at 861 // the starting vertex, and Z is "up". We use the coordinate frame to 862 // define a mapping from 3D direction vectors to a one-dimensional "ray 863 // angle" in the range (-Pi, Pi]. The angle of a direction vector is 864 // computed by transforming it into the X,Y,Z basis, and then calculating 865 // atan2(y,x). This mapping allows us to represent a wedge of angles as a 866 // 1D interval. Since the interval wraps around, we represent it as an 867 // S1Interval, i.e. an interval on the unit circle. 868 Matrix3x3_d frame; 869 const(S2Point) origin = polyline.vertex(index); 870 getFrame(origin, frame); 871 872 // As we go along, we keep track of the current wedge of angles and the 873 // distance to the last vertex (which must be non-decreasing). 874 S1Interval current_wedge = S1Interval.full(); 875 double last_distance = 0; 876 877 for (++index; index < polyline.numVertices(); ++index) { 878 const(S2Point) candidate = polyline.vertex(index); 879 double distance = origin.angle(candidate); 880 881 // We don't allow simplification to create edges longer than 90 degrees, 882 // to avoid numeric instability as lengths approach 180 degrees. (We do 883 // need to allow for original edges longer than 90 degrees, though.) 884 if (distance > M_PI_2 && last_distance > 0) break; 885 886 // Vertices must be in increasing order along the ray, except for the 887 // initial disc around the origin. 888 if (distance < last_distance && last_distance > tolerance.radians()) break; 889 last_distance = distance; 890 891 // Points that are within the tolerance distance of the origin do not 892 // constrain the ray direction, so we can ignore them. 893 if (distance <= tolerance.radians()) continue; 894 895 // If the current wedge of angles does not contain the angle to this 896 // vertex, then stop right now. Note that the wedge of possible ray 897 // angles is not necessarily empty yet, but we can't continue unless we 898 // are willing to backtrack to the last vertex that was contained within 899 // the wedge (since we don't create new vertices). This would be more 900 // complicated and also make the worst-case running time more than linear. 901 S2Point direction = toFrame(frame, candidate); 902 double center = atan2(direction.y(), direction.x()); 903 if (!current_wedge.contains(center)) break; 904 905 // To determine how this vertex constrains the possible ray angles, 906 // consider the triangle ABC where A is the origin, B is the candidate 907 // vertex, and C is one of the two tangent points between A and the 908 // spherical cap of radius "tolerance" centered at B. Then from the 909 // spherical law of sines, sin(a)/sin(A) = sin(c)/sin(C), where "a" and 910 // "c" are the lengths of the edges opposite A and C. In our case C is a 911 // 90 degree angle, therefore A = asin(sin(a) / sin(c)). Angle A is the 912 // half-angle of the allowable wedge. 913 914 double half_angle = asin(sin(tolerance.radians()) / sin(distance)); 915 S1Interval target = S1Interval.fromPoint(center).expanded(half_angle); 916 current_wedge = current_wedge.intersection(target); 917 enforce(!current_wedge.isEmpty()); 918 } 919 // We break out of the loop when we reach a vertex index that can't be 920 // included in the line segment, so back up by one vertex. 921 return index - 1; 922 } 923 924 // Return the first i > "index" such that the ith vertex of "pline" is not at 925 // the same point as the "index"th vertex. Returns pline.num_vertices() if 926 // there is no such value of i. 927 static int nextDistinctVertex(in S2Polyline pline, int index) { 928 S2Point initial = pline.vertex(index); 929 do { 930 ++index; 931 } while (index < pline.numVertices() && pline.vertex(index) == initial); 932 return index; 933 } 934 935 // This struct represents a search state in the NearlyCovers algorithm 936 // below. See the description of the algorithm for details. 937 struct SearchState { 938 int i; 939 int j; 940 bool iInProgress; 941 942 // This operator is needed for storing SearchStates in a set. The ordering 943 // chosen has no special meaning. 944 int opCmp(SearchState b) const { 945 if (i != b.i) return i - b.i; 946 if (j != b.j) return j - b.j; 947 return iInProgress - b.iInProgress; 948 } 949 } 950 } 951 952 private enum CURRENT_LOSSLESS_ENCODING_VERSION_NUMBER = 1;