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.collect.ImmutableList; 20 21 /** 22 * Tests for {@link S2EdgeUtil}. 23 * 24 */ 25 public strictfp class S2EdgeUtilTest extends GeometryTestCase { 26 27 public static final int DEGENERATE = -2; 28 compareResult(int actual, int expected)29 private void compareResult(int actual, int expected) { 30 // HACK ALERT: RobustCrossing() is allowed to return 0 or -1 if either edge 31 // is degenerate. We use the value kDegen to represent this possibility. 32 if (expected == DEGENERATE) { 33 assertTrue(actual <= 0); 34 } else { 35 assertEquals(expected, actual); 36 } 37 } 38 assertCrossing(S2Point a, S2Point b, S2Point c, S2Point d, int robust, boolean edgeOrVertex, boolean simple)39 private void assertCrossing(S2Point a, 40 S2Point b, 41 S2Point c, 42 S2Point d, 43 int robust, 44 boolean edgeOrVertex, 45 boolean simple) { 46 a = S2Point.normalize(a); 47 b = S2Point.normalize(b); 48 c = S2Point.normalize(c); 49 d = S2Point.normalize(d); 50 51 compareResult(S2EdgeUtil.robustCrossing(a, b, c, d), robust); 52 if (simple) { 53 assertEquals(robust > 0, S2EdgeUtil.simpleCrossing(a, b, c, d)); 54 } 55 S2EdgeUtil.EdgeCrosser crosser = new S2EdgeUtil.EdgeCrosser(a, b, c); 56 compareResult(crosser.robustCrossing(d), robust); 57 compareResult(crosser.robustCrossing(c), robust); 58 59 assertEquals(S2EdgeUtil.edgeOrVertexCrossing(a, b, c, d), edgeOrVertex); 60 assertEquals(edgeOrVertex, crosser.edgeOrVertexCrossing(d)); 61 assertEquals(edgeOrVertex, crosser.edgeOrVertexCrossing(c)); 62 } 63 assertCrossings(S2Point a, S2Point b, S2Point c, S2Point d, int robust, boolean edgeOrVertex, boolean simple)64 private void assertCrossings(S2Point a, 65 S2Point b, 66 S2Point c, 67 S2Point d, 68 int robust, 69 boolean edgeOrVertex, 70 boolean simple) { 71 assertCrossing(a, b, c, d, robust, edgeOrVertex, simple); 72 assertCrossing(b, a, c, d, robust, edgeOrVertex, simple); 73 assertCrossing(a, b, d, c, robust, edgeOrVertex, simple); 74 assertCrossing(b, a, d, c, robust, edgeOrVertex, simple); 75 assertCrossing(a, a, c, d, DEGENERATE, false, false); 76 assertCrossing(a, b, c, c, DEGENERATE, false, false); 77 assertCrossing(a, b, a, b, 0, true, false); 78 assertCrossing(c, d, a, b, robust, (edgeOrVertex ^ (robust == 0)), simple); 79 } 80 testCrossings()81 public void testCrossings() { 82 // The real tests of edge crossings are in s2{loop,polygon}_unittest, 83 // but we do a few simple tests here. 84 85 // Two regular edges that cross. 86 assertCrossings(new S2Point(1, 2, 1), 87 new S2Point(1, -3, 0.5), 88 new S2Point(1, -0.5, -3), 89 new S2Point(0.1, 0.5, 3), 90 1, 91 true, 92 true); 93 94 // Two regular edges that cross antipodal points. 95 assertCrossings(new S2Point(1, 2, 1), 96 new S2Point(1, -3, 0.5), 97 new S2Point(-1, 0.5, 3), 98 new S2Point(-0.1, -0.5, -3), 99 -1, 100 false, 101 true); 102 103 // Two edges on the same great circle. 104 assertCrossings(new S2Point(0, 0, -1), 105 new S2Point(0, 1, 0), 106 new S2Point(0, 1, 1), 107 new S2Point(0, 0, 1), 108 -1, 109 false, 110 true); 111 112 // Two edges that cross where one vertex is S2.Origin(). 113 assertCrossings(new S2Point(1, 0, 0), 114 new S2Point(0, 1, 0), 115 new S2Point(0, 0, 1), 116 new S2Point(1, 1, -1), 117 1, 118 true, 119 true); 120 121 // Two edges that cross antipodal points where one vertex is S2.Origin(). 122 assertCrossings(new S2Point(1, 0, 0), 123 new S2Point(0, 1, 0), 124 new S2Point(0, 0, -1), 125 new S2Point(-1, -1, 1), 126 -1, 127 false, 128 true); 129 130 // Two edges that share an endpoint. The Ortho() direction is (-4,0,2), 131 // and edge CD is further CCW around (2,3,4) than AB. 132 assertCrossings(new S2Point(2, 3, 4), 133 new S2Point(-1, 2, 5), 134 new S2Point(7, -2, 3), 135 new S2Point(2, 3, 4), 136 0, 137 false, 138 true); 139 140 // Two edges that barely cross edge other. 141 assertCrossings(new S2Point(1, 1, 1), 142 new S2Point(1, 1 - 1e-15, -1), 143 new S2Point(-1, -1, 0), 144 new S2Point(1, 1, 0), 145 1, 146 true, 147 false); 148 } 149 getEdgeBound(double x1, double y1, double z1, double x2, double y2, double z2)150 private S2LatLngRect getEdgeBound(double x1, 151 double y1, 152 double z1, 153 double x2, 154 double y2, 155 double z2) { 156 S2EdgeUtil.RectBounder bounder = new S2EdgeUtil.RectBounder(); 157 S2Point p1 = S2Point.normalize(new S2Point(x1, y1, z1)); 158 S2Point p2 = S2Point.normalize(new S2Point(x2, y2, z2)); 159 bounder.addPoint(p1); 160 bounder.addPoint(p2); 161 return bounder.getBound(); 162 } 163 testRectBounder()164 public void testRectBounder() { 165 // Check cases where min/max latitude is not at a vertex. 166 // Max, CW 167 assertDoubleNear(getEdgeBound(1, 1, 1, 1, -1, 1).lat().hi(), S2.M_PI_4); 168 // Max, CCW 169 assertDoubleNear(getEdgeBound(1, -1, 1, 1, 1, 1).lat().hi(), S2.M_PI_4); 170 // Min, CW 171 assertDoubleNear(getEdgeBound(1, -1, -1, -1, -1, -1).lat().lo(), -S2.M_PI_4); 172 // Min, CCW 173 assertDoubleNear(getEdgeBound(-1, 1, -1, -1, -1, -1).lat().lo(), -S2.M_PI_4); 174 175 // Check cases where the edge passes through one of the poles. 176 assertDoubleNear(getEdgeBound(.3, .4, 1, -.3, -.4, 1).lat().hi(), S2.M_PI_2); 177 assertDoubleNear(getEdgeBound(.3, .4, -1, -.3, -.4, -1).lat().lo(), -S2.M_PI_2); 178 179 // Check cases where the min/max latitude is attained at a vertex. 180 double kCubeLat = Math.asin(Math.sqrt(1. / 3)); // 35.26 degrees 181 assertTrue( 182 getEdgeBound(1, 1, 1, 1, -1, -1).lat().approxEquals(new R1Interval(-kCubeLat, kCubeLat))); 183 assertTrue( 184 getEdgeBound(1, -1, 1, 1, 1, -1).lat().approxEquals(new R1Interval(-kCubeLat, kCubeLat))); 185 } 186 187 // Produce a normalized S2Point for testing. S2NP(double x, double y, double z)188 private S2Point S2NP(double x, double y, double z) { 189 return S2Point.normalize(new S2Point(x, y, z)); 190 } 191 testXYZPruner()192 public void testXYZPruner() { 193 S2EdgeUtil.XYZPruner pruner = new S2EdgeUtil.XYZPruner(); 194 195 // We aren't actually normalizing these points but it doesn't 196 // matter too much as long as we are reasonably close to unit vectors. 197 // This is a simple triangle on the equator. 198 pruner.addEdgeToBounds(S2NP(0, 1, 0), S2NP(0.1, 1, 0)); 199 pruner.addEdgeToBounds(S2NP(0.1, 1, 0), S2NP(0.1, 1, 0.1)); 200 pruner.addEdgeToBounds(S2NP(0.1, 1, 0.1), S2NP(0, 1, 0)); 201 202 // try a loop around the triangle but far enough out to not overlap. 203 pruner.setFirstIntersectPoint(S2NP(-0.1, 1.0, 0.0)); 204 assertFalse(pruner.intersects(S2NP(-0.1, 1.0, 0.2))); 205 assertFalse(pruner.intersects(S2NP(0.0, 1.0, 0.2))); 206 assertFalse(pruner.intersects(S2NP(0.2, 1.0, 0.2))); 207 assertFalse(pruner.intersects(S2NP(0.2, 1.0, 0.05))); 208 assertFalse(pruner.intersects(S2NP(0.2, 1.0, -0.1))); 209 assertFalse(pruner.intersects(S2NP(-0.1, 1.0, -0.1))); 210 assertFalse(pruner.intersects(S2NP(-0.1, 1.0, 0.0))); 211 212 // now we go to a point in the bounding box of the triangle but well 213 // out of the loop. This will be a hit even though it really does not 214 // need to be. 215 assertTrue(pruner.intersects(S2NP(0.02, 1.0, 0.04))); 216 217 // now we zoom out to do an edge *just* below the triangle. This should 218 // be a hit because we are within the deformation zone. 219 assertTrue(pruner.intersects(S2NP(-0.1, 1.0, -0.03))); 220 assertFalse(pruner.intersects(S2NP(0.05, 1.0, -0.03))); // not close 221 assertTrue(pruner.intersects(S2NP(0.05, 1.0, -0.01))); // close 222 assertTrue(pruner.intersects(S2NP(0.05, 1.0, 0.13))); 223 assertFalse(pruner.intersects(S2NP(0.13, 1.0, 0.14))); 224 225 // Create a new pruner with very small area and correspondingly narrow 226 // deformation tolerances. 227 S2EdgeUtil.XYZPruner spruner = new S2EdgeUtil.XYZPruner(); 228 spruner.addEdgeToBounds(S2NP(0, 1, 0.000), S2NP(0.001, 1, 0)); 229 spruner.addEdgeToBounds(S2NP(0.001, 1, 0.000), S2NP(0.001, 1, 0.001)); 230 spruner.addEdgeToBounds(S2NP(0.001, 1, 0.001), S2NP(0.000, 1, 0)); 231 232 spruner.setFirstIntersectPoint(S2NP(0, 1.0, -0.1)); 233 assertFalse(spruner.intersects(S2NP(0.0005, 1.0, -0.0005))); 234 assertFalse(spruner.intersects(S2NP(0.0005, 1.0, -0.0005))); 235 assertFalse(spruner.intersects(S2NP(0.0005, 1.0, -0.00001))); 236 assertTrue(spruner.intersects(S2NP(0.0005, 1.0, -0.0000001))); 237 } 238 testLongitudePruner()239 public void testLongitudePruner() { 240 S2EdgeUtil.LongitudePruner pruner1 = new S2EdgeUtil.LongitudePruner( 241 new S1Interval(0.75 * S2.M_PI, -0.75 * S2.M_PI), new S2Point(0, 1, 2)); 242 243 assertFalse(pruner1.intersects(new S2Point(1, 1, 3))); 244 assertTrue(pruner1.intersects(new S2Point(-1 - 1e-15, -1, 0))); 245 assertTrue(pruner1.intersects(new S2Point(-1, 0, 0))); 246 assertTrue(pruner1.intersects(new S2Point(-1, 0, 0))); 247 assertTrue(pruner1.intersects(new S2Point(1, -1, 8))); 248 assertFalse(pruner1.intersects(new S2Point(1, 0, -2))); 249 assertTrue(pruner1.intersects(new S2Point(-1, -1e-15, 0))); 250 251 S2EdgeUtil.LongitudePruner pruner2 = new S2EdgeUtil.LongitudePruner( 252 new S1Interval(0.25 * S2.M_PI, 0.25 * S2.M_PI), new S2Point(1, 0, 0)); 253 254 assertFalse(pruner2.intersects(new S2Point(2, 1, 2))); 255 assertTrue(pruner2.intersects(new S2Point(1, 2, 3))); 256 assertFalse(pruner2.intersects(new S2Point(0, 1, 4))); 257 assertFalse(pruner2.intersects(new S2Point(-1e-15, -1, -1))); 258 } 259 assertWedge(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2, boolean contains, boolean intersects, boolean crosses)260 private void assertWedge(S2Point a0, 261 S2Point ab1, 262 S2Point a2, 263 S2Point b0, 264 S2Point b2, 265 boolean contains, 266 boolean intersects, 267 boolean crosses) { 268 a0 = S2Point.normalize(a0); 269 ab1 = S2Point.normalize(ab1); 270 a2 = S2Point.normalize(a2); 271 b0 = S2Point.normalize(b0); 272 b2 = S2Point.normalize(b2); 273 274 assertEquals(new S2EdgeUtil.WedgeContains().test(a0, ab1, a2, b0, b2), contains ? 1 : 0); 275 assertEquals(new S2EdgeUtil.WedgeIntersects().test(a0, ab1, a2, b0, b2), intersects ? -1 : 0); 276 assertEquals(new S2EdgeUtil.WedgeContainsOrIntersects().test(a0, ab1, a2, b0, b2), 277 contains ? 1 : intersects ? -1 : 0); 278 assertEquals(new S2EdgeUtil.WedgeContainsOrCrosses().test(a0, ab1, a2, b0, b2), 279 contains ? 1 : crosses ? -1 : 0); 280 } 281 testWedges()282 public void testWedges() { 283 // For simplicity, all of these tests use an origin of (0, 0, 1). 284 // This shouldn't matter as long as the lower-level primitives are 285 // implemented correctly. 286 287 // Intersection in one wedge. 288 assertWedge(new S2Point(-1, 0, 10), 289 new S2Point(0, 0, 1), 290 new S2Point(1, 2, 10), 291 new S2Point(0, 1, 10), 292 new S2Point(1, -2, 10), 293 false, 294 true, 295 true); 296 // Intersection in two wedges. 297 assertWedge(new S2Point(-1, -1, 10), 298 new S2Point(0, 0, 1), 299 new S2Point(1, -1, 10), 300 new S2Point(1, 0, 10), 301 new S2Point(-1, 1, 10), 302 false, 303 true, 304 true); 305 306 // Normal containment. 307 assertWedge(new S2Point(-1, -1, 10), 308 new S2Point(0, 0, 1), 309 new S2Point(1, -1, 10), 310 new S2Point(-1, 0, 10), 311 new S2Point(1, 0, 10), 312 true, 313 true, 314 false); 315 // Containment with equality on one side. 316 assertWedge(new S2Point(2, 1, 10), 317 new S2Point(0, 0, 1), 318 new S2Point(-1, -1, 10), 319 new S2Point(2, 1, 10), 320 new S2Point(1, -5, 10), 321 true, 322 true, 323 false); 324 // Containment with equality on the other side. 325 assertWedge(new S2Point(2, 1, 10), 326 new S2Point(0, 0, 1), 327 new S2Point(-1, -1, 10), 328 new S2Point(1, -2, 10), 329 new S2Point(-1, -1, 10), 330 true, 331 true, 332 false); 333 // Containment with equality on both sides. 334 assertWedge(new S2Point(-2, 3, 10), 335 new S2Point(0, 0, 1), 336 new S2Point(4, -5, 10), 337 new S2Point(-2, 3, 10), 338 new S2Point(4, -5, 10), 339 true, 340 true, 341 false); 342 343 // Disjoint with equality on one side. 344 assertWedge(new S2Point(-2, 3, 10), 345 new S2Point(0, 0, 1), 346 new S2Point(4, -5, 10), 347 new S2Point(4, -5, 10), 348 new S2Point(-2, -3, 10), 349 false, 350 false, 351 false); 352 // Disjoint with equality on the other side. 353 assertWedge(new S2Point(-2, 3, 10), 354 new S2Point(0, 0, 1), 355 new S2Point(0, 5, 10), 356 new S2Point(4, -5, 10), 357 new S2Point(-2, 3, 10), 358 false, 359 false, 360 false); 361 // Disjoint with equality on both sides. 362 assertWedge(new S2Point(-2, 3, 10), 363 new S2Point(0, 0, 1), 364 new S2Point(4, -5, 10), 365 new S2Point(4, -5, 10), 366 new S2Point(-2, 3, 10), 367 false, 368 false, 369 false); 370 371 // B contains A with equality on one side. 372 assertWedge(new S2Point(2, 1, 10), 373 new S2Point(0, 0, 1), 374 new S2Point(1, -5, 10), 375 new S2Point(2, 1, 10), 376 new S2Point(-1, -1, 10), 377 false, 378 true, 379 false); 380 // B contains A with equality on the other side. 381 assertWedge(new S2Point(2, 1, 10), 382 new S2Point(0, 0, 1), 383 new S2Point(1, -5, 10), 384 new S2Point(-2, 1, 10), 385 new S2Point(1, -5, 10), 386 false, 387 true, 388 false); 389 } 390 testGetClosestPoint()391 public void testGetClosestPoint() { 392 final double kMargin = 1e-6; 393 394 S2Point a = S2LatLng.fromDegrees(-0.5, 0).toPoint(); 395 S2Point b = S2LatLng.fromDegrees(+0.5, 0).toPoint(); 396 397 // On edge at end points. 398 assertEquals(a, S2EdgeUtil.getClosestPoint(a, a, b)); 399 assertEquals(b, S2EdgeUtil.getClosestPoint(b, a, b)); 400 401 // On edge in between. 402 S2Point mid = S2LatLng.fromDegrees(0, 0).toPoint(); 403 assertEquals(mid, S2EdgeUtil.getClosestPoint(mid, a, b)); 404 405 // End points are closest 406 assertEquals(a, S2EdgeUtil.getClosestPoint(S2LatLng.fromDegrees(-1, 0).toPoint(), a, b)); 407 assertEquals(b, S2EdgeUtil.getClosestPoint(S2LatLng.fromDegrees(+1, 0).toPoint(), a, b)); 408 409 // Intermediate point is closest. 410 S2Point x = S2LatLng.fromDegrees(+0.1, 1).toPoint(); 411 S2Point expectedClosestPoint = S2LatLng.fromDegrees(+0.1, 0).toPoint(); 412 413 assertTrue(expectedClosestPoint.aequal(S2EdgeUtil.getClosestPoint(x, a, b), kMargin)); 414 } 415 416 // Given a point X and an edge AB, check that the distance from X to AB is 417 // "distanceRadians" and the closest point on AB is "expectedClosest". checkDistance( S2Point x, S2Point a, S2Point b, double distanceRadians, S2Point expectedClosest)418 private static void checkDistance( 419 S2Point x, S2Point a, S2Point b, double distanceRadians, S2Point expectedClosest) { 420 final double kEpsilon = 1e-10; 421 x = S2Point.normalize(x); 422 a = S2Point.normalize(a); 423 b = S2Point.normalize(b); 424 expectedClosest = S2Point.normalize(expectedClosest); 425 426 assertEquals(distanceRadians, S2EdgeUtil.getDistance(x, a, b).radians(), kEpsilon); 427 428 S2Point closest = S2EdgeUtil.getClosestPoint(x, a, b); 429 if (expectedClosest.equals(new S2Point(0, 0, 0))) { 430 // This special value says that the result should be A or B. 431 assertTrue(closest == a || closest == b); 432 } else { 433 assertTrue(S2.approxEquals(closest, expectedClosest)); 434 } 435 } 436 testGetDistance()437 public void testGetDistance() { 438 checkDistance( 439 new S2Point(1, 0, 0), new S2Point(1, 0, 0), new S2Point(0, 1, 0), 0, new S2Point(1, 0, 0)); 440 checkDistance( 441 new S2Point(0, 1, 0), new S2Point(1, 0, 0), new S2Point(0, 1, 0), 0, new S2Point(0, 1, 0)); 442 checkDistance( 443 new S2Point(1, 3, 0), new S2Point(1, 0, 0), new S2Point(0, 1, 0), 0, new S2Point(1, 3, 0)); 444 checkDistance(new S2Point(0, 0, 1), new S2Point(1, 0, 0), new S2Point(0, 1, 0), Math.PI / 2, 445 new S2Point(1, 0, 0)); 446 checkDistance(new S2Point(0, 0, -1), new S2Point(1, 0, 0), new S2Point(0, 1, 0), Math.PI / 2, 447 new S2Point(1, 0, 0)); 448 checkDistance(new S2Point(-1, -1, 0), new S2Point(1, 0, 0), new S2Point(0, 1, 0), 449 0.75 * Math.PI, new S2Point(0, 0, 0)); 450 checkDistance(new S2Point(0, 1, 0), new S2Point(1, 0, 0), new S2Point(1, 1, 0), Math.PI / 4, 451 new S2Point(1, 1, 0)); 452 checkDistance(new S2Point(0, -1, 0), new S2Point(1, 0, 0), new S2Point(1, 1, 0), Math.PI / 2, 453 new S2Point(1, 0, 0)); 454 checkDistance(new S2Point(0, -1, 0), new S2Point(1, 0, 0), new S2Point(-1, 1, 0), Math.PI / 2, 455 new S2Point(1, 0, 0)); 456 checkDistance(new S2Point(-1, -1, 0), new S2Point(1, 0, 0), new S2Point(-1, 1, 0), Math.PI / 2, 457 new S2Point(-1, 1, 0)); 458 checkDistance(new S2Point(1, 1, 1), new S2Point(1, 0, 0), new S2Point(0, 1, 0), 459 Math.asin(Math.sqrt(1. / 3)), new S2Point(1, 1, 0)); 460 checkDistance(new S2Point(1, 1, -1), new S2Point(1, 0, 0), new S2Point(0, 1, 0), 461 Math.asin(Math.sqrt(1. / 3)), new S2Point(1, 1, 0)); 462 checkDistance(new S2Point(-1, 0, 0), new S2Point(1, 1, 0), new S2Point(1, 1, 0), 0.75 * Math.PI, 463 new S2Point(1, 1, 0)); 464 checkDistance(new S2Point(0, 0, -1), new S2Point(1, 1, 0), new S2Point(1, 1, 0), Math.PI / 2, 465 new S2Point(1, 1, 0)); 466 checkDistance(new S2Point(-1, 0, 0), new S2Point(1, 0, 0), new S2Point(1, 0, 0), Math.PI, 467 new S2Point(1, 0, 0)); 468 } 469 testIntersectionTolerance()470 public void testIntersectionTolerance() { 471 // We repeatedly construct two edges that cross near a random point "p", 472 // and measure the distance from the actual intersection point "x" to the 473 // the expected intersection point "p" and also to the edges that cross 474 // near "p". 475 // 476 // Note that getIntersection() does not guarantee that "x" and "p" will be 477 // close together (since the intersection point is numerically unstable 478 // when the edges cross at a very small angle), but it does guarantee that 479 // "x" will be close to both of the edges that cross. 480 S1Angle maxPointDist = new S1Angle(); 481 S1Angle maxEdgeDist = new S1Angle(); 482 483 for (int i = 0; i < 1000; ++i) { 484 // We construct two edges AB and CD that intersect near "p". The angle 485 // between AB and CD (expressed as a slope) is chosen randomly between 486 // 1e-15 and 1.0 such that its logarithm is uniformly distributed. This 487 // implies that small values are much more likely to be chosen. 488 // 489 // Once the slope is chosen, the four points ABCD must be offset from P 490 // by at least (1e-15 / slope) so that the points are guaranteed to have 491 // the correct circular ordering around P. This is the distance from P 492 // at which the two edges are separated by about 1e-15, which is 493 // approximately the minimum distance at which we can expect computed 494 // points on the two lines to be distinct and have the correct ordering. 495 // 496 // The actual offset distance from P is chosen randomly in the range 497 // [1e-15 / slope, 1.0], again uniformly distributing the logarithm. 498 // This ensures that we test both long and very short segments that 499 // intersect at both large and very small angles. 500 501 ImmutableList<S2Point> points = getRandomFrame(); 502 S2Point p = points.get(0); 503 S2Point d1 = points.get(1); 504 S2Point d2 = points.get(2); 505 double slope = Math.pow(1e-15, rand.nextDouble()); 506 d2 = S2Point.add(d1, S2Point.mul(d2, slope)); 507 S2Point a = S2Point.normalize( 508 S2Point.add(p, S2Point.mul(d1, Math.pow(1e-15 / slope, rand.nextDouble())))); 509 S2Point b = S2Point.normalize( 510 S2Point.sub(p, S2Point.mul(d1, Math.pow(1e-15 / slope, rand.nextDouble())))); 511 S2Point c = S2Point.normalize( 512 S2Point.add(p, S2Point.mul(d2, Math.pow(1e-15 / slope, rand.nextDouble())))); 513 S2Point d = S2Point.normalize( 514 S2Point.sub(p, S2Point.mul(d2, Math.pow(1e-15 / slope, rand.nextDouble())))); 515 S2Point x = S2EdgeUtil.getIntersection(a, b, c, d); 516 S1Angle distAb = S2EdgeUtil.getDistance(x, a, b); 517 S1Angle distCd = S2EdgeUtil.getDistance(x, c, d); 518 519 assertTrue(distAb.lessThan(S2EdgeUtil.DEFAULT_INTERSECTION_TOLERANCE)); 520 assertTrue(distCd.lessThan(S2EdgeUtil.DEFAULT_INTERSECTION_TOLERANCE)); 521 522 // test getIntersection() post conditions 523 assertTrue(S2.orderedCCW(a, x, b, S2Point.normalize(S2.robustCrossProd(a, b)))); 524 assertTrue(S2.orderedCCW(c, x, d, S2Point.normalize(S2.robustCrossProd(c, d)))); 525 526 maxEdgeDist = S1Angle.max(maxEdgeDist, S1Angle.max(distAb, distCd)); 527 maxPointDist = S1Angle.max(maxPointDist, new S1Angle(p, x)); 528 } 529 } 530 } 531