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 module s2.builder.util.s2polyline_layer;
20 
21 import s2.id_set_lexicon;
22 import s2.mutable_s2shape_index;
23 import s2.s2point;
24 import s2.s2builder;
25 import s2.builder.graph;
26 import s2.builder.layer;
27 import s2.s2debug;
28 import s2.s2error;
29 import s2.s2polyline;
30 
31 alias DegenerateEdges = GraphOptions.DegenerateEdges;
32 alias DuplicateEdges = GraphOptions.DuplicateEdges;
33 alias SiblingPairs = GraphOptions.SiblingPairs;
34 
35 /**
36  * A layer type that assembles edges (directed or undirected) into an
37  * S2Polyline.  Returns an error if the edges cannot be assembled into a
38  * single unbroken polyline.
39  *
40  * Duplicate edges are handled correctly (e.g., if a polyline backtracks on
41  * itself, or loops around and retraces some of its previous edges.)  The
42  * implementation attempts to preserve the order of directed input edges
43  * whenever possible, so that if the input is a polyline and it is not
44  * modified by S2Builder, then the output will be the same polyline (even if
45  * the polyline backtracks on itself or forms a loop).  With undirected edges,
46  * there are no such guarantees; for example, even if the input consists of a
47  * single undirected edge, then either directed edge may be returned.
48  *
49  * S2PolylineLayer does not support options such as discarding sibling pairs
50  * or merging duplicate edges because these options can split the polyline
51  * into several pieces.  Use S2PolylineVectorLayer if you need these features.
52  */
53 class S2PolylineLayer : Layer {
54 public:
55   static struct Options {
56   public:
57     /**
58      * Indicates whether the input edges provided to S2Builder are directed or
59      * undirected.  Directed edges should be used whenever possible to avoid
60      * ambiguity.
61      *
62      * DEFAULT: S2Builder::EdgeType::DIRECTED
63      */
64     S2Builder.EdgeType edgeType() const {
65       return _edgeType;
66     }
67 
68     void setEdgeType(S2Builder.EdgeType edge_type) {
69       _edgeType = edge_type;
70     }
71 
72     /**
73      * If true, calls FindValidationError() on the output polyline.  If any
74      * error is found, it will be returned by S2Builder::Build().
75      *
76      * Note that this option calls set_s2debug_override(S2Debug::DISABLE) in
77      * order to turn off the default error checking in debug builds.
78      *
79      * DEFAULT: false
80      */
81     bool validate() const {
82       return _validate;
83     }
84 
85     void setValidate(bool validate) {
86       _validate = validate;
87     }
88 
89   private:
90     S2Builder.EdgeType _edgeType = S2Builder.EdgeType.DIRECTED;
91     bool _validate = false;
92   }
93 
94   /// Specifies that a polyline should be constructed using the given options.
95   this(S2Polyline polyline, Options options = Options()) {
96     initialize(polyline, null, null, options);
97   }
98 
99   alias LabelSetIds = LabelSetId[];
100 
101   /**
102    * Specifies that a polyline should be constructed using the given options,
103    * and that any labels attached to the input edges should be returned in
104    * "label_set_ids" and "label_set_lexicion".
105    *
106    * The labels associated with the edge "polyline.vertex({j, j+1})" can be
107    * retrieved as follows:
108    *
109    *   for (int32 label : label_set_lexicon.id_set(label_set_ids[j])) {...}
110    */
111   this(S2Polyline polyline, LabelSetIds* label_set_ids,
112       IdSetLexicon* label_set_lexicon,
113       Options options = Options()) {
114     initialize(polyline, label_set_ids, label_set_lexicon, options);
115   }
116 
117   /// Layer interface:
118   override
119   GraphOptions graphOptions() const {
120     // Remove edges that collapse to a single vertex, but keep duplicate and
121     // sibling edges, since merging duplicates or discarding siblings can make
122     // it impossible to assemble the edges into a single polyline.
123     return new GraphOptions(_options.edgeType(), DegenerateEdges.DISCARD,
124         DuplicateEdges.KEEP, SiblingPairs.KEEP);
125   }
126 
127   override
128   void build(in Graph g, ref S2Error error) {
129     if (g.numEdges() == 0) {
130       _polyline.initialize(new S2Point[0]);
131       return;
132     }
133     Graph.EdgePolyline[] edge_polylines = g.getPolylines(Graph.PolylineType.WALK);
134     if (edge_polylines.length != 1) {
135       error.initialize(S2Error.Code.BUILDER_EDGES_DO_NOT_FORM_POLYLINE,
136           "Input edges cannot be assembled into polyline");
137       return;
138     }
139     const(Graph.EdgePolyline) edge_polyline = edge_polylines[0];
140     S2Point[] vertices;  // Temporary storage for vertices.
141     vertices.reserve(edge_polyline.length);
142     vertices ~= g.vertex(g.edge(edge_polyline[0])[0]);
143     foreach (Graph.EdgeId e; edge_polyline) {
144       vertices ~= g.vertex(g.edge(e)[1]);
145     }
146     if (_labelSetIds) {
147       auto fetcher = new Graph.LabelFetcher(g, _options.edgeType());
148       Label[] labels;  // Temporary storage for labels.
149       (*_labelSetIds).reserve(edge_polyline.length);
150       foreach (Graph.EdgeId e; edge_polyline) {
151         fetcher.fetch(e, labels);
152         (*_labelSetIds) ~= _labelSetLexicon.add(labels);
153       }
154     }
155     _polyline.initialize(vertices);
156     if (_options.validate()) {
157       _polyline.findValidationError(error);
158     }
159   }
160 
161 private:
162   void initialize(S2Polyline polyline, LabelSetIds* label_set_ids,
163       IdSetLexicon* label_set_lexicon, Options options)
164   in {
165     assert((label_set_ids is null) == (label_set_lexicon is null));
166   } do {
167     _polyline = polyline;
168     _labelSetIds = label_set_ids;
169     _labelSetLexicon = label_set_lexicon;
170     _options = options;
171 
172     if (_options.validate()) {
173       _polyline.setS2debugOverride(S2Debug.DISABLE);
174     }
175   }
176 
177   S2Polyline _polyline;
178   LabelSetIds* _labelSetIds;
179   IdSetLexicon* _labelSetLexicon;
180   Options _options;
181 }
182 
183 /// Like S2PolylineLayer, but adds the polyline to a MutableS2ShapeIndex (if the
184 /// polyline is non-empty).
185 class IndexedS2PolylineLayer : Layer {
186 public:
187   alias Options = S2PolylineLayer.Options;
188 
189   this(MutableS2ShapeIndex index, Options options = Options()) {
190     _index = index;
191     _polyline = new S2Polyline();
192     _layer = new S2PolylineLayer(_polyline, options);
193   }
194 
195   override
196   GraphOptions graphOptions() const {
197     return _layer.graphOptions();
198   }
199 
200   override
201   void build(in Graph g, ref S2Error error) {
202     _layer.build(g, error);
203     if (error.ok() && _polyline.numVertices() > 0) {
204       _index.add(new S2Polyline.Shape(_polyline));
205     }
206   }
207 
208 private:
209   MutableS2ShapeIndex _index;
210   S2Polyline _polyline;
211   S2PolylineLayer _layer;
212 }