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 }