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 }