Пример #1
0
        /**
         * Given two edge chains (see WedgeRelation above), this function returns +1
         * if the region to the left of A contains the region to the left of B, and
         * 0 otherwise.
         */

        public int Test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)
        {
            // For A to contain B (where each loop interior is defined to be its left
            // side), the CCW edge order around ab1 must be a2 b2 b0 a0. We split
            // this test into two parts that test three vertices each.
            return(S2.OrderedCcw(a2, b2, b0, ab1) && S2.OrderedCcw(b0, a0, a2, ab1) ? 1 : 0);
        }
Пример #2
0
        /**
         * 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);
        }
Пример #3
0
        private void InitOrigin()
        {
            // The bounding box does not need to be correct before calling this
            // function, but it must at least contain vertex(1) since we need to
            // do a Contains() test on this point below.
            Preconditions.CheckState(_bound.Contains(Vertex(1)));

            // To ensure that every point is contained in exactly one face of a
            // subdivision of the sphere, all containment tests are done by counting the
            // edge crossings starting at a fixed point on the sphere (S2::Origin()).
            // We need to know whether this point is inside or outside of the loop.
            // We do this by first guessing that it is outside, and then seeing whether
            // we get the correct containment result for vertex 1. If the result is
            // incorrect, the origin must be inside the loop.
            //
            // A loop with consecutive vertices A,B,C contains vertex B if and only if
            // the fixed vector R = S2::Ortho(B) is on the left side of the wedge ABC.
            // The test below is written so that B is inside if C=R but not if A=R.

            _originInside = false; // Initialize before calling Contains().
            var v1Inside = S2.OrderedCcw(S2.Ortho(Vertex(1)), Vertex(0), Vertex(2), Vertex(1));

            if (v1Inside != Contains(Vertex(1)))
            {
                _originInside = true;
            }
        }
Пример #4
0
        /**
         * Given two edge chains (see WedgeRelation above), this function returns -1
         * if the region to the left of A intersects the region to the left of B,
         * and 0 otherwise. Note that regions are defined such that points along a
         * boundary are contained by one side or the other, not both. So for
         * example, if A,B,C are distinct points ordered CCW around a vertex O, then
         * the wedges BOA, AOC, and COB do not intersect.
         */

        public int Test(S2Point a0, S2Point ab1, S2Point a2, S2Point b0, S2Point b2)
        {
            // For A not to intersect B (where each loop interior is defined to be
            // its left side), the CCW edge order around ab1 must be a0 b2 b0 a2.
            // Note that it's important to write these conditions as negatives
            // (!OrderedCCW(a,b,c,o) rather than Ordered(c,b,a,o)) to get correct
            // results when two vertices are the same.
            return(S2.OrderedCcw(a0, b2, b0, ab1) && S2.OrderedCcw(b0, a2, a0, ab1) ? 0 : -1);
        }
