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 }