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 }