1 /**
2    Represents polygonal geometry in a flexible way.
3 
4    Copyright: 2012 Google Inc. All Rights Reserved.
5 
6    License:
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10 
11    $(LINK http://www.apache.org/licenses/LICENSE-2.0)
12 
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS-IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 
19    Authors: ericv@google.com (Eric Veach), madric@gmail.com (Vijay Nayar)
20 */
21 module s2.s2shape;
22 
23 import s2.s2point;
24 import s2.s2pointutil : origin;
25 
26 /**
27    The purpose of S2Shape is to represent polygonal geometry in a flexible
28    way.  It is organized as a collection of edges that optionally defines an
29    interior.  All geometry represented by an S2Shape must have the same
30    dimension, which means that an S2Shape can represent either a set of
31    points, a set of polylines, or a set of polygons.
32 
33    S2Shape is defined as an abstract base class in order to give clients
34    control over the underlying data representation.  Sometimes an S2Shape does
35    not have any data of its own, but instead "wraps" some other class.  There
36    are various useful subtypes defined in *_shape.h, and some S2 classes also
37    have a nested "Shape" class (e.g., S2Polygon::Shape).  It is easy for
38    clients to implement their own subtypes, since the interface is minimal.
39 
40    S2Shape operations are typically defined on S2ShapeIndex objects rather
41    than individual shapes.  An S2ShapeIndex is simply a collection of
42    S2Shapes, possibly of different dimensions (e.g. 10 points and 3 polygons),
43    organized into a data structure for efficient edge access.
44 
45    The edges of an S2Shape are identified by a contiguous range of "edge ids"
46    starting at 0.  The edges are further subdivided into "chains", where each
47    chain consists of a sequence of edges connected end-to-end (a polyline).
48    For example, an S2Shape representing two polylines AB and CDE would have
49    three edges (AB, CD, DE) grouped into two chains: (AB) and (CD, DE).
50    Similarly, an S2Shape representing 5 points would have 5 chains consisting
51    of one edge each.
52 
53    S2Shape has methods that allow edges to be accessed either using the global
54    numbering (edge id) or within a particular chain.  The global numbering is
55    sufficient for most purposes, but the chain representation is useful for
56    certain algorithms such as intersection (see S2BooleanOperation).
57 */
58 abstract class S2Shape {
59 public:
60   /**
61      An edge, consisting of two vertices "v0" and "v1".  Zero-length edges are
62      allowed, and can be used to represent points.
63   */
64   struct Edge {
65     S2Point v0, v1;
66 
67     bool opEquals(in Edge e) const {
68       return v0 == e.v0 && v1 == e.v1;
69     }
70 
71     int opCmp(in Edge e) const {
72       if (v0 < e.v0 || (v0 == e.v0 && v1 < e.v1)) {
73         return -1;
74       } else if (v0 > e.v0 || (v0 == e.v0 && v1 > e.v1)) {
75         return 1;
76       }
77       return 0;
78     }
79   }
80 
81   /**
82      A range of edge ids corresponding to a chain of zero or more connected
83      edges, specified as a (start, length) pair.  The chain is defined to
84      consist of edge ids {start, start + 1, ..., start + length - 1}.
85   */
86   struct Chain {
87     int start, length;
88   }
89 
90   /**
91      The position of an edge within a given edge chain, specified as a
92      (chain_id, offset) pair.  Chains are numbered sequentially starting from
93      zero, and offsets are measured from the start of each chain.
94   */
95   struct ChainPosition {
96     int chainId, offset;
97   }
98 
99   /**
100      A ReferencePoint consists of a point P and a boolean indicating whether P
101      is contained by a particular shape.
102   */
103   struct ReferencePoint {
104     S2Point point;
105     bool contained;
106 
107     this(in S2Point p, in bool c) {
108       point = p;
109       contained = c;
110     }
111 
112     // Returns a ReferencePoint with the given "contained" value and a default
113     // "point".  It should be used when all points or no points are contained.
114     // (Was called "contained")
115     this(bool contained) {
116       this.point = origin();
117       this.contained = contained;
118     }
119   }
120 
121   this() {
122     _id = -1;
123   }
124 
125   /// Returns the number of edges in this shape.  Edges have ids ranging from 0 to num_edges() - 1.
126   abstract int numEdges() const;
127 
128   /**
129      Returns the endpoints of the given edge id.
130 
131      REQUIRES: 0 <= id < num_edges()
132   */
133   abstract Edge edge(int edge_id) const
134   in {
135     assert(0 <= edge_id && edge_id < numEdges());
136   }
137 
138   /**
139      Returns the dimension of the geometry represented by this shape.
140 
141       0 - Point geometry.  Each point is represented as a degenerate edge.
142 
143       1 - Polyline geometry.  Polyline edges may be degenerate.  A shape may
144           represent any number of polylines.  Polylines edges may intersect.
145 
146       2 - Polygon geometry.  Edges should be oriented such that the polygon
147           interior is always on the left.  In theory the edges may be returned
148           in any order, but typically the edges are organized as a collection
149           of edge chains where each chain represents one polygon loop.
150           Polygons may have degeneracies (e.g., degenerate edges or sibling
151           pairs consisting of an edge and its corresponding reversed edge).
152 
153      Note that this method allows degenerate geometry of different dimensions
154      to be distinguished, e.g. it allows a point to be distinguished from a
155      polyline or polygon that has been simplified to a single point.
156   */
157   abstract int dimension() const;
158 
159   /// Convenience function that returns true if this shape has an interior.
160   bool hasInterior() const {
161     return dimension() == 2;
162   }
163 
164   /**
165      Returns an arbitrary point P along with a boolean indicating whether P is
166      contained by the shape.  (The boolean value must be false for shapes that
167      do not have an interior.)
168 
169      This ReferencePoint may then be used to compute the containment of other
170      points by counting edge crossings.
171   */
172   abstract ReferencePoint getReferencePoint() const;
173 
174   /**
175      Returns the number of contiguous edge chains in the shape.  For example,
176      a shape whose edges are [AB, BC, CD, AE, EF] would consist of two chains
177      (AB,BC,CD and AE,EF).  Every chain is assigned a "chain id" numbered
178      sequentially starting from zero.
179 
180      Note that it is always acceptable to implement this method by returning
181      num_edges() (i.e. every chain consists of a single edge), but this may
182      reduce the efficiency of some algorithms.
183   */
184   abstract int numChains() const;
185 
186   /**
187      Returns the range of edge ids corresponding to the given edge chain.  The
188      edge chains must form contiguous, non-overlapping ranges that cover the
189      entire range of edge ids.  This is spelled out more formally below:
190 
191      REQUIRES: 0 <= i < num_chains()
192      REQUIRES: chain(i).length >= 0, for all i
193      REQUIRES: chain(0).start == 0
194      REQUIRES: chain(i).start + chain(i).length == chain(i+1).start,
195                for i < num_chains() - 1
196      REQUIRES: chain(i).start + chain(i).length == num_edges(),
197                for i == num_chains() - 1
198   */
199   abstract Chain chain(int chain_id) const
200   in {
201     assert(0 <= chain_id && chain_id < numChains());
202   } out (c) {
203     assert(c.length >= 0);
204     if (chain_id == numChains() - 1) {
205       assert(c.start + c.length == numEdges());
206     }
207   }
208 
209   /**
210      Returns the edge at offset "offset" within edge chain "chain_id".
211      Equivalent to "shape.edge(shape.chain(chain_id).start + offset)"
212      but may be more efficient.
213   */
214   abstract Edge chainEdge(int chain_id, int offset) const;
215 
216   /**
217      Finds the chain containing the given edge, and returns the position of
218      that edge as a (chain_id, offset) pair.
219 
220      REQUIRES: shape.chain(pos.chain_id).start + pos.offset == edge_id
221      REQUIRES: shape.chain(pos.chain_id + 1).start > edge_id
222 
223      where     pos == shape.chain_position(edge_id).
224   */
225   abstract ChainPosition chainPosition(int edge_id) const;
226 
227   /**
228      A unique id assigned to this shape by S2ShapeIndex.  Shape ids are
229      assigned sequentially starting from 0 in the order shapes are added.
230   */
231   int id() const { return _id; }
232 
233   /**
234      Virtual methods that return pointers of your choice.
235 
236      These methods are intended to help with the problem of attaching additional data to S2Shape
237      objects.  For example, you could return a pointer to a source object, or a pointer to a bundle
238      of additional data allocated with the S2Shape.  Because this method exists in all S2Shapes, you
239      can override it in each type of shape you have and call it without knowing the concrete
240      subtype.  For example, if you have polyline and polygon shapes, you can do this:
241 
242      ---
243        class MyPolyline : S2Polyline.Shape {
244        public:
245          void* mutableUserData() { return &_myData; }
246        private:
247          MyData _myData;
248        }
249        class MyPolygon : S2Polygon.Shape {
250        public:
251          void* mutableUserData() { return &_myData; }
252        private:
253          MyData _myData;
254        };
255        ...
256        S2Shape* shape = index.shape(id);
257        const(MyData)* data = cast(const(MyData)*)(shape.userData());
258      ---
259 
260      This is not the only way to map from an S2Shape back to your source
261      data.  Other reasonable techniques include:
262 
263       - Every shape has an id() assigned by S2ShapeIndex.  Ids are assigned
264         sequentially starting from 0 in the order the shapes are added to the
265         index.  You can use this id to look up arbitrary data stored in your
266         own vector.
267 
268       - If all of your shapes are the same type, then you can create your own
269         subclass of some existing S2Shape type (such as S2Polyline::Shape) and
270         add your own methods and fields.  You can access this data by
271         downcasting the S2Shape pointers returned by S2ShapeIndex methods.
272   */
273   const(void*) userData() const {
274     return null;
275   }
276   void* mutableUserData() {
277     return null;
278   }
279 
280   /// Assigned by MutableS2ShapeIndex when the shape is added.
281   package int _id;
282 }