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); } } }
private static void TestCrossings(S2Point a, S2Point b, S2Point c, S2Point d, int crossing_sign, int signed_crossing_sign) { a = a.Normalize(); b = b.Normalize(); c = c.Normalize(); d = d.Normalize(); TestCrossing(a, b, c, d, crossing_sign, signed_crossing_sign); TestCrossing(b, a, c, d, crossing_sign, -signed_crossing_sign); TestCrossing(a, b, d, c, crossing_sign, -signed_crossing_sign); TestCrossing(b, a, d, c, crossing_sign, signed_crossing_sign); TestCrossing(a, a, c, d, -1, 0); TestCrossing(a, b, c, c, -1, 0); TestCrossing(a, a, c, c, -1, 0); TestCrossing(a, b, a, b, 0, 1); if (crossing_sign == 0) { // For vertex crossings, if AB crosses CD then CD does not cross AB. // In order to get the crossing sign right in both cases, all tests are // specified such that AB crosses CD. The other case is tested here. Assert.NotEqual(signed_crossing_sign, 0); TestCrossing(c, d, a, b, crossing_sign, 0); } else { // TODO(ericv): Document properties of SignedEdgeOrVertexCrossing. TestCrossing(c, d, a, b, crossing_sign, -signed_crossing_sign); } }
private void assertCrossing(S2Point a, S2Point b, S2Point c, S2Point d, int robust, bool edgeOrVertex, bool simple) { a = S2Point.Normalize(a); b = S2Point.Normalize(b); c = S2Point.Normalize(c); d = S2Point.Normalize(d); compareResult(S2EdgeUtil.RobustCrossing(a, b, c, d), robust); if (simple) { assertEquals(robust > 0, S2EdgeUtil.SimpleCrossing(a, b, c, d)); } var crosser = new EdgeCrosser(a, b, c); compareResult(crosser.RobustCrossing(d), robust); compareResult(crosser.RobustCrossing(c), robust); assertEquals(S2EdgeUtil.EdgeOrVertexCrossing(a, b, c, d), edgeOrVertex); assertEquals(edgeOrVertex, crosser.EdgeOrVertexCrossing(d)); assertEquals(edgeOrVertex, crosser.EdgeOrVertexCrossing(c)); }
// Given a point X and an edge AB, check that the distance from X to AB is // "distance_radians" and the closest point on AB is "expected_closest". private static void CheckDistance(S2Point x, S2Point a, S2Point b, double distance_radians, S2Point expected_closest) { x = x.Normalize(); a = a.Normalize(); b = b.Normalize(); expected_closest = expected_closest.Normalize(); Assert2.Near(distance_radians, S2.GetDistance(x, a, b).Radians, S2.DoubleError); S2Point closest = S2.Project(x, a, b); Assert.True(S2Pred.CompareEdgeDistance( closest, a, b, new S1ChordAngle(S2.kProjectPerpendicularErrorS1Angle)) < 0); // If X is perpendicular to AB then there is nothing further we can expect. if (distance_radians != S2.M_PI_2) { if (expected_closest == new S2Point()) { // This special value says that the result should be A or B. Assert.True(closest == a || closest == b); } else { Assert.True(S2.ApproxEquals(closest, expected_closest)); } } S1ChordAngle min_distance = S1ChordAngle.Zero; Assert.False(S2.UpdateMinDistance(x, a, b, ref min_distance)); min_distance = S1ChordAngle.Infinity; Assert.True(S2.UpdateMinDistance(x, a, b, ref min_distance)); Assert2.Near(distance_radians, min_distance.ToAngle().Radians, S2.DoubleError); }
public void testGetLengthCentroid() { // Construct random great circles and divide them randomly into segments. // Then make sure that the length and centroid are correct. Note that // because of the way the centroid is computed, it does not matter how // we split the great circle into segments. for (var i = 0; i < 100; ++i) { // Choose a coordinate frame for the great circle. var x = randomPoint(); var y = S2Point.Normalize(S2Point.CrossProd(x, randomPoint())); var z = S2Point.Normalize(S2Point.CrossProd(x, y)); var vertices = new List <S2Point>(); for (double theta = 0; theta < 2 * S2.Pi; theta += Math.Pow(rand.NextDouble(), 10)) { var p = (x * Math.Cos(theta)) + (y * Math.Sin(theta)); if (vertices.Count == 0 || !p.Equals(vertices[vertices.Count - 1])) { vertices.Add(p); } } // Close the circle. vertices.Add(vertices[0]); var line = new S2Polyline(vertices); var length = line.ArcLengthAngle; assertTrue(Math.Abs(length.Radians - 2 * S2.Pi) < 2e-14); } }
public void testEqualsAndHashCode() { var vertices = new List <S2Point>(); vertices.Add(new S2Point(1, 0, 0)); vertices.Add(new S2Point(0, 1, 0)); vertices.Add(S2Point.Normalize(new S2Point(0, 1, 1))); vertices.Add(new S2Point(0, 0, 1)); var line1 = new S2Polyline(vertices); var line2 = new S2Polyline(vertices); checkEqualsAndHashCodeMethods(line1, line2, true); var moreVertices = new List <S2Point>(vertices); moreVertices.RemoveAt(0); var line3 = new S2Polyline(moreVertices); checkEqualsAndHashCodeMethods(line1, line3, false); checkEqualsAndHashCodeMethods(line1, null, false); checkEqualsAndHashCodeMethods(line1, "", false); }
// maybe these should be put in a special testing util class /** Return a random unit-length vector. */ public S2Point randomPoint() { return(S2Point.Normalize(new S2Point( 2 * rand.NextDouble() - 1, 2 * rand.NextDouble() - 1, 2 * rand.NextDouble() - 1))); }
/** * Return a right-handed coordinate frame (three orthonormal vectors). Returns * an array of three points: x,y,z */ public IReadOnlyList <S2Point> getRandomFrame() { var p0 = randomPoint(); var p1 = S2Point.Normalize(S2Point.CrossProd(p0, randomPoint())); var p2 = S2Point.Normalize(S2Point.CrossProd(p0, p1)); return(new List <S2Point>(new[] { p0, p1, p2 })); }
public void testIntersectionTolerance() { // We repeatedly construct two edges that cross near a random point "p", // and measure the distance from the actual intersection point "x" to the // the expected intersection point "p" and also to the edges that cross // near "p". // // Note that getIntersection() does not guarantee that "x" and "p" will be // close together (since the intersection point is numerically unstable // when the edges cross at a very small angle), but it does guarantee that // "x" will be close to both of the edges that cross. var maxPointDist = new S1Angle(); var maxEdgeDist = new S1Angle(); for (var i = 0; i < 1000; ++i) { // We construct two edges AB and CD that intersect near "p". The angle // between AB and CD (expressed as a slope) is chosen randomly between // 1e-15 and 1.0 such that its logarithm is uniformly distributed. This // implies that small values are much more likely to be chosen. // // Once the slope is chosen, the four points ABCD must be offset from P // by at least (1e-15 / slope) so that the points are guaranteed to have // the correct circular ordering around P. This is the distance from P // at which the two edges are separated by about 1e-15, which is // approximately the minimum distance at which we can expect computed // points on the two lines to be distinct and have the correct ordering. // // The actual offset distance from P is chosen randomly in the range // [1e-15 / slope, 1.0], again uniformly distributing the logarithm. // This ensures that we test both long and very short segments that // intersect at both large and very small angles. var points = getRandomFrame(); var p = points[0]; var d1 = points[1]; var d2 = points[2]; var slope = Math.Pow(1e-15, rand.NextDouble()); d2 = d1 + (d2 * slope); var a = S2Point.Normalize(p + (d1 * Math.Pow(1e-15 / slope, rand.NextDouble()))); var b = S2Point.Normalize(p - (d1 * Math.Pow(1e-15 / slope, rand.NextDouble()))); var c = S2Point.Normalize(p + (d2 * Math.Pow(1e-15 / slope, rand.NextDouble()))); var d = S2Point.Normalize(p - (d2 * Math.Pow(1e-15 / slope, rand.NextDouble()))); var x = S2EdgeUtil.GetIntersection(a, b, c, d); var distAb = S2EdgeUtil.GetDistance(x, a, b); var distCd = S2EdgeUtil.GetDistance(x, c, d); assertTrue(distAb < S2EdgeUtil.DefaultIntersectionTolerance); assertTrue(distCd < S2EdgeUtil.DefaultIntersectionTolerance); // test getIntersection() post conditions assertTrue(S2.OrderedCcw(a, x, b, S2Point.Normalize(S2.RobustCrossProd(a, b)))); assertTrue(S2.OrderedCcw(c, x, d, S2Point.Normalize(S2.RobustCrossProd(c, d)))); maxEdgeDist = S1Angle.Max(maxEdgeDist, S1Angle.Max(distAb, distCd)); maxPointDist = S1Angle.Max(maxPointDist, new S1Angle(p, x)); } }
private static S2LatLngRect getEdgeBound(double x1, double y1, double z1, double x2, double y2, double z2) { return(S2LatLngRect.FromEdge( S2Point.Normalize(new S2Point(x1, y1, z1)), S2Point.Normalize(new S2Point(x2, y2, z2)))); }
private static void TestWedge(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2, bool contains, bool intersects, WedgeRelation wedgeRelation) { a0 = a0.Normalize(); ab1 = ab1.Normalize(); a2 = a2.Normalize(); b0 = b0.Normalize(); b2 = b2.Normalize(); Assert.True(contains == S2WedgeRelations.WedgeContains(a0, ab1, a2, b0, b2)); Assert.True(intersects == S2WedgeRelations.WedgeIntersects(a0, ab1, a2, b0, b2)); Assert.True(wedgeRelation == S2WedgeRelations.GetWedgeRelation(a0, ab1, a2, b0, b2)); }
public void testFaces() { IDictionary <S2Point, int> edgeCounts = new Dictionary <S2Point, int>(); IDictionary <S2Point, int> vertexCounts = new Dictionary <S2Point, int>(); for (var face = 0; face < 6; ++face) { var id = S2CellId.FromFacePosLevel(face, 0, 0); var cell = new S2Cell(id); JavaAssert.Equal(cell.Id, id); JavaAssert.Equal(cell.Face, face); JavaAssert.Equal(cell.Level, 0); // Top-level faces have alternating orientations to get RHS coordinates. JavaAssert.Equal(cell.Orientation, face & S2.SwapMask); Assert.True(!cell.IsLeaf); for (var k = 0; k < 4; ++k) { if (edgeCounts.ContainsKey(cell.GetEdgeRaw(k))) { edgeCounts[cell.GetEdgeRaw(k)] = edgeCounts[cell .GetEdgeRaw(k)] + 1; } else { edgeCounts[cell.GetEdgeRaw(k)] = 1; } if (vertexCounts.ContainsKey(cell.GetVertexRaw(k))) { vertexCounts[cell.GetVertexRaw(k)] = vertexCounts[cell .GetVertexRaw(k)] + 1; } else { vertexCounts[cell.GetVertexRaw(k)] = 1; } assertDoubleNear(cell.GetVertexRaw(k).DotProd(cell.GetEdgeRaw(k)), 0); assertDoubleNear(cell.GetVertexRaw((k + 1) & 3).DotProd( cell.GetEdgeRaw(k)), 0); assertDoubleNear(S2Point.Normalize( S2Point.CrossProd(cell.GetVertexRaw(k), cell .GetVertexRaw((k + 1) & 3))).DotProd(cell.GetEdge(k)), 1.0); } } // Check that edges have multiplicity 2 and vertices have multiplicity 3. foreach (var i in edgeCounts.Values) { JavaAssert.Equal(i, 2); } foreach (var i in vertexCounts.Values) { JavaAssert.Equal(i, 3); } }
public void Test_EdgeTrueCentroid_SemiEquator() { // Test the centroid of polyline ABC that follows the equator and consists // of two 90 degree edges (i.e., C = -A). The centroid (multiplied by // length) should point toward B and have a norm of 2.0. (The centroid // itself has a norm of 2/Pi, and the total edge length is Pi.) S2Point a = new(0, -1, 0), b = new(1, 0, 0), c = new(0, 1, 0); S2Point centroid = S2Centroid.TrueCentroid(a, b) + S2Centroid.TrueCentroid(b, c); Assert.True(S2.ApproxEquals(b, centroid.Normalize())); Assert2.DoubleEqual(2.0, centroid.Norm()); }
private static void CheckMaxDistance(S2Point x, S2Point a, S2Point b, double distance_radians) { x = x.Normalize(); a = a.Normalize(); b = b.Normalize(); S1ChordAngle max_distance = S1ChordAngle.Straight; Assert.False(S2.UpdateMaxDistance(x, a, b, ref max_distance)); max_distance = S1ChordAngle.Negative; Assert.True(S2.UpdateMaxDistance(x, a, b, ref max_distance)); Assert2.Near(distance_radians, max_distance.Radians(), S2.DoubleError); }
public void testMayIntersect() { var vertices = new List <S2Point>(); vertices.Add(S2Point.Normalize(new S2Point(1, -1.1, 0.8))); vertices.Add(S2Point.Normalize(new S2Point(1, -0.8, 1.1))); var line = new S2Polyline(vertices); for (var face = 0; face < 6; ++face) { var cell = S2Cell.FromFacePosLevel(face, (byte)0, 0); assertEquals(line.MayIntersect(cell), (face & 1) == 0); } }
// Given two edges a0a1 and b0b1, check that the maximum distance between them // is "distance_radians". Parameters are passed by value so that this // function can normalize them. private static void CheckEdgePairMaxDistance(S2Point a0, S2Point a1, S2Point b0, S2Point b1, double distance_radians) { a0 = a0.Normalize(); a1 = a1.Normalize(); b0 = b0.Normalize(); b1 = b1.Normalize(); S1ChordAngle max_distance = S1ChordAngle.Straight; Assert.False(S2.UpdateEdgePairMaxDistance(a0, a1, b0, b1, ref max_distance)); max_distance = S1ChordAngle.Negative; Assert.True(S2.UpdateEdgePairMaxDistance(a0, a1, b0, b1, ref max_distance)); Assert2.Near(distance_radians, max_distance.Radians(), S2.DoubleError); }
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 testRectBound() { // Empty and full caps. Assert.True(S2Cap.Empty.RectBound.IsEmpty); Assert.True(S2Cap.Full.RectBound.IsFull); var kDegreeEps = 1e-13; // Maximum allowable error for latitudes and longitudes measured in // degrees. (assertDoubleNear uses a fixed tolerance that is too small.) // Cap that includes the south pole. var rect = S2Cap.FromAxisAngle(getLatLngPoint(-45, 57), S1Angle.FromDegrees(50)).RectBound; assertDoubleNear(rect.LatLo.Degrees, -90, kDegreeEps); assertDoubleNear(rect.LatHi.Degrees, 5, kDegreeEps); Assert.True(rect.Lng.IsFull); // Cap that is tangent to the north pole. rect = S2Cap.FromAxisAngle(S2Point.Normalize(new S2Point(1, 0, 1)), S1Angle.FromRadians(S2.PiOver4)).RectBound; assertDoubleNear(rect.Lat.Lo, 0); assertDoubleNear(rect.Lat.Hi, S2.PiOver2); Assert.True(rect.Lng.IsFull); rect = S2Cap .FromAxisAngle(S2Point.Normalize(new S2Point(1, 0, 1)), S1Angle.FromDegrees(45)).RectBound; assertDoubleNear(rect.LatLo.Degrees, 0, kDegreeEps); assertDoubleNear(rect.LatHi.Degrees, 90, kDegreeEps); Assert.True(rect.Lng.IsFull); // The eastern hemisphere. rect = S2Cap .FromAxisAngle(new S2Point(0, 1, 0), S1Angle.FromRadians(S2.PiOver2 + 5e-16)).RectBound; assertDoubleNear(rect.LatLo.Degrees, -90, kDegreeEps); assertDoubleNear(rect.LatHi.Degrees, 90, kDegreeEps); Assert.True(rect.Lng.IsFull); // A cap centered on the equator. rect = S2Cap.FromAxisAngle(getLatLngPoint(0, 50), S1Angle.FromDegrees(20)).RectBound; assertDoubleNear(rect.LatLo.Degrees, -20, kDegreeEps); assertDoubleNear(rect.LatHi.Degrees, 20, kDegreeEps); assertDoubleNear(rect.LngLo.Degrees, 30, kDegreeEps); assertDoubleNear(rect.LngHi.Degrees, 70, kDegreeEps); // A cap centered on the north pole. rect = S2Cap.FromAxisAngle(getLatLngPoint(90, 123), S1Angle.FromDegrees(10)).RectBound; assertDoubleNear(rect.LatLo.Degrees, 80, kDegreeEps); assertDoubleNear(rect.LatHi.Degrees, 90, kDegreeEps); Assert.True(rect.Lng.IsFull); }
private S2LatLngRect getEdgeBound(double x1, double y1, double z1, double x2, double y2, double z2) { var bounder = new RectBounder(); var p1 = S2Point.Normalize(new S2Point(x1, y1, z1)); var p2 = S2Point.Normalize(new S2Point(x2, y2, z2)); bounder.AddPoint(p1); bounder.AddPoint(p2); return(bounder.Bound); }
public void testInterpolate() { var vertices = new List <S2Point>(); vertices.Add(new S2Point(1, 0, 0)); vertices.Add(new S2Point(0, 1, 0)); vertices.Add(S2Point.Normalize(new S2Point(0, 1, 1))); vertices.Add(new S2Point(0, 0, 1)); var line = new S2Polyline(vertices); assertEquals(line.Interpolate(-0.1), vertices[0]); assertTrue(S2.ApproxEquals( line.Interpolate(0.1), S2Point.Normalize(new S2Point(1, Math.Tan(0.2 * S2.Pi / 2), 0)))); assertTrue(S2.ApproxEquals(line.Interpolate(0.25), S2Point.Normalize(new S2Point(1, 1, 0)))); assertTrue(S2.ApproxEquals(line.Interpolate(0.5), vertices[1])); assertTrue(S2.ApproxEquals(line.Interpolate(0.75), vertices[2])); assertTrue(S2.ApproxEquals(line.Interpolate(1.1), vertices[3])); }
protected S2Point samplePoint(S2Cap cap) { // We consider the cap axis to be the "z" axis. We choose two other axes to // complete the coordinate frame. var z = cap.Axis; var x = z.Ortho; var y = S2Point.CrossProd(z, x); // The surface area of a spherical cap is directly proportional to its // height. First we choose a random height, and then we choose a random // point along the circle at that height. var h = rand.NextDouble() * cap.Height; var theta = 2 * S2.Pi * rand.NextDouble(); var r = Math.Sqrt(h * (2 - h)); // Radius of circle. // (cos(theta)*r*x + sin(theta)*r*y + (1-h)*z).Normalize() return(S2Point.Normalize(((x * Math.Cos(theta) * r) + (y * Math.Sin(theta) * r)) + (z * (1 - h)))); }
private void getVertices(String str, S2Point x, S2Point y, S2Point z, double maxPerturbation, List <S2Point> vertices) { // Parse the vertices, perturb them if desired, and transform them into the // given frame. var line = makePolyline(str); for (var i = 0; i < line.NumVertices; ++i) { var p = line.Vertex(i); // (p[0]*x + p[1]*y + p[2]*z).Normalize() var axis = S2Point.Normalize(((x * p.X) + (y * p.Y)) + (z * p.Z)); var cap = S2Cap.FromAxisAngle(axis, S1Angle.FromRadians(maxPerturbation)); vertices.Add(samplePoint(cap)); } }
// A slightly more efficient version of Project() where the cross product of // the two endpoints has been precomputed. The cross product does not need to // be normalized, but should be computed using S2.RobustCrossProd() for the // most accurate results. Requires that x, a, and b have unit length. public static S2Point Project(S2Point x, S2Point a, S2Point b, S2Point a_cross_b) { System.Diagnostics.Debug.Assert(a.IsUnitLength()); System.Diagnostics.Debug.Assert(b.IsUnitLength()); System.Diagnostics.Debug.Assert(x.IsUnitLength()); // TODO(ericv): When X is nearly perpendicular to the plane containing AB, // the result is guaranteed to be close to the edge AB but may be far from // the true projected result. This could be fixed by computing the product // (A x B) x X x (A x B) using methods similar to S2::RobustCrossProd() and // S2::GetIntersection(). However note that the error tolerance would need // to be significantly larger in order for this calculation to succeed in // double precision most of the time. For example to avoid higher precision // when X is within 60 degrees of AB the minimum error would be 18 * DBL_ERR, // and to avoid higher precision when X is within 87 degrees of AB the // minimum error would be 120 * DBL_ERR. // The following is not necessary to meet accuracy guarantees but helps // to avoid unexpected results in unit tests. if (x == a || x == b) { return(x); } // Find the closest point to X along the great circle through AB. Note that // we use "n" rather than a_cross_b in the final cross product in order to // avoid the possibility of underflow. S2Point n = a_cross_b.Normalize(); S2Point p = S2.RobustCrossProd(n, x).CrossProd(n).Normalize(); // If this point is on the edge AB, then it's the closest point. S2Point pn = p.CrossProd(n); if (S2Pred.Sign(p, n, a, pn) > 0 && S2Pred.Sign(p, n, b, pn) < 0) { return(p); } // Otherwise, the closest point is either A or B. return(((x - a).Norm2() <= (x - b).Norm2()) ? a : b); }
public void Test_S2_GoodFastHashSpreads() { int kTestPoints = 1 << 16; var set = new List <int>(); var points = new SortedSet <S2Point>(); var base_ = new S2Point(1, 1, 1); for (var i = 0; i < kTestPoints; ++i) { // All points in a tiny cap to test avalanche property of hash // function (the cap would be of radius 1mm on Earth (4*10^9/2^35). S2Point perturbed = base_ + S2Testing.RandomPoint() / (1UL << 35); perturbed = perturbed.Normalize(); set.Add(perturbed.GetHashCode()); points.Add(perturbed); } // A real collision is extremely unlikely. Assert.Equal(0, kTestPoints - points.Count); // Allow a few for the hash. Assert.True(10 >= kTestPoints - set.Count); }
private void assertWedge(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2, bool contains, bool intersects, bool crosses) { a0 = S2Point.Normalize(a0); ab1 = S2Point.Normalize(ab1); a2 = S2Point.Normalize(a2); b0 = S2Point.Normalize(b0); b2 = S2Point.Normalize(b2); assertEquals(new WedgeContains().Test(a0, ab1, a2, b0, b2), contains ? 1 : 0); assertEquals(new WedgeIntersects().Test(a0, ab1, a2, b0, b2), intersects ? -1 : 0); assertEquals(new WedgeContainsOrIntersects().Test(a0, ab1, a2, b0, b2), contains ? 1 : intersects ? -1 : 0); assertEquals(new WedgeContainsOrCrosses().Test(a0, ab1, a2, b0, b2), contains ? 1 : crosses ? -1 : 0); }
public void testGetDistance() { // Error margin since we're doing numerical computations var epsilon = 1e-15; // A rectangle with (lat,lng) vertices (3,1), (3,-1), (-3,-1) and (-3,1) var inner = "3:1, 3:-1, -3:-1, -3:1;"; // A larger rectangle with (lat,lng) vertices (4,2), (4,-2), (-4,-2) and // (-4,s) var outer = "4:2, 4:-2, -4:-2, -4:2;"; var rect = makePolygon(inner); var shell = makePolygon(inner + outer); // All of the vertices of a polygon should be distance 0 for (var i = 0; i < shell.NumLoops; i++) { for (var j = 0; j < shell.Loop(i).NumVertices; j++) { assertEquals(0d, shell.GetDistance(shell.Loop(i).Vertex(j)).Radians, epsilon); } } // A non-vertex point on an edge should be distance 0 assertEquals(0d, rect.GetDistance( S2Point.Normalize(rect.Loop(0).Vertex(0) + rect.Loop(0).Vertex(1))).Radians, epsilon); var origin = S2LatLng.FromDegrees(0, 0).ToPoint(); // rect contains the origin assertEquals(0d, rect.GetDistance(origin).Radians, epsilon); // shell does NOT contain the origin, since it has a hole. The shortest // distance is to (1,0) or (-1,0), and should be 1 degree assertEquals(1d, shell.GetDistance(origin).Degrees, epsilon); }
// Given two edges a0a1 and b0b1, check that the minimum distance between them // is "distance_radians", and that GetEdgePairClosestPoints() returns // "expected_a" and "expected_b" as the points that achieve this distance. // S2Point.Empty may be passed for "expected_a" or "expected_b" to indicate // that both endpoints of the corresponding edge are equally distant, and // therefore either one might be returned. // // Parameters are passed by value so that this function can normalize them. private static void CheckEdgePairMinDistance(S2Point a0, S2Point a1, S2Point b0, S2Point b1, double distance_radians, S2Point expected_a, S2Point expected_b) { a0 = a0.Normalize(); a1 = a1.Normalize(); b0 = b0.Normalize(); b1 = b1.Normalize(); expected_a = expected_a.Normalize(); expected_b = expected_b.Normalize(); var closest = S2.GetEdgePairClosestPoints(a0, a1, b0, b1); S2Point actual_a = closest.Item1; S2Point actual_b = closest.Item2; if (expected_a == S2Point.Empty) { // This special value says that the result should be a0 or a1. Assert.True(actual_a == a0 || actual_a == a1); } else { Assert.True(S2.ApproxEquals(expected_a, actual_a)); } if (expected_b == S2Point.Empty) { // This special value says that the result should be b0 or b1. Assert.True(actual_b == b0 || actual_b == b1); } else { Assert.True(S2.ApproxEquals(expected_b, actual_b)); } S1ChordAngle min_distance = S1ChordAngle.Zero; Assert.False(S2.UpdateEdgePairMinDistance(a0, a1, b0, b1, ref min_distance)); min_distance = S1ChordAngle.Infinity; Assert.True(S2.UpdateEdgePairMinDistance(a0, a1, b0, b1, ref min_distance)); Assert2.Near(distance_radians, min_distance.Radians(), S2.DoubleError); }
// Given a point X and an edge AB, check that the distance from X to AB is // "distanceRadians" and the closest point on AB is "expectedClosest". private static void checkDistance( S2Point x, S2Point a, S2Point b, double distanceRadians, S2Point expectedClosest) { var kEpsilon = 1e-10; x = S2Point.Normalize(x); a = S2Point.Normalize(a); b = S2Point.Normalize(b); expectedClosest = S2Point.Normalize(expectedClosest); assertEquals(distanceRadians, S2EdgeUtil.GetDistance(x, a, b).Radians, kEpsilon); var closest = S2EdgeUtil.GetClosestPoint(x, a, b); if (expectedClosest.Equals(new S2Point(0, 0, 0))) { // This special value says that the result should be A or B. assertTrue(closest == a || closest == b); } else { assertTrue(S2.ApproxEquals(closest, expectedClosest)); } }
public void Test_RectBounder_MaxLatitudeRandom() { // Check that the maximum latitude of edges is computed accurately to within // 3 * S2Constants.DoubleEpsilon (the expected maximum error). We concentrate on maximum // latitudes near the equator and north pole since these are the extremes. const int kIters = 100; for (int iter = 0; iter < kIters; ++iter) { // Construct a right-handed coordinate frame (U,V,W) such that U points // slightly above the equator, V points at the equator, and W is slightly // offset from the north pole. S2Point u = S2Testing.RandomPoint(); u = new S2Point(u.X, u.Y, S2.DoubleEpsilon * 1e-6 * Math.Pow(1e12, S2Testing.Random.RandDouble())); // log is uniform u = u.Normalize(); S2Point v = S2.RobustCrossProd(new S2Point(0, 0, 1), u).Normalize(); S2Point w = S2.RobustCrossProd(u, v).Normalize(); // Construct a line segment AB that passes through U, and check that the // maximum latitude of this segment matches the latitude of U. S2Point a = (u - S2Testing.Random.RandDouble() * v).Normalize(); S2Point b = (u + S2Testing.Random.RandDouble() * v).Normalize(); S2LatLngRect ab_bound = GetEdgeBound(a, b); Assert2.Near(S2LatLng.Latitude(u).Radians, ab_bound.Lat.Hi, kRectError.LatRadians); // Construct a line segment CD that passes through W, and check that the // maximum latitude of this segment matches the latitude of W. S2Point c = (w - S2Testing.Random.RandDouble() * v).Normalize(); S2Point d = (w + S2Testing.Random.RandDouble() * v).Normalize(); S2LatLngRect cd_bound = GetEdgeBound(c, d); Assert2.Near(S2LatLng.Latitude(w).Radians, cd_bound.Lat.Hi, kRectError.LatRadians); } }
public void S2CapBasicTest() { // Test basic properties of empty and full caps. var empty = S2Cap.Empty; var full = S2Cap.Full; Assert.True(empty.IsValid); Assert.True(empty.IsEmpty); Assert.True(empty.Complement.IsFull); Assert.True(full.IsValid); Assert.True(full.IsFull); Assert.True(full.Complement.IsEmpty); JavaAssert.Equal(full.Height, 2.0); assertDoubleNear(full.Angle.Degrees, 180); // Containment and intersection of empty and full caps. Assert.True(empty.Contains(empty)); Assert.True(full.Contains(empty)); Assert.True(full.Contains(full)); Assert.True(!empty.InteriorIntersects(empty)); Assert.True(full.InteriorIntersects(full)); Assert.True(!full.InteriorIntersects(empty)); // Singleton cap containing the x-axis. var xaxis = S2Cap.FromAxisHeight(new S2Point(1, 0, 0), 0); Assert.True(xaxis.Contains(new S2Point(1, 0, 0))); Assert.True(!xaxis.Contains(new S2Point(1, 1e-20, 0))); JavaAssert.Equal(xaxis.Angle.Radians, 0.0); // Singleton cap containing the y-axis. var yaxis = S2Cap.FromAxisAngle(new S2Point(0, 1, 0), S1Angle.FromRadians(0)); Assert.True(!yaxis.Contains(xaxis.Axis)); JavaAssert.Equal(xaxis.Height, 0.0); // Check that the complement of a singleton cap is the full cap. var xcomp = xaxis.Complement; Assert.True(xcomp.IsValid); Assert.True(xcomp.IsFull); Assert.True(xcomp.Contains(xaxis.Axis)); // Check that the complement of the complement is *not* the original. Assert.True(xcomp.Complement.IsValid); Assert.True(xcomp.Complement.IsEmpty); Assert.True(!xcomp.Complement.Contains(xaxis.Axis)); // Check that very small caps can be represented accurately. // Here "kTinyRad" is small enough that unit vectors perturbed by this // amount along a tangent do not need to be renormalized. var kTinyRad = 1e-10; var tiny = S2Cap.FromAxisAngle(S2Point.Normalize(new S2Point(1, 2, 3)), S1Angle.FromRadians(kTinyRad)); var tangent = S2Point.Normalize(S2Point.CrossProd(tiny.Axis, new S2Point(3, 2, 1))); Assert.True(tiny.Contains(tiny.Axis + (tangent * 0.99 * kTinyRad))); Assert.True(!tiny.Contains(tiny.Axis + (tangent * 1.01 * kTinyRad))); // Basic tests on a hemispherical cap. var hemi = S2Cap.FromAxisHeight(S2Point.Normalize(new S2Point(1, 0, 1)), 1); JavaAssert.Equal(hemi.Complement.Axis, -hemi.Axis); JavaAssert.Equal(hemi.Complement.Height, 1.0); Assert.True(hemi.Contains(new S2Point(1, 0, 0))); Assert.True(!hemi.Complement.Contains(new S2Point(1, 0, 0))); Assert.True(hemi.Contains(S2Point.Normalize(new S2Point(1, 0, -(1 - EPS))))); Assert.True(!hemi.InteriorContains(S2Point.Normalize(new S2Point(1, 0, -(1 + EPS))))); // A concave cap. var concave = S2Cap.FromAxisAngle(getLatLngPoint(80, 10), S1Angle.FromDegrees(150)); Assert.True(concave.Contains(getLatLngPoint(-70 * (1 - EPS), 10))); Assert.True(!concave.Contains(getLatLngPoint(-70 * (1 + EPS), 10))); Assert.True(concave.Contains(getLatLngPoint(-50 * (1 - EPS), -170))); Assert.True(!concave.Contains(getLatLngPoint(-50 * (1 + EPS), -170))); // Cap containment tests. Assert.True(!empty.Contains(xaxis)); Assert.True(!empty.InteriorIntersects(xaxis)); Assert.True(full.Contains(xaxis)); Assert.True(full.InteriorIntersects(xaxis)); Assert.True(!xaxis.Contains(full)); Assert.True(!xaxis.InteriorIntersects(full)); Assert.True(xaxis.Contains(xaxis)); Assert.True(!xaxis.InteriorIntersects(xaxis)); Assert.True(xaxis.Contains(empty)); Assert.True(!xaxis.InteriorIntersects(empty)); Assert.True(hemi.Contains(tiny)); Assert.True(hemi.Contains( S2Cap.FromAxisAngle(new S2Point(1, 0, 0), S1Angle.FromRadians(S2.PiOver4 - EPS)))); Assert.True(!hemi.Contains( S2Cap.FromAxisAngle(new S2Point(1, 0, 0), S1Angle.FromRadians(S2.PiOver4 + EPS)))); Assert.True(concave.Contains(hemi)); Assert.True(concave.InteriorIntersects(hemi.Complement)); Assert.True(!concave.Contains(S2Cap.FromAxisHeight(-concave.Axis, 0.1))); }