예제 #1
0
        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);
            }
        }
예제 #2
0
        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());
                }
            }
        }
예제 #3
0
    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}");
        }
    }
예제 #4
0
 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);
     }
 }
예제 #5
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);
        }
    }
예제 #6
0
    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, ""));
    }
예제 #7
0
    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);
        }
    }
예제 #8
0
        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);
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
        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);
            }
        }
예제 #11
0
 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));
     }
 }
예제 #12
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;
            }
        }
예제 #13
0
    // 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);
    }
예제 #14
0
    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())));
    }
예제 #15
0
 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);
     }
 }
예제 #16
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));
        }
    }
예제 #17
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()));
    }
예제 #18
0
 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);
     }
 }
예제 #19
0
 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);
     }
 }
예제 #20
0
            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);
                }
            }
예제 #21
0
        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));
            }
        }
예제 #22
0
    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);
    }
예제 #23
0
 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);
     }
 }
예제 #24
0
        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());
                        }
                    }
                }
            }
        }
예제 #25
0
        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);
        }
    }
}
예제 #27
0
    // 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());
    }
예제 #28
0
    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();
    }
예제 #29
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);
     }
 }
예제 #30
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"));
    }