private static void TestInterpolate(S2Point a, S2Point b, double t, S2Point expected) { a = a.Normalize(); b = b.Normalize(); expected = expected.Normalize(); // We allow a bit more than the usual 1e-15 error tolerance because // interpolation uses trig functions. S1Angle kError = S1Angle.FromRadians(3e-15); Assert.True(new S1Angle(S2.Interpolate(a, b, t), expected) <= kError); // Now test the other interpolation functions. S1Angle r = t * new S1Angle(a, b); Assert.True(new S1Angle(S2.GetPointOnLine(a, b, r), expected) <= kError); if (a.DotProd(b) == 0) { // Common in the test cases below. Assert.True(new S1Angle(S2.GetPointOnRay(a, b, r), expected) <= kError); } if (r.Radians >= 0 && r.Radians < 0.99 * S2.M_PI) { S1ChordAngle r_ca = new(r); Assert.True(new S1Angle(S2.GetPointOnLine(a, b, r_ca), expected) <= kError); if (a.DotProd(b) == 0) { Assert.True(new S1Angle(S2.GetPointOnRay(a, b, r_ca), expected) <= kError); } } }
public void Test_S2_InterpolateCanExtrapolate() { S2Point i = new(1, 0, 0); S2Point j = new(0, 1, 0); // Initial vectors at 90 degrees. TestInterpolate(i, j, 0, new S2Point(1, 0, 0)); TestInterpolate(i, j, 1, new S2Point(0, 1, 0)); TestInterpolate(i, j, 1.5, new S2Point(-1, 1, 0)); TestInterpolate(i, j, 2, new S2Point(-1, 0, 0)); TestInterpolate(i, j, 3, new S2Point(0, -1, 0)); TestInterpolate(i, j, 4, new S2Point(1, 0, 0)); // Negative values of t. TestInterpolate(i, j, -1, new S2Point(0, -1, 0)); TestInterpolate(i, j, -2, new S2Point(-1, 0, 0)); TestInterpolate(i, j, -3, new S2Point(0, 1, 0)); TestInterpolate(i, j, -4, new S2Point(1, 0, 0)); // Initial vectors at 45 degrees. TestInterpolate(i, new S2Point(1, 1, 0), 2, new S2Point(0, 1, 0)); TestInterpolate(i, new S2Point(1, 1, 0), 3, new S2Point(-1, 1, 0)); TestInterpolate(i, new S2Point(1, 1, 0), 4, new S2Point(-1, 0, 0)); // Initial vectors at 135 degrees. TestInterpolate(i, new S2Point(-1, 1, 0), 2, new S2Point(0, -1, 0)); // Take a small fraction along the curve. S2Point p = S2.Interpolate(i, j, 0.001); // We should get back where we started. TestInterpolate(i, p, 1000, j); }
public void Test_S2Cell_GetMaxDistanceToEdge() { // Test an edge for which its antipode crosses the cell. Validates both the // standard and brute force implementations for this case. S2Cell cell = S2Cell.FromFacePosLevel(0, 0, 20); S2Point a = -S2.Interpolate(2.0, cell.Center(), cell.Vertex(0)); S2Point b = -S2.Interpolate(2.0, cell.Center(), cell.Vertex(2)); S1ChordAngle actual = cell.MaxDistance(a, b); S1ChordAngle expected = GetMaxDistanceToEdgeBruteForce(cell, a, b); Assert2.Near(expected.Radians(), S1ChordAngle.Straight.Radians(), S2.DoubleError); Assert2.Near(actual.Radians(), S1ChordAngle.Straight.Radians(), S2.DoubleError); }
public void Test_S2_RepeatedInterpolation() { // Check that points do not drift away from unit length when repeated // interpolations are done. for (int i = 0; i < 100; ++i) { S2Point a = S2Testing.RandomPoint(); S2Point b = S2Testing.RandomPoint(); for (int j = 0; j < 1000; ++j) { a = S2.Interpolate(a, b, 0.01); } Assert.True(a.IsUnitLength()); } }
public void Test_S2Cell_ConsistentWithS2CellIdFromPoint() { // Construct many points that are nearly on an S2Cell edge, and verify that // S2Cell(S2CellId(p)).Contains(p) is always true. for (int iter = 0; iter < 1000; ++iter) { S2Cell cell = new(S2Testing.GetRandomCellId()); int i = S2Testing.Random.Uniform(4); S2Point v1 = cell.Vertex(i); S2Point v2 = S2Testing.SamplePoint( new S2Cap(cell.Vertex(i + 1), S1Angle.FromRadians(1e-15))); S2Point p = S2.Interpolate(S2Testing.Random.RandDouble(), v1, v2); Assert.True(new S2Cell(new S2CellId(p)).Contains(p)); } }
public void Test_S2_CollinearEdgesThatDontTouch() { int kIters = 500; for (int iter = 0; iter < kIters; ++iter) { S2Point a = S2Testing.RandomPoint(); S2Point d = S2Testing.RandomPoint(); S2Point b = S2.Interpolate(0.05, a, d); S2Point c = S2.Interpolate(0.95, a, d); Assert.True(0 > S2.CrossingSign(a, b, c, d)); Assert.True(0 > S2.CrossingSign(a, b, c, d)); S2EdgeCrosser crosser = new(a, b, c); Assert.True(0 > crosser.CrossingSign(d)); Assert.True(0 > crosser.CrossingSign(c)); } }
public void Test_S2_Interpolate() { // Choose test points designed to expose floating-point errors. S2Point p1 = new S2Point(0.1, 1e-30, 0.3).Normalize(); S2Point p2 = new S2Point(-0.7, -0.55, -1e30).Normalize(); // A zero-length edge. TestInterpolate(p1, p1, 0, p1); TestInterpolate(p1, p1, 1, p1); // Start, end, and middle of a medium-length edge. TestInterpolate(p1, p2, 0, p1); TestInterpolate(p1, p2, 1, p2); TestInterpolate(p1, p2, 0.5, 0.5 * (p1 + p2)); // Test that interpolation is done using distances on the sphere rather than // linear distances. TestInterpolate(new S2Point(1, 0, 0), new S2Point(0, 1, 0), 1.0 / 3, new S2Point(Math.Sqrt(3), 1, 0)); TestInterpolate(new S2Point(1, 0, 0), new S2Point(0, 1, 0), 2.0 / 3, new S2Point(1, Math.Sqrt(3), 0)); // Test that interpolation is accurate on a long edge (but not so long that // the definition of the edge itself becomes too unstable). { double kLng = Math.PI - 1e-2; S2Point a = S2LatLng.FromRadians(0, 0).ToPoint(); S2Point b = S2LatLng.FromRadians(0, kLng).ToPoint(); for (double f = 0.4; f > 1e-15; f *= 0.1) { TestInterpolate(a, b, f, S2LatLng.FromRadians(0, f * kLng).ToPoint()); TestInterpolate(a, b, 1 - f, S2LatLng.FromRadians(0, (1 - f) * kLng).ToPoint()); } } // Test that interpolation on a 180 degree edge (antipodal endpoints) yields // a result with the correct distance from each endpoint. for (double t = 0; t <= 1; t += 0.125) { S2Point actual = S2.Interpolate(p1, -p1, t); Assert2.Near(new S1Angle(actual, p1).Radians, t * Math.PI, 3e-15); } }
private S1ChordAngle EstimateMaxError(R2Point pa, S2Point a, R2Point pb, S2Point b) { // See the algorithm description at the top of this file. // We always tessellate edges longer than 90 degrees on the sphere, since the // approximation below is not robust enough to handle such edges. if (a.DotProd(b) < -1e-14) { return(S1ChordAngle.Infinity); } const double t1 = kInterpolationFraction; const double t2 = 1 - kInterpolationFraction; S2Point mid1 = S2.Interpolate(a, b, t1); S2Point mid2 = S2.Interpolate(a, b, t2); S2Point pmid1 = proj_.Unproject(Projection.Interpolate(t1, pa, pb)); S2Point pmid2 = proj_.Unproject(Projection.Interpolate(t2, pa, pb)); return(S1ChordAngle.Max(new S1ChordAngle(mid1, pmid1), new S1ChordAngle(mid2, pmid2))); }
public void Test_S2Cell_RectBoundIsLargeEnough() { // Construct many points that are nearly on an S2Cell edge, and verify that // whenever the cell contains a point P then its bound contains S2LatLng(P). for (int iter = 0; iter < 1000; /* advanced in loop below */) { S2Cell cell = new(S2Testing.GetRandomCellId()); int i = S2Testing.Random.Uniform(4); S2Point v1 = cell.Vertex(i); S2Point v2 = S2Testing.SamplePoint( new S2Cap(cell.Vertex(i + 1), S1Angle.FromRadians(1e-15))); S2Point p = S2.Interpolate(S2Testing.Random.RandDouble(), v1, v2); if (new S2Loop(cell).Contains(p)) { Assert.True(cell.GetRectBound().Contains(new S2LatLng(p))); ++iter; } } }