1 // Copyright 2016 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 // Note that there are two supported output types for polygons: S2Polygon and 20 // S2LaxPolygonShape. Use S2Polygon if you need the full range of operations 21 // that S2Polygon implements. Use S2LaxPolygonShape if you want to represent 22 // polygons with zero-area degenerate regions, or if you need a type that has 23 // low memory overhead and fast initialization. However, be aware that to 24 // convert from a S2LaxPolygonShape to an S2Polygon you will need to use 25 // S2Builder again. 26 // 27 // Similarly, there are two supported output formats for polygon meshes: 28 // S2LaxPolygonShapeVector and S2PolygonMesh. Use S2PolygonMesh if you need 29 // to be able to determine which polygons are adjacent to each edge or vertex; 30 // otherwise use S2LaxPolygonShapeVector, which uses less memory and is faster 31 // to construct. 32 33 module s2.builder.util.s2polygon_layer; 34 35 import s2.builder.graph; 36 import s2.builder.layer; 37 import s2.s2point; 38 import s2.id_set_lexicon; 39 import s2.mutable_s2shape_index; 40 import s2.s2builder; 41 import s2.s2debug; 42 import s2.s2error; 43 import s2.s2loop; 44 import s2.s2polygon; 45 46 import std.algorithm; 47 import std.range; 48 import std.typecons; 49 50 alias LoopType = Graph.LoopType; 51 alias SiblingPairs = GraphOptions.SiblingPairs; 52 alias DegenerateEdges = GraphOptions.DegenerateEdges; 53 alias DuplicateEdges = GraphOptions.DuplicateEdges; 54 55 /** 56 * A layer type that assembles edges (directed or undirected) into an 57 * S2Polygon. Returns an error if the edges cannot be assembled into loops. 58 * 59 * If the input edges are directed, they must be oriented such that the 60 * polygon interior is to the left of all edges. Directed edges are always 61 * preferred (see S2Builder::EdgeType). 62 * 63 * Before the edges are assembled into loops, "sibling pairs" consisting of an 64 * edge and its reverse edge are automatically removed. Such edge pairs 65 * represent zero-area degenerate regions, which S2Polygon does not allow. 66 * (If you need to build polygons with degeneracies, use LaxPolygonLayer 67 * instead.) 68 * 69 * S2PolygonLayer is implemented such that if the input to S2Builder is a 70 * polygon and is not modified, then the output has the same cyclic ordering 71 * of loop vertices and the same loop ordering as the input polygon. 72 * 73 * CAVEAT: Because polygons are constructed from their boundaries, this method 74 * cannot distinguish between the empty and full polygons. An empty boundary 75 * always yields an empty polygon. If the result should sometimes be the full 76 * polygon, such logic must be implemented outside of this class (and will 77 * need to consider factors other than the polygon's boundary). 78 */ 79 class S2PolygonLayer : Layer { 80 public: 81 82 struct Options { 83 public: 84 /** 85 * Indicates whether the input edges provided to S2Builder are directed or 86 * undirected. Directed edges should be used whenever possible (see 87 * S2Builder::EdgeType for details). 88 * 89 * If the input edges are directed, they should be oriented so that the 90 * polygon interior is to the left of all edges. This means that for a 91 * polygon with holes, the outer loops ("shells") should be directed 92 * counter-clockwise while the inner loops ("holes") should be directed 93 * clockwise. Note that S2Builder::AddPolygon() does this automatically. 94 * 95 * DEFAULT: S2Builder::EdgeType::DIRECTED 96 */ 97 S2Builder.EdgeType edgeType() const { 98 return _edgeType; 99 } 100 101 void setEdgeType(S2Builder.EdgeType edge_type) { 102 _edgeType = edge_type; 103 } 104 105 /** 106 * If true, calls FindValidationError() on the output polygon. If any 107 * error is found, it will be returned by S2Builder::Build(). 108 * 109 * Note that this option calls set_s2debug_override(S2Debug::DISABLE) in 110 * order to turn off the default error checking in debug builds. 111 * 112 * DEFAULT: false 113 */ 114 bool validate() const { 115 return _validate; 116 } 117 118 void setValidate(bool validate) { 119 _validate = validate; 120 } 121 122 private: 123 S2Builder.EdgeType _edgeType = S2Builder.EdgeType.DIRECTED; 124 bool _validate = false; 125 } 126 127 /// Specifies that a polygon should be constructed using the given options. 128 this(S2Polygon polygon, Options options = Options()) { 129 initialize(polygon, null, null, options); 130 } 131 132 alias LabelSetIds = LabelSetId[][]; 133 134 /** 135 * Specifies that a polygon should be constructed using the given options, 136 * and that any labels attached to the input edges should be returned in 137 * "label_set_ids" and "label_set_lexicion". 138 * 139 * The labels associated with the edge "polygon.loop(i).vertex({j, j+1})" 140 * can be retrieved as follows: 141 * 142 * for (int32 label : label_set_lexicon.id_set(label_set_ids[i][j])) {...} 143 */ 144 this(S2Polygon polygon, ref LabelSetIds label_set_ids, 145 IdSetLexicon label_set_lexicon, 146 in Options options = Options()) { 147 initialize(polygon, &label_set_ids, label_set_lexicon, options); 148 } 149 150 151 // Layer interface: 152 override 153 GraphOptions graphOptions() const { 154 // Prevent degenerate edges and sibling edge pairs. There should not be any 155 // duplicate edges if the input is valid, but if there are then we keep them 156 // since this tends to produce more comprehensible errors. 157 return new GraphOptions(_options.edgeType(), DegenerateEdges.DISCARD, 158 DuplicateEdges.KEEP, SiblingPairs.DISCARD); 159 } 160 161 override 162 void build(in Graph g, ref S2Error error) { 163 if (_labelSetIds) _labelSetIds.length = 0; 164 165 // It's tricky to compute the edge labels for S2Polygons because the 166 // S2Polygon::Init methods can reorder and/or invert the loops. We handle 167 // this by remembering the original vector index of each loop and whether or 168 // not the loop contained S2::Origin(). By comparing this with the final 169 // S2Polygon loops we can fix up the edge labels appropriately. 170 LoopMap loop_map; 171 if (g.options().edgeType() == EdgeType.DIRECTED) { 172 Graph.EdgeLoop[] edge_loops; 173 if (!g.getDirectedLoops(LoopType.SIMPLE, edge_loops, error)) { 174 return; 175 } 176 S2Loop[] loops; 177 appendS2Loops(g, edge_loops, loops); 178 appendEdgeLabels(g, edge_loops); 179 initializeLoopMap(loops, loop_map); 180 _polygon.initializeOriented(loops); 181 } else { 182 Graph.UndirectedComponent[] components; 183 if (!g.getUndirectedComponents(LoopType.SIMPLE, components, error)) { 184 return; 185 } 186 // It doesn't really matter which complement of each component we use, 187 // since below we normalize all the loops so that they enclose at most 188 // half of the sphere (to ensure that the loops can always be nested). 189 // 190 // The only reason to prefer one over the other is that when there are 191 // multiple loops that touch, only one of the two complements matches the 192 // structure of the input loops. GetUndirectedComponents() tries to 193 // ensure that this is always complement 0 of each component. 194 S2Loop[] loops; 195 foreach (component; components) { 196 appendS2Loops(g, component[0], loops); 197 appendEdgeLabels(g, component[0]); 198 } 199 initializeLoopMap(loops, loop_map); 200 foreach (loop; loops) loop.normalize(); 201 _polygon.initializeNested(loops); 202 } 203 reorderEdgeLabels(loop_map); 204 if (_options.validate()) { 205 _polygon.findValidationError(error); 206 } 207 } 208 209 private: 210 void initialize(S2Polygon polygon, LabelSetIds* label_set_ids, 211 IdSetLexicon label_set_lexicon, in Options options) 212 in { 213 assert((label_set_ids is null) == (label_set_lexicon is null)); 214 } do { 215 _polygon = polygon; 216 _labelSetIds = label_set_ids; 217 _labelSetLexicon = label_set_lexicon; 218 _options = options; 219 220 if (_options.validate()) { 221 _polygon.setS2debugOverride(S2Debug.DISABLE); 222 } 223 } 224 225 void appendS2Loops(in Graph g, 226 in Graph.EdgeLoop[] edge_loops, 227 ref S2Loop[] loops) const { 228 S2Point[] vertices; 229 foreach (edge_loop; edge_loops) { 230 vertices.reserve(edge_loop.length); 231 foreach (edge_id; edge_loop) { 232 vertices ~= g.vertex(g.edge(edge_id)[0]); 233 } 234 loops ~= new S2Loop(vertices, _polygon.s2debugOverride()); 235 vertices.length = 0; 236 } 237 } 238 239 void appendEdgeLabels(in Graph g, in Graph.EdgeLoop[] edge_loops) { 240 if (!_labelSetIds) return; 241 242 Label[] labels; // Temporary storage for labels. 243 auto fetcher = new Graph.LabelFetcher(g, _options.edgeType()); 244 foreach (edge_loop; edge_loops) { 245 LabelSetId[] loop_label_set_ids; 246 loop_label_set_ids.reserve(edge_loop.length); 247 foreach (edge_id; edge_loop) { 248 fetcher.fetch(edge_id, labels); 249 loop_label_set_ids ~= _labelSetLexicon.add(labels); 250 } 251 *_labelSetIds ~= loop_label_set_ids; 252 } 253 } 254 255 alias LoopMap = Tuple!(int, bool)[const(S2Loop)]; 256 257 void initializeLoopMap(in S2Loop[] loops, ref LoopMap loop_map) const { 258 if (!_labelSetIds) return; 259 foreach (i, loop; loops) { 260 loop_map[loop] = tuple(cast(int) i, loop.containsOrigin()); 261 } 262 } 263 264 void reorderEdgeLabels(in LoopMap loop_map) { 265 if (!_labelSetIds) return; 266 auto new_ids = new LabelSetIds(_labelSetIds.length); 267 for (int i = 0; i < _polygon.numLoops(); ++i) { 268 S2Loop loop = _polygon.loop(i); 269 Tuple!(int, bool) old = loop_map[loop]; 270 new_ids[i] = (*_labelSetIds)[old[0]]; 271 if (loop.containsOrigin() != old[1]) { 272 // S2Loop::Invert() reverses the order of the vertices, which leaves 273 // the last edge unchanged. For example, the loop ABCD (with edges 274 // AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD). 275 reverse(new_ids[i][0 .. $-1]); 276 } 277 } 278 *_labelSetIds = new_ids; 279 } 280 281 S2Polygon _polygon; 282 LabelSetIds* _labelSetIds; 283 IdSetLexicon _labelSetLexicon; 284 Options _options; 285 } 286 287 /** 288 * Like S2PolygonLayer, but adds the polygon to a MutableS2ShapeIndex (if the 289 * polygon is non-empty). 290 */ 291 class IndexedS2PolygonLayer : Layer { 292 public: 293 alias Options = S2PolygonLayer.Options; 294 295 this(MutableS2ShapeIndex index, Options options = Options()) { 296 _index = index; 297 _polygon = new S2Polygon(); 298 _layer = new S2PolygonLayer(_polygon, options); 299 } 300 301 override 302 GraphOptions graphOptions() const { 303 return _layer.graphOptions(); 304 } 305 306 override 307 void build(in Graph g, ref S2Error error) { 308 _layer.build(g, error); 309 if (error.ok() && !_polygon.isEmpty()) { 310 _index.add(new S2Polygon.Shape(_polygon)); 311 } 312 } 313 314 private: 315 MutableS2ShapeIndex _index; 316 S2Polygon _polygon; 317 S2PolygonLayer _layer; 318 }