private static void ChooseEdgeNearCell(S2Cell cell, out S2Point a, out S2Point b) { S2Cap cap = cell.GetCapBound(); if (S2Testing.Random.OneIn(5)) { // Choose a point anywhere on the sphere. a = S2Testing.RandomPoint(); } else { // Choose a point inside or somewhere near the cell. a = S2Testing.SamplePoint(new S2Cap(cap.Center, 1.5 * cap.RadiusAngle())); } // Now choose a maximum edge length ranging from very short to very long // relative to the cell size, and choose the other endpoint. double max_length = Math.Min(100 * Math.Pow(1e-4, S2Testing.Random.RandDouble()) * cap.Radius.Radians(), S2.M_PI_2); b = S2Testing.SamplePoint(new S2Cap(a, S1Angle.FromRadians(max_length))); if (S2Testing.Random.OneIn(20)) { // Occasionally replace edge with antipodal edge. a = -a; b = -b; } }
public void Test_S2ConvexHullQuery_PointsInsideHull() { // Repeatedly build the convex hull of a set of points, then add more points // inside that loop and build the convex hull again. The result should // always be the same. int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. // Choose points from within a cap of random size, up to but not including // an entire hemisphere. S2Cap cap = S2Testing.GetRandomCap(S2.DoubleError, 1.999 * Math.PI); S2ConvexHullQuery query = new(); int num_points1 = S2Testing.Random.Uniform(100) + 3; for (int i = 0; i < num_points1; ++i) { query.AddPoint(S2Testing.SamplePoint(cap)); } var hull = query.GetConvexHull(); // When the convex hull is nearly a hemisphere, the algorithm sometimes // returns a full cap instead. This is because it first computes a // bounding rectangle for all the input points/edges and then converts it // to a bounding cap, which sometimes yields a non-convex cap (radius // larger than 90 degrees). This should not be a problem in practice // (since most convex hulls are not hemispheres), but in order make this // test pass reliably it means that we need to reject convex hulls whose // bounding cap (when computed from a bounding rectangle) is not convex. // // TODO(b/203702905): This test can still fail (about 1 iteration in // 500,000) because the S2LatLngRect::GetCapBound implementation does not // guarantee that A.Contains(B) implies // A.GetCapBound().Contains(B.GetCapBound()). if (hull.GetCapBound().Height() >= 1) { continue; } // Otherwise, add more points inside the convex hull. int num_points2 = 1000; for (int i = 0; i < num_points2; ++i) { S2Point p = S2Testing.SamplePoint(cap); if (hull.Contains(p)) { query.AddPoint(p); } } // Finally, build a new convex hull and check that it hasn't changed. var hull2 = (query.GetConvexHull()); _logger.WriteLine("Iteration: " + iter); Assert.True(hull2.BoundaryEquals(hull)); } }
public void AddEdges(S2Cap index_cap, int num_edges, MutableS2ShapeIndex index) { var points = new List <S2Point>(); for (int i = 0; i < num_edges; ++i) { points.Add(S2Testing.SamplePoint(index_cap)); } index.Add(new S2PointVectorShape(points.ToArray())); }
public void Test_S2_ProjectError() { for (int iter = 0; iter < 1000; ++iter) { S2Testing.Random.Reset(iter + 1); // Easier to reproduce a specific case. S2Point a = ChoosePoint(); S2Point b = ChoosePoint(); S2Point n = S2.RobustCrossProd(a, b).Normalize(); S2Point x = S2Testing.SamplePoint(new S2Cap(n, S1Angle.FromRadians(1e-15))); S2Point p = S2.Project(x, a, b); Assert.True(S2Pred.CompareEdgeDistance( p, a, b, new S1ChordAngle(S2.kProjectPerpendicularErrorS1Angle)) < 0); } }
private static void CompareS2LoopToShape(S2Loop loop, S2Shape shape) { MutableS2ShapeIndex index = new(); index.Add(shape); S2Cap cap = loop.GetCapBound(); var query = index.MakeS2ContainsPointQuery(); for (int iter = 0; iter < 100; ++iter) { S2Point point = S2Testing.SamplePoint(cap); Assert.Equal(loop.Contains(point), query.ShapeContains(index.Shape(0), point)); } }
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_VisitIntersectingShapes_Polygons() { MutableS2ShapeIndex index = new(); S2Cap center_cap = new(new(1, 0, 0), S1Angle.FromRadians(0.5)); S2Testing.Fractal fractal = new(); for (int i = 0; i < 10; ++i) { fractal.SetLevelForApproxMaxEdges(3 * 64); S2Point center = S2Testing.SamplePoint(center_cap); index.Add(new S2Loop.Shape( fractal.MakeLoop(S2Testing.GetRandomFrameAt(center), S1Angle.FromRadians(S2Testing.Random.RandDouble())))); } // Also add a big polygon containing most of the polygons above to ensure // that we test containment of cells that are ancestors of index cells. index.Add(NewPaddedCell(S2CellId.FromFace(0), 0)); new VisitIntersectingShapesTest(index).Run(); }
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; } } }
public void Test_S2ContainsPointQuery_GetContainingShapes() { // Also tests ShapeContains(). int kNumVerticesPerLoop = 10; S1Angle kMaxLoopRadius = S2Testing.KmToAngle(10); S2Cap center_cap = new(S2Testing.RandomPoint(), kMaxLoopRadius); MutableS2ShapeIndex index = new(); for (int i = 0; i < 100; ++i) { var loop = S2Loop.MakeRegularLoop( S2Testing.SamplePoint(center_cap), S2Testing.Random.RandDouble() * kMaxLoopRadius, kNumVerticesPerLoop); index.Add(new S2Loop.Shape(loop)); } var query = index.MakeS2ContainsPointQuery(); for (int i = 0; i < 100; ++i) { S2Point p = S2Testing.SamplePoint(center_cap); List <S2Shape> expected = new(); foreach (var shape in index) { var loop = ((S2Loop.Shape)shape).Loop; if (loop.Contains(p)) { Assert.True(query.ShapeContains(shape, p)); expected.Add(shape); } else { Assert.False(query.ShapeContains(shape, p)); } } var actual = query.GetContainingShapes(p); Assert.Equal(expected, actual); } }
public void Test_VisitIntersectingShapes_Polylines() { MutableS2ShapeIndex index = new(); S2Cap center_cap = new(new S2Point(1, 0, 0), S1Angle.FromRadians(0.5)); for (int i = 0; i < 50; ++i) { S2Point center = S2Testing.SamplePoint(center_cap); S2Point[] vertices; if (S2Testing.Random.OneIn(10)) { vertices = new[] { center, center }; // Try a few degenerate polylines. } else { vertices = S2Testing.MakeRegularPoints( center, S1Angle.FromRadians(S2Testing.Random.RandDouble()), S2Testing.Random.Uniform(20) + 3); } index.Add(new S2LaxPolylineShape(vertices)); } new VisitIntersectingShapesTest(index).Run(); }
// The running time of this test is proportional to // (num_indexes + num_queries) * num_edges. // (Note that every query is checked using the brute force algorithm.) private static void TestWithIndexFactory(IShapeIndexFactory factory, int num_indexes, int num_edges, int num_queries) { // Build a set of MutableS2ShapeIndexes containing the desired geometry. List <S2Cap> index_caps = new(); List <MutableS2ShapeIndex> indexes = new(); for (int i = 0; i < num_indexes; ++i) { S2Testing.Random.Reset(S2Testing.Random.RandomSeed + i); index_caps.Add(new S2Cap(S2Testing.RandomPoint(), kTestCapRadius)); indexes.Add(new MutableS2ShapeIndex()); factory.AddEdges(index_caps.Last(), num_edges, indexes.Last()); } for (int i = 0; i < num_queries; ++i) { S2Testing.Random.Reset(S2Testing.Random.RandomSeed + i); int i_index = S2Testing.Random.Uniform(num_indexes); var index_cap = index_caps[i_index]; // Choose query points from an area approximately 4x larger than the // geometry being tested. var query_radius = 2 * index_cap.RadiusAngle(); S2Cap query_cap = new(index_cap.Center, query_radius); S2ClosestEdgeQuery query = new(indexes[i_index]); // Occasionally we don't set any limit on the number of result edges. // (This may return all edges if we also don't set a distance limit.) if (!S2Testing.Random.OneIn(5)) { query.Options_.MaxResults = (1 + S2Testing.Random.Uniform(10)); } // We set a distance limit 2/3 of the time. if (!S2Testing.Random.OneIn(3)) { query.Options_.MaxDistance = new(S2Testing.Random.RandDouble() * query_radius); } if (S2Testing.Random.OneIn(2)) { // Choose a maximum error whose logarithm is uniformly distributed over // a reasonable range, except that it is sometimes zero. query.Options_.MaxError = new(S1Angle.FromRadians( Math.Pow(1e-4, S2Testing.Random.RandDouble()) * query_radius.Radians)); } query.Options_.IncludeInteriors = (S2Testing.Random.OneIn(2)); int target_type = S2Testing.Random.Uniform(4); if (target_type == 0) { // Find the edges closest to a given point. S2Point point = S2Testing.SamplePoint(query_cap); S2ClosestEdgeQuery.PointTarget target = new(point); var closest = TestFindClosestEdges(target, query); if (!closest.Distance.IsInfinity()) { // Also test the Project method. Assert2.Near( closest.Distance.ToAngle().Radians, new S1Angle(point, query.Project(point, closest)).Radians, kTestChordAngleError); } } else if (target_type == 1) { // Find the edges closest to a given edge. S2Point a = S2Testing.SamplePoint(query_cap); S2Point b = S2Testing.SamplePoint( new S2Cap(a, Math.Pow(1e-4, S2Testing.Random.RandDouble()) * query_radius)); S2ClosestEdgeQuery.EdgeTarget target = new(a, b); TestFindClosestEdges(target, query); } else if (target_type == 2) { // Find the edges closest to a given cell. int min_level = S2.kMaxDiag.GetLevelForMaxValue(query_radius.Radians); int level = min_level + S2Testing.Random.Uniform( S2.kMaxCellLevel - min_level + 1); S2Point a = S2Testing.SamplePoint(query_cap); S2Cell cell = new(new S2CellId(a).Parent(level)); S2ClosestEdgeQuery.CellTarget target = new(cell); TestFindClosestEdges(target, query); } else { Assert.Equal(3, target_type); // Use another one of the pre-built indexes as the target. int j_index = S2Testing.Random.Uniform(num_indexes); S2ClosestEdgeQuery.ShapeIndexTarget target = new(indexes[j_index]); target.IncludeInteriors = (S2Testing.Random.OneIn(2)); TestFindClosestEdges(target, query); } } }