shapeutil.go (8329B)
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 // CrossingType defines different ways of reporting edge intersections. 18 type CrossingType int 19 20 const ( 21 // CrossingTypeInterior reports intersections that occur at a point 22 // interior to both edges (i.e., not at a vertex). 23 CrossingTypeInterior CrossingType = iota 24 25 // CrossingTypeAll reports all intersections, even those where two edges 26 // intersect only because they share a common vertex. 27 CrossingTypeAll 28 29 // CrossingTypeNonAdjacent reports all intersections except for pairs of 30 // the form (AB, BC) where both edges are from the same ShapeIndex. 31 CrossingTypeNonAdjacent 32 ) 33 34 // rangeIterator is a wrapper over ShapeIndexIterator with extra methods 35 // that are useful for merging the contents of two or more ShapeIndexes. 36 type rangeIterator struct { 37 it *ShapeIndexIterator 38 // The min and max leaf cell ids covered by the current cell. If done() is 39 // true, these methods return a value larger than any valid cell id. 40 rangeMin CellID 41 rangeMax CellID 42 } 43 44 // newRangeIterator creates a new rangeIterator positioned at the first cell of the given index. 45 func newRangeIterator(index *ShapeIndex) *rangeIterator { 46 r := &rangeIterator{ 47 it: index.Iterator(), 48 } 49 r.refresh() 50 return r 51 } 52 53 func (r *rangeIterator) cellID() CellID { return r.it.CellID() } 54 func (r *rangeIterator) indexCell() *ShapeIndexCell { return r.it.IndexCell() } 55 func (r *rangeIterator) next() { r.it.Next(); r.refresh() } 56 func (r *rangeIterator) done() bool { return r.it.Done() } 57 58 // seekTo positions the iterator at the first cell that overlaps or follows 59 // the current range minimum of the target iterator, i.e. such that its 60 // rangeMax >= target.rangeMin. 61 func (r *rangeIterator) seekTo(target *rangeIterator) { 62 r.it.seek(target.rangeMin) 63 // If the current cell does not overlap target, it is possible that the 64 // previous cell is the one we are looking for. This can only happen when 65 // the previous cell contains target but has a smaller CellID. 66 if r.it.Done() || r.it.CellID().RangeMin() > target.rangeMax { 67 if r.it.Prev() && r.it.CellID().RangeMax() < target.cellID() { 68 r.it.Next() 69 } 70 } 71 r.refresh() 72 } 73 74 // seekBeyond positions the iterator at the first cell that follows the current 75 // range minimum of the target iterator. i.e. the first cell such that its 76 // rangeMin > target.rangeMax. 77 func (r *rangeIterator) seekBeyond(target *rangeIterator) { 78 r.it.seek(target.rangeMax.Next()) 79 if !r.it.Done() && r.it.CellID().RangeMin() <= target.rangeMax { 80 r.it.Next() 81 } 82 r.refresh() 83 } 84 85 // refresh updates the iterators min and max values. 86 func (r *rangeIterator) refresh() { 87 r.rangeMin = r.cellID().RangeMin() 88 r.rangeMax = r.cellID().RangeMax() 89 } 90 91 // referencePointForShape is a helper function for implementing various Shapes 92 // ReferencePoint functions. 93 // 94 // Given a shape consisting of closed polygonal loops, the interior of the 95 // shape is defined as the region to the left of all edges (which must be 96 // oriented consistently). This function then chooses an arbitrary point and 97 // returns true if that point is contained by the shape. 98 // 99 // Unlike Loop and Polygon, this method allows duplicate vertices and 100 // edges, which requires some extra care with definitions. The rule that we 101 // apply is that an edge and its reverse edge cancel each other: the result 102 // is the same as if that edge pair were not present. Therefore shapes that 103 // consist only of degenerate loop(s) are either empty or full; by convention, 104 // the shape is considered full if and only if it contains an empty loop (see 105 // laxPolygon for details). 106 // 107 // Determining whether a loop on the sphere contains a point is harder than 108 // the corresponding problem in 2D plane geometry. It cannot be implemented 109 // just by counting edge crossings because there is no such thing as a point 110 // at infinity that is guaranteed to be outside the loop. 111 // 112 // This function requires that the given Shape have an interior. 113 func referencePointForShape(shape Shape) ReferencePoint { 114 if shape.NumEdges() == 0 { 115 // A shape with no edges is defined to be full if and only if it 116 // contains at least one chain. 117 return OriginReferencePoint(shape.NumChains() > 0) 118 } 119 // Define a "matched" edge as one that can be paired with a corresponding 120 // reversed edge. Define a vertex as "balanced" if all of its edges are 121 // matched. In order to determine containment, we must find an unbalanced 122 // vertex. Often every vertex is unbalanced, so we start by trying an 123 // arbitrary vertex. 124 edge := shape.Edge(0) 125 126 if ref, ok := referencePointAtVertex(shape, edge.V0); ok { 127 return ref 128 } 129 130 // That didn't work, so now we do some extra work to find an unbalanced 131 // vertex (if any). Essentially we gather a list of edges and a list of 132 // reversed edges, and then sort them. The first edge that appears in one 133 // list but not the other is guaranteed to be unmatched. 134 n := shape.NumEdges() 135 var edges = make([]Edge, n) 136 var revEdges = make([]Edge, n) 137 for i := 0; i < n; i++ { 138 edge := shape.Edge(i) 139 edges[i] = edge 140 revEdges[i] = Edge{V0: edge.V1, V1: edge.V0} 141 } 142 143 sortEdges(edges) 144 sortEdges(revEdges) 145 146 for i := 0; i < n; i++ { 147 if edges[i].Cmp(revEdges[i]) == -1 { // edges[i] is unmatched 148 if ref, ok := referencePointAtVertex(shape, edges[i].V0); ok { 149 return ref 150 } 151 } 152 if revEdges[i].Cmp(edges[i]) == -1 { // revEdges[i] is unmatched 153 if ref, ok := referencePointAtVertex(shape, revEdges[i].V0); ok { 154 return ref 155 } 156 } 157 } 158 159 // All vertices are balanced, so this polygon is either empty or full except 160 // for degeneracies. By convention it is defined to be full if it contains 161 // any chain with no edges. 162 for i := 0; i < shape.NumChains(); i++ { 163 if shape.Chain(i).Length == 0 { 164 return OriginReferencePoint(true) 165 } 166 } 167 168 return OriginReferencePoint(false) 169 } 170 171 // referencePointAtVertex reports whether the given vertex is unbalanced, and 172 // returns a ReferencePoint indicating if the point is contained. 173 // Otherwise returns false. 174 func referencePointAtVertex(shape Shape, vTest Point) (ReferencePoint, bool) { 175 var ref ReferencePoint 176 177 // Let P be an unbalanced vertex. Vertex P is defined to be inside the 178 // region if the region contains a particular direction vector starting from 179 // P, namely the direction p.Ortho(). This can be calculated using 180 // ContainsVertexQuery. 181 182 containsQuery := NewContainsVertexQuery(vTest) 183 n := shape.NumEdges() 184 for e := 0; e < n; e++ { 185 edge := shape.Edge(e) 186 if edge.V0 == vTest { 187 containsQuery.AddEdge(edge.V1, 1) 188 } 189 if edge.V1 == vTest { 190 containsQuery.AddEdge(edge.V0, -1) 191 } 192 } 193 containsSign := containsQuery.ContainsVertex() 194 if containsSign == 0 { 195 return ref, false // There are no unmatched edges incident to this vertex. 196 } 197 ref.Point = vTest 198 ref.Contained = containsSign > 0 199 200 return ref, true 201 } 202 203 // containsBruteForce reports whether the given shape contains the given point. 204 // Most clients should not use this method, since its running time is linear in 205 // the number of shape edges. Instead clients should create a ShapeIndex and use 206 // ContainsPointQuery, since this strategy is much more efficient when many 207 // points need to be tested. 208 // 209 // Polygon boundaries are treated as being semi-open (see ContainsPointQuery 210 // and VertexModel for other options). 211 func containsBruteForce(shape Shape, point Point) bool { 212 if shape.Dimension() != 2 { 213 return false 214 } 215 216 refPoint := shape.ReferencePoint() 217 if refPoint.Point == point { 218 return refPoint.Contained 219 } 220 221 crosser := NewEdgeCrosser(refPoint.Point, point) 222 inside := refPoint.Contained 223 for e := 0; e < shape.NumEdges(); e++ { 224 edge := shape.Edge(e) 225 inside = inside != crosser.EdgeOrVertexCrossing(edge.V0, edge.V1) 226 } 227 return inside 228 }