예제 #1
0
        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;
            }
        }
예제 #2
0
    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));
        }
    }
예제 #3
0
    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()));
    }
예제 #4
0
 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);
     }
 }
예제 #5
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));
        }
    }
예제 #6
0
        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));
            }
        }
예제 #7
0
    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();
    }
예제 #8
0
        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;
                }
            }
        }
예제 #9
0
    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);
        }
    }
예제 #10
0
    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();
    }
예제 #11
0
        // 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);
                }
            }
        }