// 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));
            }
        }
Esempio n. 2
0
        /**
   * A relatively expensive calculation invoked by RobustCCW() if the sign of
   * the determinant is uncertain.
   */

        private static int ExpensiveCcw(S2Point a, S2Point b, S2Point c)
        {
            // Return zero if and only if two points are the same. This ensures (1).
            if (a.Equals(b) || b.Equals(c) || c.Equals(a))
            {
                return 0;
            }

            // Now compute the determinant in a stable way. Since all three points are
            // unit length and we know that the determinant is very close to zero, this
            // means that points are very nearly colinear. Furthermore, the most common
            // situation is where two points are nearly identical or nearly antipodal.
            // To get the best accuracy in this situation, it is important to
            // immediately reduce the magnitude of the arguments by computing either
            // A+B or A-B for each pair of points. Note that even if A and B differ
            // only in their low bits, A-B can be computed very accurately. On the
            // other hand we can't accurately represent an arbitrary linear combination
            // of two vectors as would be required for Gaussian elimination. The code
            // below chooses the vertex opposite the longest edge as the "origin" for
            // the calculation, and computes the different vectors to the other two
            // vertices. This minimizes the sum of the lengths of these vectors.
            //
            // This implementation is very stable numerically, but it still does not
            // return consistent results in all cases. For example, if three points are
            // spaced far apart from each other along a great circle, the sign of the
            // result will basically be random (although it will still satisfy the
            // conditions documented in the header file). The only way to return
            // consistent results in all cases is to compute the result using
            // arbitrary-precision arithmetic. I considered using the Gnu MP library,
            // but this would be very expensive (up to 2000 bits of precision may be
            // needed to store the intermediate results) and seems like overkill for
            // this problem. The MP library is apparently also quite particular about
            // compilers and compilation options and would be a pain to maintain.

            // We want to handle the case of nearby points and nearly antipodal points
            // accurately, so determine whether A+B or A-B is smaller in each case.
            double sab = (a.DotProd(b) > 0) ? -1 : 1;
            double sbc = (b.DotProd(c) > 0) ? -1 : 1;
            double sca = (c.DotProd(a) > 0) ? -1 : 1;
            var vab = a + (b * sab);
            var vbc = b + (c * sbc);
            var vca = c + (a * sca);
            var dab = vab.Norm2;
            var dbc = vbc.Norm2;
            var dca = vca.Norm2;

            // Sort the difference vectors to find the longest edge, and use the
            // opposite vertex as the origin. If two difference vectors are the same
            // length, we break ties deterministically to ensure that the symmetry
            // properties guaranteed in the header file will be true.
            double sign;
            if (dca < dbc || (dca == dbc && a < b))
            {
                if (dab < dbc || (dab == dbc && a < c))
                {
                    // The "sab" factor converts A +/- B into B +/- A.
                    sign = S2Point.CrossProd(vab, vca).DotProd(a)*sab; // BC is longest
                    // edge
                }
                else
                {
                    sign = S2Point.CrossProd(vca, vbc).DotProd(c)*sca; // AB is longest
                    // edge
                }
            }
            else
            {
                if (dab < dca || (dab == dca && b < c))
                {
                    sign = S2Point.CrossProd(vbc, vab).DotProd(b)*sbc; // CA is longest
                    // edge
                }
                else
                {
                    sign = S2Point.CrossProd(vca, vbc).DotProd(c)*sca; // AB is longest
                    // edge
                }
            }
            if (sign > 0)
            {
                return 1;
            }
            if (sign < 0)
            {
                return -1;
            }

            // The points A, B, and C are numerically indistinguishable from coplanar.
            // This may be due to roundoff error, or the points may in fact be exactly
            // coplanar. We handle this situation by perturbing all of the points by a
            // vector (eps, eps**2, eps**3) where "eps" is an infinitesmally small
            // positive number (e.g. 1 divided by a googolplex). The perturbation is
            // done symbolically, i.e. we compute what would happen if the points were
            // perturbed by this amount. It turns out that this is equivalent to
            // checking whether the points are ordered CCW around the origin first in
            // the Y-Z plane, then in the Z-X plane, and then in the X-Y plane.

            var ccw =
                PlanarOrderedCcw(new R2Vector(a.Y, a.Z), new R2Vector(b.Y, b.Z), new R2Vector(c.Y, c.Z));
            if (ccw == 0)
            {
                ccw =
                    PlanarOrderedCcw(new R2Vector(a.Z, a.X), new R2Vector(b.Z, b.X), new R2Vector(c.Z, c.X));
                if (ccw == 0)
                {
                    ccw = PlanarOrderedCcw(
                        new R2Vector(a.X, a.Y), new R2Vector(b.X, b.Y), new R2Vector(c.X, c.Y));
                    // assert (ccw != 0);
                }
            }
            return ccw;
        }
        /**
     * Given two edge chains (see WedgeRelation above), this function returns +1
     * if A contains B, 0 if A and B are disjoint, and -1 if A intersects but
     * does not contain B.
     */

        public int Test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)
        {
            // This is similar to WedgeContainsOrCrosses, except that we want to
            // distinguish cases (1) [A contains B], (3) [A and B are disjoint],
            // and (2,4,5,6) [A intersects but does not contain B].

            if (S2.OrderedCcw(a0, a2, b2, ab1))
            {
                // We are in case 1, 5, or 6, or case 2 if a2 == b2.
                return S2.OrderedCcw(b2, b0, a0, ab1) ? 1 : -1; // Case 1 vs. 2,5,6.
            }
            // We are in cases 2, 3, or 4.
            if (!S2.OrderedCcw(a2, b0, b2, ab1))
            {
                return 0; // Case 3.
            }

            // We are in case 2 or 4, or case 3 if a2 == b0.
            return (a2.Equals(b0)) ? 0 : -1; // Case 3 vs. 2,4.
        }
        /**
     * Given two edge chains (see WedgeRelation above), this function returns +1
     * if A contains B, 0 if B contains A or the two wedges do not intersect,
     * and -1 if the edge chains A and B cross each other (i.e. if A intersects
     * both the interior and exterior of the region to the left of B). In
     * degenerate cases where more than one of these conditions is satisfied,
     * the maximum possible result is returned. For example, if A == B then the
     * result is +1.
     */

        public int Test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)
        {
            // There are 6 possible edge orderings at a shared vertex (all
            // of these orderings are circular, i.e. abcd == bcda):
            //
            // (1) a2 b2 b0 a0: A contains B
            // (2) a2 a0 b0 b2: B contains A
            // (3) a2 a0 b2 b0: A and B are disjoint
            // (4) a2 b0 a0 b2: A and B intersect in one wedge
            // (5) a2 b2 a0 b0: A and B intersect in one wedge
            // (6) a2 b0 b2 a0: A and B intersect in two wedges
            //
            // In cases (4-6), the boundaries of A and B cross (i.e. the boundary
            // of A intersects the interior and exterior of B and vice versa).
            // Thus we want to distinguish cases (1), (2-3), and (4-6).
            //
            // Note that the vertices may satisfy more than one of the edge
            // orderings above if two or more vertices are the same. The tests
            // below are written so that we take the most favorable
            // interpretation, i.e. preferring (1) over (2-3) over (4-6). In
            // particular note that if orderedCCW(a,b,c,o) returns true, it may be
            // possible that orderedCCW(c,b,a,o) is also true (if a == b or b == c).

            if (S2.OrderedCcw(a0, a2, b2, ab1))
            {
                // The cases with this vertex ordering are 1, 5, and 6,
                // although case 2 is also possible if a2 == b2.
                if (S2.OrderedCcw(b2, b0, a0, ab1))
                {
                    return 1; // Case 1 (A contains B)
                }

                // We are in case 5 or 6, or case 2 if a2 == b2.
                return (a2.Equals(b2)) ? 0 : -1; // Case 2 vs. 5,6.
            }
            // We are in case 2, 3, or 4.
            return S2.OrderedCcw(a0, b0, a2, ab1) ? 0 : -1; // Case 2,3 vs. 4.
        }
        /**
   * Given a point X and an edge AB, return the distance ratio AX / (AX + BX).
   * If X happens to be on the line segment AB, this is the fraction "t" such
   * that X == Interpolate(A, B, t). Requires that A and B are distinct.
   */

        public static double GetDistanceFraction(S2Point x, S2Point a0, S2Point a1)
        {
            Preconditions.CheckArgument(!a0.Equals(a1));
            var d0 = x.Angle(a0);
            var d1 = x.Angle(a1);
            return d0/(d0 + d1);
        }
        /**
   * Given two edges AB and CD where at least two vertices are identical (i.e.
   * robustCrossing(a,b,c,d) == 0), this function defines whether the two edges
   * "cross" in a such a way that point-in-polygon containment tests can be
   * implemented by counting the number of edge crossings. The basic rule is
   * that a "crossing" occurs if AB is encountered after CD during a CCW sweep
   * around the shared vertex starting from a fixed reference point.
   *
   *  Note that according to this rule, if AB crosses CD then in general CD does
   * not cross AB. However, this leads to the correct result when counting
   * polygon edge crossings. For example, suppose that A,B,C are three
   * consecutive vertices of a CCW polygon. If we now consider the edge
   * crossings of a segment BP as P sweeps around B, the crossing number changes
   * parity exactly when BP crosses BA or BC.
   *
   *  Useful properties of VertexCrossing (VC):
   *
   *  (1) VC(a,a,c,d) == VC(a,b,c,c) == false (2) VC(a,b,a,b) == VC(a,b,b,a) ==
   * true (3) VC(a,b,c,d) == VC(a,b,d,c) == VC(b,a,c,d) == VC(b,a,d,c) (3) If
   * exactly one of a,b Equals one of c,d, then exactly one of VC(a,b,c,d) and
   * VC(c,d,a,b) is true
   *
   * It is an error to call this method with 4 distinct vertices.
   */

        public static bool VertexCrossing(S2Point a, S2Point b, S2Point c, S2Point d)
        {
            // If A == B or C == D there is no intersection. We need to check this
            // case first in case 3 or more input points are identical.
            if (a.Equals(b) || c.Equals(d))
            {
                return false;
            }

            // If any other pair of vertices is equal, there is a crossing if and only
            // if orderedCCW() indicates that the edge AB is further CCW around the
            // shared vertex than the edge CD.
            if (a.Equals(d))
            {
                return S2.OrderedCcw(S2.Ortho(a), c, b, a);
            }
            if (b.Equals(c))
            {
                return S2.OrderedCcw(S2.Ortho(b), d, a, b);
            }
            if (a.Equals(c))
            {
                return S2.OrderedCcw(S2.Ortho(a), d, b, a);
            }
            if (b.Equals(d))
            {
                return S2.OrderedCcw(S2.Ortho(b), c, a, b);
            }

            // assert (false);
            return false;
        }
        // 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));
            }
        }