1 /* 2 * Copyright 2006 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.geometry; 18 19 import com.google.common.base.Preconditions; 20 21 /** 22 * This class contains various utility functions related to edges. It collects 23 * together common code that is needed to implement polygonal geometry such as 24 * polylines, loops, and general polygons. 25 * 26 */ 27 public strictfp class S2EdgeUtil { 28 /** 29 * IEEE floating-point operations have a maximum error of 0.5 ULPS (units in 30 * the last place). For double-precision numbers, this works out to 2**-53 31 * (about 1.11e-16) times the magnitude of the result. It is possible to 32 * analyze the calculation done by getIntersection() and work out the 33 * worst-case rounding error. I have done a rough version of this, and my 34 * estimate is that the worst case distance from the intersection point X to 35 * the great circle through (a0, a1) is about 12 ULPS, or about 1.3e-15. This 36 * needs to be increased by a factor of (1/0.866) to account for the 37 * edgeSpliceFraction() in S2PolygonBuilder. Note that the maximum error 38 * measured by the unittest in 1,000,000 trials is less than 3e-16. 39 */ 40 public static final S1Angle DEFAULT_INTERSECTION_TOLERANCE = S1Angle.radians(1.5e-15); 41 42 /** 43 * This class allows a vertex chain v0, v1, v2, ... to be efficiently tested 44 * for intersection with a given fixed edge AB. 45 */ 46 public static class EdgeCrosser { 47 // The fields below are all constant. 48 49 private final S2Point a; 50 private final S2Point b; 51 private final S2Point aCrossB; 52 53 // The fields below are updated for each vertex in the chain. 54 55 // Previous vertex in the vertex chain. 56 private S2Point c; 57 // The orientation of the triangle ACB. 58 private int acb; 59 60 /** 61 * AB is the given fixed edge, and C is the first vertex of the vertex 62 * chain. All parameters must point to fixed storage that persists for the 63 * lifetime of the EdgeCrosser object. 64 */ EdgeCrosser(S2Point a, S2Point b, S2Point c)65 public EdgeCrosser(S2Point a, S2Point b, S2Point c) { 66 this.a = a; 67 this.b = b; 68 this.aCrossB = S2Point.crossProd(a, b); 69 restartAt(c); 70 } 71 72 /** 73 * Call this function when your chain 'jumps' to a new place. 74 */ restartAt(S2Point c)75 public void restartAt(S2Point c) { 76 this.c = c; 77 acb = -S2.robustCCW(a, b, c, aCrossB); 78 } 79 80 /** 81 * This method is equivalent to calling the S2EdgeUtil.robustCrossing() 82 * function (defined below) on the edges AB and CD. It returns +1 if there 83 * is a crossing, -1 if there is no crossing, and 0 if two points from 84 * different edges are the same. Returns 0 or -1 if either edge is 85 * degenerate. As a side effect, it saves vertex D to be used as the next 86 * vertex C. 87 */ robustCrossing(S2Point d)88 public int robustCrossing(S2Point d) { 89 // For there to be an edge crossing, the triangles ACB, CBD, BDA, DAC must 90 // all be oriented the same way (CW or CCW). We keep the orientation 91 // of ACB as part of our state. When each new point D arrives, we 92 // compute the orientation of BDA and check whether it matches ACB. 93 // This checks whether the points C and D are on opposite sides of the 94 // great circle through AB. 95 96 // Recall that robustCCW is invariant with respect to rotating its 97 // arguments, i.e. ABC has the same orientation as BDA. 98 int bda = S2.robustCCW(a, b, d, aCrossB); 99 int result; 100 101 if (bda == -acb && bda != 0) { 102 // Most common case -- triangles have opposite orientations. 103 result = -1; 104 } else if ((bda & acb) == 0) { 105 // At least one value is zero -- two vertices are identical. 106 result = 0; 107 } else { 108 // assert (bda == acb && bda != 0); 109 result = robustCrossingInternal(d); // Slow path. 110 } 111 // Now save the current vertex D as the next vertex C, and also save the 112 // orientation of the new triangle ACB (which is opposite to the current 113 // triangle BDA). 114 c = d; 115 acb = -bda; 116 return result; 117 } 118 119 /** 120 * This method is equivalent to the S2EdgeUtil.edgeOrVertexCrossing() method 121 * defined below. It is similar to robustCrossing, but handles cases where 122 * two vertices are identical in a way that makes it easy to implement 123 * point-in-polygon containment tests. 124 */ edgeOrVertexCrossing(S2Point d)125 public boolean edgeOrVertexCrossing(S2Point d) { 126 // We need to copy c since it is clobbered by robustCrossing(). 127 S2Point c2 = new S2Point(c.get(0), c.get(1), c.get(2)); 128 129 int crossing = robustCrossing(d); 130 if (crossing < 0) { 131 return false; 132 } 133 if (crossing > 0) { 134 return true; 135 } 136 137 return vertexCrossing(a, b, c2, d); 138 } 139 140 /** 141 * This function handles the "slow path" of robustCrossing(). 142 */ robustCrossingInternal(S2Point d)143 private int robustCrossingInternal(S2Point d) { 144 // ACB and BDA have the appropriate orientations, so now we check the 145 // triangles CBD and DAC. 146 S2Point cCrossD = S2Point.crossProd(c, d); 147 int cbd = -S2.robustCCW(c, d, b, cCrossD); 148 if (cbd != acb) { 149 return -1; 150 } 151 152 int dac = S2.robustCCW(c, d, a, cCrossD); 153 return (dac == acb) ? 1 : -1; 154 } 155 } 156 157 /** 158 * This class computes a bounding rectangle that contains all edges defined by 159 * a vertex chain v0, v1, v2, ... All vertices must be unit length. Note that 160 * the bounding rectangle of an edge can be larger than the bounding rectangle 161 * of its endpoints, e.g. consider an edge that passes through the north pole. 162 */ 163 public static class RectBounder { 164 // The previous vertex in the chain. 165 private S2Point a; 166 167 // The corresponding latitude-longitude. 168 private S2LatLng aLatLng; 169 170 // The current bounding rectangle. 171 private S2LatLngRect bound; 172 RectBounder()173 public RectBounder() { 174 this.bound = S2LatLngRect.empty(); 175 } 176 177 /** 178 * This method is called to add each vertex to the chain. 'b' must point to 179 * fixed storage that persists for the lifetime of the RectBounder. 180 */ addPoint(S2Point b)181 public void addPoint(S2Point b) { 182 // assert (S2.isUnitLength(b)); 183 184 S2LatLng bLatLng = new S2LatLng(b); 185 186 if (bound.isEmpty()) { 187 bound = bound.addPoint(bLatLng); 188 } else { 189 // We can't just call bound.addPoint(bLatLng) here, since we need to 190 // ensure that all the longitudes between "a" and "b" are included. 191 bound = bound.union(S2LatLngRect.fromPointPair(aLatLng, bLatLng)); 192 193 // Check whether the min/max latitude occurs in the edge interior. 194 // We find the normal to the plane containing AB, and then a vector 195 // "dir" in this plane that also passes through the equator. We use 196 // RobustCrossProd to ensure that the edge normal is accurate even 197 // when the two points are very close together. 198 S2Point aCrossB = S2.robustCrossProd(a, b); 199 S2Point dir = S2Point.crossProd(aCrossB, new S2Point(0, 0, 1)); 200 double da = dir.dotProd(a); 201 double db = dir.dotProd(b); 202 203 if (da * db < 0) { 204 // Minimum/maximum latitude occurs in the edge interior. This affects 205 // the latitude bounds but not the longitude bounds. 206 double absLat = Math.acos(Math.abs(aCrossB.get(2) / aCrossB.norm())); 207 R1Interval lat = bound.lat(); 208 if (da < 0) { 209 // It's possible that absLat < lat.lo() due to numerical errors. 210 lat = new R1Interval(lat.lo(), Math.max(absLat, bound.lat().hi())); 211 } else { 212 lat = new R1Interval(Math.min(-absLat, bound.lat().lo()), lat.hi()); 213 } 214 bound = new S2LatLngRect(lat, bound.lng()); 215 } 216 } 217 a = b; 218 aLatLng = bLatLng; 219 } 220 221 /** 222 * Return the bounding rectangle of the edge chain that connects the 223 * vertices defined so far. 224 */ getBound()225 public S2LatLngRect getBound() { 226 return bound; 227 } 228 229 } 230 231 /** 232 * The purpose of this class is to find edges that intersect a given XYZ 233 * bounding box. It can be used as an efficient rejection test when attempting to 234 * find edges that intersect a given region. It accepts a vertex chain v0, v1, 235 * v2, ... and returns a boolean value indicating whether each edge intersects 236 * the specified bounding box. 237 * 238 * We use XYZ intervals instead of something like longitude intervals because 239 * it is cheap to collect from S2Point lists and any slicing strategy should 240 * give essentially equivalent results. See S2Loop for an example of use. 241 */ 242 public static class XYZPruner { 243 private S2Point lastVertex; 244 245 // The region to be tested against. 246 private boolean boundSet; 247 private double xmin; 248 private double ymin; 249 private double zmin; 250 private double xmax; 251 private double ymax; 252 private double zmax; 253 private double maxDeformation; 254 XYZPruner()255 public XYZPruner() { 256 boundSet = false; 257 } 258 259 /** 260 * Accumulate a bounding rectangle from provided edges. 261 * 262 * @param from start of edge 263 * @param to end of edge. 264 */ addEdgeToBounds(S2Point from, S2Point to)265 public void addEdgeToBounds(S2Point from, S2Point to) { 266 if (!boundSet) { 267 boundSet = true; 268 xmin = xmax = from.x; 269 ymin = ymax = from.y; 270 zmin = zmax = from.z; 271 } 272 xmin = Math.min(xmin, Math.min(to.x, from.x)); 273 ymin = Math.min(ymin, Math.min(to.y, from.y)); 274 zmin = Math.min(zmin, Math.min(to.z, from.z)); 275 xmax = Math.max(xmax, Math.max(to.x, from.x)); 276 ymax = Math.max(ymax, Math.max(to.y, from.y)); 277 zmax = Math.max(zmax, Math.max(to.z, from.z)); 278 279 // Because our arcs are really geodesics on the surface of the earth 280 // an edge can have intermediate points outside the xyz bounds implicit 281 // in the end points. Based on the length of the arc we compute a 282 // generous bound for the maximum amount of deformation. For small edges 283 // it will be very small but for some large arcs (ie. from (1N,90W) to 284 // (1N,90E) the path can be wildly deformed. I did a bunch of 285 // experiments with geodesics to get safe bounds for the deformation. 286 double approxArcLen = 287 Math.abs(from.x - to.x) + Math.abs(from.y - to.y) + Math.abs(from.z - to.z); 288 if (approxArcLen < 0.025) { // less than 2 degrees 289 maxDeformation = Math.max(maxDeformation, approxArcLen * 0.0025); 290 } else if (approxArcLen < 1.0) { // less than 90 degrees 291 maxDeformation = Math.max(maxDeformation, approxArcLen * 0.11); 292 } else { 293 maxDeformation = approxArcLen * 0.5; 294 } 295 } 296 setFirstIntersectPoint(S2Point v0)297 public void setFirstIntersectPoint(S2Point v0) { 298 xmin = xmin - maxDeformation; 299 ymin = ymin - maxDeformation; 300 zmin = zmin - maxDeformation; 301 xmax = xmax + maxDeformation; 302 ymax = ymax + maxDeformation; 303 zmax = zmax + maxDeformation; 304 this.lastVertex = v0; 305 } 306 307 /** 308 * Returns true if the edge going from the last point to this point passes 309 * through the pruner bounding box, otherwise returns false. So the 310 * method returns false if we are certain there is no intersection, but it 311 * may return true when there turns out to be no intersection. 312 */ intersects(S2Point v1)313 public boolean intersects(S2Point v1) { 314 boolean result = true; 315 316 if ((v1.x < xmin && lastVertex.x < xmin) || (v1.x > xmax && lastVertex.x > xmax)) { 317 result = false; 318 } else if ((v1.y < ymin && lastVertex.y < ymin) || (v1.y > ymax && lastVertex.y > ymax)) { 319 result = false; 320 } else if ((v1.z < zmin && lastVertex.z < zmin) || (v1.z > zmax && lastVertex.z > zmax)) { 321 result = false; 322 } 323 324 lastVertex = v1; 325 return result; 326 } 327 } 328 329 /** 330 * The purpose of this class is to find edges that intersect a given longitude 331 * interval. It can be used as an efficient rejection test when attempting to 332 * find edges that intersect a given region. It accepts a vertex chain v0, v1, 333 * v2, ... and returns a boolean value indicating whether each edge intersects 334 * the specified longitude interval. 335 * 336 * This class is not currently used as the XYZPruner is preferred for 337 * S2Loop, but this should be usable in similar circumstances. Be wary 338 * of the cost of atan2() in conversions from S2Point to longitude! 339 */ 340 public static class LongitudePruner { 341 // The interval to be tested against. 342 private S1Interval interval; 343 344 // The longitude of the next v0. 345 private double lng0; 346 347 /** 348 *'interval' is the longitude interval to be tested against, and 'v0' is 349 * the first vertex of edge chain. 350 */ LongitudePruner(S1Interval interval, S2Point v0)351 public LongitudePruner(S1Interval interval, S2Point v0) { 352 this.interval = interval; 353 this.lng0 = S2LatLng.longitude(v0).radians(); 354 } 355 356 /** 357 * Returns true if the edge (v0, v1) intersects the given longitude 358 * interval, and then saves 'v1' to be used as the next 'v0'. 359 */ intersects(S2Point v1)360 public boolean intersects(S2Point v1) { 361 double lng1 = S2LatLng.longitude(v1).radians(); 362 boolean result = interval.intersects(S1Interval.fromPointPair(lng0, lng1)); 363 lng0 = lng1; 364 return result; 365 } 366 } 367 368 /** 369 * A wedge relation's test method accepts two edge chains A=(a0,a1,a2) and 370 * B=(b0,b1,b2) where a1==b1, and returns either -1, 0, or 1 to indicate the 371 * relationship between the region to the left of A and the region to the left 372 * of B. Wedge relations are used to determine the local relationship between 373 * two polygons that share a common vertex. 374 * 375 * All wedge relations require that a0 != a2 and b0 != b2. Other degenerate 376 * cases (such as a0 == b2) are handled as expected. The parameter "ab1" 377 * denotes the common vertex a1 == b1. 378 */ 379 public interface WedgeRelation { test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)380 int test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2); 381 } 382 383 public static class WedgeContains implements WedgeRelation { 384 /** 385 * Given two edge chains (see WedgeRelation above), this function returns +1 386 * if the region to the left of A contains the region to the left of B, and 387 * 0 otherwise. 388 */ 389 @Override test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)390 public int test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2) { 391 // For A to contain B (where each loop interior is defined to be its left 392 // side), the CCW edge order around ab1 must be a2 b2 b0 a0. We split 393 // this test into two parts that test three vertices each. 394 return S2.orderedCCW(a2, b2, b0, ab1) && S2.orderedCCW(b0, a0, a2, ab1) ? 1 : 0; 395 } 396 } 397 398 public static class WedgeIntersects implements WedgeRelation { 399 /** 400 * Given two edge chains (see WedgeRelation above), this function returns -1 401 * if the region to the left of A intersects the region to the left of B, 402 * and 0 otherwise. Note that regions are defined such that points along a 403 * boundary are contained by one side or the other, not both. So for 404 * example, if A,B,C are distinct points ordered CCW around a vertex O, then 405 * the wedges BOA, AOC, and COB do not intersect. 406 */ 407 @Override test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)408 public int test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2) { 409 // For A not to intersect B (where each loop interior is defined to be 410 // its left side), the CCW edge order around ab1 must be a0 b2 b0 a2. 411 // Note that it's important to write these conditions as negatives 412 // (!OrderedCCW(a,b,c,o) rather than Ordered(c,b,a,o)) to get correct 413 // results when two vertices are the same. 414 return (S2.orderedCCW(a0, b2, b0, ab1) && S2.orderedCCW(b0, a2, a0, ab1) ? 0 : -1); 415 } 416 } 417 418 public static class WedgeContainsOrIntersects implements WedgeRelation { 419 /** 420 * Given two edge chains (see WedgeRelation above), this function returns +1 421 * if A contains B, 0 if A and B are disjoint, and -1 if A intersects but 422 * does not contain B. 423 */ 424 @Override test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)425 public int test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2) { 426 // This is similar to WedgeContainsOrCrosses, except that we want to 427 // distinguish cases (1) [A contains B], (3) [A and B are disjoint], 428 // and (2,4,5,6) [A intersects but does not contain B]. 429 430 if (S2.orderedCCW(a0, a2, b2, ab1)) { 431 // We are in case 1, 5, or 6, or case 2 if a2 == b2. 432 return S2.orderedCCW(b2, b0, a0, ab1) ? 1 : -1; // Case 1 vs. 2,5,6. 433 } 434 // We are in cases 2, 3, or 4. 435 if (!S2.orderedCCW(a2, b0, b2, ab1)) { 436 return 0; // Case 3. 437 } 438 439 // We are in case 2 or 4, or case 3 if a2 == b0. 440 return (a2.equals(b0)) ? 0 : -1; // Case 3 vs. 2,4. 441 } 442 } 443 444 public static class WedgeContainsOrCrosses implements WedgeRelation { 445 /** 446 * Given two edge chains (see WedgeRelation above), this function returns +1 447 * if A contains B, 0 if B contains A or the two wedges do not intersect, 448 * and -1 if the edge chains A and B cross each other (i.e. if A intersects 449 * both the interior and exterior of the region to the left of B). In 450 * degenerate cases where more than one of these conditions is satisfied, 451 * the maximum possible result is returned. For example, if A == B then the 452 * result is +1. 453 */ 454 @Override test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)455 public int test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2) { 456 // There are 6 possible edge orderings at a shared vertex (all 457 // of these orderings are circular, i.e. abcd == bcda): 458 // 459 // (1) a2 b2 b0 a0: A contains B 460 // (2) a2 a0 b0 b2: B contains A 461 // (3) a2 a0 b2 b0: A and B are disjoint 462 // (4) a2 b0 a0 b2: A and B intersect in one wedge 463 // (5) a2 b2 a0 b0: A and B intersect in one wedge 464 // (6) a2 b0 b2 a0: A and B intersect in two wedges 465 // 466 // In cases (4-6), the boundaries of A and B cross (i.e. the boundary 467 // of A intersects the interior and exterior of B and vice versa). 468 // Thus we want to distinguish cases (1), (2-3), and (4-6). 469 // 470 // Note that the vertices may satisfy more than one of the edge 471 // orderings above if two or more vertices are the same. The tests 472 // below are written so that we take the most favorable 473 // interpretation, i.e. preferring (1) over (2-3) over (4-6). In 474 // particular note that if orderedCCW(a,b,c,o) returns true, it may be 475 // possible that orderedCCW(c,b,a,o) is also true (if a == b or b == c). 476 477 if (S2.orderedCCW(a0, a2, b2, ab1)) { 478 // The cases with this vertex ordering are 1, 5, and 6, 479 // although case 2 is also possible if a2 == b2. 480 if (S2.orderedCCW(b2, b0, a0, ab1)) { 481 return 1; // Case 1 (A contains B) 482 } 483 484 // We are in case 5 or 6, or case 2 if a2 == b2. 485 return (a2.equals(b2)) ? 0 : -1; // Case 2 vs. 5,6. 486 } 487 // We are in case 2, 3, or 4. 488 return S2.orderedCCW(a0, b0, a2, ab1) ? 0 : -1; // Case 2,3 vs. 4. 489 } 490 } 491 492 /** 493 * Return true if edge AB crosses CD at a point that is interior to both 494 * edges. Properties: 495 * 496 * (1) simpleCrossing(b,a,c,d) == simpleCrossing(a,b,c,d) (2) 497 * simpleCrossing(c,d,a,b) == simpleCrossing(a,b,c,d) 498 */ simpleCrossing(S2Point a, S2Point b, S2Point c, S2Point d)499 public static boolean simpleCrossing(S2Point a, S2Point b, S2Point c, S2Point d) { 500 // We compute simpleCCW() for triangles ACB, CBD, BDA, and DAC. All 501 // of these triangles need to have the same orientation (CW or CCW) 502 // for an intersection to exist. Note that this is slightly more 503 // restrictive than the corresponding definition for planar edges, 504 // since we need to exclude pairs of line segments that would 505 // otherwise "intersect" by crossing two antipodal points. 506 507 S2Point ab = S2Point.crossProd(a, b); 508 double acb = -(ab.dotProd(c)); 509 double bda = ab.dotProd(d); 510 if (acb * bda <= 0) { 511 return false; 512 } 513 514 S2Point cd = S2Point.crossProd(c, d); 515 double cbd = -(cd.dotProd(b)); 516 double dac = cd.dotProd(a); 517 return (acb * cbd > 0) && (acb * dac > 0); 518 } 519 520 /** 521 * Like SimpleCrossing, except that points that lie exactly on a line are 522 * arbitrarily classified as being on one side or the other (according to the 523 * rules of S2.robustCCW). It returns +1 if there is a crossing, -1 if there 524 * is no crossing, and 0 if any two vertices from different edges are the 525 * same. Returns 0 or -1 if either edge is degenerate. Properties of 526 * robustCrossing: 527 * 528 * (1) robustCrossing(b,a,c,d) == robustCrossing(a,b,c,d) (2) 529 * robustCrossing(c,d,a,b) == robustCrossing(a,b,c,d) (3) 530 * robustCrossing(a,b,c,d) == 0 if a==c, a==d, b==c, b==d (3) 531 * robustCrossing(a,b,c,d) <= 0 if a==b or c==d 532 * 533 * Note that if you want to check an edge against a *chain* of other edges, 534 * it is much more efficient to use an EdgeCrosser (above). 535 */ robustCrossing(S2Point a, S2Point b, S2Point c, S2Point d)536 public static int robustCrossing(S2Point a, S2Point b, S2Point c, S2Point d) { 537 // For there to be a crossing, the triangles ACB, CBD, BDA, DAC must 538 // all have the same orientation (clockwise or counterclockwise). 539 // 540 // First we compute the orientation of ACB and BDA. We permute the 541 // arguments to robustCCW so that we can reuse the cross-product of A and B. 542 // Recall that when the arguments to robustCCW are permuted, the sign of the 543 // result changes according to the sign of the permutation. Thus ACB and 544 // ABC are oppositely oriented, while BDA and ABD are the same. 545 S2Point aCrossB = S2Point.crossProd(a, b); 546 int acb = -S2.robustCCW(a, b, c, aCrossB); 547 int bda = S2.robustCCW(a, b, d, aCrossB); 548 549 // If any two vertices are the same, the result is degenerate. 550 if ((bda & acb) == 0) { 551 return 0; 552 } 553 554 // If ABC and BDA have opposite orientations (the most common case), 555 // there is no crossing. 556 if (bda != acb) { 557 return -1; 558 } 559 560 // Otherwise we compute the orientations of CBD and DAC, and check whether 561 // their orientations are compatible with the other two triangles. 562 S2Point cCrossD = S2Point.crossProd(c, d); 563 int cbd = -S2.robustCCW(c, d, b, cCrossD); 564 if (cbd != acb) { 565 return -1; 566 } 567 568 int dac = S2.robustCCW(c, d, a, cCrossD); 569 return (dac == acb) ? 1 : -1; 570 } 571 572 /** 573 * Given two edges AB and CD where at least two vertices are identical (i.e. 574 * robustCrossing(a,b,c,d) == 0), this function defines whether the two edges 575 * "cross" in a such a way that point-in-polygon containment tests can be 576 * implemented by counting the number of edge crossings. The basic rule is 577 * that a "crossing" occurs if AB is encountered after CD during a CCW sweep 578 * around the shared vertex starting from a fixed reference point. 579 * 580 * Note that according to this rule, if AB crosses CD then in general CD does 581 * not cross AB. However, this leads to the correct result when counting 582 * polygon edge crossings. For example, suppose that A,B,C are three 583 * consecutive vertices of a CCW polygon. If we now consider the edge 584 * crossings of a segment BP as P sweeps around B, the crossing number changes 585 * parity exactly when BP crosses BA or BC. 586 * 587 * Useful properties of VertexCrossing (VC): 588 * 589 * (1) VC(a,a,c,d) == VC(a,b,c,c) == false (2) VC(a,b,a,b) == VC(a,b,b,a) == 590 * true (3) VC(a,b,c,d) == VC(a,b,d,c) == VC(b,a,c,d) == VC(b,a,d,c) (3) If 591 * exactly one of a,b equals one of c,d, then exactly one of VC(a,b,c,d) and 592 * VC(c,d,a,b) is true 593 * 594 * It is an error to call this method with 4 distinct vertices. 595 */ vertexCrossing(S2Point a, S2Point b, S2Point c, S2Point d)596 public static boolean vertexCrossing(S2Point a, S2Point b, S2Point c, S2Point d) { 597 // If A == B or C == D there is no intersection. We need to check this 598 // case first in case 3 or more input points are identical. 599 if (a.equals(b) || c.equals(d)) { 600 return false; 601 } 602 603 // If any other pair of vertices is equal, there is a crossing if and only 604 // if orderedCCW() indicates that the edge AB is further CCW around the 605 // shared vertex than the edge CD. 606 if (a.equals(d)) { 607 return S2.orderedCCW(S2.ortho(a), c, b, a); 608 } 609 if (b.equals(c)) { 610 return S2.orderedCCW(S2.ortho(b), d, a, b); 611 } 612 if (a.equals(c)) { 613 return S2.orderedCCW(S2.ortho(a), d, b, a); 614 } 615 if (b.equals(d)) { 616 return S2.orderedCCW(S2.ortho(b), c, a, b); 617 } 618 619 // assert (false); 620 return false; 621 } 622 623 /** 624 * A convenience function that calls robustCrossing() to handle cases where 625 * all four vertices are distinct, and VertexCrossing() to handle cases where 626 * two or more vertices are the same. This defines a crossing function such 627 * that point-in-polygon containment tests can be implemented by simply 628 * counting edge crossings. 629 */ edgeOrVertexCrossing(S2Point a, S2Point b, S2Point c, S2Point d)630 public static boolean edgeOrVertexCrossing(S2Point a, S2Point b, S2Point c, S2Point d) { 631 int crossing = robustCrossing(a, b, c, d); 632 if (crossing < 0) { 633 return false; 634 } 635 if (crossing > 0) { 636 return true; 637 } 638 return vertexCrossing(a, b, c, d); 639 } 640 641 static class CloserResult { 642 private double dmin2; 643 private S2Point vmin; 644 getDmin2()645 public double getDmin2() { 646 return dmin2; 647 } 648 getVmin()649 public S2Point getVmin() { 650 return vmin; 651 } 652 CloserResult(double dmin2, S2Point vmin)653 public CloserResult(double dmin2, S2Point vmin) { 654 this.dmin2 = dmin2; 655 this.vmin = vmin; 656 } 657 replaceIfCloser(S2Point x, S2Point y)658 public void replaceIfCloser(S2Point x, S2Point y) { 659 // If the squared distance from x to y is less than dmin2, then replace 660 // vmin by y and update dmin2 accordingly. 661 double d2 = S2Point.minus(x, y).norm2(); 662 if (d2 < dmin2 || (d2 == dmin2 && y.lessThan(vmin))) { 663 dmin2 = d2; 664 vmin = y; 665 } 666 } 667 } 668 669 /* 670 * Given two edges AB and CD such that robustCrossing() is true, return their 671 * intersection point. Useful properties of getIntersection (GI): 672 * 673 * (1) GI(b,a,c,d) == GI(a,b,d,c) == GI(a,b,c,d) (2) GI(c,d,a,b) == 674 * GI(a,b,c,d) 675 * 676 * The returned intersection point X is guaranteed to be close to the edges AB 677 * and CD, but if the edges intersect at a very small angle then X may not be 678 * close to the true mathematical intersection point P. See the description of 679 * "DEFAULT_INTERSECTION_TOLERANCE" below for details. 680 */ getIntersection(S2Point a0, S2Point a1, S2Point b0, S2Point b1)681 public static S2Point getIntersection(S2Point a0, S2Point a1, S2Point b0, S2Point b1) { 682 Preconditions.checkArgument(robustCrossing(a0, a1, b0, b1) > 0, 683 "Input edges a0a1 and b0b1 muct have a true robustCrossing."); 684 685 // We use robustCrossProd() to get accurate results even when two endpoints 686 // are close together, or when the two line segments are nearly parallel. 687 S2Point aNorm = S2Point.normalize(S2.robustCrossProd(a0, a1)); 688 S2Point bNorm = S2Point.normalize(S2.robustCrossProd(b0, b1)); 689 S2Point x = S2Point.normalize(S2.robustCrossProd(aNorm, bNorm)); 690 691 // Make sure the intersection point is on the correct side of the sphere. 692 // Since all vertices are unit length, and edges are less than 180 degrees, 693 // (a0 + a1) and (b0 + b1) both have positive dot product with the 694 // intersection point. We use the sum of all vertices to make sure that the 695 // result is unchanged when the edges are reversed or exchanged. 696 if (x.dotProd(S2Point.add(S2Point.add(a0, a1), S2Point.add(b0, b1))) < 0) { 697 x = S2Point.neg(x); 698 } 699 700 // The calculation above is sufficient to ensure that "x" is within 701 // DEFAULT_INTERSECTION_TOLERANCE of the great circles through (a0,a1) and 702 // (b0,b1). 703 // However, if these two great circles are very close to parallel, it is 704 // possible that "x" does not lie between the endpoints of the given line 705 // segments. In other words, "x" might be on the great circle through 706 // (a0,a1) but outside the range covered by (a0,a1). In this case we do 707 // additional clipping to ensure that it does. 708 709 if (S2.orderedCCW(a0, x, a1, aNorm) && S2.orderedCCW(b0, x, b1, bNorm)) { 710 return x; 711 } 712 713 // Find the acceptable endpoint closest to x and return it. An endpoint is 714 // acceptable if it lies between the endpoints of the other line segment. 715 CloserResult r = new CloserResult(10, x); 716 if (S2.orderedCCW(b0, a0, b1, bNorm)) { 717 r.replaceIfCloser(x, a0); 718 } 719 if (S2.orderedCCW(b0, a1, b1, bNorm)) { 720 r.replaceIfCloser(x, a1); 721 } 722 if (S2.orderedCCW(a0, b0, a1, aNorm)) { 723 r.replaceIfCloser(x, b0); 724 } 725 if (S2.orderedCCW(a0, b1, a1, aNorm)) { 726 r.replaceIfCloser(x, b1); 727 } 728 return r.getVmin(); 729 } 730 731 /** 732 * Given a point X and an edge AB, return the distance ratio AX / (AX + BX). 733 * If X happens to be on the line segment AB, this is the fraction "t" such 734 * that X == Interpolate(A, B, t). Requires that A and B are distinct. 735 */ getDistanceFraction(S2Point x, S2Point a0, S2Point a1)736 public static double getDistanceFraction(S2Point x, S2Point a0, S2Point a1) { 737 Preconditions.checkArgument(!a0.equals(a1)); 738 double d0 = x.angle(a0); 739 double d1 = x.angle(a1); 740 return d0 / (d0 + d1); 741 } 742 743 /** 744 * Return the minimum distance from X to any point on the edge AB. The result 745 * is very accurate for small distances but may have some numerical error if 746 * the distance is large (approximately Pi/2 or greater). The case A == B is 747 * handled correctly. Note: x, a and b must be of unit length. Throws 748 * IllegalArgumentException if this is not the case. 749 */ getDistance(S2Point x, S2Point a, S2Point b)750 public static S1Angle getDistance(S2Point x, S2Point a, S2Point b) { 751 return getDistance(x, a, b, S2.robustCrossProd(a, b)); 752 } 753 754 /** 755 * A slightly more efficient version of getDistance() where the cross product 756 * of the two endpoints has been precomputed. The cross product does not need 757 * to be normalized, but should be computed using S2.robustCrossProd() for the 758 * most accurate results. 759 */ getDistance(S2Point x, S2Point a, S2Point b, S2Point aCrossB)760 public static S1Angle getDistance(S2Point x, S2Point a, S2Point b, S2Point aCrossB) { 761 Preconditions.checkArgument(S2.isUnitLength(x)); 762 Preconditions.checkArgument(S2.isUnitLength(a)); 763 Preconditions.checkArgument(S2.isUnitLength(b)); 764 765 // There are three cases. If X is located in the spherical wedge defined by 766 // A, B, and the axis A x B, then the closest point is on the segment AB. 767 // Otherwise the closest point is either A or B; the dividing line between 768 // these two cases is the great circle passing through (A x B) and the 769 // midpoint of AB. 770 771 if (S2.simpleCCW(aCrossB, a, x) && S2.simpleCCW(x, b, aCrossB)) { 772 // The closest point to X lies on the segment AB. We compute the distance 773 // to the corresponding great circle. The result is accurate for small 774 // distances but not necessarily for large distances (approaching Pi/2). 775 776 double sinDist = Math.abs(x.dotProd(aCrossB)) / aCrossB.norm(); 777 return S1Angle.radians(Math.asin(Math.min(1.0, sinDist))); 778 } 779 780 // Otherwise, the closest point is either A or B. The cheapest method is 781 // just to compute the minimum of the two linear (as opposed to spherical) 782 // distances and convert the result to an angle. Again, this method is 783 // accurate for small but not large distances (approaching Pi). 784 785 double linearDist2 = Math.min(S2Point.minus(x, a).norm2(), S2Point.minus(x, b).norm2()); 786 return S1Angle.radians(2 * Math.asin(Math.min(1.0, 0.5 * Math.sqrt(linearDist2)))); 787 } 788 789 /** 790 * Returns the point on edge AB closest to X. x, a and b must be of unit 791 * length. Throws IllegalArgumentException if this is not the case. 792 * 793 */ getClosestPoint(S2Point x, S2Point a, S2Point b)794 public static S2Point getClosestPoint(S2Point x, S2Point a, S2Point b) { 795 Preconditions.checkArgument(S2.isUnitLength(x)); 796 Preconditions.checkArgument(S2.isUnitLength(a)); 797 Preconditions.checkArgument(S2.isUnitLength(b)); 798 799 S2Point crossProd = S2.robustCrossProd(a, b); 800 // Find the closest point to X along the great circle through AB. 801 S2Point p = S2Point.minus(x, S2Point.mul(crossProd, x.dotProd(crossProd) / crossProd.norm2())); 802 803 // If p is on the edge AB, then it's the closest point. 804 if (S2.simpleCCW(crossProd, a, p) && S2.simpleCCW(p, b, crossProd)) { 805 return S2Point.normalize(p); 806 } 807 // Otherwise, the closest point is either A or B. 808 return S2Point.minus(x, a).norm2() <= S2Point.minus(x, b).norm2() ? a : b; 809 } 810 811 /** Constructor is private so that this class is never instantiated. */ S2EdgeUtil()812 private S2EdgeUtil() { 813 } 814 } 815