Пример #5
0
        /*
         * Given two edges AB and CD such that robustCrossing() is true, return their
         * intersection point. Useful properties of getIntersection (GI):
         *
         * (1) GI(b,a,c,d) == GI(a,b,d,c) == GI(a,b,c,d) (2) GI(c,d,a,b) ==
         * GI(a,b,c,d)
         *
         * The returned intersection point X is guaranteed to be close to the edges AB
         * and CD, but if the edges intersect at a very small angle then X may not be
         * close to the true mathematical intersection point P. See the description of
         * "DEFAULT_INTERSECTION_TOLERANCE" below for details.
         */

        public static S2Point GetIntersection(S2Point a0, S2Point a1, S2Point b0, S2Point b1)
        {
            Preconditions.CheckArgument(RobustCrossing(a0, a1, b0, b1) > 0,
                                        "Input edges a0a1 and b0b1 muct have a true robustCrossing.");

            // We use robustCrossProd() to get accurate results even when two endpoints
            // are close together, or when the two line segments are nearly parallel.
            var aNorm = S2Point.Normalize(S2.RobustCrossProd(a0, a1));
            var bNorm = S2Point.Normalize(S2.RobustCrossProd(b0, b1));
            var x     = S2Point.Normalize(S2.RobustCrossProd(aNorm, bNorm));

            // Make sure the intersection point is on the correct side of the sphere.
            // Since all vertices are unit length, and edges are less than 180 degrees,
            // (a0 + a1) and (b0 + b1) both have positive dot product with the
            // intersection point. We use the sum of all vertices to make sure that the
            // result is unchanged when the edges are reversed or exchanged.
            if (x.DotProd(a0 + a1 + b0 + b1) < 0)
            {
                x = -x;
            }

            // The calculation above is sufficient to ensure that "x" is within
            // DEFAULT_INTERSECTION_TOLERANCE of the great circles through (a0,a1) and
            // (b0,b1).
            // However, if these two great circles are very close to parallel, it is
            // possible that "x" does not lie between the endpoints of the given line
            // segments. In other words, "x" might be on the great circle through
            // (a0,a1) but outside the range covered by (a0,a1). In this case we do
            // additional clipping to ensure that it does.

            if (S2.OrderedCcw(a0, x, a1, aNorm) && S2.OrderedCcw(b0, x, b1, bNorm))
            {
                return(x);
            }

            // Find the acceptable endpoint closest to x and return it. An endpoint is
            // acceptable if it lies between the endpoints of the other line segment.
            var r = new CloserResult(10, x);

            if (S2.OrderedCcw(b0, a0, b1, bNorm))
            {
                r.ReplaceIfCloser(x, a0);
            }
            if (S2.OrderedCcw(b0, a1, b1, bNorm))
            {
                r.ReplaceIfCloser(x, a1);
            }
            if (S2.OrderedCcw(a0, b0, a1, aNorm))
            {
                r.ReplaceIfCloser(x, b0);
            }
            if (S2.OrderedCcw(a0, b1, a1, aNorm))
            {
                r.ReplaceIfCloser(x, b1);
            }
            return(r.Vmin);
        }
Пример #6
0
        /**
         * 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.
        }
Пример #7
0
        /**
         * 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.
        }
        /**
         * We start at the given edge and assemble a loop taking left turns whenever
         * possible. We stop the loop as soon as we encounter any vertex that we have
         * seen before *except* for the first vertex (v0). This ensures that only CCW
         * loops are constructed when possible.
         */

        private S2Loop AssembleLoop(S2Point v0, S2Point v1, System.Collections.Generic.IList <S2Edge> unusedEdges)
        {
            // The path so far.
            var path = new List <S2Point>();

            // Maps a vertex to its index in "path".
            var index = new Dictionary <S2Point, int>();

            path.Add(v0);
            path.Add(v1);

            index.Add(v1, 1);

            while (path.Count >= 2)
            {
                // Note that "v0" and "v1" become invalid if "path" is modified.
                v0 = path[path.Count - 2];
                v1 = path[path.Count - 1];

                var v2      = default(S2Point);
                var v2Found = false;
                HashBag <S2Point> vset;
                _edges.TryGetValue(v1, out vset);
                if (vset != null)
                {
                    foreach (var v in vset)
                    {
                        // We prefer the leftmost outgoing edge, ignoring any reverse edges.
                        if (v.Equals(v0))
                        {
                            continue;
                        }
                        if (!v2Found || S2.OrderedCcw(v0, v2, v, v1))
                        {
                            v2 = v;
                        }
                        v2Found = true;
                    }
                }
                if (!v2Found)
                {
                    // We've hit a dead end. Remove this edge and backtrack.
                    unusedEdges.Add(new S2Edge(v0, v1));
                    EraseEdge(v0, v1);
                    index.Remove(v1);
                    path.RemoveAt(path.Count - 1);
                }
                else if (!index.ContainsKey(v2))
                {
                    // This is the first time we've visited this vertex.
                    index.Add(v2, path.Count);
                    path.Add(v2);
                }
                else
                {
                    // We've completed a loop. Throw away any initial vertices that
                    // are not part of the loop.
                    var start = index[v2];
                    path = path.GetRange(start, path.Count - start);

                    if (_options.Validate && !S2Loop.IsValidLoop(path))
                    {
                        // We've constructed a loop that crosses itself, which can only happen
                        // if there is bad input data. Throw away the whole loop.
                        RejectLoop(path, path.Count, unusedEdges);
                        EraseLoop(path, path.Count);
                        return(null);
                    }
                    return(new S2Loop(path));
                }
            }
            return(null);
        }