public void Test_S2CellUnion_FromMinMax() { // Check the very first leaf cell and face cell. S2CellId face1_id = S2CellId.FromFace(0); TestFromMinMax(face1_id.RangeMin(), face1_id.RangeMin()); TestFromMinMax(face1_id.RangeMin(), face1_id.RangeMax()); // Check the very last leaf cell and face cell. S2CellId face5_id = S2CellId.FromFace(5); TestFromMinMax(face5_id.RangeMin(), face5_id.RangeMax()); TestFromMinMax(face5_id.RangeMax(), face5_id.RangeMax()); // Check random ranges of leaf cells. for (int iter = 0; iter < 100; ++iter) { S2CellId x = S2Testing.GetRandomCellId(S2.kMaxCellLevel); S2CellId y = S2Testing.GetRandomCellId(S2.kMaxCellLevel); if (x > y) { var tmp = x; x = y; y = tmp; } TestFromMinMax(x, y); } }
public void Test_S2PaddedCell_GetEntryExitVertices() { int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { S2CellId id = S2Testing.GetRandomCellId(); // Check that entry/exit vertices do not depend on padding. Assert.Equal(new S2PaddedCell(id, 0).GetEntryVertex(), new S2PaddedCell(id, 0.5).GetEntryVertex()); Assert.Equal(new S2PaddedCell(id, 0).GetExitVertex(), new S2PaddedCell(id, 0.5).GetExitVertex()); // Check that the exit vertex of one cell is the same as the entry vertex // of the immediately following cell. (This also tests wrapping from the // end to the start of the S2CellId curve with high probability.) Assert.Equal(new S2PaddedCell(id, 0).GetExitVertex(), new S2PaddedCell(id.NextWrap(), 0).GetEntryVertex()); // Check that the entry vertex of a cell is the same as the entry vertex // of its first child, and similarly for the exit vertex. if (!id.IsLeaf()) { Assert.Equal(new S2PaddedCell(id, 0).GetEntryVertex(), new S2PaddedCell(id.Child(0), 0).GetEntryVertex()); Assert.Equal(new S2PaddedCell(id, 0).GetExitVertex(), new S2PaddedCell(id.Child(3), 0).GetExitVertex()); } } }
public void Test_EncodedS2PointVectorTest_SnappedFractalLoops() { S2Testing.Random.Reset(S2Testing.Random.RandomSeed); #if DEBUG const int kMaxPoints = 3 << 10; #else const int kMaxPoints = 3 << 14; #endif for (int num_points = 3; num_points <= kMaxPoints; num_points *= 4) { int s2polygon_size = 0, lax_polygon_size = 0; for (int i = 0; i < 10; ++i) { S2Testing.Fractal fractal = new(); fractal.SetLevelForApproxMaxEdges(num_points); var frame = S2Testing.GetRandomFrame(); var loop = fractal.MakeLoop(frame, S2Testing.KmToAngle(10)); List <S2Point> points = new(); for (int j = 0; j < loop.NumVertices; ++j) { points.Add(new S2CellId(loop.Vertex(j)).ToPoint()); } S2Polygon s2polygon = new(new S2Loop(points)); Encoder encoder = new(); s2polygon.Encode(encoder); s2polygon_size += encoder.Length(); // S2LaxPolygonShape has 2 extra bytes of overhead to encode one loop. lax_polygon_size += TestEncodedS2PointVector(points.ToArray(), CodingHint.COMPACT, -1) + 2; } _logger.WriteLine($"n={num_points:d5} s2={s2polygon_size:d9} lax={lax_polygon_size:9}"); } }
public void Test_S1ChordAngle_GetS2PointConstructorMaxError() { // Check that the error bound returned by GetS2PointConstructorMaxError() is // large enough. for (var iter = 0; iter < 100000; ++iter) { S2Testing.Random.Reset(iter); // Easier to reproduce a specific case. var x = S2Testing.RandomPoint(); var y = S2Testing.RandomPoint(); if (S2Testing.Random.OneIn(10)) { // Occasionally test a point pair that is nearly identical or antipodal. var r = S1Angle.FromRadians(S2.DoubleError * S2Testing.Random.RandDouble()); y = S2.GetPointOnLine(x, y, r); if (S2Testing.Random.OneIn(2)) { y = -y; } } S1ChordAngle dist = new(x, y); var error = dist.GetS2PointConstructorMaxError(); var er1 = S2Pred.CompareDistance(x, y, dist.PlusError(error)); if (er1 > 0) { } Assert.True(er1 <= 0); var er2 = S2Pred.CompareDistance(x, y, dist.PlusError(-error)); if (er2 < 0) { } Assert.True(er2 >= 0); } }
public void Test_S2LatLngRect_GetDirectedHausdorffDistanceRandomPairs() { // Test random pairs. int kIters = 1000; for (int i = 0; i < kIters; ++i) { S2LatLngRect a = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); S2LatLngRect b = S2LatLngRect.FromPointPair(new S2LatLng(S2Testing.RandomPoint()), new S2LatLng(S2Testing.RandomPoint())); // a and b are *minimum* bounding rectangles of two random points, in // particular, their Voronoi diagrams are always of the same topology. We // take the "complements" of a and b for more thorough testing. S2LatLngRect a2 = new(a.Lat, a.Lng.Complement()); S2LatLngRect b2 = new(b.Lat, b.Lng.Complement()); // Note that "a" and "b" come from the same distribution, so there is no // need to test pairs such as (b, a), (b, a2), etc. VerifyGetDirectedHausdorffDistance(a, b); VerifyGetDirectedHausdorffDistance(a, b2); VerifyGetDirectedHausdorffDistance(a2, b); VerifyGetDirectedHausdorffDistance(a2, b2); } }
public void Test_S2RegionTermIndexer_MaxLevelSetLoosely() { // Test that correct terms are generated even when (max_level - min_level) // is not a multiple of level_mod. var options = new S2RegionTermIndexer.Options(); options.MinLevel = (1); options.LevelMod = (2); options.MaxLevel = (19); var indexer1 = new S2RegionTermIndexer(options); options.MaxLevel = (20); var indexer2 = new S2RegionTermIndexer(options); S2Point point = S2Testing.RandomPoint(); Assert.Equal(indexer1.GetIndexTerms(point, ""), indexer2.GetIndexTerms(point, "")); Assert.Equal(indexer1.GetQueryTerms(point, ""), indexer2.GetQueryTerms(point, "")); S2Cap cap = S2Testing.GetRandomCap(0.0, 1.0); // Area range. Assert.Equal(indexer1.GetIndexTerms(cap, ""), indexer2.GetIndexTerms(cap, "")); Assert.Equal(indexer1.GetQueryTerms(cap, ""), indexer2.GetQueryTerms(cap, "")); }
public void Test_S2PointVectorShape_ConstructionAndAccess() { const int kNumPoints = 100; S2Point[] points = new S2Point[kNumPoints]; S2Testing.Random.Reset(S2Testing.Random.RandomSeed); for (int i = 0; i < kNumPoints; ++i) { points[i] = S2Testing.RandomPoint(); } S2PointVectorShape shape = new(points); Assert.Equal(kNumPoints, shape.NumEdges()); Assert.Equal(kNumPoints, shape.NumChains()); Assert.Equal(0, shape.Dimension()); Assert.False(shape.IsEmpty()); Assert.False(shape.IsFull()); for (int i = 0; i < kNumPoints; ++i) { Assert.Equal(i, shape.GetChain(i).Start); Assert.Equal(1, shape.GetChain(i).Length); var edge = shape.GetEdge(i); S2Point pt = points[i]; Assert.Equal(pt, edge.V0); Assert.Equal(pt, edge.V1); } }
private static void CheckCovering(S2RegionCoverer.Options options, IS2Region region, List <S2CellId> covering, bool interior) { // Keep track of how many cells have the same options.min_level() ancestor. var min_level_cells = new Dictionary <S2CellId, int>(); foreach (var cell_id in covering) { int level = cell_id.Level(); Assert.True(level >= options.MinLevel); Assert.False(level <= options.MaxLevel); Assert.Equal(0, (level - options.MinLevel) % options.LevelMod); min_level_cells[cell_id.Parent(options.MinLevel)] += 1; } if (covering.Count > options.MaxCells) { // If the covering has more than the requested number of cells, then check // that the cell count cannot be reduced by using the parent of some cell. foreach (var count in min_level_cells.Values) { Assert.Equal(1, count); } } if (interior) { foreach (S2CellId cell_id in covering) { Assert.True(region.Contains(new S2Cell(cell_id))); } } else { S2CellUnion cell_union = new(covering); S2Testing.CheckCovering(region, cell_union, true); } }
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); }
public void Test_S2EdgeVectorShape_EdgeAccess() { S2EdgeVectorShape shape = new(); S2Testing.Random.Reset(S2Testing.Random.RandomSeed); int kNumEdges = 100; List <(S2Point, S2Point)> edges = new(); for (int i = 0; i < kNumEdges; ++i) { S2Point a = S2Testing.RandomPoint(); // Control the evaluation order edges.Add((a, S2Testing.RandomPoint())); shape.Add(edges.Last().Item1, edges.Last().Item2); } Assert.Equal(kNumEdges, shape.NumEdges()); Assert.Equal(kNumEdges, shape.NumChains()); Assert.Equal(1, shape.Dimension()); Assert.False(shape.IsEmpty()); Assert.False(shape.IsFull()); for (int i = 0; i < kNumEdges; ++i) { Assert.Equal(i, shape.GetChain(i).Start); Assert.Equal(1, shape.GetChain(i).Length); var edge = shape.GetEdge(i); Assert.Equal(edges[i].Item1, edge.V0); Assert.Equal(edges[i].Item2, edge.V1); } }
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 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; } }
// This function is designed to choose line segment endpoints that are difficult // to handle correctly. Given two adjacent cube vertices P and Q, it returns // either an edge midpoint, face midpoint, or corner vertex along the edge PQ // and then perturbs it slightly. It also sometimes returns a random point from // anywhere on the sphere. private S2Point PerturbedCornerOrMidpoint(S2Point p, S2Point q) { S2Point a = (S2Testing.Random.Uniform(3) - 1) * p + (S2Testing.Random.Uniform(3) - 1) * q; if (S2Testing.Random.OneIn(10)) { // This perturbation often has no effect except on coordinates that are // zero, in which case the perturbed value is so small that operations on // it often result in underflow. a += Math.Pow(1e-300, S2Testing.Random.RandDouble()) * S2Testing.RandomPoint(); } else if (S2Testing.Random.OneIn(2)) { // For coordinates near 1 (say > 0.5), this perturbation yields values // that are only a few representable values away from the initial value. a += 4 * S2.DoubleEpsilon * S2Testing.RandomPoint(); } else { // A perturbation whose magnitude is in the range [1e-25, 1e-10]. a += 1e-10 * Math.Pow(S2.DoubleError, S2Testing.Random.RandDouble()) * S2Testing.RandomPoint(); } if (a.Norm2() < S2.DoubleMinNorm) { // If a.Norm2 is denormalized, Normalize() loses too much precision. return(PerturbedCornerOrMidpoint(p, q)); } return(a); }
public void AddEdges(S2Cap index_cap, int num_edges, MutableS2ShapeIndex index) { var fractal = new S2Testing.Fractal(); fractal.SetLevelForApproxMaxEdges(num_edges); index.Add(new S2Loop.Shape( fractal.MakeLoop(S2Testing.GetRandomFrameAt(index_cap.Center), index_cap.RadiusAngle()))); }
public void Test_E6() { for (var i = 0; i < kIters; i++) { var ll = S2LatLng.FromPoint(S2Testing.RandomPoint()); var ll_e6 = S2LatLng.FromE6(ll.Lat().E6(), ll.Lng().E6()); ExpectMaxDigits(ll_e6, 6); } }
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_E7() { ExpectMaxDigits(S2LatLng.FromDegrees(0, 0), 7); for (var i = 0; i < kIters; i++) { var ll = S2LatLng.FromPoint(S2Testing.RandomPoint()); var ll_e7 = S2LatLng.FromE7(ll.Lat().E7(), ll.Lng().E7()); ExpectMaxDigits(ll_e7, 7); } }
public void Test_GetCrossingCandidates_DegenerateEdgeOnCellVertexIsItsOwnCandidate() { for (int i = 0; i < 100; ++i) { List <(S2Point, S2Point)> edges = new(); S2Cell cell = new(S2Testing.GetRandomCellId()); edges.Add((cell.Vertex(0), cell.Vertex(0))); TestAllCrossings(edges); } }
public void AddPoints(S2Cap index_cap, int num_points, TestIndex index) { var points = S2Testing.MakeRegularPoints( index_cap.Center, index_cap.RadiusAngle(), num_points); for (int i = 0; i < points.Length; ++i) { index.Add(points[i], i); } }
public void Test_S2CellId_ExpandedByDistanceUV() { const double max_dist_degrees = 10; for (var iter = 0; iter < 100; ++iter) { S2CellId id = S2Testing.GetRandomCellId(); double dist_degrees = S2Testing.Random.UniformDouble(-max_dist_degrees, max_dist_degrees); TestExpandedByDistanceUV(id, S1Angle.FromDegrees(dist_degrees)); } }
public void Test_S2_GetPointToRightS1ChordAngle() { S2Point a = S2LatLng.FromDegrees(0, 0).ToPoint(); S2Point b = S2LatLng.FromDegrees(0, 5).ToPoint(); // east S1Angle kDistance = S2Testing.MetersToAngle(10); S2Point c = S2.GetPointToRight(a, b, new S1ChordAngle(kDistance)); Assert2.Near(new S1Angle(a, c).Radians, kDistance.Radians, 1e-15); // CAB must be a right angle with C to the right of AB. Assert2.Near(S2.TurnAngle(c, a, b), -S2.M_PI_2 /*radians*/, 1e-15); }
public void Test_S2CellId_Inverses() { // Check the conversion of random leaf cells to S2LatLngs and back. for (int i = 0; i < 200000; ++i) { S2CellId id = S2Testing.GetRandomCellId(S2.kMaxCellLevel); Assert.True(id.IsLeaf()); Assert.Equal(S2.kMaxCellLevel, id.Level()); S2LatLng center = id.ToLatLng(); Assert.Equal(id.Id, new S2CellId(center).Id); } }
public void Test_XYZToFaceSiTi() { // Check the conversion of random cells to center points and back. for (int level = 0; level <= S2.kMaxCellLevel; ++level) { for (int i = 0; i < 1000; ++i) { S2CellId id = S2Testing.GetRandomCellId(level); int actual_level = S2.XYZtoFaceSiTi(id.ToPoint(), out var face, out var si, out var ti); Assert.Equal(level, actual_level); S2CellId actual_id = S2CellId.FromFaceIJ(face, (int)(si / 2), (int)(ti / 2)).Parent(level); Assert.Equal(id, actual_id); // Now test a point near the cell center but not equal to it. S2Point p_moved = id.ToPoint() + new S2Point(1e-13, 1e-13, 1e-13); actual_level = S2.XYZtoFaceSiTi(p_moved, out var face_moved, out var si_moved, out var ti_moved); Assert.Equal(-1, actual_level); Assert.Equal(face, face_moved); Assert.Equal(si, si_moved); Assert.Equal(ti, ti_moved); // Finally, test some random (si,ti) values that may be at different // levels, or not at a valid level at all (for example, si == 0). int face_random = S2Testing.Random.Uniform(S2CellId.kNumFaces); uint si_random, ti_random; uint mask = ~0U << (S2.kMaxCellLevel - level); do { si_random = S2Testing.Random.Rand32() & mask; ti_random = S2Testing.Random.Rand32() & mask; } while (si_random > S2.kMaxSiTi || ti_random > S2.kMaxSiTi); S2Point p_random = S2.FaceSiTitoXYZ(face_random, si_random, ti_random); actual_level = S2.XYZtoFaceSiTi(p_random, out face, out si, out ti); if (face != face_random) { // The chosen point is on the edge of a top-level face cell. Assert.Equal(-1, actual_level); Assert.True(si == 0 || si == S2.kMaxSiTi || ti == 0 || ti == S2.kMaxSiTi); } else { Assert.Equal(si_random, si); Assert.Equal(ti_random, ti); if (actual_level >= 0) { Assert.Equal(p_random, S2CellId.FromFaceIJ(face, (int)(si / 2), (int)(ti / 2)).Parent(actual_level).ToPoint()); } } } } }
public void Test_S2CellUnion_Expand() { // This test generates coverings for caps of random sizes, expands // the coverings by a random radius, and then make sure that the new // covering covers the expanded cap. It also makes sure that the // new covering is not too much larger than expected. var coverer = new S2RegionCoverer(); for (var i = 0; i < 1000; ++i) { _logger.WriteLine($"Iteration {i}"); var cap = S2Testing.GetRandomCap( S2Cell.AverageArea(S2.kMaxCellLevel), S2.M_4_PI); // Expand the cap area by a random factor whose log is uniformly // distributed between 0 and log(1e2). var expanded_cap = S2Cap.FromCenterHeight( cap.Center, Math.Min(2.0, Math.Pow(1e2, rnd.RandDouble()) * cap.Height())); var radius = (expanded_cap.Radius - cap.Radius).Radians(); var max_level_diff = rnd.Uniform(8); // Generate a covering for the original cap, and measure the maximum // distance from the cap center to any point in the covering. coverer.Options_.MaxCells = 1 + rnd.Skewed(10); var covering = coverer.GetCovering(cap); S2Testing.CheckCovering(cap, covering, true); var covering_radius = GetRadius(covering, cap.Center); // This code duplicates the logic in Expand(min_radius, max_level_diff) // that figures out an appropriate cell level to use for the expansion. int min_level = S2.kMaxCellLevel; foreach (var id in covering) { min_level = Math.Min(min_level, id.Level()); } var expand_level = Math.Min(min_level + max_level_diff, S2.kMinWidth.GetLevelForMinValue(radius)); // Generate a covering for the expanded cap, and measure the new maximum // distance from the cap center to any point in the covering. covering.Expand(S1Angle.FromRadians(radius), max_level_diff); S2Testing.CheckCovering(expanded_cap, covering, false); double expanded_covering_radius = GetRadius(covering, cap.Center); // If the covering includes a tiny cell along the boundary, in theory the // maximum angle of the covering from the cap center can increase by up to // twice the maximum length of a cell diagonal. Assert.True(expanded_covering_radius - covering_radius <= 2 * S2.kMaxDiag.GetValue(expand_level)); } }
public void Test_GetReferencePoint_PartiallyDegenerateLoops() { for (var iter = 0; iter < 100; ++iter) { // First we construct a long convoluted edge chain that follows the // S2CellId Hilbert curve. At some random point along the curve, we // insert a small triangular loop. List <List <S2Point> > loops = new(1) { new() }; var loop = loops[0]; int num_vertices = 100; var start = S2Testing.GetRandomCellId(S2.kMaxCellLevel - 1); var end = start.AdvanceWrap(num_vertices); var loop_cellid = start.AdvanceWrap( S2Testing.Random.Uniform(num_vertices - 2) + 1); var triangle = new List <S2Point>(); for (var cellid = start; cellid != end; cellid = cellid.NextWrap()) { if (cellid == loop_cellid) { // Insert a small triangular loop. We save the loop so that we can // test whether it contains the origin later. triangle.Add(cellid.Child(0).ToPoint()); triangle.Add(cellid.Child(1).ToPoint()); triangle.Add(cellid.Child(2).ToPoint()); loop.AddRange(triangle); loop.Add(cellid.Child(0).ToPoint()); } else { loop.Add(cellid.ToPoint()); } } // Now we retrace our steps, except that we skip the three edges that form // the triangular loop above. for (S2CellId cellid = end; cellid != start; cellid = cellid.PrevWrap()) { if (cellid == loop_cellid) { loop.Add(cellid.Child(0).ToPoint()); } else { loop.Add(cellid.ToPoint()); } } S2LaxPolygonShape shape = new(loops); S2Loop triangle_loop = new(triangle); var rp = shape.GetReferencePoint(); Assert.Equal(triangle_loop.Contains(rp.Point), rp.Contained); } } }
// Chooses a random S2Point that is often near the intersection of one of the // coodinates planes or coordinate axes with the unit sphere. (It is possible // to represent very small perturbations near such points.) private static S2Point ChoosePoint() { var x = S2Testing.RandomPoint().ToArray(); for (int i = 0; i < 3; ++i) { if (S2Testing.Random.OneIn(3)) { x[i] *= Math.Pow(1e-50, S2Testing.Random.RandDouble()); } } return(new S2Point(x).Normalize()); }
public void Test_VisitIntersectingShapes_Points() { List <S2Point> vertices = new(); for (int i = 0; i < 100; ++i) { vertices.Add(S2Testing.RandomPoint()); } MutableS2ShapeIndex index = new(); index.Add(new S2PointVectorShape(vertices.ToArray())); new VisitIntersectingShapesTest(index).Run(); }
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); } }
public void Test_GetCrossings_ShapeIdsAreCorrect() { // This tests that when some index cells contain only one shape, the // intersecting edges are returned with the correct shape id. MutableS2ShapeIndex index = new(); index.Add(new S2Polyline.OwningShape( new S2Polyline(S2Testing.MakeRegularPoints( MakePointOrDie("0:0"), S1Angle.FromDegrees(5), 100)))); index.Add(new S2Polyline.OwningShape( new S2Polyline(S2Testing.MakeRegularPoints( MakePointOrDie("0:20"), S1Angle.FromDegrees(5), 100)))); TestPolylineCrossings(index, MakePointOrDie("1:-10"), MakePointOrDie("1:30")); }