/** * Creates an empty polygon that should be initialized by calling Init(). */ public S2Polygon() { _loops = new List <S2Loop>(); _bound = S2LatLngRect.Empty; _hasHoles = false; _numVertices = 0; }
/** * Convenience constructor that calls Init() with the given loops. Clears the * given list. */ public S2Polygon(System.Collections.Generic.IList <S2Loop> loops) { _loops = new List <S2Loop>(); _bound = S2LatLngRect.Empty; Init(loops); }
private void InitBound() { // The bounding rectangle of a loop is not necessarily the same as the // bounding rectangle of its vertices. First, the loop may wrap entirely // around the sphere (e.g. a loop that defines two revolutions of a // candy-cane stripe). Second, the loop may include one or both poles. // Note that a small clockwise loop near the equator contains both poles. var bounder = new RectBounder(); for (var i = 0; i <= NumVertices; ++i) { bounder.AddPoint(Vertex(i)); } var b = bounder.Bound; // Note that we need to initialize bound with a temporary value since // contains() does a bounding rectangle check before doing anything else. _bound = S2LatLngRect.Full; if (Contains(new S2Point(0, 0, 1))) { b = new S2LatLngRect(new R1Interval(b.Lat.Lo, S2.PiOver2), S1Interval.Full); } // If a loop contains the south pole, then either it wraps entirely // around the sphere (full longitude range), or it also contains the // north pole in which case b.lng().isFull() due to the test above. if (b.Lng.IsFull && Contains(new S2Point(0, 0, -1))) { b = new S2LatLngRect(new R1Interval(-S2.PiOver2, b.Lat.Hi), b.Lng); } _bound = b; }
/** * Copy constructor. */ public S2Polygon(S2Loop loop) { _loops = new List <S2Loop>(); _bound = loop.RectBound; _hasHoles = false; _numVertices = loop.NumVertices; _loops.Add(loop); }
/** * Initialize a polygon by taking ownership of the given loops and clearing * the given list. This method figures out the loop nesting hierarchy and then * reorders the loops by following a preorder traversal. This implies that * each loop is immediately followed by its descendants in the nesting * hierarchy. (See also getParent and getLastDescendant.) */ public void Init(System.Collections.Generic.IList <S2Loop> loops) { // assert isValid(loops); // assert (this.loops.isEmpty()); //Dictionary<S2Loop, List<S2Loop>> loopMap =new Dictionary<S2Loop, List<S2Loop>>(); // Note: We're using C5's HashDictionary because SCG's Dictionary<,> does not allow // NULL keys var loopMap = new Dictionary <NullObject <S2Loop>, List <S2Loop> >(); // Yes, a null key is valid. It is used here to refer to the root of the // loopMap loopMap[null] = new List <S2Loop>(); foreach (var loop in loops) { InsertLoop(loop, null, loopMap); _numVertices += loop.NumVertices; } loops.Clear(); // Sort all of the lists of loops; in this way we guarantee a total ordering // on loops in the polygon. Loops will be sorted by their natural ordering, // while also preserving the requirement that each loop is immediately // followed by its descendants in the nesting hierarchy. // // TODO(andriy): as per kirilll in CL 18750833 code review comments: // This should work for now, but I think it's possible to guarantee the // correct order inside insertLoop by searching for the correct position in // the children list before inserting. SortValueLoops(loopMap); // Reorder the loops in depth-first traversal order. // Starting at null == starting at the root InitLoop(null, -1, loopMap); // TODO(dbeaumont): Add tests or preconditions for these asserts (here and elesewhere). // forall i != j : containsChild(loop(i), loop(j), loopMap) == loop(i).containsNested(loop(j))); // Compute the bounding rectangle of the entire polygon. _hasHoles = false; _bound = S2LatLngRect.Empty; for (var i = 0; i < NumLoops; ++i) { if (Loop(i).Sign < 0) { _hasHoles = true; } else { _bound = _bound.Union(Loop(i).RectBound); } } }
/** * Copy constructor. */ public S2Loop(S2Loop src) { _numVertices = src._numVertices; _vertices = (S2Point[])src._vertices.Clone(); _vertexToIndex = src._vertexToIndex; _index = src._index; _firstLogicalVertex = src._firstLogicalVertex; _bound = src.RectBound; _originInside = src._originInside; _depth = src._depth; }
/** * Copy constructor. */ public S2Polygon(S2Polygon src) { _loops = new List <S2Loop>(); _bound = src.RectBound; _hasHoles = src._hasHoles; _numVertices = src._numVertices; for (var i = 0; i < src.NumLoops; ++i) { _loops.Add(new S2Loop(src.Loop(i))); } }
/** * Release ownership of the loops of this polygon by appending them to the * given list. Resets the polygon to be empty. */ public void Release(System.Collections.Generic.IList <S2Loop> loops) { foreach (var item in _loops) { loops.Add(item); } _loops.Clear(); _bound = S2LatLngRect.Empty; _hasHoles = false; _numVertices = 0; }
/** * Like the constructor above, but assumes that the cell's bounding rectangle * has been precomputed. * * @param cell * @param bound */ public S2Loop(S2Cell cell, S2LatLngRect bound) { _bound = bound; _numVertices = 4; _vertices = new S2Point[_numVertices]; _vertexToIndex = null; _index = null; _depth = 0; for (var i = 0; i < 4; ++i) { _vertices[i] = cell.GetVertex(i); } InitOrigin(); InitFirstLogicalVertex(); }
public void AddPoint(S2Point b) { // assert (S2.isUnitLength(b)); var bLatLng = new S2LatLng(b); if (bound.IsEmpty) { bound = bound.AddPoint(bLatLng); } else { // We can't just call bound.addPoint(bLatLng) here, since we need to // ensure that all the longitudes between "a" and "b" are included. bound = bound.Union(S2LatLngRect.FromPointPair(aLatLng, bLatLng)); // Check whether the Min/Max latitude occurs in the edge interior. // We find the normal to the plane containing AB, and then a vector // "dir" in this plane that also passes through the equator. We use // RobustCrossProd to ensure that the edge normal is accurate even // when the two points are very close together. var aCrossB = S2.RobustCrossProd(a, b); var dir = S2Point.CrossProd(aCrossB, new S2Point(0, 0, 1)); var da = dir.DotProd(a); var db = dir.DotProd(b); if (da * db < 0) { // Minimum/maximum latitude occurs in the edge interior. This affects // the latitude bounds but not the longitude bounds. var absLat = Math.Acos(Math.Abs(aCrossB[2] / aCrossB.Norm)); var lat = bound.Lat; if (da < 0) { // It's possible that absLat < lat.lo() due to numerical errors. lat = new R1Interval(lat.Lo, Math.Max(absLat, bound.Lat.Hi)); } else { lat = new R1Interval(Math.Min(-absLat, bound.Lat.Lo), lat.Hi); } bound = new S2LatLngRect(lat, bound.Lng); } } a = b; aLatLng = bLatLng; }
/** * Initialize a loop connecting the given vertices. The last vertex is * implicitly connected to the first. All points should be unit length. Loops * must have at least 3 vertices. * * @param vertices */ public S2Loop(IEnumerable <S2Point> vertices) { _vertices = vertices.ToArray(); _numVertices = _vertices.Length; _bound = S2LatLngRect.Full; _depth = 0; // if (debugMode) { // assert (isValid(vertices, DEFAULT_MAX_ADJACENT)); // } // initOrigin() must be called before InitBound() because the latter // function expects Contains() to work properly. InitOrigin(); InitBound(); InitFirstLogicalVertex(); }
/** * Reverse the order of the loop vertices, effectively complementing the * region represented by the loop. */ public void Invert() { var last = NumVertices - 1; for (var i = (last - 1) / 2; i >= 0; --i) { var t = _vertices[i]; _vertices[i] = _vertices[last - i]; _vertices[last - i] = t; } _vertexToIndex = null; _index = null; _originInside ^= true; if (_bound.Lat.Lo > -S2.PiOver2 && _bound.Lat.Hi < S2.PiOver2) { // The complement of this loop contains both poles. _bound = S2LatLngRect.Full; } else { InitBound(); } InitFirstLogicalVertex(); }
public RectBounder() { bound = S2LatLngRect.Empty; }