1 // Copyright 2013 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 // Original author: ericv@google.com (Eric Veach) 16 // Converted to D: madric@gmail.com (Vijay Nayar) 17 18 module s2.s2crossing_edge_query; 19 20 import s2.r2point; 21 import s2.r2rect; 22 import s2.s2cell_id; 23 import s2.s2edge_clipping; 24 import s2.s2edge_crosser; 25 import s2.s2padded_cell; 26 import s2.s2point; 27 import s2.s2shape; 28 import s2.s2shape_index; 29 import s2.shapeutil.count_edges; 30 import s2.shapeutil.shape_edge; 31 import s2.shapeutil.shape_edge_id; 32 import s2.util.container.btree_map; 33 34 import std.algorithm : sort, uniq; 35 import std.array : array; 36 import std.exception : enforce; 37 import std.range : empty; 38 39 /** 40 * A parameter that controls the reporting of edge intersections. 41 * 42 * - CrossingType::INTERIOR reports intersections that occur at a point 43 * interior to both edges (i.e., not at a vertex). 44 * 45 * - CrossingType::ALL reports all intersections, even those where two edges 46 * intersect only because they share a common vertex. 47 */ 48 enum CrossingType { INTERIOR, ALL } 49 50 // For small loops it is faster to use brute force. The threshold below was 51 // determined using the benchmarks in the unit test. 52 private enum int MAX_BRUTE_FORCE_EDGES = 27; 53 54 /** 55 * S2CrossingEdgeQuery is used to find edges or shapes that are crossed by 56 * an edge. Here is an example showing how to index a set of polylines, 57 * and then find the polylines that are crossed by a given edge AB: 58 * 59 * void Test(const vector<S2Polyline*>& polylines,` 60 * const S2Point& a0, const S2Point &a1) { 61 * MutableS2ShapeIndex index; 62 * for (S2Polyline* polyline : polylines) { 63 * index.Add(absl::make_unique<S2Polyline::Shape>(polyline)); 64 * } 65 * S2CrossingEdgeQuery query(&index); 66 * for (const auto& edge : query.GetCrossingEdges(a, b, CrossingType::ALL)) { 67 * CHECK_GE(S2::CrossingSign(a0, a1, edge.v0(), edge.v1()), 0); 68 * } 69 * } 70 * 71 * Note that if you need to query many edges, it is more efficient to declare 72 * a single S2CrossingEdgeQuery object and reuse it so that temporary storage 73 * does not need to be reallocated each time. 74 * 75 * If you want to find *all* pairs of crossing edges, use 76 * s2shapeutil::VisitCrossingEdgePairs() instead. 77 */ 78 class S2CrossingEdgeQuery { 79 public: 80 81 /// Default constructor; requires Init() to be called. 82 this() { 83 _iter = new S2ShapeIndex.Iterator(); 84 } 85 86 /// Convenience constructor that calls Init(). 87 this(S2ShapeIndex index) { 88 this(); 89 initialize(index); 90 } 91 92 inout(S2ShapeIndex) index() inout { 93 return _index; 94 } 95 96 /// REQUIRES: "index" is not modified after this method is called. 97 void initialize(S2ShapeIndex index) { 98 _index = index; 99 _iter.initialize(index); 100 } 101 102 /** 103 * Returns all edges that intersect the given query edge (a0,a1) and that 104 * have the given CrossingType (ALL or INTERIOR). Edges are sorted and 105 * unique. 106 */ 107 ShapeEdge[] getCrossingEdges(in S2Point a0, in S2Point a1, CrossingType type) { 108 ShapeEdge[] edges; 109 getCrossingEdges(a0, a1, type, edges); 110 return edges; 111 } 112 113 /** 114 * A specialized version of GetCrossingEdges() that only returns the edges 115 * that belong to a particular S2Shape. 116 */ 117 ShapeEdge[] getCrossingEdges( 118 in S2Point a0, in S2Point a1, in S2Shape shape, CrossingType type) { 119 ShapeEdge[] edges; 120 getCrossingEdges(a0, a1, shape, type, edges); 121 return edges; 122 } 123 124 /** 125 * These versions can be more efficient when they are called many times, 126 * since they do not require allocating a new vector on each call. 127 */ 128 void getCrossingEdges( 129 in S2Point a0, in S2Point a1, CrossingType type, ref ShapeEdge[] edges) { 130 edges.length = 0; 131 getCandidates(a0, a1, _tmpCandidates); 132 int min_sign = (type == CrossingType.ALL) ? 0 : 1; 133 auto crosser = new S2CopyingEdgeCrosser(a0, a1); 134 int shape_id = -1; 135 S2Shape shape = null; 136 foreach (ShapeEdgeId candidate; _tmpCandidates) { 137 if (candidate.shapeId != shape_id) { 138 shape_id = candidate.shapeId; 139 shape = _index.shape(shape_id); 140 } 141 int edge_id = candidate.edgeId; 142 S2Shape.Edge b = shape.edge(edge_id); 143 if (crosser.crossingSign(b.v0, b.v1) >= min_sign) { 144 edges ~= new ShapeEdge(shape_id, edge_id, b); 145 } 146 } 147 } 148 149 void getCrossingEdges( 150 in S2Point a0, in S2Point a1, in S2Shape shape, CrossingType type, ref ShapeEdge[] edges) { 151 edges.length = 0; 152 getCandidates(a0, a1, shape, _tmpCandidates); 153 int min_sign = (type == CrossingType.ALL) ? 0 : 1; 154 auto crosser = new S2CopyingEdgeCrosser(a0, a1); 155 foreach (ShapeEdgeId candidate; _tmpCandidates) { 156 int edge_id = candidate.edgeId; 157 S2Shape.Edge b = shape.edge(edge_id); 158 if (crosser.crossingSign(b.v0, b.v1) >= min_sign) { 159 edges ~= new ShapeEdge(shape.id(), edge_id, b); 160 } 161 } 162 } 163 164 /////////////////////////// Low-Level Methods //////////////////////////// 165 // 166 // Most clients will not need the following methods. They can be slightly 167 // more efficient but are harder to use, since they require the client to do 168 // all the actual crossing tests. 169 170 // Returns a superset of the edges that intersect a query edge (a0, a1). 171 // This method is useful for clients that want to test intersections in some 172 // other way, e.g. using S2::EdgeOrVertexCrossing(). 173 ShapeEdgeId[] getCandidates(in S2Point a0, in S2Point a1) { 174 ShapeEdgeId[] edges; 175 getCandidates(a0, a1, edges); 176 return edges; 177 } 178 179 180 // A specialized version of GetCandidates() that only returns the edges that 181 // belong to a particular S2Shape. 182 ShapeEdgeId[] getCandidates(in S2Point a0, in S2Point a1, in S2Shape shape) { 183 ShapeEdgeId[] edges; 184 getCandidates(a0, a1, shape, edges); 185 return edges; 186 } 187 188 // These versions can be more efficient when they are called many times, 189 // since they do not require allocating a new vector on each call. 190 void getCandidates(in S2Point a0, in S2Point a1, out ShapeEdgeId[] edges) { 191 int num_edges = countEdgesUpTo(_index, MAX_BRUTE_FORCE_EDGES + 1); 192 if (num_edges <= MAX_BRUTE_FORCE_EDGES) { 193 edges.reserve(num_edges); 194 } 195 visitRawCandidates(a0, a1, (in ShapeEdgeId id) { 196 edges ~= id; 197 return true; 198 }); 199 if (edges.length > 1) { 200 edges = edges.sort().uniq().array(); 201 } 202 } 203 204 void getCandidates(in S2Point a0, in S2Point a1, in S2Shape shape, out ShapeEdgeId[] edges) { 205 int num_edges = shape.numEdges(); 206 if (num_edges <= MAX_BRUTE_FORCE_EDGES) { 207 edges.reserve(num_edges); 208 } 209 visitRawCandidates(a0, a1, shape, (in ShapeEdgeId id) { 210 edges ~= id; 211 return true; 212 }); 213 if (edges.length > 1) { 214 edges = edges.sort.uniq.array; 215 } 216 } 217 218 // A function that is called with each candidate intersecting edge. The 219 // function may return false in order to request that the algorithm should 220 // be terminated, i.e. no further crossings are needed. 221 alias ShapeEdgeIdVisitor = bool delegate(in ShapeEdgeId id); 222 223 // Visits a superset of the edges that intersect the query edge (a0, a1), 224 // terminating early if the given ShapeEdgeIdVisitor returns false (in which 225 // case this function returns false as well). 226 // 227 // CAVEAT: Edges may be visited more than once. 228 bool visitRawCandidates(in S2Point a0, in S2Point a1, ShapeEdgeIdVisitor visitor) { 229 int num_edges = countEdgesUpTo(_index, MAX_BRUTE_FORCE_EDGES + 1); 230 if (num_edges <= MAX_BRUTE_FORCE_EDGES) { 231 int num_shape_ids = _index.numShapeIds(); 232 for (int s = 0; s < num_shape_ids; ++s) { 233 const(S2Shape) shape = _index.shape(s); 234 if (shape is null) continue; 235 int num_shape_edges = shape.numEdges(); 236 for (int e = 0; e < num_shape_edges; ++e) { 237 if (!visitor(ShapeEdgeId(s, e))) return false; 238 } 239 } 240 return true; 241 } 242 return visitCells(a0, a1, (in S2ShapeIndexCell cell) { 243 for (int s = 0; s < cell.numClipped(); ++s) { 244 const(S2ClippedShape) clipped = cell.clipped(s); 245 for (int j = 0; j < clipped.numEdges(); ++j) { 246 if (!visitor(ShapeEdgeId(clipped.shapeId(), clipped.edge(j)))) { 247 return false; 248 } 249 } 250 } 251 return true; 252 }); 253 } 254 255 bool visitRawCandidates( 256 in S2Point a0, in S2Point a1, in S2Shape shape, ShapeEdgeIdVisitor visitor) { 257 int num_edges = shape.numEdges(); 258 if (num_edges <= MAX_BRUTE_FORCE_EDGES) { 259 for (int e = 0; e < num_edges; ++e) { 260 if (!visitor(ShapeEdgeId(shape.id(), e))) return false; 261 } 262 return true; 263 } 264 return visitCells(a0, a1, (in S2ShapeIndexCell cell) { 265 const(S2ClippedShape)* clipped = cell.findClipped(shape.id()); 266 if (clipped is null) return true; 267 for (int j = 0; j < clipped.numEdges(); ++j) { 268 if (!visitor(ShapeEdgeId(shape.id(), clipped.edge(j)))) return false; 269 } 270 return true; 271 }); 272 } 273 274 // A function that is called with each S2ShapeIndexCell that might contain 275 // edges intersecting the given query edge. The function may return false 276 // in order to request that the algorithm should be terminated, i.e. no 277 // further crossings are needed. 278 alias CellVisitor = bool delegate(in S2ShapeIndexCell); 279 280 // Visits all S2ShapeIndexCells that might contain edges intersecting the 281 // given query edge (a0, a1), terminating early if the given CellVisitor 282 // returns false (in which case this function returns false as well). 283 // 284 // NOTE: Each candidate cell is visited exactly once. 285 bool visitCells(in S2Point a0, in S2Point a1, CellVisitor visitor) { 286 _visitor = visitor; 287 FaceSegmentVector segments; 288 getFaceSegments(a0, a1, segments); 289 foreach (segment; segments) { 290 _a0 = segment.a; 291 _a1 = segment.b; 292 // Optimization: rather than always starting the recursive subdivision at 293 // the top level face cell, instead we start at the smallest S2CellId that 294 // contains the edge (the "edge root cell"). This typically lets us skip 295 // quite a few levels of recursion since most edges are short. 296 R2Rect edge_bound = R2Rect.fromPointPair(_a0, _a1); 297 auto pcell = new S2PaddedCell(S2CellId.fromFace(segment.face), 0); 298 S2CellId edge_root = pcell.shrinkToFit(edge_bound); 299 300 // Now we need to determine how the edge root cell is related to the cells 301 // in the spatial index (cell_map_). There are three cases: 302 // 303 // 1. edge_root is an index cell or is contained within an index cell. 304 // In this case we only need to look at the contents of that cell. 305 // 2. edge_root is subdivided into one or more index cells. In this case 306 // we recursively subdivide to find the cells intersected by a0a1. 307 // 3. edge_root does not intersect any index cells. In this case there 308 // is nothing to do. 309 S2ShapeIndex.CellRelation relation = _iter.locate(edge_root); 310 if (relation == S2ShapeIndex.CellRelation.INDEXED) { 311 // edge_root is an index cell or is contained by an index cell (case 1). 312 enforce(_iter.id().contains(edge_root)); 313 if (!visitor(_iter.cell())) return false; 314 } else if (relation == S2ShapeIndex.CellRelation.SUBDIVIDED) { 315 // edge_root is subdivided into one or more index cells (case 2). We 316 // find the cells intersected by a0a1 using recursive subdivision. 317 if (!edge_root.isFace()) pcell = new S2PaddedCell(edge_root, 0); 318 if (!visitCells(pcell, edge_bound)) return false; 319 } 320 } 321 return true; 322 } 323 324 325 // Visits all S2ShapeIndexCells within "root" that might contain edges 326 // intersecting the given query edge (a0, a1), terminating early if the 327 // given CellVisitor returns false (in which case this function returns 328 // false as well). 329 // 330 // NOTE: Each candidate cell is visited exactly once. 331 bool visitCells(in S2Point a0, in S2Point a1, in S2PaddedCell root, CellVisitor visitor) { 332 _visitor = visitor; 333 FaceSegmentVector segments; 334 getFaceSegments(a0, a1, segments); 335 foreach (segment; segments) { 336 _a0 = segment.a; 337 _a1 = segment.b; 338 339 // Optimization: rather than always starting the recursive subdivision at 340 // the top level face cell, instead we start at the smallest S2CellId that 341 // contains the edge (the "edge root cell"). This typically lets us skip 342 // quite a few levels of recursion since most edges are short. 343 R2Rect edge_bound = R2Rect.fromPointPair(_a0, _a1); 344 auto pcell = new S2PaddedCell(S2CellId.fromFace(segment.face), 0); 345 S2CellId edge_root = pcell.shrinkToFit(edge_bound); 346 347 // Now we need to determine how the edge root cell is related to the cells 348 // in the spatial index (cell_map_). There are three cases: 349 // 350 // 1. edge_root is an index cell or is contained within an index cell. 351 // In this case we only need to look at the contents of that cell. 352 // 2. edge_root is subdivided into one or more index cells. In this case 353 // we recursively subdivide to find the cells intersected by a0a1. 354 // 3. edge_root does not intersect any index cells. In this case there 355 // is nothing to do. 356 S2ShapeIndex.CellRelation relation = _iter.locate(edge_root); 357 if (relation == S2ShapeIndex.CellRelation.INDEXED) { 358 // edge_root is an index cell or is contained by an index cell (case 1). 359 enforce(_iter.id().contains(edge_root)); 360 if (!visitor(_iter.cell())) return false; 361 } else if (relation == S2ShapeIndex.CellRelation.SUBDIVIDED) { 362 // edge_root is subdivided into one or more index cells (case 2). We 363 // find the cells intersected by a0a1 using recursive subdivision. 364 if (!edge_root.isFace()) pcell = new S2PaddedCell(edge_root, 0); 365 if (!visitCells(pcell, edge_bound)) return false; 366 } 367 } 368 return true; 369 } 370 371 372 // Given a query edge AB and a cell "root", returns all S2ShapeIndex cells 373 // within "root" that might contain edges intersecting AB. 374 void getCells(in S2Point a0, in S2Point a1, in S2PaddedCell root, out const(S2ShapeIndexCell)[] cells) { 375 cells.length = 0; 376 visitCells( 377 a0, a1, root, (in S2ShapeIndexCell cell) { 378 cells ~= cell; 379 return true; 380 }); 381 } 382 383 ///////////////////// DEPRECATED METHODS ////////////////////////////// 384 385 alias EdgeMap = int[][S2Shape]; 386 387 deprecated("Use GetCrossingEdges") 388 bool getCrossings( 389 in S2Point a0, in S2Point a1, in S2Shape shape, CrossingType type, ref int[] edges) { 390 edges.length = 0; 391 foreach (const(ShapeEdge) edge; getCrossingEdges(a0, a1, shape, type)) { 392 edges ~= edge.id().edgeId; 393 } 394 return !edges.empty(); 395 } 396 397 deprecated("Use GetCrossingEdges") 398 bool getCrossings(in S2Point a0, in S2Point a1, CrossingType type, out EdgeMap edge_map) { 399 // Since this API is obsolete, don't worry about reserving vectors, etc. 400 foreach (const(ShapeEdge) edge; getCrossingEdges(a0, a1, type)) { 401 S2Shape shape = _index.shape(edge.id().shapeId); 402 edge_map[shape] ~= edge.id().edgeId; 403 } 404 return !edge_map.empty(); 405 } 406 407 deprecated("Use method returning std::vector") 408 bool getCandidates(in S2Point a0, in S2Point a1, in S2Shape shape, out int[] edges) { 409 foreach (const(ShapeEdgeId) edge; getCandidates(a0, a1, shape)) { 410 edges ~= edge.edgeId; 411 } 412 return !edges.empty(); 413 } 414 415 deprecated("Use method returning std::vector") 416 bool getCandidates(in S2Point a0, in S2Point a1, out EdgeMap edge_map) { 417 // Since this API is obsolete, don't worry about reserving vectors, etc. 418 foreach (const(ShapeEdgeId) edge; getCandidates(a0, a1)) { 419 S2Shape shape = _index.shape(edge.shapeId); 420 edge_map[shape] ~= edge.edgeId; 421 } 422 return !edge_map.empty(); 423 } 424 425 private: 426 // Internal methods are documented with their definitions. 427 428 /** 429 * Computes the index cells intersected by the current edge that are 430 * descendants of "pcell" and calls visitor_ for each one. 431 * 432 * WARNING: This function is recursive with a maximum depth of 30. The frame 433 * size is about 2K in versions of GCC prior to 4.7 due to poor overlapping 434 * of storage for temporaries. This is fixed in GCC 4.7, reducing the frame 435 * size to about 350 bytes (i.e., worst-case total stack usage of about 10K). 436 */ 437 bool visitCells(S2PaddedCell pcell, in R2Rect edge_bound) { 438 _iter.seek(pcell.id().rangeMin()); 439 if (_iter.done() || _iter.id() > pcell.id().rangeMax()) { 440 // The index does not contain "pcell" or any of its descendants. 441 return true; 442 } 443 if (_iter.id() == pcell.id()) { 444 return _visitor(_iter.cell()); 445 } 446 447 // Otherwise, split the edge among the four children of "pcell". 448 R2Point center = pcell.middle().lo(); 449 if (edge_bound[0].hi() < center[0]) { 450 // Edge is entirely contained in the two left children. 451 return clipVAxis(edge_bound, center[1], 0, pcell); 452 } else if (edge_bound[0].lo() >= center[0]) { 453 // Edge is entirely contained in the two right children. 454 return clipVAxis(edge_bound, center[1], 1, pcell); 455 } else { 456 R2Rect[2] child_bounds; 457 splitUBound(edge_bound, center[0], child_bounds); 458 if (edge_bound[1].hi() < center[1]) { 459 // Edge is entirely contained in the two lower children. 460 return visitCells(new S2PaddedCell(pcell, 0, 0), child_bounds[0]) 461 && visitCells(new S2PaddedCell(pcell, 1, 0), child_bounds[1]); 462 } else if (edge_bound[1].lo() >= center[1]) { 463 // Edge is entirely contained in the two upper children. 464 return visitCells(new S2PaddedCell(pcell, 0, 1), child_bounds[0]) 465 && visitCells(new S2PaddedCell(pcell, 1, 1), child_bounds[1]); 466 } else { 467 // The edge bound spans all four children. The edge itself intersects 468 // at most three children (since no padding is being used). 469 return clipVAxis(child_bounds[0], center[1], 0, pcell) 470 && clipVAxis(child_bounds[1], center[1], 1, pcell); 471 } 472 } 473 } 474 475 /** 476 * Given either the left (i=0) or right (i=1) side of a padded cell "pcell", 477 * determine whether the current edge intersects the lower child, upper child, 478 * or both children, and call VisitCells() recursively on those children. 479 * "center" is the v-coordinate at the center of "pcell". 480 */ 481 bool clipVAxis(in R2Rect edge_bound, double center, int i, S2PaddedCell pcell) { 482 if (edge_bound[1].hi() < center) { 483 // Edge is entirely contained in the lower child. 484 return visitCells(new S2PaddedCell(pcell, i, 0), edge_bound); 485 } else if (edge_bound[1].lo() >= center) { 486 // Edge is entirely contained in the upper child. 487 return visitCells(new S2PaddedCell(pcell, i, 1), edge_bound); 488 } else { 489 // The edge intersects both children. 490 R2Rect[2] child_bounds; 491 splitVBound(edge_bound, center, child_bounds); 492 return visitCells(new S2PaddedCell(pcell, i, 0), child_bounds[0]) 493 && visitCells(new S2PaddedCell(pcell, i, 1), child_bounds[1]); 494 } 495 } 496 497 /** 498 * Split the current edge into two child edges at the given u-value "u" and 499 * return the bound for each child. 500 */ 501 void splitUBound(in R2Rect edge_bound, double u, ref R2Rect[2] child_bounds) const { 502 // See comments in MutableS2ShapeIndex::ClipUBound. 503 double v = edge_bound[1].project(interpolateDouble(u, _a0[0], _a1[0], _a0[1], _a1[1])); 504 505 // "diag_" indicates which diagonal of the bounding box is spanned by a0a1: 506 // it is 0 if a0a1 has positive slope, and 1 if a0a1 has negative slope. 507 int diag = (_a0[0] > _a1[0]) != (_a0[1] > _a1[1]); 508 splitBound(edge_bound, 0, u, diag, v, child_bounds); 509 } 510 511 /** 512 * Split the current edge into two child edges at the given v-value "v" and 513 * return the bound for each child. 514 */ 515 void splitVBound(in R2Rect edge_bound, double v, ref R2Rect[2] child_bounds) const { 516 double u = edge_bound[0].project(interpolateDouble(v, _a0[1], _a1[1], _a0[0], _a1[0])); 517 int diag = (_a0[0] > _a1[0]) != (_a0[1] > _a1[1]); 518 splitBound(edge_bound, diag, u, 0, v, child_bounds); 519 } 520 521 /** 522 * Split the current edge into two child edges at the given point (u,v) and 523 * return the bound for each child. "u_end" and "v_end" indicate which bound 524 * endpoints of child 1 will be updated. 525 */ 526 static void splitBound( 527 in R2Rect edge_bound, int u_end, double u, int v_end, double v, ref R2Rect[2] child_bounds) { 528 child_bounds[0] = edge_bound; 529 child_bounds[0][0][1 - u_end] = u; 530 child_bounds[0][1][1 - v_end] = v; 531 enforce(!child_bounds[0].isEmpty()); 532 enforce(edge_bound.contains(child_bounds[0])); 533 534 child_bounds[1] = edge_bound; 535 child_bounds[1][0][u_end] = u; 536 child_bounds[1][1][v_end] = v; 537 enforce(!child_bounds[1].isEmpty()); 538 enforce(edge_bound.contains(child_bounds[1])); 539 } 540 541 S2ShapeIndex _index; 542 543 //////////// Temporary storage used while processing a query /////////// 544 545 R2Point _a0, _a1; 546 S2ShapeIndex.Iterator _iter; 547 CellVisitor _visitor; 548 549 // Avoids repeated allocation when methods are called many times. 550 ShapeEdgeId[] _tmpCandidates; 551 }