S2Loop

An S2Loop represents a simple spherical polygon. It consists of a single chain of vertices where the first vertex is implicitly connected to the last. All loops are defined to have a CCW orientation, i.e. the interior of the loop is on the left side of the edges. This implies that a clockwise loop enclosing a small area is interpreted to be a CCW loop enclosing a very large area.

Loops are not allowed to have any duplicate vertices (whether adjacent or not). Non-adjacent edges are not allowed to intersect, and furthermore edges of length 180 degrees are not allowed (i.e., adjacent vertices cannot be antipodal). Loops must have at least 3 vertices (except for the "empty" and "full" loops discussed below). Although these restrictions are not enforced in optimized code, you may get unexpected results if they are violated.

There are two special loops: the "empty" loop contains no points, while the "full" loop contains all points. These loops do not have any edges, but to preserve the invariant that every loop can be represented as a vertex chain, they are defined as having exactly one vertex each (see kEmpty and kFull).

Point containment of loops is defined such that if the sphere is subdivided into faces (loops), every point is contained by exactly one face. This implies that loops do not necessarily contain their vertices.

Note: The reason that duplicate vertices and intersecting edges are not allowed is that they make it harder to define and implement loop relationships, e.g. whether one loop contains another. If your data does not satisfy these restrictions, you can use S2Builder to normalize it.

TODO: Convert logic to use a ForwardRange rather than making a copy of its vertices.

class S2Loop : S2Region {}

Constructors

this
this()

Default constructor. The loop must be initialized by calling Init() or Decode() before it is used.

this
this(S2Point[] vertices)
Undocumented in source.
this
this(S2Point[] vertices, S2Debug s2DebugOverride)

Convenience constructor that calls Init() with the given vertices.

this
this(S2Cell cell)

Construct a loop corresponding to the given cell.

this
this(S2Loop src)

Internal copy constructor used only by Clone() that makes a deep copy of its argument.

Members

Classes

Shape
class Shape

Wrapper class for indexing a loop (see S2ShapeIndex). Once this object is inserted into an S2ShapeIndex it is owned by that index, and will be automatically deleted when no longer needed by the index. Note that this class does not take ownership of the loop itself (see OwningShape below). You can also subtype this class to store additional data (see S2Shape for details).

Functions

boundaryApproxEquals
bool boundaryApproxEquals(S2Loop b, S1Angle max_error)

Returns true if two loops have the same boundary except for vertex perturbations. More precisely, the vertices in the two loops must be in the same cyclic order, and corresponding vertex pairs must be separated by no more than "max_error".

boundaryEquals
bool boundaryEquals(S2Loop b)

Returns true if two loops have the same boundary. This is true if and only if the loops have the same vertices in the same cyclic order (i.e., the vertices may be cyclically rotated). The empty and full loops are considered to have different boundaries.

boundaryNear
bool boundaryNear(S2Loop b, S1Angle max_error)

Returns true if the two loop boundaries are within "max_error" of each other along their entire lengths. The two loops may have different numbers of vertices. More precisely, this method returns true if the two loops have parameterizations a:[0,1] -> S^2, b:[0,1] -> S^2 such that distance(a(t), b(t)) <= max_error for all t. You can think of this as testing whether it is possible to drive two cars all the way around the two loops such that no car ever goes backward and the cars are always within "max_error" of each other.

bruteForceContains
bool bruteForceContains(S2Point p)
Undocumented in source. Be warned that the author may not have intended to support it.
clone
S2Loop clone()

/////////////////////////////////////////////////////////////////////

compareBoundary
int compareBoundary(S2Loop b)

Return +1 if A contains the boundary of B, -1 if A excludes the boundary of B, and 0 if the boundaries of A and B cross. Shared edges are handled as follows: If XY is a shared edge, define Reversed(XY) to be true if XY appears in opposite directions in A and B. Then A contains XY if and only if Reversed(XY) == B->is_hole(). (Intuitively, this checks whether A contains a vanishingly small region extending from the boundary of B toward the interior of the polygon to which loop B belongs.)

contains
bool contains(S2Loop b)

Returns true if the region contained by this loop is a superset of the region contained by the given other loop.

contains
bool contains(S2Cell target)
Undocumented in source. Be warned that the author may not have intended to support it.
contains
bool contains(S2Point p)
Undocumented in source. Be warned that the author may not have intended to support it.
containsNested
bool containsNested(S2Loop b)

Given two loops of a polygon, return true if A contains B. This version of Contains() is cheap because it does not test for edge intersections. The loops must meet all the S2Polygon requirements; for example this implies that their boundaries may not cross or have any shared edges (although they may have shared vertices).

containsNonCrossingBoundary
bool containsNonCrossingBoundary(S2Loop b, bool reverse_b)

