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 module s2.s2lax_polygon_shape;
20 
21 import s2.s2loop;
22 import s2.s2point;
23 import s2.s2polygon;
24 import s2.s2shape;
25 import s2.shapeutil.get_reference_point : getReferencePoint;
26 
27 import std.algorithm : copy;
28 import std.range : assumeSorted, enumerate, SortedRange;
29 
30 alias ChainPosition = S2Shape.ChainPosition;
31 
32 /**
33  * S2LaxPolygonShape represents a region defined by a collection of zero or
34  * more closed loops.  The interior is the region to the left of all loops.
35  * This is similar to S2Polygon::Shape except that this class supports
36  * polygons with degeneracies.  Degeneracies are of two types: degenerate
37  * edges (from a vertex to itself) and sibling edge pairs (consisting of two
38  * oppositely oriented edges).  Degeneracies can represent either "shells" or
39  * "holes" depending on the loop they are contained by.  For example, a
40  * degenerate edge or sibling pair contained by a "shell" would be interpreted
41  * as a degenerate hole.  Such edges form part of the boundary of the polygon.
42  *
43  * Loops with fewer than three vertices are interpreted as follows:
44  *  - A loop with two vertices defines two edges (in opposite directions).
45  *  - A loop with one vertex defines a single degenerate edge.
46  *  - A loop with no vertices is interpreted as the "full loop" containing
47  *    all points on the sphere.  If this loop is present, then all other loops
48  *    must form degeneracies (i.e., degenerate edges or sibling pairs).  For
49  *    example, two loops {} and {X} would be interpreted as the full polygon
50  *    with a degenerate single-point hole at X.
51  *
52  * S2LaxPolygonShape does not have any error checking, and it is perfectly
53  * fine to create S2LaxPolygonShape objects that do not meet the requirements
54  * below (e.g., in order to analyze or fix those problems).  However,
55  * S2LaxPolygonShapes must satisfy some additional conditions in order to
56  * perform certain operations:
57  *
58  *  - In order to be valid for point containment tests, the polygon must
59  *    satisfy the "interior is on the left" rule.  This means that there must
60  *    not be any crossing edges, and if there are duplicate edges then all but
61  *    at most one of thm must belong to a sibling pair (i.e., the number of
62  *    edges in opposite directions must differ by at most one).
63  *
64  *  - To be valid for boolean operations (S2BooleanOperation), degenerate
65  *    edges and sibling pairs cannot coincide with any other edges.  For
66  *    example, the following situations are not allowed:
67  *
68  *      {AA, AA}      // degenerate edge coincides with another edge
69  *      {AA, AB}      // degenerate edge coincides with another edge
70  *      {AB, BA, AB}  // sibling pair coincides with another edge
71  *
72  * Note that S2LaxPolygonShape is must faster to initialize and is more
73  * compact than S2Polygon, but unlike S2Polygon it does not have any built-in
74  * operations.  Instead you should use S2ShapeIndex operations
75  * (S2BooleanOperation, S2ClosestEdgeQuery, etc).
76  */
77 class S2LaxPolygonShape : S2Shape {
78 public:
79   // Constructs an empty polygon.
80   this() {
81     _numLoops = 0;
82     _numVertices = 0;
83   }
84 
85   // Constructs an S2LaxPolygonShape from the given vertex loops.
86   alias Loop = S2Point[];
87 
88   this(in Loop[] loops) {
89     initialize(loops);
90   }
91 
92   /**
93    * Constructs an S2LaxPolygonShape from an S2Polygon, by copying its data.
94    * Full and empty S2Polygons are supported.
95    */
96   this(in S2Polygon polygon) {
97     initialize(polygon);
98   }
99 
100   /// Initializes an S2LaxPolygonShape from the given vertex loops.
101   void initialize(in S2Point[][] loops) {
102     _numLoops = cast(int) loops.length;
103     if (_numLoops == 0) {
104       _numVertices = 0;
105       _vertices = null;
106     } else if (_numLoops == 1) {
107       _numVertices = cast(int) loops[0].length;
108       _vertices = loops[0].dup;
109     } else {
110       _cumulativeVertices = new int[_numLoops + 1];
111       int num_vertices = 0;
112       for (int i = 0; i < _numLoops; ++i) {
113         _cumulativeVertices[i] = num_vertices;
114         num_vertices += loops[i].length;
115       }
116       _cumulativeVertices[_numLoops] = num_vertices;
117       _vertices = new S2Point[num_vertices];
118       for (int i = 0; i < _numLoops; ++i) {
119         copy(loops[i], _vertices[_cumulativeVertices[i] .. $]);
120       }
121     }
122   }
123 
124   /**
125    * Initializes an S2LaxPolygonShape from an S2Polygon, by copying its data.
126    * Full and empty S2Polygons are supported.
127    */
128   void initialize(in S2Polygon polygon) {
129     const(S2Point[])[] spans;
130     for (int i = 0; i < polygon.numLoops(); ++i) {
131       const S2Loop loop = polygon.loop(i);
132       if (loop.isFull()) {
133         spans ~= new S2Point[0];  // Empty span.
134       } else {
135         spans ~= loop.vertices();
136       }
137     }
138     initialize(spans);
139   }
140 
141   // Returns the number of loops.
142   int numLoops() const {
143     return _numLoops;
144   }
145 
146   // Returns the total number of vertices in all loops.
147   int numVertices() const {
148     if (numLoops() <= 1) {
149       return _numVertices;
150     } else {
151       return _cumulativeVertices[numLoops()];
152     }
153   }
154 
155   // Returns the number of vertices in the given loop.
156   int numLoopVertices(int i) const
157   in {
158     assert(i < numLoops());
159   } do {
160     if (numLoops() == 1) {
161       return _numVertices;
162     } else {
163       return _cumulativeVertices[i + 1] - _cumulativeVertices[i];
164     }
165   }
166 
167   // Returns the vertex from loop "i" at index "j".
168   S2Point loopVertex(int i, int j) const
169   in {
170     assert(i < numLoops());
171     assert(j < numLoopVertices(i));
172   } do {
173     if (numLoops() == 1) {
174       return _vertices[j];
175     } else {
176       return _vertices[_cumulativeVertices[i] + j];
177     }
178   }
179 
180   const(S2Point[]) loopVertices(int i) const
181   in {
182     assert(i < numLoops());
183   } do {
184     if (numLoops() == 1) {
185       return _vertices;
186     } else {
187       size_t indexStart = _cumulativeVertices[i];
188       return _vertices[indexStart .. indexStart + numLoopVertices(i)];
189     }
190   }
191 
192   // S2Shape interface:
193   final override
194   int numEdges() const {
195     return numVertices();
196   }
197 
198   final override
199   Edge edge(int e0) const
200   in {
201     assert(e0 < numEdges());
202   } do {
203     int e1 = e0 + 1;
204     if (numLoops() == 1) {
205       if (e1 == _numVertices) { e1 = 0; }
206     } else {
207       // Find the index of the first vertex of the loop following this one.
208       const int kMaxLinearSearchLoops = 12;  // From benchmarks.
209       //int* next = _cumulativeVertices + 1;
210       size_t nextIndex = 1;
211       if (numLoops() <= kMaxLinearSearchLoops) {
212         while (_cumulativeVertices[nextIndex] <= e0) ++nextIndex;
213       } else {
214         //next = std::lower_bound(next, next + num_loops(), e1);
215         nextIndex += _cumulativeVertices[nextIndex .. nextIndex + numLoops()]
216             .assumeSorted
217             .lowerBound(e1)
218             .length;
219       }
220       // Wrap around to the first vertex of the loop if necessary.
221       if (e1 == _cumulativeVertices[nextIndex]) { e1 = _cumulativeVertices[nextIndex - 1]; }
222     }
223     return Edge(_vertices[e0], _vertices[e1]);
224   }
225 
226   final override
227   int dimension() const {
228     return 2;
229   }
230 
231   final override
232   ReferencePoint getReferencePoint() const {
233     return .getReferencePoint(this);
234   }
235 
236   final override
237   int numChains() const {
238     return numLoops();
239   }
240 
241   final override
242   Chain chain(int i) const
243   in {
244     assert(i < numLoops());
245   } do {
246     if (numLoops() == 1) {
247       return Chain(0, _numVertices);
248     } else {
249       int start = _cumulativeVertices[i];
250       return Chain(start, _cumulativeVertices[i + 1] - start);
251     }
252   }
253 
254   final override
255   Edge chainEdge(int i, int j) const
256   in {
257     assert(i < numLoops());
258     assert(j < numLoopVertices(i));
259   } do {
260     int n = numLoopVertices(i);
261     int k = (j + 1 == n) ? 0 : j + 1;
262     if (numLoops() == 1) {
263       return Edge(_vertices[j], _vertices[k]);
264     } else {
265       int base = _cumulativeVertices[i];
266       return Edge(_vertices[base + j], _vertices[base + k]);
267     }
268   }
269 
270   final override
271   ChainPosition chainPosition(int e) const
272   in {
273     assert(e < numEdges());
274   } do {
275     const int kMaxLinearSearchLoops = 12;  // From benchmarks.
276     if (numLoops() == 1) {
277       return ChainPosition(0, e);
278     } else {
279       // Find the index of the first vertex of the loop following this one.
280       //int* nextIndex = _cumulativeVertices + 1;
281       int nextIndex = 1;
282       if (numLoops() <= kMaxLinearSearchLoops) {
283         while (_cumulativeVertices[nextIndex] <= e) ++nextIndex;
284       } else {
285         nextIndex = cast(int) _cumulativeVertices[nextIndex .. $]
286             .assumeSorted!("a <= b")
287             .lowerBound(e)
288             .length + 1;
289       }
290       return ChainPosition(nextIndex - 1, e - _cumulativeVertices[nextIndex - 1]);
291     }
292   }
293 
294 private:
295 
296   int _numLoops;
297   S2Point[] _vertices;
298   // If num_loops_ <= 1, this union stores the number of vertices.
299   // Otherwise it points to an array of size (num_loops + 1) where element "i"
300   // is the total number of vertices in loops 0..i-1.
301   union {
302     int _numVertices;
303     int[] _cumulativeVertices;  // Don't use unique_ptr in unions.
304   }
305 }