예제 #1
0
        private static S1ChordAngle GetMaxDistanceToEdgeBruteForce(S2Cell cell, S2Point a, S2Point b)
        {
            // If any antipodal endpoint is within the cell, the max distance is Pi.
            if (cell.Contains(-a) || cell.Contains(-b))
            {
                return(S1ChordAngle.Straight);
            }

            S1ChordAngle max_dist = S1ChordAngle.Negative;

            for (int i = 0; i < 4; ++i)
            {
                S2Point v0 = cell.Vertex(i);
                S2Point v1 = cell.Vertex(i + 1);
                // If the antipodal edge crosses through the cell, max distance is Pi.
                if (S2.CrossingSign(-a, -b, v0, v1) >= 0)
                {
                    return(S1ChordAngle.Straight);
                }
                S2.UpdateMaxDistance(a, v0, v1, ref max_dist);
                S2.UpdateMaxDistance(b, v0, v1, ref max_dist);
                S2.UpdateMaxDistance(v0, a, b, ref max_dist);
            }
            return(max_dist);
        }
예제 #2
0
    // Returns the pair of points (a, b) that achieves the minimum distance
    // between edges a0a1 and b0b1, where "a" is a point on a0a1 and "b" is a
    // point on b0b1.  If the two edges intersect, "a" and "b" are both equal to
    // the intersection point.  Handles a0 == a1 and b0 == b1 correctly.
    public static (S2Point, S2Point) GetEdgePairClosestPoints(S2Point a0, S2Point a1, S2Point b0, S2Point b1)
    {
        if (S2.CrossingSign(a0, a1, b0, b1) > 0)
        {
            S2Point x = S2.GetIntersection(a0, a1, b0, b1, null);
            return(x, x);
        }
        // We save some work by first determining which vertex/edge pair achieves
        // the minimum distance, and then computing the closest point on that edge.
        var min_dist = S1ChordAngle.Zero;

        AlwaysUpdateMinDistance(a0, b0, b1, ref min_dist, true);

        var closest_vertex = 0;

        if (UpdateMinDistance(a1, b0, b1, ref min_dist))
        {
            closest_vertex = 1;
        }
        if (UpdateMinDistance(b0, a0, a1, ref min_dist))
        {
            closest_vertex = 2;
        }
        if (UpdateMinDistance(b1, a0, a1, ref min_dist))
        {
            closest_vertex = 3;
        }
        return(closest_vertex switch
        {
            0 => (a0, Project(a0, b0, b1)),
            1 => (a1, Project(a1, b0, b1)),
            2 => (Project(b0, a0, a1), b0),
            3 => (Project(b1, a0, a1), b1),
            _ => throw new ApplicationException("Unreached (to suppress Android compiler warning)"),
        });
예제 #3
0
    public void Test_S2_CoincidentZeroLengthEdgesThatDontTouch()
    {
        // It is important that the edge primitives can handle vertices that exactly
        // exactly proportional to each other, i.e. that are not identical but are
        // nevertheless exactly coincident when projected onto the unit sphere.
        // There are various ways that such points can arise.  For example,
        // Normalize() itself is not idempotent: there exist distinct points A,B
        // such that Normalize(A) == B  and Normalize(B) == A.  Another issue is
        // that sometimes calls to Normalize() are skipped when the result of a
        // calculation "should" be unit length mathematically (e.g., when computing
        // the cross product of two orthonormal vectors).
        //
        // This test checks pairs of edges AB and CD where A,B,C,D are exactly
        // coincident on the sphere and the norms of A,B,C,D are monotonically
        // increasing.  Such edge pairs should never intersect.  (This is not
        // obvious, since it depends on the particular symbolic perturbations used
        // by S2Pred.Sign().  It would be better to replace this with a test that
        // says that the CCW results must be consistent with each other.)
        int kIters = 1000;

        for (int iter = 0; iter < kIters; ++iter)
        {
            // Construct a point P where every component is zero or a power of 2.
            var t = new double[3];
            for (int i = 0; i < 3; ++i)
            {
                int binary_exp = S2Testing.Random.Skewed(11);
                t[i] = (binary_exp > 1022) ? 0 : Math.Pow(2, -binary_exp);
            }
            // If all components were zero, try again.  Note that normalization may
            // convert a non-zero point into a zero one due to underflow (!)
            var p = new S2Point(t).Normalize();
            if (p == S2Point.Empty)
            {
                --iter; continue;
            }

            // Now every non-zero component should have exactly the same mantissa.
            // This implies that if we scale the point by an arbitrary factor, every
            // non-zero component will still have the same mantissa.  Scale the points
            // so that they are all distinct and are still very likely to satisfy
            // S2.IsUnitLength (which allows for a small amount of error in the norm).
            S2Point a = (1 - 3e-16) * p;
            S2Point b = (1 - 1e-16) * p;
            S2Point c = p;
            S2Point d = (1 + 2e-16) * p;
            if (!a.IsUnitLength() || !d.IsUnitLength())
            {
                --iter;
                continue;
            }
            // Verify that the expected edges do not cross.
            Assert.True(0 > S2.CrossingSign(a, b, c, d));
            S2EdgeCrosser crosser = new(a, b, c);
            Assert.True(0 > crosser.CrossingSign(d));
            Assert.True(0 > crosser.CrossingSign(c));
        }
    }
예제 #4
0
    public void Test_S2_CollinearEdgesThatDontTouch()
    {
        int kIters = 500;

        for (int iter = 0; iter < kIters; ++iter)
        {
            S2Point a = S2Testing.RandomPoint();
            S2Point d = S2Testing.RandomPoint();
            S2Point b = S2.Interpolate(0.05, a, d);
            S2Point c = S2.Interpolate(0.95, a, d);
            Assert.True(0 > S2.CrossingSign(a, b, c, d));
            Assert.True(0 > S2.CrossingSign(a, b, c, d));
            S2EdgeCrosser crosser = new(a, b, c);
            Assert.True(0 > crosser.CrossingSign(d));
            Assert.True(0 > crosser.CrossingSign(c));
        }
    }
예제 #5
0
 // As above, but for maximum distances.  If one edge crosses the antipodal
 // reflection of the other, the distance is Pi.
 public static bool UpdateEdgePairMaxDistance(S2Point a0, S2Point a1, S2Point b0, S2Point b1, ref S1ChordAngle max_dist)
 {
     if (max_dist == S1ChordAngle.Straight)
     {
         return(false);
     }
     if (S2.CrossingSign(a0, a1, -b0, -b1) >= 0)
     {
         max_dist = S1ChordAngle.Straight;
         return(true);
     }
     // Otherwise, the maximum distance is achieved at an endpoint of at least
     // one of the two edges.  We use "|" rather than "||" below to ensure that
     // all four possibilities are always checked.
     //
     // The calculation below computes each of the six vertex-vertex distances
     // twice (this could be optimized).
     return(UpdateMaxDistance(a0, b0, b1, ref max_dist) |
            UpdateMaxDistance(a1, b0, b1, ref max_dist) |
            UpdateMaxDistance(b0, a0, a1, ref max_dist) |
            UpdateMaxDistance(b1, a0, a1, ref max_dist));
 }
예제 #6
0
        private static S1ChordAngle GetDistanceToEdgeBruteForce(S2Cell cell, S2Point a, S2Point b)
        {
            if (cell.Contains(a) || cell.Contains(b))
            {
                return(S1ChordAngle.Zero);
            }

            S1ChordAngle min_dist = S1ChordAngle.Infinity;

            for (int i = 0; i < 4; ++i)
            {
                S2Point v0 = cell.Vertex(i);
                S2Point v1 = cell.Vertex(i + 1);
                // If the edge crosses through the cell, max distance is 0.
                if (S2.CrossingSign(a, b, v0, v1) >= 0)
                {
                    return(S1ChordAngle.Zero);
                }
                S2.UpdateMinDistance(a, v0, v1, ref min_dist);
                S2.UpdateMinDistance(b, v0, v1, ref min_dist);
                S2.UpdateMinDistance(v0, a, b, ref min_dist);
            }
            return(min_dist);
        }
예제 #7
0
    private static void TestCrossing(S2Point a, S2Point b, S2Point c, S2Point d,
                                     int crossing_sign, int signed_crossing_sign)
    {
        // For degenerate edges, CrossingSign() is documented to return 0 if two
        // vertices from different edges are the same and -1 otherwise.  The
        // TestCrossings() function below uses various argument permutations that
        // can sometimes create this case, so we fix it now if necessary.
        if (a == c || a == d || b == c || b == d)
        {
            crossing_sign = 0;
        }

        // As a sanity check, make sure that the expected value of
        // "signed_crossing_sign" is consistent with its documented properties.
        if (crossing_sign == 1)
        {
            Assert.Equal(signed_crossing_sign, S2Pred.Sign(a, b, c));
        }
        else if (crossing_sign == 0 && signed_crossing_sign != 0)
        {
            Assert.Equal(signed_crossing_sign, (a == c || b == d) ? 1 : -1);
        }

        Assert.Equal(crossing_sign, S2.CrossingSign(a, b, c, d));

        S2EdgeCrosser crosser = new(a, b, c);

        Assert.Equal(crossing_sign, crosser.CrossingSign(d));
        Assert.Equal(crossing_sign, crosser.CrossingSign(c));
        Assert.Equal(crossing_sign, crosser.CrossingSign(d, c));
        Assert.Equal(crossing_sign, crosser.CrossingSign(c, d));

        Assert.Equal(signed_crossing_sign != 0, S2.EdgeOrVertexCrossing(a, b, c, d));
        crosser.RestartAt(c);
        Assert.Equal(signed_crossing_sign != 0, crosser.EdgeOrVertexCrossing(d));
        Assert.Equal(signed_crossing_sign != 0, crosser.EdgeOrVertexCrossing(c));
        Assert.Equal(signed_crossing_sign != 0, crosser.EdgeOrVertexCrossing(d, c));
        Assert.Equal(signed_crossing_sign != 0, crosser.EdgeOrVertexCrossing(c, d));

        crosser.RestartAt(c);
        Assert.Equal(signed_crossing_sign, crosser.SignedEdgeOrVertexCrossing(d));
        Assert.Equal(-signed_crossing_sign, crosser.SignedEdgeOrVertexCrossing(c));
        Assert.Equal(-signed_crossing_sign, crosser.SignedEdgeOrVertexCrossing(d, c));
        Assert.Equal(signed_crossing_sign, crosser.SignedEdgeOrVertexCrossing(c, d));

        // Check that the crosser can be re-used.
        crosser.Init(c, d);
        crosser.RestartAt(a);
        Assert.Equal(crossing_sign, crosser.CrossingSign(b));
        Assert.Equal(crossing_sign, crosser.CrossingSign(a));

        // Now try all the same tests with CopyingEdgeCrosser.
        S2CopyingEdgeCrosser crosser2 = new(a, b, c);

        Assert.Equal(crossing_sign, crosser2.CrossingSign(d));
        Assert.Equal(crossing_sign, crosser2.CrossingSign(c));
        Assert.Equal(crossing_sign, crosser2.CrossingSign(d, c));
        Assert.Equal(crossing_sign, crosser2.CrossingSign(c, d));

        crosser2.RestartAt(c);
        Assert.Equal(signed_crossing_sign != 0, crosser2.EdgeOrVertexCrossing(d));
        Assert.Equal(signed_crossing_sign != 0, crosser2.EdgeOrVertexCrossing(c));
        Assert.Equal(signed_crossing_sign != 0, crosser2.EdgeOrVertexCrossing(d, c));
        Assert.Equal(signed_crossing_sign != 0, crosser2.EdgeOrVertexCrossing(c, d));

        crosser2.RestartAt(c);
        Assert.Equal(signed_crossing_sign, crosser2.SignedEdgeOrVertexCrossing(d));
        Assert.Equal(-signed_crossing_sign, crosser2.SignedEdgeOrVertexCrossing(c));
        Assert.Equal(-signed_crossing_sign, crosser2.SignedEdgeOrVertexCrossing(d, c));
        Assert.Equal(signed_crossing_sign, crosser2.SignedEdgeOrVertexCrossing(c, d));

        // Check that the crosser can be re-used.
        crosser2.Init(c, d);
        crosser2.RestartAt(a);
        Assert.Equal(crossing_sign, crosser2.CrossingSign(b));
        Assert.Equal(crossing_sign, crosser2.CrossingSign(a));
    }