Given two loops whose boundaries do not cross (see CompareBoundary), return true if A contains the boundary of B. If "reverse_b" is true, the boundary of B is reversed first (which only affects the result when there are shared edges). This method is cheaper than CompareBoundary() because it does not test for edge intersections.

containsOrigin
bool containsOrigin()

Returns true if this loop contains S2.origin().

decode
bool decode(Decoder!IRangeT decoder)

Decodes a loop encoded with Encode() or the private method EncodeCompressed() (used by the S2Polygon encoder). Returns true on success.

depth
int depth()

The depth of a loop is defined as its nesting level within its containing polygon. "Outer shell" loops have depth 0, holes within those loops have depth 1, shells within those holes have depth 2, etc. This field is only used by the S2Polygon implementation.

encode
void encode(Encoder!ORangeT encoder)

Appends a serialized representation of the S2Loop to "encoder".

equals
bool equals(S2Loop b)

Returns true if two loops have the same vertices in the same linear order (i.e., cyclic rotations are not allowed).

findValidationError
bool findValidationError(S2Error error)

Returns true if this is *not* a valid loop and sets "error" appropriately. Otherwise returns false and leaves "error" unchanged.

findValidationErrorNoIndex
bool findValidationErrorNoIndex(S2Error error)

Like FindValidationError(), but skips any checks that would require building the S2ShapeIndex (i.e., self-intersection tests). This is used by the S2Polygon implementation, which uses its own index to check for loop self-intersections.

getArea
double getArea()

Return the area of the loop interior, i.e. the region on the left side of the loop. The return value is between 0 and 4*Pi. (Note that the return value is not affected by whether this loop is a "hole" or a "shell".)

getCanonicalFirstVertex
int getCanonicalFirstVertex(int dir)

Returns an index "first" and a direction "dir" (either +1 or -1) such that the vertex sequence (first, first+dir, ..., first+(n-1)*dir) does not change when the loop vertex order is rotated or inverted. This allows the loop vertices to be traversed in a canonical order. The return values are chosen such that (first, ..., first+n*dir) are in the range [0, 2*n-1] as expected by the vertex() method.

getCapBound
S2Cap getCapBound()

GetRectBound() returns essentially tight results, while GetCapBound() might have a lot of extra padding. Both bounds are conservative in that if the loop contains a point P, then the bound contains P also.

getCellUnionBound
void getCellUnionBound(S2CellId[] cell_ids)
Undocumented in source. Be warned that the author may not have intended to support it.
getCentroid
S2Point getCentroid()

Returns the true centroid of the loop multiplied by the area of the loop (see s2centroids.h for details on centroids). The result is not unit length, so you may want to normalize it. Also note that in general, the centroid may not be contained by the loop.

getDistance
S1Angle getDistance(S2Point x)

Returns the distance from the given point to the loop interior. If the loop is empty, return S1Angle::Infinity(). "x" should be unit length.

getDistanceToBoundary
S1Angle getDistanceToBoundary(S2Point x)

Returns the distance from the given point to the loop boundary. If the loop is empty or full, return S1Angle::Infinity() (since the loop has no boundary). "x" should be unit length.

getRectBound
S2LatLngRect getRectBound()
Undocumented in source. Be warned that the author may not have intended to support it.
getSurfaceIntegral
T getSurfaceIntegral(T function(in Vector!(double, 3), in Vector!(double, 3), in Vector!(double, 3)) fTri)

This method computes the oriented surface integral of some quantity f(x) over the loop interior, given a function f_tri(A,B,C) that returns the corresponding integral over the spherical triangle ABC. Here "oriented surface integral" means:

getTurningAngle
double getTurningAngle()

Returns the sum of the turning angles at each vertex. The return value is positive if the loop is counter-clockwise, negative if the loop is clockwise, and zero if the loop is a great circle. Degenerate and nearly-degenerate loops are handled consistently with s2pred::Sign(). So for example, if a loop has zero area (i.e., it is a very small CCW loop) then the turning angle will always be negative.

getTurningAngleMaxError
double getTurningAngleMaxError()

Return the maximum error in GetTurningAngle(). The return value is not constant; it depends on the loop.

getXYZFaceSiTiVertices
void getXYZFaceSiTiVertices(S2XYZFaceSiTi[] vertices, size_t current_index)

Converts the loop vertices to the S2XYZFaceSiTi format and store the result in the given array, which must be large enough to store all the vertices.

initialize
void initialize(RangeT vertexRange)

Initialize a loop with given vertices. The last vertex is implicitly connected to the first. All points should be unit length. Loops must have at least 3 vertices (except for the "empty" and "full" loops, see kEmpty and kFull). This method may be called multiple times.

intersects
bool intersects(S2Loop b)

Returns true if the region contained by this loop intersects the region contained by the given other loop.

invert
void invert()

Reverse the order of the loop vertices, effectively complementing the region represented by the loop. For example, the loop ABCD (with edges AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD). Notice that the last edge is the same in both cases except that its direction has been reversed.

