예제 #1
0
    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);
            }
        }
    }
예제 #2
0
 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));
        }
예제 #4
0
    // 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))));
 }
예제 #11
0
 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);
            }
        }
예제 #13
0
        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());
        }
예제 #14
0
    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);
            }
        }
예제 #16
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);
    }
예제 #17
0
    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);
    }
예제 #18
0
        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))));
        }
예제 #22
0
        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));
            }
        }
예제 #23
0
    // 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);
    }
예제 #24
0
        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);
        }
예제 #27
0
    // 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));
            }
        }
예제 #29
0
    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);
        }
    }
예제 #30
0
        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)));
        }