public void Test_S2ClosestEdgeQuery_IsConservativeDistanceLessOrEqual() { // Test int num_tested = 0; int num_conservative_needed = 0; for (int iter = 0; iter < 1000; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. S2Point x = S2Testing.RandomPoint(); S2Point dir = S2Testing.RandomPoint(); S1Angle r = S1Angle.FromRadians(Math.PI * Math.Pow(1e-30, S2Testing.Random.RandDouble())); S2Point y = S2.InterpolateAtDistance(r, x, dir); Distance limit = new(r); if (S2Pred.CompareDistance(x, y, limit) <= 0) { MutableS2ShapeIndex index = new(); index.Add(new S2PointVectorShape(new S2Point[] { x })); S2ClosestEdgeQuery query = new(index); S2ClosestEdgeQuery.PointTarget target = new(y); Assert.True(query.IsConservativeDistanceLessOrEqual(target, limit)); ++num_tested; if (!query.IsDistanceLess(target, limit)) { ++num_conservative_needed; } } } // Verify that in most test cases, the distance between the target points // was close to the desired value. Also verify that at least in some test // cases, the conservative distance test was actually necessary. Assert.True(num_tested >= 300); Assert.True(num_tested <= 700); Assert.True(num_conservative_needed >= 25); }
private static S2Point PerturbATowardsB(S2Point a, S2Point b) { var choice = S2Testing.Random.RandDouble(); if (choice < 0.1) { return(a); } if (choice < 0.3) { // Return a point that is exactly proportional to A and that still // satisfies S2.IsUnitLength(). for (; ;) { var b2 = (2 - a.Norm() + 5 * (S2Testing.Random.RandDouble() - 0.5) * S2.DoubleEpsilon) * a; if (b2 != a && b2.IsUnitLength()) { return(b2); } } } if (choice < 0.5) { // Return a point such that the distance squared to A will underflow. return(S2.InterpolateAtDistance(S1Angle.FromRadians(1e-300), a, b)); } // Otherwise return a point whose distance from A is near S2Constants.DoubleEpsilon such // that the log of the pdf is uniformly distributed. double distance = S2.DoubleEpsilon * 1e-5 * Math.Pow(1e6, S2Testing.Random.RandDouble()); return(S2.InterpolateAtDistance(S1Angle.FromRadians(distance), a, b)); }
public void Test_S2_Rotate() { for (int iter = 0; iter < 1000; ++iter) { S2Point axis = S2Testing.RandomPoint(); S2Point target = S2Testing.RandomPoint(); // Choose a distance whose logarithm is uniformly distributed. double distance = Math.PI * Math.Pow(1e-15, S2Testing.Random.RandDouble()); // Sometimes choose points near the far side of the axis. if (S2Testing.Random.OneIn(5)) { distance = Math.PI - distance; } S2Point p = S2.InterpolateAtDistance(S1Angle.FromRadians(distance), axis, target); // Choose the rotation angle. double angle = S2.M_2_PI * Math.Pow(1e-15, S2Testing.Random.RandDouble()); if (S2Testing.Random.OneIn(3)) { angle = -angle; } if (S2Testing.Random.OneIn(10)) { angle = 0; } TestRotate(p, axis, S1Angle.FromRadians(angle)); } }
private static S2Point PerturbAtDistance(S1Angle distance, S2Point a0, S2Point b0) { S2Point x = S2.InterpolateAtDistance(distance, a0, b0); if (S2Testing.Random.OneIn(2)) { for (int i = 0; i < 3; ++i) { x = x.SetAxis(i, MathUtils.NextAfter(x[i], S2Testing.Random.OneIn(2) ? 1 : -1)); } x = x.Normalize(); } return(x); }
public void Test_S2PolylineSimplifier_Precision() { // This is a rough upper bound on both the error in constructing the disc // locations (i.e., S2.InterpolateAtDistance, etc), and also on the // padding that S2PolylineSimplifier uses to ensure that its results are // conservative (i.e., the error calculated by GetSemiwidth). S1Angle kMaxError = S1Angle.FromRadians(25 * S2.DoubleEpsilon); // We repeatedly generate a random edge. We then target several discs that // barely overlap the edge, and avoid several discs that barely miss the // edge. About half the time, we choose one disc and make it slightly too // large or too small so that targeting fails. int kIters = 1000; // Passes with 1 million iterations. for (int iter = 0; iter < kIters; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. S2Point src = S2Testing.RandomPoint(); var simplifier = new S2PolylineSimplifier(src); S2Point dst = S2.InterpolateAtDistance( S1Angle.FromRadians(S2Testing.Random.RandDouble()), src, S2Testing.RandomPoint()); S2Point n = S2.RobustCrossProd(src, dst).Normalize(); // If bad_disc >= 0, then we make targeting fail for that disc. int kNumDiscs = 5; int bad_disc = S2Testing.Random.Uniform(2 * kNumDiscs) - kNumDiscs; for (int i = 0; i < kNumDiscs; ++i) { // The center of the disc projects to a point that is the given fraction // "f" along the edge (src, dst). If f < 0, the center is located // behind "src" (in order to test this case). double f = S2Testing.Random.UniformDouble(-0.5, 1.0); S2Point a = ((1 - f) * src + f * dst).Normalize(); S1Angle r = S1Angle.FromRadians(S2Testing.Random.RandDouble()); bool on_left = S2Testing.Random.OneIn(2); S2Point x = S2.InterpolateAtDistance(r, a, on_left ? n : -n); // If the disc is behind "src", adjust its radius so that it just // touches "src" rather than just touching the line through (src, dst). if (f < 0) { r = new S1Angle(src, x); } // We grow the radius slightly if we want to target the disc and shrink // it otherwise, *unless* we want targeting to fail for this disc, in // which case these actions are reversed. bool avoid = S2Testing.Random.OneIn(2); bool grow_radius = (avoid == (i == bad_disc)); var radius = new S1ChordAngle(grow_radius ? r + kMaxError : r - kMaxError); if (avoid) { simplifier.AvoidDisc(x, radius, on_left); } else { simplifier.TargetDisc(x, radius); } } // The result is true iff all the discraints were satisfiable. Assert.Equal(bad_disc < 0, simplifier.Extend(dst)); } }