/** * 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); }
/** * 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); }
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; } }
/** * 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); }
/* * 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); }
/** * 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. }
/** * 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); }