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.graph; 20 21 import s2.s2builder; 22 import s2.s2error; 23 import s2.s2point; 24 import s2.id_set_lexicon; 25 26 import std.algorithm; 27 import std.exception; 28 import std.range; 29 import std.typecons; 30 31 /** 32 * An S2Builder::Graph represents a collection of snapped edges that is passed 33 * to a Layer for assembly. (Example layers include polygons, polylines, and 34 * polygon meshes.) The Graph object does not own any of its underlying data; 35 * it is simply a view of data that is stored elsewhere. You will only 36 * need this interface if you want to implement a new Layer subtype. 37 * 38 * The graph consists of vertices and directed edges. Vertices are numbered 39 * sequentially starting from zero. An edge is represented as a pair of 40 * vertex ids. The edges are sorted in lexicographic order, therefore all of 41 * the outgoing edges from a particular vertex form a contiguous range. 42 * 43 * S2Builder::Graph is movable and copyable. Note that although this class 44 * does not own the underlying vertex and edge data, S2Builder guarantees that 45 * all Graph objects passed to S2Builder::Layer::Build() methods will remain 46 * valid until all layers have been built. 47 * 48 * TODO(ericv): Consider pulling out the methods that are helper functions for 49 * Layer implementations (such as GetDirectedLoops) into s2builderutil_graph.h. 50 */ 51 class Graph { 52 public: 53 // Identifies a vertex in the graph. Vertices are numbered sequentially 54 // starting from zero. 55 alias VertexId = int; 56 57 // Defines an edge as an (origin, destination) vertex pair. 58 alias Edge = Tuple!(VertexId, VertexId); 59 60 // Identifies an edge in the graph. Edges are numbered sequentially 61 // starting from zero. 62 alias EdgeId = int; 63 64 alias EdgeType = S2Builder.EdgeType; 65 66 // Identifies an S2Builder *input* edge (before snapping). 67 alias InputEdgeId = S2Builder.InputEdgeId; 68 69 // Identifies a set of S2Builder input edges. 70 alias InputEdgeIdSetId = S2Builder.InputEdgeIdSetId; 71 72 // Identifies a set of edge labels. 73 alias LabelSetId = S2Builder.LabelSetId; 74 75 // Determines whether a degenerate polygon is empty or full. 76 alias IsFullPolygonPredicate = S2Builder.IsFullPolygonPredicate; 77 78 alias SiblingPairs = GraphOptions.SiblingPairs; 79 alias DegenerateEdges = GraphOptions.DegenerateEdges; 80 alias DuplicateEdges = GraphOptions.DuplicateEdges; 81 82 /// The default constructor exists only for the benefit of STL containers. 83 /// The graph must be initialized (using the assignment operator) before it 84 /// is used. 85 this() { 86 _options = new GraphOptions(); 87 _numVertices = -1; 88 _vertices = null; 89 _edges = null; 90 _inputEdgeIdSetIds = null; 91 _inputEdgeIdSetLexicon = null; 92 _labelSetIds = null; 93 _labelSetLexicon = null; 94 } 95 96 /** 97 * Note that most of the parameters are passed by const reference and must 98 * exist for the duration of the Graph object. Notes on parameters: 99 * "options": 100 * - the GraphOptions used to build the Graph. In some cases these 101 * can be different than the options provided by the Layer. 102 * "vertices": 103 * - a vector of S2Points indexed by VertexId. 104 * "edges": 105 * - a vector of VertexId pairs (sorted in lexicographic order) 106 * indexed by EdgeId. 107 * "input_edge_id_set_ids": 108 * - a vector indexed by EdgeId that allows access to the set of 109 * InputEdgeIds that were mapped to the given edge, by looking up the 110 * returned value (an InputEdgeIdSetId) in "input_edge_id_set_lexicon". 111 * "input_edge_id_set_lexicon": 112 * - a class that maps an InputEdgeIdSetId to a set of InputEdgeIds. 113 * "label_set_ids": 114 * - a vector indexed by InputEdgeId that allows access to the set of 115 * labels that were attached to the given input edge, by looking up the 116 * returned value (a LabelSetId) in the "label_set_lexicon". 117 * "label_set_lexicon": 118 * - a class that maps a LabelSetId to a set of S2Builder::Labels. 119 * "is_full_polygon_predicate": 120 * - a predicate called to determine whether a graph consisting only of 121 * polygon degeneracies represents the empty polygon or the full polygon 122 * (see s2builder.h for details). 123 */ 124 this( 125 in GraphOptions options, 126 in S2Point[] vertices, 127 in Edge[] edges, 128 in InputEdgeIdSetId[] input_edge_id_set_ids, 129 in IdSetLexicon input_edge_id_set_lexicon, 130 in LabelSetId[] label_set_ids, 131 in IdSetLexicon label_set_lexicon, 132 // TODO(ericv/hagzonal): Fix st_lib and remove default parameter. 133 IsFullPolygonPredicate is_full_polygon_predicate = IsFullPolygonPredicate()) { 134 _options = options; 135 _numVertices = cast(int) vertices.length; 136 _vertices = vertices; 137 _edges = edges; 138 _inputEdgeIdSetIds = input_edge_id_set_ids; 139 _inputEdgeIdSetLexicon = input_edge_id_set_lexicon; 140 _labelSetIds = label_set_ids; 141 _labelSetLexicon = label_set_lexicon; 142 _isFullPolygonPredicate = is_full_polygon_predicate; 143 debug enforce(isSorted(edges)); 144 } 145 146 const(GraphOptions) options() const { 147 return _options; 148 } 149 150 /// Returns the number of vertices in the graph. 151 VertexId numVertices() const { 152 return _numVertices; // vertices_.size() requires division by 24. 153 } 154 155 /// Returns the vertex at the given index. 156 const(S2Point) vertex(VertexId v) const { 157 return vertices()[v]; 158 } 159 160 /// Returns the entire set of vertices. 161 const(S2Point[]) vertices() const { 162 return _vertices; 163 } 164 165 /// Returns the total number of edges in the graph. 166 EdgeId numEdges() const { 167 return cast(EdgeId)(edges().length); 168 } 169 170 /// Returns the endpoints of the given edge (as vertex indices). 171 Edge edge(EdgeId e) const { 172 return edges()[e]; 173 } 174 175 /// Returns the entire set of edges. 176 const(Edge[]) edges() const { 177 return _edges; 178 } 179 180 /// Given an edge (src, dst), returns the reverse edge (dst, src). 181 static Edge reverse(in Edge e) { 182 return Edge(e[1], e[0]); 183 } 184 185 /** 186 * Returns a vector of edge ids sorted in lexicographic order by 187 * (destination, origin). All of the incoming edges to each vertex form a 188 * contiguous subrange of this ordering. 189 */ 190 EdgeId[] getInEdgeIds() const { 191 EdgeId[] in_edge_ids = new EdgeId[](numEdges()); 192 for (EdgeId i = 0; i < numEdges(); i++) in_edge_ids[i] = i; 193 sort!((ai, bi) { 194 return stableLessThan(reverse(edge(ai)), reverse(edge(bi)), ai, bi); 195 })(in_edge_ids); 196 return in_edge_ids; 197 } 198 199 /** 200 * Given a graph such that every directed edge has a sibling, returns a map 201 * from EdgeId to the sibling EdgeId. This method is identical to 202 * GetInEdgeIds() except that (1) it requires edges to have siblings, and 203 * (2) undirected degenerate edges are grouped together in pairs such that 204 * one edge is the sibling of the other. Handles duplicate edges correctly 205 * and is also consistent with GetLeftTurnMap(). 206 * 207 * REQUIRES: An option is chosen that guarantees sibling pairs: 208 * (options.sibling_pairs() == { REQUIRE, CREATE } || 209 * options.edge_type() == UNDIRECTED) 210 */ 211 EdgeId[] getSiblingMap() const { 212 EdgeId[] in_edge_ids = getInEdgeIds(); 213 makeSiblingMap(in_edge_ids); 214 return in_edge_ids; 215 } 216 217 /** 218 * Like GetSiblingMap(), but constructs the map starting from the vector of 219 * incoming edge ids returned by GetInEdgeIds(). (This operation is a no-op 220 * except unless undirected degenerate edges are present, in which case such 221 * edges are grouped together in pairs to satisfy the requirement that every 222 * edge must have a sibling edge.) 223 */ 224 void makeSiblingMap(ref EdgeId[] in_edge_ids) const { 225 debug enforce( 226 _options.siblingPairs() == SiblingPairs.REQUIRE 227 || _options.siblingPairs() == SiblingPairs.CREATE 228 || _options.edgeType() == EdgeType.UNDIRECTED); 229 for (EdgeId e = 0; e < numEdges(); ++e) { 230 debug enforce(edge(e) == reverse(edge(in_edge_ids[e]))); 231 } 232 if (_options.edgeType() == EdgeType.DIRECTED) return; 233 if (_options.degenerateEdges() == DegenerateEdges.DISCARD) return; 234 235 for (EdgeId e = 0; e < numEdges(); ++e) { 236 VertexId v = edge(e)[0]; 237 if (edge(e)[1] == v) { 238 debug enforce(e + 1 < numEdges()); 239 debug enforce(edge(e + 1)[0] == v); 240 debug enforce(edge(e + 1)[1] == v); 241 debug enforce(in_edge_ids[e] == e); 242 debug enforce(in_edge_ids[e + 1] == e + 1); 243 in_edge_ids[e] = e + 1; 244 in_edge_ids[e + 1] = e; 245 ++e; 246 } 247 } 248 } 249 250 /// A helper class for VertexOutMap that represents the outgoing edges 251 /// from a given vertex. 252 // class VertexOutEdges { 253 // public: 254 // const Edge* begin() const { return begin_; } 255 // const Edge* end() const { return end_; } 256 // size_t size() const { return end_ - begin_; } 257 // private: 258 // friend class VertexOutMap; 259 // VertexOutEdges(const Edge* begin, const Edge* end); 260 // const Edge* begin_; 261 // const Edge* end_; 262 // }; 263 264 /// A helper class for VertexOutMap that represents the outgoing edge *ids* 265 /// from a given vertex. 266 // class VertexOutEdgeIds { 267 // public: 268 // // An iterator over a range of edge ids (like boost::counting_iterator). 269 // class Iterator { 270 // public: 271 // explicit Iterator(EdgeId id) : id_(id) {} 272 // const EdgeId& operator*() const { return id_; } 273 // Iterator& operator++() { ++id_; return *this; } 274 // Iterator operator++(int) { return Iterator(id_++); } 275 // size_t operator-(const Iterator& x) const { return id_ - x.id_; } 276 // bool operator==(const Iterator& x) const { return id_ == x.id_; } 277 // bool operator!=(const Iterator& x) const { return id_ != x.id_; } 278 // private: 279 // EdgeId id_; 280 // }; 281 // Iterator begin() const { return Iterator(begin_); } 282 // Iterator end() const { return Iterator(end_); } 283 // size_t size() const { return end_ - begin_; } 284 // private: 285 // friend class VertexOutMap; 286 // VertexOutEdgeIds(EdgeId begin, EdgeId end); 287 // EdgeId begin_, end_; 288 // }; 289 290 /** 291 * A class that maps vertices to their outgoing edge ids. Example usage: 292 * VertexOutMap out(g); 293 * for (Graph::EdgeId e : out.edge_ids(v)) { ... } 294 * for (const Graph::Edge& edge : out.edges(v)) { ... } 295 */ 296 static class VertexOutMap { 297 public: 298 this(const(Graph) g) { 299 _edges = g.edges(); 300 _edgeBegins = new EdgeId[](g.numVertices() + 1); 301 EdgeId e = 0; 302 for (VertexId v = 0; v <= g.numVertices(); ++v) { 303 while (e < g.numEdges() && g.edge(e)[0] < v) ++e; 304 _edgeBegins[v] = e; 305 } 306 } 307 308 int degree(VertexId v) const { 309 return cast(int) edgeIds(v).length; 310 } 311 312 const(Edge[]) edges(VertexId v) const { 313 return _edges[_edgeBegins[v] .. _edgeBegins[v + 1]]; 314 } 315 316 // Returns a range of EdgeId from the given VertexId to the next. 317 auto edgeIds(VertexId v) const { 318 return iota(_edgeBegins[v], _edgeBegins[v + 1]); 319 } 320 321 /// Returns a range of Edge (or edge ids) between a specific pair of vertices. 322 auto edges(VertexId v0, VertexId v1) const { 323 return assumeSorted(_edges[_edgeBegins[v0] .. _edgeBegins[v0 + 1]]) 324 .equalRange(Edge(v0, v1)); 325 } 326 327 EdgeId[] edgeIds(VertexId v0, VertexId v1) const { 328 auto trisectRanges = assumeSorted(_edges[_edgeBegins[v0] .. _edgeBegins[v0 + 1]]) 329 .trisect(Edge(v0, v1)); 330 331 // Start past the ids that are less than those that match Edge(v0, v1). 332 EdgeId startId = _edgeBegins[v0] + cast(int) trisectRanges[0].length; 333 return iota(startId, startId + cast(int) trisectRanges[1].length).array; 334 } 335 336 private: 337 const(Edge[]) _edges; 338 EdgeId[] _edgeBegins; 339 } 340 341 // A helper class for VertexInMap that represents the incoming edge *ids* 342 // to a given vertex. 343 // class VertexInEdgeIds { 344 // public: 345 // const EdgeId* begin() const { return begin_; } 346 // const EdgeId* end() const { return end_; } 347 // size_t size() const { return end_ - begin_; } 348 349 // private: 350 // friend class VertexInMap; 351 // VertexInEdgeIds(const EdgeId* begin, const EdgeId* end); 352 // const EdgeId* begin_; 353 // const EdgeId* end_; 354 // } 355 356 // A class that maps vertices to their incoming edge ids. Example usage: 357 // VertexInMap in(g); 358 // for (Graph::EdgeId e : in.edge_ids(v)) { ... } 359 static class VertexInMap { 360 public: 361 this(in Graph g) { 362 _inEdgeIds = g.getInEdgeIds(); 363 _inEdgeBegins = new EdgeId[](g.numVertices() + 1); 364 EdgeId e = 0; 365 for (VertexId v = 0; v <= g.numVertices(); ++v) { 366 while (e < g.numEdges() && g.edge(_inEdgeIds[e])[1] < v) ++e; 367 _inEdgeBegins[v] = e; 368 } 369 } 370 371 int degree(VertexId v) const { 372 return cast(int)(edgeIds(v).length); 373 } 374 375 const(EdgeId[]) edgeIds(VertexId v) const { 376 return _inEdgeIds[_inEdgeBegins[v] .. _inEdgeBegins[v + 1]]; 377 } 378 379 // Returns a sorted vector of all incoming edges (see GetInEdgeIds). 380 const(EdgeId[]) inEdgeIds() const { 381 return _inEdgeIds; 382 } 383 384 private: 385 EdgeId[] _inEdgeIds; 386 EdgeId[] _inEdgeBegins; 387 } 388 389 /// Defines a value larger than any valid InputEdgeId. 390 static immutable InputEdgeId MAX_INPUT_EDGE_ID = InputEdgeId.max; 391 392 /// The following value of InputEdgeId means that an edge does not 393 /// corresponds to any input edge. 394 static immutable InputEdgeId NO_INPUT_EDGE_ID = MAX_INPUT_EDGE_ID - 1; 395 396 /** 397 * Returns the set of input edge ids that were snapped to the given 398 * edge. ("Input edge ids" are assigned to input edges sequentially in 399 * the order they are added to the builder.) For example, if input 400 * edges 2 and 17 were snapped to edge 12, then input_edge_ids(12) 401 * returns a set containing the numbers 2 and 17. Example usage: 402 * 403 * for (InputEdgeId input_edge_id : g.input_edge_ids(e)) { ... } 404 * 405 * Please note the following: 406 * 407 * - When edge chains are simplified, the simplified edge is assigned all 408 * the input edge ids associated with edges of the chain. 409 * 410 * - Edges can also have multiple input edge ids due to edge merging 411 * (if DuplicateEdges::MERGE is specified). 412 * 413 * - Siblings edges automatically created by EdgeType::UNDIRECTED or 414 * SiblingPairs::CREATE have an empty set of input edge ids. (However 415 * you can use a LabelFetcher to retrieve the set of labels associated 416 * with both edges of a given sibling pair.) 417 */ 418 const(IdSetLexicon.IdSet) inputEdgeIds(EdgeId e) const { 419 return inputEdgeIdSetLexicon().idSet(inputEdgeIdSetIds()[e]); 420 } 421 422 /** 423 * Low-level method that returns an integer representing the entire set of 424 * input edge ids that were snapped to the given edge. The elements of the 425 * IdSet can be accessed using input_edge_id_set_lexicon(). 426 */ 427 InputEdgeIdSetId inputEdgeIdSetId(EdgeId e) const { 428 return inputEdgeIdSetIds()[e]; 429 } 430 431 /// Low-level method that returns a vector where each element represents the 432 /// set of input edge ids that were snapped to a particular output edge. 433 const(InputEdgeIdSetId[]) inputEdgeIdSetIds() const { 434 return _inputEdgeIdSetIds; 435 } 436 437 /// Returns a mapping from an InputEdgeIdSetId to a set of input edge ids. 438 const(IdSetLexicon) inputEdgeIdSetLexicon() const { 439 return _inputEdgeIdSetLexicon; 440 } 441 442 /** 443 * Returns the minimum input edge id that was snapped to this edge, or -1 if 444 * no input edges were snapped (see SiblingPairs::CREATE). This is 445 * useful for layers that wish to preserve the input edge ordering as much 446 * as possible (e.g., to ensure idempotency). 447 */ 448 InputEdgeId minInputEdgeId(EdgeId e) const { 449 const(IdSetLexicon.IdSet) id_set = inputEdgeIds(e); 450 return (id_set.length == 0) ? NO_INPUT_EDGE_ID : id_set[0]; 451 } 452 453 /// Returns a vector containing the minimum input edge id for every edge. 454 /// If an edge has no input ids, kNoInputEdgeId is used. 455 InputEdgeId[] getMinInputEdgeIds() const { 456 auto min_input_ids = new InputEdgeId[numEdges()]; 457 for (EdgeId e = 0; e < numEdges(); ++e) { 458 min_input_ids[e] = minInputEdgeId(e); 459 } 460 return min_input_ids; 461 } 462 463 /// Returns a vector of EdgeIds sorted by minimum input edge id. This is an 464 /// approximation of the input edge ordering. 465 EdgeId[] getInputEdgeOrder(in InputEdgeId[] input_ids) const { 466 auto order = new EdgeId[input_ids.length]; 467 for (EdgeId i = 0; i < order.length; i++) order[i] = i; 468 // Comparison function ensures sort is stable. 469 sort!((EdgeId a, EdgeId b) => Edge(input_ids[a], a) < Edge(input_ids[b], b))(order); 470 return order; 471 } 472 473 /** 474 * Convenience class to return the set of labels associated with a given 475 * graph edge. Note that due to snapping, one graph edge may correspond to 476 * several different input edges and will have all of their labels. 477 * This class is the preferred way to retrieve edge labels. 478 * 479 * The reason this is a class rather than a graph method is because for 480 * undirected edges, we need to fetch the labels associated with both 481 * siblings. This is because only the original edge of the sibling pair has 482 * labels; the automatically generated sibling edge does not. 483 */ 484 static class LabelFetcher { 485 public: 486 /** 487 * Prepares to fetch labels associated with the given edge type. For 488 * EdgeType::UNDIRECTED, labels associated with both edges of the sibling 489 * pair will be returned. "edge_type" is a parameter (rather than using 490 * g.options().edge_type()) so that clients can explicitly control whether 491 * labels from one or both siblings are returned. 492 */ 493 this(in Graph g, EdgeType edge_type) { 494 _g = g; 495 _edgeType = edge_type; 496 if (edge_type == EdgeType.UNDIRECTED) _siblingMap = g.getSiblingMap(); 497 } 498 499 /** 500 * Returns the set of labels associated with edge "e" (and also the labels 501 * associated with the sibling of "e" if edge_type() is UNDIRECTED). 502 * Labels are sorted and duplicate labels are automatically removed. 503 * 504 * This method uses an output parameter rather than returning by value in 505 * order to avoid allocating a new vector on every call to this method. 506 */ 507 void fetch(EdgeId e, ref S2Builder.Label[] labels) { 508 labels.length = 0; 509 foreach (InputEdgeId input_edge_id; _g.inputEdgeIds(e)) { 510 foreach (S2Builder.Label label; _g.labels(input_edge_id)) { 511 labels ~= label; 512 } 513 } 514 if (_edgeType == EdgeType.UNDIRECTED) { 515 foreach (InputEdgeId input_edge_id; _g.inputEdgeIds(_siblingMap[e])) { 516 foreach (S2Builder.Label label; _g.labels(input_edge_id)) { 517 labels ~= label; 518 } 519 } 520 } 521 if (labels.length > 1) { 522 labels = sort(labels).uniq.array; 523 } 524 } 525 526 private: 527 const(Graph) _g; 528 EdgeType _edgeType; 529 EdgeId[] _siblingMap; 530 } 531 532 /// Returns the set of labels associated with a given input edge. Example: 533 /// for (Label label : g.labels(input_edge_id)) { ... } 534 const(IdSetLexicon.IdSet) labels(InputEdgeId id) const { 535 return labelSetLexicon().idSet(labelSetIds()[id]); 536 } 537 538 /** 539 * Low-level method that returns an integer representing the set of 540 * labels associated with a given input edge. The elements of 541 * the IdSet can be accessed using label_set_lexicon(). 542 */ 543 LabelSetId labelSetId(InputEdgeId e) const { 544 return _labelSetIds[e]; 545 } 546 547 /// Low-level method that returns a vector where each element represents the 548 /// set of labels associated with a particular output edge. 549 const(LabelSetId[]) labelSetIds() const { 550 return _labelSetIds; 551 } 552 553 /// Returns a mapping from a LabelSetId to a set of labels. 554 const(IdSetLexicon) labelSetLexicon() const { 555 return _labelSetLexicon; 556 } 557 558 /** 559 * Convenience method that calls is_full_polygon_predicate() to determine 560 * whether a graph that consists only of polygon degeneracies represents the 561 * empty polygon or the full polygon (see s2builder.h for details). 562 */ 563 bool isFullPolygon(ref S2Error error) const { 564 return _isFullPolygonPredicate(this, error); 565 } 566 567 /** 568 * Returns a method that determines whether a graph that consists only of 569 * polygon degeneracies represents the empty polygon or the full polygon 570 * (see s2builder.h for details). 571 */ 572 const(IsFullPolygonPredicate) isFullPolygonPredicate() const { 573 return _isFullPolygonPredicate; 574 } 575 576 /** 577 * Returns a map "m" that maps each edge e=(v0,v1) to the following outgoing 578 * edge around "v1" in clockwise order. \(This corresponds to making a "left 579 * turn" at the vertex.\) By starting at a given edge and making only left 580 * turns, you can construct a loop whose interior does not contain any edges 581 * in the same connected component. 582 * 583 * If the incoming and outgoing edges around a vertex do not alternate 584 * perfectly \(e.g., there are two incoming edges in a row\), then adjacent 585 * \(incoming, outgoing\) pairs are repeatedly matched and removed. This is 586 * similar to finding matching parentheses in a string such as "\(\(\)\(\)\)\(\)". 587 * 588 * For sibling edge pairs, the incoming edge is assumed to immediately 589 * follow the outgoing edge in clockwise order. Thus a left turn is made 590 * from an edge to its sibling only if there are no other outgoing edges. 591 * With respect to the parentheses analogy, a sibling pair is "\(\)". 592 * Similarly, if there are multiple copies of a sibling edge pair then the 593 * duplicate incoming and outgoing edges are sorted in alternating order 594 * \(e.g., "\)\(\)\("\). 595 * 596 * Degenerate edges \(edges from a vertex to itself\) are treated as loops 597 * consisting of a single edge. This avoids the problem of deciding the 598 * connectivity and ordering of such edges when they share a vertex with 599 * other edges \(possibly including other degenerate edges\). 600 * 601 * If it is not possible to make a left turn from every input edge, this 602 * method returns false and sets "error" appropriately. In this situation 603 * the left turn map is still valid except that any incoming edge where it 604 * is not possible to make a left turn will have its entry set to -1. 605 * 606 * "in_edge_ids" should be equal to GetInEdgeIds() or GetSiblingMap(). 607 */ 608 bool getLeftTurnMap( 609 in EdgeId[] in_edge_ids, ref EdgeId[] left_turn_map, ref S2Error error) const { 610 left_turn_map.length = numEdges(); 611 left_turn_map[] = -1; 612 if (numEdges() == 0) return true; 613 614 // Declare vectors outside the loop to avoid reallocating them each time. 615 VertexEdge[] v0_edges; 616 EdgeId[] e_in; 617 EdgeId[] e_out; 618 619 // Walk through the two sorted arrays of edges (outgoing and incoming) and 620 // gather all the edges incident to each vertex. Then we sort those edges 621 // and add an entry to the left turn map from each incoming edge to the 622 // immediately following outgoing edge in clockwise order. 623 int outId = 0; 624 int inId = 0; 625 Edge out_edge = edge(outId); 626 Edge in_edge = edge(in_edge_ids[inId]); 627 auto sentinel = Edge(numVertices(), numVertices()); 628 Edge min_edge = min(out_edge, reverse(in_edge)); 629 while (min_edge != sentinel) { 630 // Gather all incoming and outgoing edges around vertex "v0". 631 VertexId v0 = min_edge[0]; 632 for (; min_edge[0] == v0; min_edge = min(out_edge, reverse(in_edge))) { 633 VertexId v1 = min_edge[1]; 634 // Count the number of copies of "min_edge" in each direction. 635 int out_begin = outId; 636 int in_begin = inId; 637 while (out_edge == min_edge) { 638 out_edge = (++outId == numEdges()) ? sentinel : Edge(edge(outId)); 639 } 640 while (reverse(in_edge) == min_edge) { 641 in_edge = (++inId == numEdges()) ? sentinel : Edge(edge(in_edge_ids[inId])); 642 } 643 if (v0 != v1) { 644 addVertexEdges(out_begin, outId, in_begin, inId, v1, v0_edges); 645 } else { 646 // Each degenerate edge becomes its own loop. 647 for (; in_begin < inId; ++in_begin) { 648 left_turn_map[in_begin] = in_begin; 649 } 650 } 651 } 652 if (v0_edges.empty()) continue; 653 654 // Sort the edges in clockwise order around "v0". 655 VertexId min_endpoint = v0_edges.front().endpoint; 656 sort!((in VertexEdge a, in VertexEdge b) { 657 import s2.s2predicates : orderedCCW; 658 if (a.endpoint == b.endpoint) return a.rank < b.rank; 659 if (a.endpoint == min_endpoint) return true; 660 if (b.endpoint == min_endpoint) return false; 661 return !orderedCCW( 662 vertex(a.endpoint), vertex(b.endpoint), vertex(min_endpoint), vertex(v0)); 663 })(v0_edges); 664 // Match incoming with outgoing edges. We do this by keeping a stack of 665 // unmatched incoming edges. We also keep a stack of outgoing edges with 666 // no previous incoming edge, and match these at the end by wrapping 667 // around circularly to the start of the edge ordering. 668 foreach (const VertexEdge e; v0_edges) { 669 if (e.incoming) { 670 e_in ~= in_edge_ids[e.index]; 671 } else if (!e_in.empty()) { 672 left_turn_map[e_in.back()] = e.index; 673 e_in.popBack(); 674 } else { 675 e_out ~= e.index; // Matched below. 676 } 677 } 678 // Pair up additional edges using the fact that the ordering is circular. 679 .reverse(e_out); 680 for (; !e_out.empty() && !e_in.empty(); e_out.popBack(), e_in.popBack()) { 681 left_turn_map[e_in.back()] = e_out.back(); 682 } 683 // We only need to process unmatched incoming edges, since we are only 684 // responsible for creating left turn map entries for those edges. 685 if (!e_in.empty() && error.ok()) { 686 error.initialize(S2Error.Code.BUILDER_EDGES_DO_NOT_FORM_LOOPS, 687 "Given edges do not form loops (indegree != outdegree)"); 688 } 689 e_in.length = 0; 690 e_out.length = 0; 691 v0_edges.length = 0; 692 } 693 return error.ok(); 694 } 695 696 /** 697 * Rotates the edges of "loop" if necessary so that the edge(s) with the 698 * largest input edge ids are last. This ensures that when an output loop 699 * is equivalent to an input loop, their cyclic edge orders are the same. 700 * "min_input_ids" is the output of GetMinInputEdgeIds(). 701 */ 702 static void canonicalizeLoopOrder(in InputEdgeId[] min_input_ids, ref EdgeId[] loop) { 703 if (loop.empty()) return; 704 // Find the position of the element with the highest input edge id. If 705 // there are multiple such elements together (i.e., the edge was split 706 // into several pieces by snapping it to several vertices), then we choose 707 // the last such position in cyclic order (this attempts to preserve the 708 // original loop order even when new vertices are added). For example, if 709 // the input edge id sequence is (7, 7, 4, 5, 6, 7) then we would rotate 710 // it to obtain (4, 5, 6, 7, 7, 7). 711 712 // The reason that we put the highest-numbered edge last, rather than the 713 // lowest-numbered edge first, is that S2Loop::Invert() reverses the loop 714 // edge order *except* for the last edge. For example, the loop ABCD (with 715 // edges AB, BC, CD, DA) becomes DCBA (with edges DC, CB, BA, AD). Note 716 // that the last edge is the same except for its direction (DA vs. AD). 717 // This has the advantage that if an undirected loop is assembled with the 718 // wrong orientation and later inverted (e.g. by S2Polygon::InitOriented), 719 // we still end up preserving the original cyclic vertex order. 720 int pos = 0; 721 bool saw_gap = false; 722 for (int i = 1; i < loop.length; ++i) { 723 int cmp = min_input_ids[loop[i]] - min_input_ids[loop[pos]]; 724 if (cmp < 0) { 725 saw_gap = true; 726 } else if (cmp > 0 || !saw_gap) { 727 pos = i; 728 saw_gap = false; 729 } 730 } 731 if (++pos == loop.length) pos = 0; // Convert loop end to loop start. 732 bringToFront(loop[0 .. pos], loop[pos .. $]); 733 } 734 735 /** 736 * Sorts the given edge chains (i.e., loops or polylines) by the minimum 737 * input edge id of each chains's first edge. This ensures that when the 738 * output consists of multiple loops or polylines, they are sorted in the 739 * same order as they were provided in the input. 740 */ 741 static void canonicalizeVectorOrder(in InputEdgeId[] min_input_ids, ref EdgeId[][] chains) { 742 sort!((in EdgeId[] a, in EdgeId[] b) { 743 return min_input_ids[a[0]] < min_input_ids[b[0]]; 744 })(chains); 745 } 746 747 /// A loop consisting of a sequence of edges. 748 alias EdgeLoop = EdgeId[]; 749 750 /** 751 * Indicates whether loops should be simple cycles (no repeated vertices) or 752 * circuits (which allow repeated vertices but not repeated edges). In 753 * terms of how the loops are built, this corresponds to closing off a loop 754 * at the first repeated vertex vs. the first repeated edge. 755 */ 756 enum LoopType { SIMPLE, CIRCUIT } 757 758 /** 759 * Builds loops from a set of directed edges, turning left at each vertex 760 * until either a repeated vertex (for LoopType::SIMPLE) or a repeated edge 761 * (for LoopType::CIRCUIT) is found. (Use LoopType::SIMPLE if you intend to 762 * construct an S2Loop.) 763 * 764 * Each loop is represented as a sequence of edges. The edge ordering and 765 * loop ordering are automatically canonicalized in order to preserve the 766 * input ordering as much as possible. Loops are non-crossing provided that 767 * the graph contains no crossing edges. If some edges cannot be turned 768 * into loops, returns false and sets "error" appropriately. 769 * 770 * If any degenerate edges are present, then each such edge is treated as a 771 * separate loop. This is mainly useful in conjunction with 772 * options.degenerate_edges() == DISCARD_EXCESS, in order to build polygons 773 * that preserve degenerate geometry. 774 * 775 * REQUIRES: options.degenerate_edges() == {DISCARD, DISCARD_EXCESS} 776 * REQUIRES: options.edge_type() == DIRECTED 777 */ 778 bool getDirectedLoops(LoopType loop_type, ref EdgeLoop[] loops, ref S2Error error) const 779 in { 780 assert(_options.degenerateEdges() == DegenerateEdges.DISCARD 781 || _options.degenerateEdges() == DegenerateEdges.DISCARD_EXCESS); 782 assert(_options.edgeType() == EdgeType.DIRECTED); 783 } do { 784 EdgeId[] left_turn_map; 785 if (!getLeftTurnMap(getInEdgeIds(), left_turn_map, error)) return false; 786 InputEdgeId[] min_input_ids = getMinInputEdgeIds(); 787 788 // If we are breaking loops at repeated vertices, we maintain a map from 789 // VertexId to its position in "path". 790 int[] path_index; 791 if (loop_type == LoopType.SIMPLE) { 792 path_index.length = numVertices(); 793 fill(path_index, -1); 794 } 795 796 // Visit edges in arbitrary order, and try to build a loop from each edge. 797 EdgeId[] path; 798 for (EdgeId start = 0; start < numEdges(); ++start) { 799 if (left_turn_map[start] < 0) continue; 800 801 // Build a loop by making left turns at each vertex until we return to 802 // "start". We use "left_turn_map" to keep track of which edges have 803 // already been visited by setting its entries to -1 as we go along. If 804 // we are building vertex cycles, then whenever we encounter a vertex that 805 // is already part of the path, we "peel off" a loop by removing those 806 // edges from the path so far. 807 for (EdgeId e = start, next; left_turn_map[e] >= 0; e = next) { 808 path ~= e; 809 next = left_turn_map[e]; 810 left_turn_map[e] = -1; 811 if (loop_type == LoopType.SIMPLE) { 812 path_index[edge(e)[0]] = cast(int) path.length - 1; 813 int loop_start = path_index[edge(e)[1]]; 814 if (loop_start < 0) continue; 815 // Peel off a loop from the path. 816 EdgeId[] loop = path[loop_start .. $]; 817 path.length = loop_start; 818 foreach (EdgeId e2; loop) path_index[edge(e2)[0]] = -1; 819 canonicalizeLoopOrder(min_input_ids, loop); 820 loops ~= loop; 821 } 822 } 823 if (loop_type == LoopType.SIMPLE) { 824 debug enforce(path.empty()); // Invariant. 825 } else { 826 canonicalizeLoopOrder(min_input_ids, path); 827 loops ~= path; 828 path.length = 0; 829 } 830 } 831 canonicalizeVectorOrder(min_input_ids, loops); 832 return true; 833 } 834 835 /** 836 * Builds loops from a set of directed edges, turning left at each vertex 837 * until a repeated edge is found (i.e., LoopType::CIRCUIT). The loops are 838 * further grouped into connected components, where each component consists 839 * of one or more loops connected by shared vertices. 840 * 841 * This method is used to build polygon meshes from directed or undirected 842 * input edges. To convert the output of this method into a mesh, the 843 * client must determine how the loops in different components are related 844 * to each other: for example, several loops from different components may 845 * bound the same region on the sphere, in which case all of those loops are 846 * combined into a single polygon. (See s2shapeutil::BuildPolygonBoundaries 847 * and s2builderutil::LaxPolygonVectorLayer for details.) 848 * 849 * Note that loops may include both edges of a sibling pair. When several 850 * such edges are connected in a chain or a spanning tree, they form a 851 * zero-area "filament". The entire loop may be a filament (i.e., a 852 * degenerate loop with an empty interior), or the loop may have have 853 * non-empty interior with several filaments that extend inside it, or the 854 * loop may consist of several "holes" connected by filaments. These 855 * filaments do not change the interior of any loop, so if you are only 856 * interested in point containment then they can safely be removed by 857 * setting the "degenerate_boundaries" parameter to DISCARD. (They can't be 858 * removed by setting (options.sibling_pairs() == DISCARD) because the two 859 * siblings might belong to different polygons of the mesh.) Note that you 860 * can prevent multiple copies of sibling pairs by specifying 861 * options.duplicate_edges() == MERGE. 862 * 863 * Each loop is represented as a sequence of edges. The edge ordering and 864 * loop ordering are automatically canonicalized in order to preserve the 865 * input ordering as much as possible. Loops are non-crossing provided that 866 * the graph contains no crossing edges. If some edges cannot be turned 867 * into loops, returns false and sets "error" appropriately. 868 * 869 * REQUIRES: options.degenerate_edges() == { DISCARD, DISCARD_EXCESS } 870 * (but requires DISCARD if degenerate_boundaries == DISCARD) 871 * REQUIRES: options.sibling_pairs() == { REQUIRE, CREATE } 872 * [i.e., every edge must have a sibling edge] 873 */ 874 enum DegenerateBoundaries { DISCARD, KEEP } 875 876 alias DirectedComponent = EdgeLoop[]; 877 878 bool getDirectedComponents( 879 DegenerateBoundaries degenerate_boundaries, 880 ref DirectedComponent[] components, 881 ref S2Error error) const 882 in { 883 assert(_options.degenerateEdges() == DegenerateEdges.DISCARD 884 || (_options.degenerateEdges() == DegenerateEdges.DISCARD_EXCESS 885 && degenerate_boundaries == DegenerateBoundaries.KEEP)); 886 assert(_options.siblingPairs() == SiblingPairs.REQUIRE 887 || _options.siblingPairs() == SiblingPairs.CREATE); 888 assert(_options.edgeType() == EdgeType.DIRECTED); // Implied by above. 889 } do { 890 EdgeId[] sibling_map = getInEdgeIds(); 891 EdgeId[] left_turn_map; 892 if (!getLeftTurnMap(sibling_map, left_turn_map, error)) return false; 893 makeSiblingMap(sibling_map); 894 InputEdgeId[] min_input_ids = getMinInputEdgeIds(); 895 EdgeId[] frontier; // Unexplored sibling edges. 896 897 // A map from EdgeId to the position of that edge in "path". Only needed if 898 // degenerate boundaries are being discarded. 899 int[] path_index; 900 if (degenerate_boundaries == DegenerateBoundaries.DISCARD) { 901 path_index.length = numEdges(); 902 path_index[] = -1; 903 } 904 for (EdgeId min_start = 0; min_start < numEdges(); ++min_start) { 905 if (left_turn_map[min_start] < 0) continue; // Already used. 906 907 // Build a connected component by keeping a stack of unexplored siblings 908 // of the edges used so far. 909 DirectedComponent component; 910 frontier ~= min_start; 911 while (!frontier.empty()) { 912 EdgeId start = frontier.back(); 913 frontier.popBack(); 914 if (left_turn_map[start] < 0) continue; // Already used. 915 916 // Build a path by making left turns at each vertex until we return to 917 // "start". Whenever we encounter an edge that is a sibling of an edge 918 // that is already on the path, we "peel off" a loop consisting of any 919 // edges that were between these two edges. 920 EdgeId[] path; 921 for (EdgeId e = start, next; left_turn_map[e] >= 0; e = next) { 922 path ~= e; 923 next = left_turn_map[e]; 924 left_turn_map[e] = -1; 925 // If the sibling hasn't been visited yet, add it to the frontier. 926 EdgeId sibling = sibling_map[e]; 927 if (left_turn_map[sibling] >= 0) { 928 frontier ~= sibling; 929 } 930 if (degenerate_boundaries == DegenerateBoundaries.DISCARD) { 931 path_index[e] = cast(int) path.length - 1; 932 int sibling_index = path_index[sibling]; 933 if (sibling_index < 0) continue; 934 935 // Common special case: the edge and its sibling are adjacent, in 936 // which case we can simply remove them from the path and continue. 937 if (sibling_index == path.length - 2) { 938 path.length = sibling_index; 939 // We don't need to update "path_index" for these two edges 940 // because both edges of the sibling pair have now been used. 941 continue; 942 } 943 // Peel off a loop from the path. 944 // TODO: Resume here. 945 EdgeId[] loop = path[sibling_index + 1 .. $ - 1]; 946 path.length = sibling_index; 947 // Mark the edges that are no longer part of the path. 948 foreach (EdgeId e2; loop) path_index[e2] = -1; 949 canonicalizeLoopOrder(min_input_ids, loop); 950 component ~= loop; 951 } 952 } 953 // Mark the edges that are no longer part of the path. 954 if (degenerate_boundaries == DegenerateBoundaries.DISCARD) { 955 foreach (EdgeId e2; path) path_index[e2] = -1; 956 } 957 canonicalizeLoopOrder(min_input_ids, path); 958 component ~= path; 959 } 960 canonicalizeVectorOrder(min_input_ids, component); 961 components ~= component; 962 } 963 // Sort the components to correspond to the input edge ordering. 964 sort!((in DirectedComponent a, in DirectedComponent b) { 965 return min_input_ids[a[0][0]] < min_input_ids[b[0][0]]; 966 })(components); 967 return true; 968 } 969 970 alias UndirectedComponent = EdgeLoop[][2]; 971 972 /** 973 * Builds loops from a set of undirected edges, turning left at each vertex 974 * until either a repeated vertex (for LoopType::SIMPLE) or a repeated edge 975 * (for LoopType::CIRCUIT) is found. The loops are further grouped into 976 * "components" such that all the loops in a component are connected by 977 * shared vertices. Finally, the loops in each component are divided into 978 * two "complements" such that every edge in one complement is the sibling 979 * of an edge in the other complement. This corresponds to the fact that 980 * given any set of non-crossing undirected loops, there are exactly two 981 * possible interpretations of the region that those loops represent (where 982 * one possibility is the complement of the other). This method does not 983 * attempt to resolve this ambiguity, but instead returns both possibilities 984 * for each connected component and lets the client choose among them. 985 * 986 * This method is used to build single polygons. (Use GetDirectedComponents 987 * to build polygon meshes, even when the input edges are undirected.) To 988 * convert the output of this method into a polygon, the client must choose 989 * one complement from each component such that the entire set of loops is 990 * oriented consistently (i.e., they define a region such that the interior 991 * of the region is always on the left). The non-chosen complements form 992 * another set of loops that are also oriented consistently but represent 993 * the complementary region on the sphere. Finally, the client needs to 994 * choose one of these two sets of loops based on heuristics (e.g., the area 995 * of each region), since both sets of loops are equally valid 996 * interpretations of the input. 997 * 998 * Each loop is represented as a sequence of edges. The edge ordering and 999 * loop ordering are automatically canonicalized in order to preserve the 1000 * input ordering as much as possible. Loops are non-crossing provided that 1001 * the graph contains no crossing edges. If some edges cannot be turned 1002 * into loops, returns false and sets "error" appropriately. 1003 * 1004 * REQUIRES: options.degenerate_edges() == { DISCARD, DISCARD_EXCESS } 1005 * REQUIRES: options.edge_type() == UNDIRECTED 1006 * REQUIRES: options.siblings_pairs() == { DISCARD, DISCARD_EXCESS, KEEP } 1007 * [since REQUIRE, CREATE convert the edge_type() to DIRECTED] 1008 */ 1009 bool getUndirectedComponents( 1010 LoopType loop_type, ref UndirectedComponent[] components, ref S2Error error) const 1011 in { 1012 assert(_options.degenerateEdges() == DegenerateEdges.DISCARD 1013 || _options.degenerateEdges() == DegenerateEdges.DISCARD_EXCESS); 1014 assert(_options.edgeType() == EdgeType.UNDIRECTED); 1015 } do { 1016 import std.typecons : Tuple, tuple; 1017 EdgeId[] sibling_map = getInEdgeIds(); 1018 EdgeId[] left_turn_map; 1019 if (!getLeftTurnMap(sibling_map, left_turn_map, error)) return false; 1020 makeSiblingMap(sibling_map); 1021 InputEdgeId[] min_input_ids = getMinInputEdgeIds(); 1022 1023 // A stack of unexplored sibling edges. Each sibling edge has a "slot" 1024 // (0 or 1) that indicates which of the two complements it belongs to. 1025 Tuple!(EdgeId, int)[] frontier; 1026 1027 // If we are breaking loops at repeated vertices, we maintain a map from 1028 // VertexId to its position in "path". 1029 int[] path_index; 1030 if (loop_type == LoopType.SIMPLE) { 1031 path_index.length = numVertices(); 1032 path_index[] = -1; 1033 } 1034 1035 for (EdgeId min_start = 0; min_start < numEdges(); ++min_start) { 1036 if (left_turn_map[min_start] < 0) continue; // Already used. 1037 1038 // Build a connected component by keeping a stack of unexplored siblings 1039 // of the edges used so far. 1040 UndirectedComponent component; 1041 frontier ~= tuple(min_start, 0); 1042 while (!frontier.empty()) { 1043 EdgeId start = frontier.back()[0]; 1044 int slot = frontier.back()[1]; 1045 frontier.popBack(); 1046 if (left_turn_map[start] < 0) continue; // Already used. 1047 1048 // Build a path by making left turns at each vertex until we return to 1049 // "start". We use "left_turn_map" to keep track of which edges have 1050 // already been visited, and which complement they were assigned to, by 1051 // setting its entries to negative values as we go along. 1052 EdgeId[] path; 1053 for (EdgeId e = start, next; left_turn_map[e] >= 0; e = next) { 1054 path ~= e; 1055 next = left_turn_map[e]; 1056 left_turn_map[e] = markEdgeUsed(slot); 1057 // If the sibling hasn't been visited yet, add it to the frontier. 1058 EdgeId sibling = sibling_map[e]; 1059 if (left_turn_map[sibling] >= 0) { 1060 frontier ~= tuple(sibling, 1 - slot); 1061 } else if (left_turn_map[sibling] != markEdgeUsed(1 - slot)) { 1062 // Two siblings edges can only belong the same complement if the 1063 // given undirected edges do not form loops. 1064 error.initialize(S2Error.Code.BUILDER_EDGES_DO_NOT_FORM_LOOPS, 1065 "Given undirected edges do not form loops"); 1066 return false; 1067 } 1068 if (loop_type == LoopType.SIMPLE) { 1069 // Whenever we encounter a vertex that is already part of the path, 1070 // we "peel off" a loop by removing those edges from the path. 1071 path_index[edge(e)[0]] = cast(int) path.length - 1; 1072 int loop_start = path_index[edge(e)[1]]; 1073 if (loop_start < 0) continue; 1074 EdgeId[] loop = path[loop_start .. $]; 1075 path.length = loop_start; 1076 // Mark the vertices that are no longer part of the path. 1077 foreach (EdgeId e2; loop) path_index[edge(e2)[0]] = -1; 1078 canonicalizeLoopOrder(min_input_ids, loop); 1079 component[slot] ~= loop; 1080 } 1081 } 1082 if (loop_type == LoopType.SIMPLE) { 1083 enforce(path.empty()); // Invariant. 1084 } else { 1085 canonicalizeLoopOrder(min_input_ids, path); 1086 component[slot] ~= path; 1087 } 1088 } 1089 canonicalizeVectorOrder(min_input_ids, component[0]); 1090 canonicalizeVectorOrder(min_input_ids, component[1]); 1091 // To save some work in S2PolygonLayer, we swap the two loop sets of the 1092 // component so that the loop set whose first loop most closely follows 1093 // the input edge ordering is first. (If the input was a valid S2Polygon, 1094 // then this component will contain normalized loops.) 1095 if (min_input_ids[component[0][0][0]] > min_input_ids[component[1][0][0]]) { 1096 swap(component[0], component[1]); 1097 } 1098 components ~= component; 1099 } 1100 // Sort the components to correspond to the input edge ordering. 1101 sort!((in UndirectedComponent a, in UndirectedComponent b) { 1102 return min_input_ids[a[0][0][0]] < min_input_ids[b[0][0][0]]; 1103 })(components); 1104 return true; 1105 } 1106 1107 /// Encodes the index of one of the two complements of each component 1108 /// (a.k.a. the "slot", either 0 or 1) as a negative EdgeId. 1109 private static EdgeId markEdgeUsed(int slot) { 1110 return -1 - slot; 1111 } 1112 1113 /** 1114 * Indicates whether polylines should be "paths" (which don't allow 1115 * duplicate vertices, except possibly the first and last vertex) or 1116 * "walks" (which allow duplicate vertices and edges). 1117 */ 1118 enum PolylineType { PATH, WALK } 1119 1120 /** 1121 * Builds polylines from a set of edges. If "polyline_type" is PATH, then 1122 * only vertices of indegree and outdegree 1 (or degree 2 in the case of 1123 * undirected edges) will appear in the interior of polylines. This 1124 * essentially generates one polyline for each edge chain in the graph. If 1125 * "polyline_type" is WALK, then polylines may pass through the same vertex 1126 * or even the same edge multiple times (if duplicate edges are present), 1127 * and each polyline will be as long as possible. This option is useful for 1128 * reconstructing a polyline that has been snapped to a lower resolution, 1129 * since snapping can cause edges to become identical. 1130 * 1131 * This method attempts to preserve the input edge ordering in order to 1132 * implement idempotency, even when there are repeated edges or loops. This 1133 * is true whether directed or undirected edges are used. Degenerate edges 1134 * are also handled appropriately. 1135 * 1136 * REQUIRES: options.sibling_pairs() == { DISCARD, DISCARD_EXCESS, KEEP } 1137 */ 1138 alias EdgePolyline = EdgeId[]; 1139 EdgePolyline[] getPolylines(PolylineType polyline_type) const 1140 in { 1141 assert(_options.siblingPairs() == SiblingPairs.DISCARD 1142 || _options.siblingPairs() == SiblingPairs.DISCARD_EXCESS 1143 || _options.siblingPairs() == SiblingPairs.KEEP); 1144 } do { 1145 auto builder = new PolylineBuilder(this); 1146 if (polyline_type == PolylineType.PATH) { 1147 return builder.buildPaths(); 1148 } else { 1149 return builder.buildWalks(); 1150 } 1151 } 1152 1153 //////////////////////////////////////////////////////////////////////// 1154 //////////////// Helper Functions for Creating Graphs ////////////////// 1155 1156 /** 1157 * Given an unsorted collection of edges, transform them according to the 1158 * given set of GraphOptions. This includes actions such as discarding 1159 * degenerate edges; merging duplicate edges; and canonicalizing sibling 1160 * edge pairs in several possible ways (e.g. discarding or creating them). 1161 * The output is suitable for passing to the Graph constructor. 1162 * 1163 * If options.edge_type() == EdgeType::UNDIRECTED, then all input edges 1164 * should already have been transformed into a pair of directed edges. 1165 * 1166 * "input_ids" is a vector of the same length as "edges" that indicates 1167 * which input edges were snapped to each edge. This vector is also updated 1168 * appropriately as edges are discarded, merged, etc. 1169 * 1170 * Note that "options" may be modified by this method: in particular, the 1171 * edge_type() can be changed if sibling_pairs() is CREATE or REQUIRE (see 1172 * the description of S2Builder::GraphOptions). 1173 */ 1174 static void processEdges( 1175 GraphOptions options, ref Edge[] edges, ref InputEdgeIdSetId[] input_ids, 1176 IdSetLexicon id_set_lexicon, ref S2Error error) { 1177 auto processor = new EdgeProcessor(options, &edges, &input_ids, id_set_lexicon); 1178 processor.run(error); 1179 // Certain values of sibling_pairs() discard half of the edges and change 1180 // the edge_type() to DIRECTED (see the description of GraphOptions). 1181 if (options.siblingPairs() == SiblingPairs.REQUIRE 1182 || options.siblingPairs() == SiblingPairs.CREATE) { 1183 options.setEdgeType(EdgeType.DIRECTED); 1184 } 1185 } 1186 1187 /** 1188 * Given a set of vertices and edges, removes all vertices that do not have 1189 * any edges and returned the new, minimal set of vertices. Also updates 1190 * each edge in "edges" to correspond to the new vertex numbering. (Note 1191 * that this method does *not* merge duplicate vertices, it simply removes 1192 * vertices of degree zero.) 1193 * 1194 * The new vertex ordering is a subsequence of the original ordering, 1195 * therefore if the edges were lexicographically sorted before calling this 1196 * method then they will still be sorted after calling this method. 1197 * 1198 * The extra argument "tmp" points to temporary storage used by this method. 1199 * All calls to this method from a single thread can reuse the same 1200 * temporary storage. It should initially point to an empty vector. This 1201 * can make a big difference to efficiency when this method is called many 1202 * times (e.g. to extract the vertices for different layers), since the 1203 * incremental running time for each layer becomes O(edges.size()) rather 1204 * than O(vertices.size() + edges.size()). 1205 */ 1206 static S2Point[] filterVertices(in S2Point[] vertices, ref Edge[] edges, ref VertexId[] tmp) { 1207 // Gather the vertices that are actually used. 1208 VertexId[] used; 1209 used.reserve(2 * edges.length); 1210 foreach (e; edges) { 1211 used ~= e[0]; 1212 used ~= e[1]; 1213 } 1214 // Sort the vertices and find the distinct ones. 1215 used = sort(used).uniq().array; 1216 1217 // Build the list of new vertices, and generate a map from old vertex id to 1218 // new vertex id. 1219 VertexId[] vmap = tmp; 1220 vmap.length = vertices.length; 1221 auto new_vertices = new S2Point[used.length]; 1222 for (int i = 0; i < used.length; ++i) { 1223 new_vertices[i] = vertices[used[i]]; 1224 vmap[used[i]] = i; 1225 } 1226 // Update the edges. 1227 foreach (e; edges) { 1228 e[0] = vmap[e[0]]; 1229 e[1] = vmap[e[1]]; 1230 } 1231 return new_vertices; 1232 } 1233 1234 // A comparison function that allows stable sorting with std::sort (which is 1235 // fast but not stable). It breaks ties between equal edges by comparing 1236 // their edge ids. 1237 static bool stableLessThan(in Edge a, in Edge b, EdgeId ai, EdgeId bi) { 1238 // The following is simpler but the compiler (2016) doesn't optimize it as 1239 // well as it should: 1240 // return make_pair(a, ai) < make_pair(b, bi); 1241 if (a[0] < b[0]) return true; 1242 if (b[0] < a[0]) return false; 1243 if (a[1] < b[1]) return true; 1244 if (b[1] < a[1]) return false; 1245 return ai < bi; // Stable sort. 1246 } 1247 1248 private: 1249 1250 static class EdgeProcessor { 1251 public: 1252 this( 1253 in GraphOptions options, 1254 Edge[]* edges, 1255 InputEdgeIdSetId[]* input_ids, 1256 IdSetLexicon id_set_lexicon) { 1257 _options = options; 1258 _edges = edges; 1259 _inputIds = input_ids; 1260 _idSetLexicon = id_set_lexicon; 1261 1262 // TODO: Resume here. 1263 1264 // Sort the outgoing and incoming edges in lexigraphic order. We use a 1265 // stable sort to ensure that each undirected edge becomes a sibling pair, 1266 // even if there are multiple identical input edges. 1267 _outEdges = iota(0, cast(int) (*_edges).length).array; 1268 sort!((EdgeId a, EdgeId b) { 1269 return stableLessThan((*_edges)[a], (*_edges)[b], a, b); 1270 })(_outEdges); 1271 _inEdges = iota(0, cast(int) (*_edges).length).array; 1272 sort!((EdgeId a, EdgeId b) { 1273 return stableLessThan(reverse((*_edges)[a]), reverse((*_edges)[b]), a, b); 1274 })(_inEdges); 1275 _newEdges.reserve((*_edges).length); 1276 _newInputIds.reserve((*_edges).length); 1277 } 1278 1279 void run(ref S2Error error) { 1280 int num_edges = cast(int) (*_edges).length; 1281 if (num_edges == 0) return; 1282 1283 // Walk through the two sorted arrays performing a merge join. For each 1284 // edge, gather all the duplicate copies of the edge in both directions 1285 // (outgoing and incoming). Then decide what to do based on "options_" and 1286 // how many copies of the edge there are in each direction. 1287 int outId = 0; 1288 int inId = 0; 1289 Edge out_edge = (*_edges)[_outEdges[outId]]; 1290 Edge in_edge = (*_edges)[_inEdges[inId]]; 1291 Edge sentinel = tuple(VertexId.max, VertexId.max); 1292 for (;;) { 1293 Edge edge = min(out_edge, reverse(in_edge)); 1294 if (edge == sentinel) break; 1295 1296 int out_begin = outId; 1297 int in_begin = inId; 1298 while (out_edge == edge) { 1299 out_edge = (++outId == num_edges) ? sentinel : (*_edges)[_outEdges[outId]]; 1300 } 1301 while (reverse(in_edge) == edge) { 1302 in_edge = (++inId == num_edges) ? sentinel : (*_edges)[_inEdges[inId]]; 1303 } 1304 int n_out = outId - out_begin; 1305 int n_in = inId - in_begin; 1306 if (edge[0] == edge[1]) { 1307 debug enforce(n_in == n_out); 1308 if (_options.degenerateEdges() == DegenerateEdges.DISCARD) { 1309 continue; 1310 } 1311 if (_options.degenerateEdges() == DegenerateEdges.DISCARD_EXCESS 1312 && ((out_begin > 0 && (*_edges)[_outEdges[out_begin - 1]][0] == edge[0]) 1313 || (outId < num_edges && (*_edges)[_outEdges[outId]][0] == edge[0]) 1314 || (in_begin > 0 && (*_edges)[_inEdges[in_begin - 1]][1] == edge[0]) 1315 || (inId < num_edges && (*_edges)[_inEdges[inId]][1] == edge[0]))) { 1316 continue; // There were non-degenerate incident edges, so discard. 1317 } 1318 if (_options.edgeType() == EdgeType.UNDIRECTED 1319 && (_options.siblingPairs() == SiblingPairs.REQUIRE 1320 || _options.siblingPairs() == SiblingPairs.CREATE)) { 1321 // When we have undirected edges and are guaranteed to have siblings, 1322 // we cut the number of edges in half (see s2builder.h). 1323 debug enforce((n_out & 1) == 0); // Number of edges is always even. 1324 addEdges(_options.duplicateEdges() == DuplicateEdges.MERGE ? 1 : (n_out / 2), 1325 edge, mergeInputIds(out_begin, outId)); 1326 } else if (_options.duplicateEdges() == DuplicateEdges.MERGE) { 1327 addEdges(_options.edgeType() == EdgeType.UNDIRECTED ? 2 : 1, 1328 edge, mergeInputIds(out_begin, outId)); 1329 } else if (_options.siblingPairs() == SiblingPairs.DISCARD || 1330 _options.siblingPairs() == SiblingPairs.DISCARD_EXCESS) { 1331 // Any SiblingPair option that discards edges causes the labels of all 1332 // duplicate edges to be merged together (see s2builder.h). 1333 addEdges(n_out, edge, mergeInputIds(out_begin, outId)); 1334 } else { 1335 copyEdges(out_begin, outId); 1336 } 1337 } else if (_options.siblingPairs() == SiblingPairs.KEEP) { 1338 if (n_out > 1 && _options.duplicateEdges() == DuplicateEdges.MERGE) { 1339 addEdge(edge, mergeInputIds(out_begin, outId)); 1340 } else { 1341 copyEdges(out_begin, outId); 1342 } 1343 } else if (_options.siblingPairs() == SiblingPairs.DISCARD) { 1344 if (_options.edgeType() == EdgeType.DIRECTED) { 1345 // If n_out == n_in: balanced sibling pairs 1346 // If n_out < n_in: unbalanced siblings, in the form AB, BA, BA 1347 // If n_out > n_in: unbalanced siblings, in the form AB, AB, BA 1348 if (n_out <= n_in) continue; 1349 // Any option that discards edges causes the labels of all duplicate 1350 // edges to be merged together (see s2builder.h). 1351 addEdges(_options.duplicateEdges() == DuplicateEdges.MERGE 1352 ? 1 : (n_out - n_in), edge, mergeInputIds(out_begin, outId)); 1353 } else { 1354 if ((n_out & 1) == 0) continue; 1355 addEdge(edge, mergeInputIds(out_begin, outId)); 1356 } 1357 } else if (_options.siblingPairs() == SiblingPairs.DISCARD_EXCESS) { 1358 if (_options.edgeType() == EdgeType.DIRECTED) { 1359 // See comments above. The only difference is that if there are 1360 // balanced sibling pairs, we want to keep one such pair. 1361 if (n_out < n_in) continue; 1362 addEdges(_options.duplicateEdges() == DuplicateEdges.MERGE 1363 ? 1 : max(1, n_out - n_in), edge, mergeInputIds(out_begin, outId)); 1364 } else { 1365 addEdges((n_out & 1) ? 1 : 2, edge, mergeInputIds(out_begin, outId)); 1366 } 1367 } else { 1368 debug enforce(_options.siblingPairs() == SiblingPairs.REQUIRE 1369 || _options.siblingPairs() == SiblingPairs.CREATE); 1370 if (error.ok() && _options.siblingPairs() == SiblingPairs.REQUIRE 1371 && (_options.edgeType() == EdgeType.DIRECTED 1372 ? (n_out != n_in) : ((n_out & 1) != 0))) { 1373 error.initialize(S2Error.Code.BUILDER_MISSING_EXPECTED_SIBLING_EDGES, 1374 "Expected all input edges to have siblings, but some were missing"); 1375 } 1376 if (_options.duplicateEdges() == DuplicateEdges.MERGE) { 1377 addEdge(edge, mergeInputIds(out_begin, outId)); 1378 } else if (_options.edgeType() == EdgeType.UNDIRECTED) { 1379 addEdges((n_out + 1) / 2, edge, mergeInputIds(out_begin, outId)); 1380 } else { 1381 copyEdges(out_begin, outId); 1382 if (n_in > n_out) { 1383 addEdges(n_in - n_out, edge, mergeInputIds(outId, outId)); 1384 } 1385 } 1386 } 1387 } 1388 *_edges = _newEdges; 1389 *_inputIds = _newInputIds; 1390 } 1391 1392 private: 1393 void addEdge(Edge edge, InputEdgeIdSetId input_edge_id_set_id) { 1394 _newEdges ~= edge; 1395 _newInputIds ~= input_edge_id_set_id; 1396 } 1397 1398 void addEdges(int num_edges, Edge edge, InputEdgeIdSetId input_edge_id_set_id) { 1399 for (int i = 0; i < num_edges; ++i) { 1400 addEdge(edge, input_edge_id_set_id); 1401 } 1402 } 1403 1404 void copyEdges(int out_begin, int out_end) { 1405 for (int i = out_begin; i < out_end; ++i) { 1406 addEdge((*_edges)[_outEdges[i]], (*_inputIds)[_outEdges[i]]); 1407 } 1408 } 1409 1410 InputEdgeIdSetId mergeInputIds(int out_begin, int out_end) { 1411 if (out_end - out_begin == 1) { 1412 return (*_inputIds)[_outEdges[out_begin]]; 1413 } 1414 _tmpIds.length = 0; 1415 for (int i = out_begin; i < out_end; ++i) { 1416 foreach (id; _idSetLexicon.idSet((*_inputIds)[_outEdges[i]])) { 1417 _tmpIds ~= id; 1418 } 1419 } 1420 return _idSetLexicon.add(_tmpIds); 1421 } 1422 1423 const(GraphOptions) _options; 1424 Edge[]* _edges; 1425 InputEdgeIdSetId[]* _inputIds; 1426 IdSetLexicon _idSetLexicon; 1427 EdgeId[] _outEdges; 1428 EdgeId[] _inEdges; 1429 1430 Edge[] _newEdges; 1431 InputEdgeIdSetId[] _newInputIds; 1432 1433 InputEdgeId[] _tmpIds; 1434 } 1435 1436 static class PolylineBuilder { 1437 public: 1438 this(in Graph g) { 1439 _g = g; 1440 _in = new VertexInMap(g); 1441 _out = new VertexOutMap(g); 1442 _minInputIds = g.getMinInputEdgeIds(); 1443 _directed = _g.options().edgeType() == Graph.EdgeType.DIRECTED; 1444 _edgesLeft = g.numEdges() / (_directed ? 1 : 2); 1445 _used.length = g.numEdges(); 1446 _used[] = false; 1447 if (!_directed) { 1448 _siblingMap = _in.inEdgeIds().dup; 1449 g.makeSiblingMap(_siblingMap); 1450 } 1451 } 1452 1453 EdgePolyline[] buildPaths() { 1454 // First build polylines starting at all the vertices that cannot be in the 1455 // polyline interior (i.e., indegree != 1 or outdegree != 1 for directed 1456 // edges, or degree != 2 for undirected edges). We consider the possible 1457 // starting edges in input edge id order so that we preserve the input path 1458 // direction even when undirected edges are used. (Undirected edges are 1459 // represented by sibling pairs where only the edge in the input direction 1460 // is labeled with an input edge id.) 1461 EdgePolyline[] polylines; 1462 EdgeId[] edges = _g.getInputEdgeOrder(_minInputIds); 1463 foreach (i; 0 .. edges.length) { 1464 EdgeId e = edges[i]; 1465 if (!_used[e] && !isInterior(_g.edge(e)[0])) { 1466 polylines ~= buildPath(e); 1467 } 1468 } 1469 // If there are any edges left, they form non-intersecting loops. We build 1470 // each loop and then canonicalize its edge order. We consider candidate 1471 // starting edges in input edge id order in order to preserve the input 1472 // direction of undirected loops. Even so, we still need to canonicalize 1473 // the edge order to ensure that when an input edge is split into an edge 1474 // chain, the loop does not start in the middle of such a chain. 1475 for (int i = 0; i < edges.length && _edgesLeft > 0; ++i) { 1476 EdgeId e = edges[i]; 1477 if (_used[e]) continue; 1478 EdgePolyline polyline = buildPath(e); 1479 canonicalizeLoopOrder(_minInputIds, polyline); 1480 polylines ~= polyline; 1481 } 1482 debug enforce(_edgesLeft == 0); 1483 1484 // Sort the polylines to correspond to the input order (if possible). 1485 canonicalizeVectorOrder(_minInputIds, polylines); 1486 return polylines; 1487 } 1488 1489 EdgePolyline[] buildWalks() { 1490 // Note that some of this code is worst-case quadratic in the maximum vertex 1491 // degree. This could be fixed with a few extra arrays, but it should not 1492 // be a problem in practice. 1493 1494 // First, build polylines from all vertices where outdegree > indegree (or 1495 // for undirected edges, vertices whose degree is odd). We consider the 1496 // possible starting edges in input edge id order, for idempotency in the 1497 // case where multiple input polylines share vertices or edges. 1498 EdgePolyline[] polylines; 1499 EdgeId[] edges = _g.getInputEdgeOrder(_minInputIds); 1500 for (int i = 0; i < edges.length; ++i) { 1501 EdgeId e = edges[i]; 1502 if (_used[e]) continue; 1503 VertexId v = _g.edge(e)[0]; 1504 int excess = excessDegree(v); 1505 if (excess <= 0) continue; 1506 if (v !in _excessUsed) _excessUsed[v] = 0; 1507 excess -= _excessUsed[v]; 1508 if (_directed ? (excess <= 0) : (excess % 2 == 0)) continue; 1509 ++_excessUsed[v]; 1510 polylines ~= buildWalk(v); 1511 --_excessUsed[_g.edge(polylines.back().back())[1]]; 1512 } 1513 // Now all vertices have outdegree == indegree (or even degree if undirected 1514 // edges are being used). Therefore all remaining edges can be assembled 1515 // into loops. We first try to expand the existing polylines if possible by 1516 // adding loops to them. 1517 if (_edgesLeft > 0) { 1518 foreach (ref Graph.EdgePolyline polyline; polylines) { 1519 maximizeWalk(polyline); 1520 } 1521 } 1522 // Finally, if there are still unused edges then we build loops. If the 1523 // input is a polyline that forms a loop, then for idempotency we need to 1524 // start from the edge with minimum input edge id. If the minimal input 1525 // edge was split into several edges, then we start from the first edge of 1526 // the chain. 1527 for (int i = 0; i < edges.length && _edgesLeft > 0; ++i) { 1528 EdgeId e = edges[i]; 1529 if (_used[e]) continue; 1530 1531 // Determine whether the origin of this edge is the start of an edge 1532 // chain. To do this, we test whether (outdegree - indegree == 1) for the 1533 // origin, considering only unused edges with the same minimum input edge 1534 // id. (Undirected edges have input edge ids in one direction only.) 1535 VertexId v = _g.edge(e)[0]; 1536 InputEdgeId id = _minInputIds[e]; 1537 int excess = 0; 1538 for (int j = i; j < edges.length && _minInputIds[edges[j]] == id; ++j) { 1539 EdgeId e2 = edges[j]; 1540 if (_used[e2]) continue; 1541 if (_g.edge(e2)[0] == v) ++excess; 1542 if (_g.edge(e2)[1] == v) --excess; 1543 } 1544 // It is also acceptable to start a polyline from any degenerate edge. 1545 if (excess == 1 || _g.edge(e)[1] == v) { 1546 Graph.EdgePolyline polyline = buildWalk(v); 1547 maximizeWalk(polyline); 1548 polylines ~= polyline; 1549 } 1550 } 1551 debug enforce(_edgesLeft == 0); 1552 1553 // Sort the polylines to correspond to the input order (if possible). 1554 canonicalizeVectorOrder(_minInputIds, polylines); 1555 return polylines; 1556 } 1557 1558 private: 1559 bool isInterior(VertexId v) { 1560 if (_directed) { 1561 return _in.degree(v) == 1 && _out.degree(v) == 1; 1562 } else { 1563 return _out.degree(v) == 2; 1564 } 1565 } 1566 1567 int excessDegree(VertexId v) { 1568 return _directed ? _out.degree(v) - _in.degree(v) : _out.degree(v) % 2; 1569 } 1570 1571 EdgePolyline buildPath(EdgeId e) { 1572 // We simply follow edges until either we reach a vertex where there is a 1573 // choice about which way to go (where is_interior(v) is false), or we 1574 // return to the starting vertex (if the polyline is actually a loop). 1575 EdgePolyline polyline; 1576 VertexId start = _g.edge(e)[0]; 1577 for (;;) { 1578 polyline ~= e; 1579 debug enforce(!_used[e]); 1580 _used[e] = true; 1581 if (!_directed) _used[_siblingMap[e]] = true; 1582 --_edgesLeft; 1583 VertexId v = _g.edge(e)[1]; 1584 if (!isInterior(v) || v == start) break; 1585 if (_directed) { 1586 debug enforce(_out.degree(v) == 1); 1587 e = _out.edgeIds(v).front(); 1588 } else { 1589 debug enforce(_out.degree(v) == 2); 1590 foreach (EdgeId e2; _out.edgeIds(v)) if (!_used[e2]) e = e2; 1591 } 1592 } 1593 return polyline; 1594 } 1595 1596 EdgePolyline buildWalk(VertexId v) { 1597 EdgePolyline polyline; 1598 for (;;) { 1599 // Follow the edge with the smallest input edge id. 1600 EdgeId best_edge = -1; 1601 InputEdgeId best_out_id = InputEdgeId.max; 1602 foreach (EdgeId e; _out.edgeIds(v)) { 1603 if (_used[e] || _minInputIds[e] >= best_out_id) continue; 1604 best_out_id = _minInputIds[e]; 1605 best_edge = e; 1606 } 1607 if (best_edge < 0) return polyline; 1608 // For idempotency when there are multiple input polylines, we stop the 1609 // walk early if "best_edge" might be a continuation of a different 1610 // incoming edge. 1611 if (v !in _excessUsed) _excessUsed[v] = 0; 1612 int excess = excessDegree(v) - _excessUsed[v]; 1613 if (_directed ? (excess < 0) : (excess % 2) == 1) { 1614 foreach (EdgeId e; _in.edgeIds(v)) { 1615 if (!_used[e] && _minInputIds[e] <= best_out_id) { 1616 return polyline; 1617 } 1618 } 1619 } 1620 polyline ~= best_edge; 1621 _used[best_edge] = true; 1622 if (!_directed) _used[_siblingMap[best_edge]] = true; 1623 --_edgesLeft; 1624 v = _g.edge(best_edge)[1]; 1625 } 1626 } 1627 1628 void maximizeWalk(ref EdgePolyline polyline) { 1629 // Examine all vertices of the polyline and check whether there are any 1630 // unused outgoing edges. If so, then build a loop starting at that vertex 1631 // and insert it into the polyline. (The walk is guaranteed to be a loop 1632 // because this method is only called when all vertices have equal numbers 1633 // of unused incoming and outgoing edges.) 1634 for (int i = 0; i <= polyline.length; ++i) { 1635 VertexId v = (i == 0 ? _g.edge(polyline[i])[0] : _g.edge(polyline[i - 1])[1]); 1636 foreach (EdgeId e; _out.edgeIds(v)) { 1637 if (!_used[e]) { 1638 EdgePolyline loop = buildWalk(v); 1639 debug enforce(_g.edge(loop.back())[1] == v); 1640 polyline = polyline[0 .. i] ~ loop ~ polyline[i .. $]; 1641 debug enforce(_used[e]); // All outgoing edges from "v" are now used. 1642 break; 1643 } 1644 } 1645 } 1646 } 1647 1648 const(Graph) _g; 1649 VertexInMap _in; 1650 VertexOutMap _out; 1651 EdgeId[] _siblingMap; 1652 InputEdgeId[] _minInputIds; 1653 bool _directed; 1654 int _edgesLeft; 1655 bool[] _used; 1656 1657 // A map of (outdegree(v) - indegree(v)) considering used edges only. 1658 int[VertexId] _excessUsed; 1659 } 1660 1661 const(GraphOptions) _options; 1662 VertexId _numVertices; // Cached to avoid division by 24. 1663 1664 const(S2Point[]) _vertices; 1665 const(Edge[]) _edges; 1666 const(InputEdgeIdSetId[]) _inputEdgeIdSetIds; 1667 const(IdSetLexicon) _inputEdgeIdSetLexicon; 1668 const(LabelSetId[]) _labelSetIds; 1669 const(IdSetLexicon) _labelSetLexicon; 1670 IsFullPolygonPredicate _isFullPolygonPredicate; 1671 } 1672 1673 /// A struct for sorting the incoming and outgoing edges around a vertex "v0". 1674 private struct VertexEdge { 1675 bool incoming; // Is this an incoming edge to "v0"? 1676 Graph.EdgeId index; // Index of this edge in "edges_" or "in_edge_ids" 1677 Graph.VertexId endpoint; // The other (not "v0") endpoint of this edge 1678 int rank; // Secondary key for edges with the same endpoint 1679 } 1680 1681 /** 1682 * Given a set of duplicate outgoing edges (v0, v1) and a set of duplicate 1683 * incoming edges (v1, v0), this method assigns each edge an integer "rank" so 1684 * that the edges are sorted in a consistent order with respect to their 1685 * orderings around "v0" and "v1". Usually there is just one edge, in which 1686 * case this is easy. Sometimes there is one edge in each direction, in which 1687 * case the outgoing edge is always ordered before the incoming edge. 1688 * 1689 * In general, we allow any number of duplicate edges in each direction, in 1690 * which case outgoing edges are interleaved with incoming edges so as to 1691 * create as many degenerate (two-edge) loops as possible. In order to get a 1692 * consistent ordering around "v0" and "v1", we move forwards through the list 1693 * of outgoing edges and backwards through the list of incoming edges. If 1694 * there are more incoming edges, they go at the beginning of the ordering, 1695 * while if there are more outgoing edges then they go at the end. 1696 * 1697 * For example, suppose there are 2 edges "a,b" from "v0" to "v1", and 4 edges 1698 * "w,x,y,z" from "v1" to "v0". Using lower/upper case letters to represent 1699 * incoming/outgoing edges, the clockwise ordering around v0 would be zyAxBw, 1700 * and the clockwise ordering around v1 would be WbXaYZ. (Try making a 1701 * diagram with each edge as a separate arc.) 1702 */ 1703 private void addVertexEdges( 1704 Graph.EdgeId out_begin, Graph.EdgeId out_end, 1705 Graph.EdgeId in_begin, Graph.EdgeId in_end, 1706 Graph.VertexId v1, ref VertexEdge[] v0_edges) { 1707 int rank = 0; 1708 // Any extra incoming edges go at the beginning of the ordering. 1709 while (in_end - in_begin > out_end - out_begin) { 1710 v0_edges ~= VertexEdge(true, --in_end, v1, rank++); 1711 } 1712 // Next we interleave as many outgoing and incoming edges as possible. 1713 while (in_end > in_begin) { 1714 v0_edges ~= VertexEdge(false, out_begin++, v1, rank++); 1715 v0_edges ~= VertexEdge(true, --in_end, v1, rank++); 1716 } 1717 // Any extra outgoing edges to at the end of the ordering. 1718 while (out_end > out_begin) { 1719 v0_edges ~= VertexEdge(false, out_begin++, v1, rank++); 1720 } 1721 } 1722