isEmpty
bool isEmpty()

Returns true if this is the special "empty" loop that contains no points.

isEmptyOrFull
bool isEmptyOrFull()

Returns true if this loop is either "empty" or "full".

isFull
bool isFull()

Returns true if this is the special "full" loop that contains all points.

isHole
bool isHole()

Returns true if this loop represents a hole in its containing polygon.

isNormalized
bool isNormalized()

Return true if the loop area is at most 2*Pi. Degenerate loops are handled consistently with s2pred::Sign(), i.e., if a loop can be expressed as the union of degenerate or nearly-degenerate CCW triangles, then it will always be considered normalized.

isValid
bool isValid()

Returns true if this is a valid loop. Note that validity is checked automatically during initialization when --s2debug is enabled (true by default in debug binaries).

mayIntersect
bool mayIntersect(S2Cell target)
Undocumented in source. Be warned that the author may not have intended to support it.
normalize
void normalize()

Invert the loop if necessary so that the area enclosed by the loop is at most 2*Pi.

numVertices
int numVertices()
Undocumented in source. Be warned that the author may not have intended to support it.
opCmp
int opCmp(S2Loop other)
Undocumented in source. Be warned that the author may not have intended to support it.
orientedVertex
S2Point orientedVertex(int i)

Like vertex(), but this method returns vertices in reverse order if the loop represents a polygon hole. For example, arguments 0, 1, 2 are mapped to vertices n-1, n-2, n-3, where n == num_vertices(). This ensures that the interior of the polygon is always to the left of the vertex chain.

project
S2Point project(S2Point x)

If the given point is contained by the loop, return it. Otherwise return the closest point on the loop boundary. If the loop is empty, return the input argument. Note that the result may or may not be contained by the loop. "x" should be unit length.

projectToBoundary
S2Point projectToBoundary(S2Point x)

Return the closest point on the loop boundary to the given point. If the loop is empty or full, return the input argument (since the loop has no boundary). "x" should be unit length.

setDepth
void setDepth(int depth)
Undocumented in source. Be warned that the author may not have intended to support it.
sign
int sign()

The sign of a loop is -1 if the loop represents a hole in its containing polygon, and +1 otherwise.

spaceUsed
size_t spaceUsed()

Returns the total number of bytes used by the loop.

vertex
S2Point vertex(int i)

For convenience, we make two entire copies of the vertex list available: vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == num_vertices().

vertices
const(S2Point[]) vertices()
Undocumented in source. Be warned that the author may not have intended to support it.

Properties

s2DebugOverride
S2Debug s2DebugOverride [@property setter]

Allows overriding the automatic validity checks controlled by the --s2debug flag. If this flag is true, then loops are automatically checked for validity as they are initialized. The main reason to disable this flag is if you intend to call IsValid() explicitly, like this:

s2DebugOverride
S2Debug s2DebugOverride [@property getter]
Undocumented in source. Be warned that the author may not have intended to support it.

Static functions

empty
S2Point[] empty()

A special vertex chain of length 1 that creates an empty loop (i.e., a loop with no edges that contains no points). Example usage:

full
S2Point[] full()

A special vertex chain of length 1 that creates a full loop (i.e., a loop with no edges that contains all points). See kEmpty() for details.

makeRegularLoop
S2Loop makeRegularLoop(S2Point center, S1Angle radius, int num_vertices)

Constructs a regular polygon with the given number of vertices, all located on a circle of the specified radius around "center". The radius is the actual distance from "center" to each vertex.

makeRegularLoop
S2Loop makeRegularLoop(Matrix3x3_d frame, S1Angle radius, int num_vertices)

Like the function above, but this version constructs a loop centered around the z-axis of the given coordinate frame, with the first vertex in the direction of the positive x-axis. (This allows the loop to be rotated for testing purposes.)

Inherited Members

From S2Region

clone
S2Region clone()

Returns a deep copy of the region.

getCapBound
S2Cap getCapBound()

Returns a bounding spherical cap that contains the region. The bound may not be tight.

getRectBound
S2LatLngRect getRectBound()

Returns a bounding latitude-longitude rectangle that contains the region. The bound may not be tight.

getCellUnionBound
void getCellUnionBound(S2CellId[] cellIds)

Returns a small collection of S2CellIds whose union covers the region. The cells are not sorted, may have redundancies (such as cells that contain other cells), and may cover much more area than necessary.

contains
bool contains(S2Cell cell)

Returns true if the region completely contains the given cell, otherwise returns false.

mayIntersect
bool mayIntersect(S2Cell cell)

If this method returns false, the region does not intersect the given cell. Otherwise, either region intersects the cell, or the intersection relationship could not be determined.

contains
bool contains(S2Point p)

Returns true if and only if the given point is contained by the region. The point 'p' is generally required to be unit length, although some subtypes may relax this restriction.

Meta