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 }