gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

shape.go (9703B)


      1 // Copyright 2017 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package s2
     16 
     17 import (
     18 	"sort"
     19 )
     20 
     21 // Edge represents a geodesic edge consisting of two vertices. Zero-length edges are
     22 // allowed, and can be used to represent points.
     23 type Edge struct {
     24 	V0, V1 Point
     25 }
     26 
     27 // Cmp compares the two edges using the underlying Points Cmp method and returns
     28 //
     29 //   -1 if e <  other
     30 //    0 if e == other
     31 //   +1 if e >  other
     32 //
     33 // The two edges are compared by first vertex, and then by the second vertex.
     34 func (e Edge) Cmp(other Edge) int {
     35 	if v0cmp := e.V0.Cmp(other.V0.Vector); v0cmp != 0 {
     36 		return v0cmp
     37 	}
     38 	return e.V1.Cmp(other.V1.Vector)
     39 }
     40 
     41 // sortEdges sorts the slice of Edges in place.
     42 func sortEdges(e []Edge) {
     43 	sort.Sort(edges(e))
     44 }
     45 
     46 // edges implements the Sort interface for slices of Edge.
     47 type edges []Edge
     48 
     49 func (e edges) Len() int           { return len(e) }
     50 func (e edges) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }
     51 func (e edges) Less(i, j int) bool { return e[i].Cmp(e[j]) == -1 }
     52 
     53 // ShapeEdgeID is a unique identifier for an Edge within an ShapeIndex,
     54 // consisting of a (shapeID, edgeID) pair.
     55 type ShapeEdgeID struct {
     56 	ShapeID int32
     57 	EdgeID  int32
     58 }
     59 
     60 // Cmp compares the two ShapeEdgeIDs and returns
     61 //
     62 //   -1 if s <  other
     63 //    0 if s == other
     64 //   +1 if s >  other
     65 //
     66 // The two are compared first by shape id and then by edge id.
     67 func (s ShapeEdgeID) Cmp(other ShapeEdgeID) int {
     68 	switch {
     69 	case s.ShapeID < other.ShapeID:
     70 		return -1
     71 	case s.ShapeID > other.ShapeID:
     72 		return 1
     73 	}
     74 	switch {
     75 	case s.EdgeID < other.EdgeID:
     76 		return -1
     77 	case s.EdgeID > other.EdgeID:
     78 		return 1
     79 	}
     80 	return 0
     81 }
     82 
     83 // ShapeEdge represents a ShapeEdgeID with the two endpoints of that Edge.
     84 type ShapeEdge struct {
     85 	ID   ShapeEdgeID
     86 	Edge Edge
     87 }
     88 
     89 // Chain represents a range of edge IDs corresponding to a chain of connected
     90 // edges, specified as a (start, length) pair. The chain is defined to consist of
     91 // edge IDs {start, start + 1, ..., start + length - 1}.
     92 type Chain struct {
     93 	Start, Length int
     94 }
     95 
     96 // ChainPosition represents the position of an edge within a given edge chain,
     97 // specified as a (chainID, offset) pair. Chains are numbered sequentially
     98 // starting from zero, and offsets are measured from the start of each chain.
     99 type ChainPosition struct {
    100 	ChainID, Offset int
    101 }
    102 
    103 // A ReferencePoint consists of a point and a boolean indicating whether the point
    104 // is contained by a particular shape.
    105 type ReferencePoint struct {
    106 	Point     Point
    107 	Contained bool
    108 }
    109 
    110 // OriginReferencePoint returns a ReferencePoint with the given value for
    111 // contained and the origin point. It should be used when all points or no
    112 // points are contained.
    113 func OriginReferencePoint(contained bool) ReferencePoint {
    114 	return ReferencePoint{Point: OriginPoint(), Contained: contained}
    115 }
    116 
    117 // typeTag is a 32-bit tag that can be used to identify the type of an encoded
    118 // Shape. All encodable types have a non-zero type tag. The tag associated with
    119 type typeTag uint32
    120 
    121 const (
    122 	// Indicates that a given Shape type cannot be encoded.
    123 	typeTagNone        typeTag = 0
    124 	typeTagPolygon     typeTag = 1
    125 	typeTagPolyline    typeTag = 2
    126 	typeTagPointVector typeTag = 3
    127 	typeTagLaxPolyline typeTag = 4
    128 	typeTagLaxPolygon  typeTag = 5
    129 
    130 	// The minimum allowable tag for future user-defined Shape types.
    131 	typeTagMinUser typeTag = 8192
    132 )
    133 
    134 // Shape represents polygonal geometry in a flexible way. It is organized as a
    135 // collection of edges that optionally defines an interior. All geometry
    136 // represented by a given Shape must have the same dimension, which means that
    137 // an Shape can represent either a set of points, a set of polylines, or a set
    138 // of polygons.
    139 //
    140 // Shape is defined as an interface in order to give clients control over the
    141 // underlying data representation. Sometimes an Shape does not have any data of
    142 // its own, but instead wraps some other type.
    143 //
    144 // Shape operations are typically defined on a ShapeIndex rather than
    145 // individual shapes. An ShapeIndex is simply a collection of Shapes,
    146 // possibly of different dimensions (e.g. 10 points and 3 polygons), organized
    147 // into a data structure for efficient edge access.
    148 //
    149 // The edges of a Shape are indexed by a contiguous range of edge IDs
    150 // starting at 0. The edges are further subdivided into chains, where each
    151 // chain consists of a sequence of edges connected end-to-end (a polyline).
    152 // For example, a Shape representing two polylines AB and CDE would have
    153 // three edges (AB, CD, DE) grouped into two chains: (AB) and (CD, DE).
    154 // Similarly, an Shape representing 5 points would have 5 chains consisting
    155 // of one edge each.
    156 //
    157 // Shape has methods that allow edges to be accessed either using the global
    158 // numbering (edge ID) or within a particular chain. The global numbering is
    159 // sufficient for most purposes, but the chain representation is useful for
    160 // certain algorithms such as intersection (see BooleanOperation).
    161 type Shape interface {
    162 	// NumEdges returns the number of edges in this shape.
    163 	NumEdges() int
    164 
    165 	// Edge returns the edge for the given edge index.
    166 	Edge(i int) Edge
    167 
    168 	// ReferencePoint returns an arbitrary reference point for the shape. (The
    169 	// containment boolean value must be false for shapes that do not have an interior.)
    170 	//
    171 	// This reference point may then be used to compute the containment of other
    172 	// points by counting edge crossings.
    173 	ReferencePoint() ReferencePoint
    174 
    175 	// NumChains reports the number of contiguous edge chains in the shape.
    176 	// For example, a shape whose edges are [AB, BC, CD, AE, EF] would consist
    177 	// of two chains (AB,BC,CD and AE,EF). Every chain is assigned a chain Id
    178 	// numbered sequentially starting from zero.
    179 	//
    180 	// Note that it is always acceptable to implement this method by returning
    181 	// NumEdges, i.e. every chain consists of a single edge, but this may
    182 	// reduce the efficiency of some algorithms.
    183 	NumChains() int
    184 
    185 	// Chain returns the range of edge IDs corresponding to the given edge chain.
    186 	// Edge chains must form contiguous, non-overlapping ranges that cover
    187 	// the entire range of edge IDs. This is spelled out more formally below:
    188 	//
    189 	//  0 <= i < NumChains()
    190 	//  Chain(i).length > 0, for all i
    191 	//  Chain(0).start == 0
    192 	//  Chain(i).start + Chain(i).length == Chain(i+1).start, for i < NumChains()-1
    193 	//  Chain(i).start + Chain(i).length == NumEdges(), for i == NumChains()-1
    194 	Chain(chainID int) Chain
    195 
    196 	// ChainEdgeReturns the edge at offset "offset" within edge chain "chainID".
    197 	// Equivalent to "shape.Edge(shape.Chain(chainID).start + offset)"
    198 	// but more efficient.
    199 	ChainEdge(chainID, offset int) Edge
    200 
    201 	// ChainPosition finds the chain containing the given edge, and returns the
    202 	// position of that edge as a ChainPosition(chainID, offset) pair.
    203 	//
    204 	//  shape.Chain(pos.chainID).start + pos.offset == edgeID
    205 	//  shape.Chain(pos.chainID+1).start > edgeID
    206 	//
    207 	// where pos == shape.ChainPosition(edgeID).
    208 	ChainPosition(edgeID int) ChainPosition
    209 
    210 	// Dimension returns the dimension of the geometry represented by this shape,
    211 	// either 0, 1 or 2 for point, polyline and polygon geometry respectively.
    212 	//
    213 	//  0 - Point geometry. Each point is represented as a degenerate edge.
    214 	//
    215 	//  1 - Polyline geometry. Polyline edges may be degenerate. A shape may
    216 	//      represent any number of polylines. Polylines edges may intersect.
    217 	//
    218 	//  2 - Polygon geometry. Edges should be oriented such that the polygon
    219 	//      interior is always on the left. In theory the edges may be returned
    220 	//      in any order, but typically the edges are organized as a collection
    221 	//      of edge chains where each chain represents one polygon loop.
    222 	//      Polygons may have degeneracies (e.g., degenerate edges or sibling
    223 	//      pairs consisting of an edge and its corresponding reversed edge).
    224 	//      A polygon loop may also be full (containing all points on the
    225 	//      sphere); by convention this is represented as a chain with no edges.
    226 	//      (See laxPolygon for details.)
    227 	//
    228 	// This method allows degenerate geometry of different dimensions
    229 	// to be distinguished, e.g. it allows a point to be distinguished from a
    230 	// polyline or polygon that has been simplified to a single point.
    231 	Dimension() int
    232 
    233 	// IsEmpty reports whether the Shape contains no points. (Note that the full
    234 	// polygon is represented as a chain with zero edges.)
    235 	IsEmpty() bool
    236 
    237 	// IsFull reports whether the Shape contains all points on the sphere.
    238 	IsFull() bool
    239 
    240 	// typeTag returns a value that can be used to identify the type of an
    241 	// encoded Shape.
    242 	typeTag() typeTag
    243 
    244 	// We do not support implementations of this interface outside this package.
    245 	privateInterface()
    246 }
    247 
    248 // defaultShapeIsEmpty reports whether this shape contains no points.
    249 func defaultShapeIsEmpty(s Shape) bool {
    250 	return s.NumEdges() == 0 && (s.Dimension() != 2 || s.NumChains() == 0)
    251 }
    252 
    253 // defaultShapeIsFull reports whether this shape contains all points on the sphere.
    254 func defaultShapeIsFull(s Shape) bool {
    255 	return s.NumEdges() == 0 && s.Dimension() == 2 && s.NumChains() > 0
    256 }
    257 
    258 // A minimal check for types that should satisfy the Shape interface.
    259 var (
    260 	_ Shape = &Loop{}
    261 	_ Shape = &Polygon{}
    262 	_ Shape = &Polyline{}
    263 )