1 // Copyright 2013 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 16 // Original Author: ericv@google.com (Eric Veach) 17 // Converted to D: madric@gmail.com (Vijay Nayar) 18 // 19 // This file defines various S2Shape types representing loops: 20 // 21 // S2LaxLoopShape 22 // - like S2Loop::Shape but allows duplicate vertices & edges, more compact 23 // representation, and faster to initialize. 24 // 25 // S2LaxClosedPolylineShape 26 // - like S2LaxLoopShape, but defines a loop that does not have an interior 27 // (a closed polyline). 28 // 29 // S2VertexIdLaxLoopShape 30 // - like S2LaxLoopShape, but vertices are specified as indices into an 31 // existing vertex array. 32 33 module s2.s2lax_loop_shape; 34 35 import s2.s2loop; 36 import s2.s2shape; 37 import s2.shapeutil.get_reference_point; 38 import s2.s2point; 39 40 import std.algorithm; 41 import std.typecons; 42 43 /** 44 * S2LaxLoopShape represents a closed loop of edges surrounding an interior 45 * region. It is similar to S2Loop::Shape except that this class allows 46 * duplicate vertices and edges. Loops may have any number of vertices, 47 * including 0, 1, or 2. (A one-vertex loop defines a degenerate edge 48 * consisting of a single point.) 49 * 50 * Note that S2LaxLoopShape is faster to initialize and more compact than 51 * S2Loop::Shape, but does not support the same operations as S2Loop. 52 */ 53 class S2LaxLoopShape : S2Shape { 54 public: 55 /// Constructs an empty loop. 56 this() { } 57 58 /// Constructs an S2LaxLoopShape with the given vertices. 59 this(in S2Point[] vertices) { 60 initialize(vertices); 61 } 62 63 /// Constructs an S2LaxLoopShape from the given S2Loop, by copying its data. 64 this(in S2Loop loop) { 65 initialize(loop); 66 } 67 68 /// Initializes an S2LaxLoopShape with the given vertices. 69 void initialize(in S2Point[] vertices) { 70 _vertices = new S2Point[vertices.length]; 71 copy(vertices, _vertices); 72 } 73 74 /** 75 * Initializes an S2LaxLoopShape from the given S2Loop, by copying its data. 76 * 77 * REQUIRES: !loop->is_full() 78 * [Use S2LaxPolygonShape if you need to represent a full loop.] 79 */ 80 void initialize(in S2Loop loop) 81 in { 82 assert(!loop.isFull(), "Full loops not supported; use S2LaxPolygonShape"); 83 } do { 84 if (loop.isEmpty()) { 85 _vertices.length = 0; 86 } else { 87 _vertices = new S2Point[loop.numVertices()]; 88 copy(loop.vertices(), _vertices); 89 } 90 } 91 92 int numVertices() const { 93 return cast(int) _vertices.length; 94 } 95 96 const(S2Point) vertex(int i) const { 97 return _vertices[i]; 98 } 99 100 /// S2Shape interface: 101 final override 102 int numEdges() const { 103 return numVertices(); 104 } 105 106 final override 107 Edge edge(int e0) const 108 in { 109 assert(e0 < numEdges()); 110 } do { 111 int e1 = e0 + 1; 112 if (e1 == numVertices()) e1 = 0; 113 return Edge(_vertices[e0], _vertices[e1]); 114 } 115 116 /// Not final; overridden by S2LaxClosedPolylineShape. 117 override 118 int dimension() const { 119 return 2; 120 } 121 122 /// Not final; overridden by S2LaxClosedPolylineShape. 123 override 124 ReferencePoint getReferencePoint() const { 125 // GetReferencePoint interprets a loop with no vertices as "full". 126 if (numVertices() == 0) return ReferencePoint(false); 127 return .getReferencePoint(this); 128 } 129 130 final override 131 int numChains() const { 132 return min(1, cast(int) _vertices.length); 133 } 134 135 final override 136 Chain chain(int i) const { 137 return Chain(0, cast(int) _vertices.length); 138 } 139 140 final override 141 Edge chainEdge(int i, int j) const 142 in { 143 assert(i == 0); 144 assert(j < numEdges()); 145 } do { 146 int k = (j + 1 == numVertices()) ? 0 : j + 1; 147 return Edge(_vertices[j], _vertices[k]); 148 } 149 150 final override 151 ChainPosition chainPosition(int e) const { 152 return ChainPosition(0, e); 153 } 154 155 private: 156 // For clients that have many small loops, we save some memory by 157 // representing the vertices as an array rather than using std::vector. 158 int _numVertices; 159 S2Point[] _vertices; 160 } 161 162 /** 163 * S2LaxClosedPolylineShape is like S2LaxPolylineShape except that the last 164 * vertex is implicitly joined to the first. It is also like S2LaxLoopShape 165 * except that it does not have an interior (which makes it more efficient to 166 * index). 167 */ 168 class S2LaxClosedPolylineShape : S2LaxLoopShape { 169 public: 170 171 this() { 172 super(); 173 } 174 175 this(in S2Point[] vertices) { 176 super(vertices); 177 } 178 179 this(in S2Loop loop) { 180 super(loop); 181 } 182 183 final override 184 int dimension() const { 185 return 1; 186 } 187 188 final override 189 ReferencePoint getReferencePoint() const { 190 return ReferencePoint(false); 191 } 192 } 193 194 /** 195 * S2VertexIdLaxLoopShape is just like S2LaxLoopShape, except that vertices are 196 * specified as indices into a vertex array. This representation can be more 197 * compact when many loops are arranged in a mesh structure. 198 */ 199 class S2VertexIdLaxLoopShape : S2Shape { 200 public: 201 // Constructs an empty loop. 202 this() { } 203 204 // Constructs the shape from the given vertex array and indices. 205 // "vertex_ids" is a vector of indices into "vertex_array". 206 // 207 // ENSURES: loop->vertex(i) == (*vertex_array)[vertex_ids[i]] 208 // REQUIRES: "vertex_array" persists for the lifetime of this object. 209 this(in int[] vertex_ids, in S2Point[] vertex_array) { 210 initialize(vertex_ids, vertex_array); 211 } 212 213 // Initializes the shape from the given vertex array and indices. 214 // "vertex_ids" is a vector of indices into "vertex_array". 215 void initialize(in int[] vertex_ids, in S2Point[] vertex_array) { 216 _vertexIds = new int[vertex_ids.length]; 217 copy(vertex_ids, _vertexIds); 218 _vertexArray = vertex_array; 219 } 220 221 /// Returns the number of vertices in the loop. 222 int numVertices() const { 223 return cast(int) _vertexIds.length; 224 } 225 226 int vertexId(int i) const { 227 return _vertexIds[i]; 228 } 229 230 const(S2Point) vertex(int i) const { 231 return _vertexArray[vertexId(i)]; 232 } 233 234 /// S2Shape interface: 235 final override 236 int numEdges() const { 237 return numVertices(); 238 } 239 240 final override 241 Edge edge(int e0) const 242 in { 243 assert(e0 < numEdges()); 244 } do { 245 int e1 = e0 + 1; 246 if (e1 == numVertices()) e1 = 0; 247 return Edge(vertex(e0), vertex(e1)); 248 } 249 250 final override 251 int dimension() const { 252 return 2; 253 } 254 255 final override 256 ReferencePoint getReferencePoint() const { 257 // GetReferencePoint interprets a loop with no vertices as "full". 258 if (numVertices() == 0) return ReferencePoint(false); 259 return .getReferencePoint(this); 260 } 261 262 final override 263 int numChains() const { 264 return 1; 265 } 266 267 final override 268 Chain chain(int i) const { 269 return Chain(0, cast(int) _vertexIds.length); 270 } 271 272 final override 273 Edge chainEdge(int i, int j) const 274 in { 275 assert(i == 0); 276 assert(j < numEdges()); 277 } do { 278 int k = (j + 1 == numVertices()) ? 0 : j + 1; 279 return Edge(vertex(j), vertex(k)); 280 } 281 282 final override 283 ChainPosition chainPosition(int e) const { 284 return ChainPosition(0, e); 285 } 286 287 private: 288 int[] _vertexIds; 289 Rebindable!(const(S2Point[])) _vertexArray; 290 }