loop.go (66564B)
1 // Copyright 2015 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 package s2 16 17 import ( 18 "fmt" 19 "io" 20 "math" 21 22 "github.com/golang/geo/r1" 23 "github.com/golang/geo/r3" 24 "github.com/golang/geo/s1" 25 ) 26 27 // Loop represents a simple spherical polygon. It consists of a sequence 28 // of vertices where the first vertex is implicitly connected to the 29 // last. All loops are defined to have a CCW orientation, i.e. the interior of 30 // the loop is on the left side of the edges. This implies that a clockwise 31 // loop enclosing a small area is interpreted to be a CCW loop enclosing a 32 // very large area. 33 // 34 // Loops are not allowed to have any duplicate vertices (whether adjacent or 35 // not). Non-adjacent edges are not allowed to intersect, and furthermore edges 36 // of length 180 degrees are not allowed (i.e., adjacent vertices cannot be 37 // antipodal). Loops must have at least 3 vertices (except for the "empty" and 38 // "full" loops discussed below). 39 // 40 // There are two special loops: the "empty" loop contains no points and the 41 // "full" loop contains all points. These loops do not have any edges, but to 42 // preserve the invariant that every loop can be represented as a vertex 43 // chain, they are defined as having exactly one vertex each (see EmptyLoop 44 // and FullLoop). 45 type Loop struct { 46 vertices []Point 47 48 // originInside keeps a precomputed value whether this loop contains the origin 49 // versus computing from the set of vertices every time. 50 originInside bool 51 52 // depth is the nesting depth of this Loop if it is contained by a Polygon 53 // or other shape and is used to determine if this loop represents a hole 54 // or a filled in portion. 55 depth int 56 57 // bound is a conservative bound on all points contained by this loop. 58 // If l.ContainsPoint(P), then l.bound.ContainsPoint(P). 59 bound Rect 60 61 // Since bound is not exact, it is possible that a loop A contains 62 // another loop B whose bounds are slightly larger. subregionBound 63 // has been expanded sufficiently to account for this error, i.e. 64 // if A.Contains(B), then A.subregionBound.Contains(B.bound). 65 subregionBound Rect 66 67 // index is the spatial index for this Loop. 68 index *ShapeIndex 69 } 70 71 // LoopFromPoints constructs a loop from the given points. 72 func LoopFromPoints(pts []Point) *Loop { 73 l := &Loop{ 74 vertices: pts, 75 index: NewShapeIndex(), 76 } 77 78 l.initOriginAndBound() 79 return l 80 } 81 82 // LoopFromCell constructs a loop corresponding to the given cell. 83 // 84 // Note that the loop and cell *do not* contain exactly the same set of 85 // points, because Loop and Cell have slightly different definitions of 86 // point containment. For example, a Cell vertex is contained by all 87 // four neighboring Cells, but it is contained by exactly one of four 88 // Loops constructed from those cells. As another example, the cell 89 // coverings of cell and LoopFromCell(cell) will be different, because the 90 // loop contains points on its boundary that actually belong to other cells 91 // (i.e., the covering will include a layer of neighboring cells). 92 func LoopFromCell(c Cell) *Loop { 93 l := &Loop{ 94 vertices: []Point{ 95 c.Vertex(0), 96 c.Vertex(1), 97 c.Vertex(2), 98 c.Vertex(3), 99 }, 100 index: NewShapeIndex(), 101 } 102 103 l.initOriginAndBound() 104 return l 105 } 106 107 // These two points are used for the special Empty and Full loops. 108 var ( 109 emptyLoopPoint = Point{r3.Vector{X: 0, Y: 0, Z: 1}} 110 fullLoopPoint = Point{r3.Vector{X: 0, Y: 0, Z: -1}} 111 ) 112 113 // EmptyLoop returns a special "empty" loop. 114 func EmptyLoop() *Loop { 115 return LoopFromPoints([]Point{emptyLoopPoint}) 116 } 117 118 // FullLoop returns a special "full" loop. 119 func FullLoop() *Loop { 120 return LoopFromPoints([]Point{fullLoopPoint}) 121 } 122 123 // initOriginAndBound sets the origin containment for the given point and then calls 124 // the initialization for the bounds objects and the internal index. 125 func (l *Loop) initOriginAndBound() { 126 if len(l.vertices) < 3 { 127 // Check for the special "empty" and "full" loops (which have one vertex). 128 if !l.isEmptyOrFull() { 129 l.originInside = false 130 return 131 } 132 133 // This is the special empty or full loop, so the origin depends on if 134 // the vertex is in the southern hemisphere or not. 135 l.originInside = l.vertices[0].Z < 0 136 } else { 137 // Point containment testing is done by counting edge crossings starting 138 // at a fixed point on the sphere (OriginPoint). We need to know whether 139 // the reference point (OriginPoint) is inside or outside the loop before 140 // we can construct the ShapeIndex. We do this by first guessing that 141 // it is outside, and then seeing whether we get the correct containment 142 // result for vertex 1. If the result is incorrect, the origin must be 143 // inside the loop. 144 // 145 // A loop with consecutive vertices A,B,C contains vertex B if and only if 146 // the fixed vector R = B.Ortho is contained by the wedge ABC. The 147 // wedge is closed at A and open at C, i.e. the point B is inside the loop 148 // if A = R but not if C = R. This convention is required for compatibility 149 // with VertexCrossing. (Note that we can't use OriginPoint 150 // as the fixed vector because of the possibility that B == OriginPoint.) 151 l.originInside = false 152 v1Inside := OrderedCCW(Point{l.vertices[1].Ortho()}, l.vertices[0], l.vertices[2], l.vertices[1]) 153 if v1Inside != l.ContainsPoint(l.vertices[1]) { 154 l.originInside = true 155 } 156 } 157 158 // We *must* call initBound before initializing the index, because 159 // initBound calls ContainsPoint which does a bounds check before using 160 // the index. 161 l.initBound() 162 163 // Create a new index and add us to it. 164 l.index = NewShapeIndex() 165 l.index.Add(l) 166 } 167 168 // initBound sets up the approximate bounding Rects for this loop. 169 func (l *Loop) initBound() { 170 if len(l.vertices) == 0 { 171 *l = *EmptyLoop() 172 return 173 } 174 // Check for the special "empty" and "full" loops. 175 if l.isEmptyOrFull() { 176 if l.IsEmpty() { 177 l.bound = EmptyRect() 178 } else { 179 l.bound = FullRect() 180 } 181 l.subregionBound = l.bound 182 return 183 } 184 185 // The bounding rectangle of a loop is not necessarily the same as the 186 // bounding rectangle of its vertices. First, the maximal latitude may be 187 // attained along the interior of an edge. Second, the loop may wrap 188 // entirely around the sphere (e.g. a loop that defines two revolutions of a 189 // candy-cane stripe). Third, the loop may include one or both poles. 190 // Note that a small clockwise loop near the equator contains both poles. 191 bounder := NewRectBounder() 192 for i := 0; i <= len(l.vertices); i++ { // add vertex 0 twice 193 bounder.AddPoint(l.Vertex(i)) 194 } 195 b := bounder.RectBound() 196 197 if l.ContainsPoint(Point{r3.Vector{0, 0, 1}}) { 198 b = Rect{r1.Interval{b.Lat.Lo, math.Pi / 2}, s1.FullInterval()} 199 } 200 // If a loop contains the south pole, then either it wraps entirely 201 // around the sphere (full longitude range), or it also contains the 202 // north pole in which case b.Lng.IsFull() due to the test above. 203 // Either way, we only need to do the south pole containment test if 204 // b.Lng.IsFull(). 205 if b.Lng.IsFull() && l.ContainsPoint(Point{r3.Vector{0, 0, -1}}) { 206 b.Lat.Lo = -math.Pi / 2 207 } 208 l.bound = b 209 l.subregionBound = ExpandForSubregions(l.bound) 210 } 211 212 // Validate checks whether this is a valid loop. 213 func (l *Loop) Validate() error { 214 if err := l.findValidationErrorNoIndex(); err != nil { 215 return err 216 } 217 218 // Check for intersections between non-adjacent edges (including at vertices) 219 // TODO(roberts): Once shapeutil gets findAnyCrossing uncomment this. 220 // return findAnyCrossing(l.index) 221 222 return nil 223 } 224 225 // findValidationErrorNoIndex reports whether this is not a valid loop, but 226 // skips checks that would require a ShapeIndex to be built for the loop. This 227 // is primarily used by Polygon to do validation so it doesn't trigger the 228 // creation of unneeded ShapeIndices. 229 func (l *Loop) findValidationErrorNoIndex() error { 230 // All vertices must be unit length. 231 for i, v := range l.vertices { 232 if !v.IsUnit() { 233 return fmt.Errorf("vertex %d is not unit length", i) 234 } 235 } 236 237 // Loops must have at least 3 vertices (except for empty and full). 238 if len(l.vertices) < 3 { 239 if l.isEmptyOrFull() { 240 return nil // Skip remaining tests. 241 } 242 return fmt.Errorf("non-empty, non-full loops must have at least 3 vertices") 243 } 244 245 // Loops are not allowed to have any duplicate vertices or edge crossings. 246 // We split this check into two parts. First we check that no edge is 247 // degenerate (identical endpoints). Then we check that there are no 248 // intersections between non-adjacent edges (including at vertices). The 249 // second check needs the ShapeIndex, so it does not fall within the scope 250 // of this method. 251 for i, v := range l.vertices { 252 if v == l.Vertex(i+1) { 253 return fmt.Errorf("edge %d is degenerate (duplicate vertex)", i) 254 } 255 256 // Antipodal vertices are not allowed. 257 if other := (Point{l.Vertex(i + 1).Mul(-1)}); v == other { 258 return fmt.Errorf("vertices %d and %d are antipodal", i, 259 (i+1)%len(l.vertices)) 260 } 261 } 262 263 return nil 264 } 265 266 // Contains reports whether the region contained by this loop is a superset of the 267 // region contained by the given other loop. 268 func (l *Loop) Contains(o *Loop) bool { 269 // For a loop A to contain the loop B, all of the following must 270 // be true: 271 // 272 // (1) There are no edge crossings between A and B except at vertices. 273 // 274 // (2) At every vertex that is shared between A and B, the local edge 275 // ordering implies that A contains B. 276 // 277 // (3) If there are no shared vertices, then A must contain a vertex of B 278 // and B must not contain a vertex of A. (An arbitrary vertex may be 279 // chosen in each case.) 280 // 281 // The second part of (3) is necessary to detect the case of two loops whose 282 // union is the entire sphere, i.e. two loops that contains each other's 283 // boundaries but not each other's interiors. 284 if !l.subregionBound.Contains(o.bound) { 285 return false 286 } 287 288 // Special cases to handle either loop being empty or full. 289 if l.isEmptyOrFull() || o.isEmptyOrFull() { 290 return l.IsFull() || o.IsEmpty() 291 } 292 293 // Check whether there are any edge crossings, and also check the loop 294 // relationship at any shared vertices. 295 relation := &containsRelation{} 296 if hasCrossingRelation(l, o, relation) { 297 return false 298 } 299 300 // There are no crossings, and if there are any shared vertices then A 301 // contains B locally at each shared vertex. 302 if relation.foundSharedVertex { 303 return true 304 } 305 306 // Since there are no edge intersections or shared vertices, we just need to 307 // test condition (3) above. We can skip this test if we discovered that A 308 // contains at least one point of B while checking for edge crossings. 309 if !l.ContainsPoint(o.Vertex(0)) { 310 return false 311 } 312 313 // We still need to check whether (A union B) is the entire sphere. 314 // Normally this check is very cheap due to the bounding box precondition. 315 if (o.subregionBound.Contains(l.bound) || o.bound.Union(l.bound).IsFull()) && 316 o.ContainsPoint(l.Vertex(0)) { 317 return false 318 } 319 return true 320 } 321 322 // Intersects reports whether the region contained by this loop intersects the region 323 // contained by the other loop. 324 func (l *Loop) Intersects(o *Loop) bool { 325 // Given two loops, A and B, A.Intersects(B) if and only if !A.Complement().Contains(B). 326 // 327 // This code is similar to Contains, but is optimized for the case 328 // where both loops enclose less than half of the sphere. 329 if !l.bound.Intersects(o.bound) { 330 return false 331 } 332 333 // Check whether there are any edge crossings, and also check the loop 334 // relationship at any shared vertices. 335 relation := &intersectsRelation{} 336 if hasCrossingRelation(l, o, relation) { 337 return true 338 } 339 if relation.foundSharedVertex { 340 return false 341 } 342 343 // Since there are no edge intersections or shared vertices, the loops 344 // intersect only if A contains B, B contains A, or the two loops contain 345 // each other's boundaries. These checks are usually cheap because of the 346 // bounding box preconditions. Note that neither loop is empty (because of 347 // the bounding box check above), so it is safe to access vertex(0). 348 349 // Check whether A contains B, or A and B contain each other's boundaries. 350 // (Note that A contains all the vertices of B in either case.) 351 if l.subregionBound.Contains(o.bound) || l.bound.Union(o.bound).IsFull() { 352 if l.ContainsPoint(o.Vertex(0)) { 353 return true 354 } 355 } 356 // Check whether B contains A. 357 if o.subregionBound.Contains(l.bound) { 358 if o.ContainsPoint(l.Vertex(0)) { 359 return true 360 } 361 } 362 return false 363 } 364 365 // Equal reports whether two loops have the same vertices in the same linear order 366 // (i.e., cyclic rotations are not allowed). 367 func (l *Loop) Equal(other *Loop) bool { 368 if len(l.vertices) != len(other.vertices) { 369 return false 370 } 371 372 for i, v := range l.vertices { 373 if v != other.Vertex(i) { 374 return false 375 } 376 } 377 return true 378 } 379 380 // BoundaryEqual reports whether the two loops have the same boundary. This is 381 // true if and only if the loops have the same vertices in the same cyclic order 382 // (i.e., the vertices may be cyclically rotated). The empty and full loops are 383 // considered to have different boundaries. 384 func (l *Loop) BoundaryEqual(o *Loop) bool { 385 if len(l.vertices) != len(o.vertices) { 386 return false 387 } 388 389 // Special case to handle empty or full loops. Since they have the same 390 // number of vertices, if one loop is empty/full then so is the other. 391 if l.isEmptyOrFull() { 392 return l.IsEmpty() == o.IsEmpty() 393 } 394 395 // Loop through the vertices to find the first of ours that matches the 396 // starting vertex of the other loop. Use that offset to then 'align' the 397 // vertices for comparison. 398 for offset, vertex := range l.vertices { 399 if vertex == o.Vertex(0) { 400 // There is at most one starting offset since loop vertices are unique. 401 for i := 0; i < len(l.vertices); i++ { 402 if l.Vertex(i+offset) != o.Vertex(i) { 403 return false 404 } 405 } 406 return true 407 } 408 } 409 return false 410 } 411 412 // compareBoundary returns +1 if this loop contains the boundary of the other loop, 413 // -1 if it excludes the boundary of the other, and 0 if the boundaries of the two 414 // loops cross. Shared edges are handled as follows: 415 // 416 // If XY is a shared edge, define Reversed(XY) to be true if XY 417 // appears in opposite directions in both loops. 418 // Then this loop contains XY if and only if Reversed(XY) == the other loop is a hole. 419 // (Intuitively, this checks whether this loop contains a vanishingly small region 420 // extending from the boundary of the other toward the interior of the polygon to 421 // which the other belongs.) 422 // 423 // This function is used for testing containment and intersection of 424 // multi-loop polygons. Note that this method is not symmetric, since the 425 // result depends on the direction of this loop but not on the direction of 426 // the other loop (in the absence of shared edges). 427 // 428 // This requires that neither loop is empty, and if other loop IsFull, then it must not 429 // be a hole. 430 func (l *Loop) compareBoundary(o *Loop) int { 431 // The bounds must intersect for containment or crossing. 432 if !l.bound.Intersects(o.bound) { 433 return -1 434 } 435 436 // Full loops are handled as though the loop surrounded the entire sphere. 437 if l.IsFull() { 438 return 1 439 } 440 if o.IsFull() { 441 return -1 442 } 443 444 // Check whether there are any edge crossings, and also check the loop 445 // relationship at any shared vertices. 446 relation := newCompareBoundaryRelation(o.IsHole()) 447 if hasCrossingRelation(l, o, relation) { 448 return 0 449 } 450 if relation.foundSharedVertex { 451 if relation.containsEdge { 452 return 1 453 } 454 return -1 455 } 456 457 // There are no edge intersections or shared vertices, so we can check 458 // whether A contains an arbitrary vertex of B. 459 if l.ContainsPoint(o.Vertex(0)) { 460 return 1 461 } 462 return -1 463 } 464 465 // ContainsOrigin reports true if this loop contains s2.OriginPoint(). 466 func (l *Loop) ContainsOrigin() bool { 467 return l.originInside 468 } 469 470 // ReferencePoint returns the reference point for this loop. 471 func (l *Loop) ReferencePoint() ReferencePoint { 472 return OriginReferencePoint(l.originInside) 473 } 474 475 // NumEdges returns the number of edges in this shape. 476 func (l *Loop) NumEdges() int { 477 if l.isEmptyOrFull() { 478 return 0 479 } 480 return len(l.vertices) 481 } 482 483 // Edge returns the endpoints for the given edge index. 484 func (l *Loop) Edge(i int) Edge { 485 return Edge{l.Vertex(i), l.Vertex(i + 1)} 486 } 487 488 // NumChains reports the number of contiguous edge chains in the Loop. 489 func (l *Loop) NumChains() int { 490 if l.IsEmpty() { 491 return 0 492 } 493 return 1 494 } 495 496 // Chain returns the i-th edge chain in the Shape. 497 func (l *Loop) Chain(chainID int) Chain { 498 return Chain{0, l.NumEdges()} 499 } 500 501 // ChainEdge returns the j-th edge of the i-th edge chain. 502 func (l *Loop) ChainEdge(chainID, offset int) Edge { 503 return Edge{l.Vertex(offset), l.Vertex(offset + 1)} 504 } 505 506 // ChainPosition returns a ChainPosition pair (i, j) such that edgeID is the 507 // j-th edge of the Loop. 508 func (l *Loop) ChainPosition(edgeID int) ChainPosition { 509 return ChainPosition{0, edgeID} 510 } 511 512 // Dimension returns the dimension of the geometry represented by this Loop. 513 func (l *Loop) Dimension() int { return 2 } 514 515 func (l *Loop) typeTag() typeTag { return typeTagNone } 516 517 func (l *Loop) privateInterface() {} 518 519 // IsEmpty reports true if this is the special empty loop that contains no points. 520 func (l *Loop) IsEmpty() bool { 521 return l.isEmptyOrFull() && !l.ContainsOrigin() 522 } 523 524 // IsFull reports true if this is the special full loop that contains all points. 525 func (l *Loop) IsFull() bool { 526 return l.isEmptyOrFull() && l.ContainsOrigin() 527 } 528 529 // isEmptyOrFull reports true if this loop is either the "empty" or "full" special loops. 530 func (l *Loop) isEmptyOrFull() bool { 531 return len(l.vertices) == 1 532 } 533 534 // Vertices returns the vertices in the loop. 535 func (l *Loop) Vertices() []Point { 536 return l.vertices 537 } 538 539 // RectBound returns a tight bounding rectangle. If the loop contains the point, 540 // the bound also contains it. 541 func (l *Loop) RectBound() Rect { 542 return l.bound 543 } 544 545 // CapBound returns a bounding cap that may have more padding than the corresponding 546 // RectBound. The bound is conservative such that if the loop contains a point P, 547 // the bound also contains it. 548 func (l *Loop) CapBound() Cap { 549 return l.bound.CapBound() 550 } 551 552 // Vertex returns the vertex for the given index. For convenience, the vertex indices 553 // wrap automatically for methods that do index math such as Edge. 554 // i.e., Vertex(NumEdges() + n) is the same as Vertex(n). 555 func (l *Loop) Vertex(i int) Point { 556 return l.vertices[i%len(l.vertices)] 557 } 558 559 // OrientedVertex returns the vertex in reverse order if the loop represents a polygon 560 // hole. For example, arguments 0, 1, 2 are mapped to vertices n-1, n-2, n-3, where 561 // n == len(vertices). This ensures that the interior of the polygon is always to 562 // the left of the vertex chain. 563 // 564 // This requires: 0 <= i < 2 * len(vertices) 565 func (l *Loop) OrientedVertex(i int) Point { 566 j := i - len(l.vertices) 567 if j < 0 { 568 j = i 569 } 570 if l.IsHole() { 571 j = len(l.vertices) - 1 - j 572 } 573 return l.Vertex(j) 574 } 575 576 // NumVertices returns the number of vertices in this loop. 577 func (l *Loop) NumVertices() int { 578 return len(l.vertices) 579 } 580 581 // bruteForceContainsPoint reports if the given point is contained by this loop. 582 // This method does not use the ShapeIndex, so it is only preferable below a certain 583 // size of loop. 584 func (l *Loop) bruteForceContainsPoint(p Point) bool { 585 origin := OriginPoint() 586 inside := l.originInside 587 crosser := NewChainEdgeCrosser(origin, p, l.Vertex(0)) 588 for i := 1; i <= len(l.vertices); i++ { // add vertex 0 twice 589 inside = inside != crosser.EdgeOrVertexChainCrossing(l.Vertex(i)) 590 } 591 return inside 592 } 593 594 // ContainsPoint returns true if the loop contains the point. 595 func (l *Loop) ContainsPoint(p Point) bool { 596 if !l.index.IsFresh() && !l.bound.ContainsPoint(p) { 597 return false 598 } 599 600 // For small loops it is faster to just check all the crossings. We also 601 // use this method during loop initialization because InitOriginAndBound() 602 // calls Contains() before InitIndex(). Otherwise, we keep track of the 603 // number of calls to Contains() and only build the index when enough calls 604 // have been made so that we think it is worth the effort. Note that the 605 // code below is structured so that if many calls are made in parallel only 606 // one thread builds the index, while the rest continue using brute force 607 // until the index is actually available. 608 609 const maxBruteForceVertices = 32 610 // TODO(roberts): add unindexed contains calls tracking 611 612 if len(l.index.shapes) == 0 || // Index has not been initialized yet. 613 len(l.vertices) <= maxBruteForceVertices { 614 return l.bruteForceContainsPoint(p) 615 } 616 617 // Otherwise, look up the point in the index. 618 it := l.index.Iterator() 619 if !it.LocatePoint(p) { 620 return false 621 } 622 return l.iteratorContainsPoint(it, p) 623 } 624 625 // ContainsCell reports whether the given Cell is contained by this Loop. 626 func (l *Loop) ContainsCell(target Cell) bool { 627 it := l.index.Iterator() 628 relation := it.LocateCellID(target.ID()) 629 630 // If "target" is disjoint from all index cells, it is not contained. 631 // Similarly, if "target" is subdivided into one or more index cells then it 632 // is not contained, since index cells are subdivided only if they (nearly) 633 // intersect a sufficient number of edges. (But note that if "target" itself 634 // is an index cell then it may be contained, since it could be a cell with 635 // no edges in the loop interior.) 636 if relation != Indexed { 637 return false 638 } 639 640 // Otherwise check if any edges intersect "target". 641 if l.boundaryApproxIntersects(it, target) { 642 return false 643 } 644 645 // Otherwise check if the loop contains the center of "target". 646 return l.iteratorContainsPoint(it, target.Center()) 647 } 648 649 // IntersectsCell reports whether this Loop intersects the given cell. 650 func (l *Loop) IntersectsCell(target Cell) bool { 651 it := l.index.Iterator() 652 relation := it.LocateCellID(target.ID()) 653 654 // If target does not overlap any index cell, there is no intersection. 655 if relation == Disjoint { 656 return false 657 } 658 // If target is subdivided into one or more index cells, there is an 659 // intersection to within the ShapeIndex error bound (see Contains). 660 if relation == Subdivided { 661 return true 662 } 663 // If target is an index cell, there is an intersection because index cells 664 // are created only if they have at least one edge or they are entirely 665 // contained by the loop. 666 if it.CellID() == target.id { 667 return true 668 } 669 // Otherwise check if any edges intersect target. 670 if l.boundaryApproxIntersects(it, target) { 671 return true 672 } 673 // Otherwise check if the loop contains the center of target. 674 return l.iteratorContainsPoint(it, target.Center()) 675 } 676 677 // CellUnionBound computes a covering of the Loop. 678 func (l *Loop) CellUnionBound() []CellID { 679 return l.CapBound().CellUnionBound() 680 } 681 682 // boundaryApproxIntersects reports if the loop's boundary intersects target. 683 // It may also return true when the loop boundary does not intersect target but 684 // some edge comes within the worst-case error tolerance. 685 // 686 // This requires that it.Locate(target) returned Indexed. 687 func (l *Loop) boundaryApproxIntersects(it *ShapeIndexIterator, target Cell) bool { 688 aClipped := it.IndexCell().findByShapeID(0) 689 690 // If there are no edges, there is no intersection. 691 if len(aClipped.edges) == 0 { 692 return false 693 } 694 695 // We can save some work if target is the index cell itself. 696 if it.CellID() == target.ID() { 697 return true 698 } 699 700 // Otherwise check whether any of the edges intersect target. 701 maxError := (faceClipErrorUVCoord + intersectsRectErrorUVDist) 702 bound := target.BoundUV().ExpandedByMargin(maxError) 703 for _, ai := range aClipped.edges { 704 v0, v1, ok := ClipToPaddedFace(l.Vertex(ai), l.Vertex(ai+1), target.Face(), maxError) 705 if ok && edgeIntersectsRect(v0, v1, bound) { 706 return true 707 } 708 } 709 return false 710 } 711 712 // iteratorContainsPoint reports if the iterator that is positioned at the ShapeIndexCell 713 // that may contain p, contains the point p. 714 func (l *Loop) iteratorContainsPoint(it *ShapeIndexIterator, p Point) bool { 715 // Test containment by drawing a line segment from the cell center to the 716 // given point and counting edge crossings. 717 aClipped := it.IndexCell().findByShapeID(0) 718 inside := aClipped.containsCenter 719 if len(aClipped.edges) > 0 { 720 center := it.Center() 721 crosser := NewEdgeCrosser(center, p) 722 aiPrev := -2 723 for _, ai := range aClipped.edges { 724 if ai != aiPrev+1 { 725 crosser.RestartAt(l.Vertex(ai)) 726 } 727 aiPrev = ai 728 inside = inside != crosser.EdgeOrVertexChainCrossing(l.Vertex(ai+1)) 729 } 730 } 731 return inside 732 } 733 734 // RegularLoop creates a loop with the given number of vertices, all 735 // located on a circle of the specified radius around the given center. 736 func RegularLoop(center Point, radius s1.Angle, numVertices int) *Loop { 737 return RegularLoopForFrame(getFrame(center), radius, numVertices) 738 } 739 740 // RegularLoopForFrame creates a loop centered around the z-axis of the given 741 // coordinate frame, with the first vertex in the direction of the positive x-axis. 742 func RegularLoopForFrame(frame matrix3x3, radius s1.Angle, numVertices int) *Loop { 743 return LoopFromPoints(regularPointsForFrame(frame, radius, numVertices)) 744 } 745 746 // CanonicalFirstVertex returns a first index and a direction (either +1 or -1) 747 // such that the vertex sequence (first, first+dir, ..., first+(n-1)*dir) does 748 // not change when the loop vertex order is rotated or inverted. This allows the 749 // loop vertices to be traversed in a canonical order. The return values are 750 // chosen such that (first, ..., first+n*dir) are in the range [0, 2*n-1] as 751 // expected by the Vertex method. 752 func (l *Loop) CanonicalFirstVertex() (firstIdx, direction int) { 753 firstIdx = 0 754 n := len(l.vertices) 755 for i := 1; i < n; i++ { 756 if l.Vertex(i).Cmp(l.Vertex(firstIdx).Vector) == -1 { 757 firstIdx = i 758 } 759 } 760 761 // 0 <= firstIdx <= n-1, so (firstIdx+n*dir) <= 2*n-1. 762 if l.Vertex(firstIdx+1).Cmp(l.Vertex(firstIdx+n-1).Vector) == -1 { 763 return firstIdx, 1 764 } 765 766 // n <= firstIdx <= 2*n-1, so (firstIdx+n*dir) >= 0. 767 firstIdx += n 768 return firstIdx, -1 769 } 770 771 // TurningAngle returns the sum of the turning angles at each vertex. The return 772 // value is positive if the loop is counter-clockwise, negative if the loop is 773 // clockwise, and zero if the loop is a great circle. Degenerate and 774 // nearly-degenerate loops are handled consistently with Sign. So for example, 775 // if a loop has zero area (i.e., it is a very small CCW loop) then the turning 776 // angle will always be negative. 777 // 778 // This quantity is also called the "geodesic curvature" of the loop. 779 func (l *Loop) TurningAngle() float64 { 780 // For empty and full loops, we return the limit value as the loop area 781 // approaches 0 or 4*Pi respectively. 782 if l.isEmptyOrFull() { 783 if l.ContainsOrigin() { 784 return -2 * math.Pi 785 } 786 return 2 * math.Pi 787 } 788 789 // Don't crash even if the loop is not well-defined. 790 if len(l.vertices) < 3 { 791 return 0 792 } 793 794 // To ensure that we get the same result when the vertex order is rotated, 795 // and that the result is negated when the vertex order is reversed, we need 796 // to add up the individual turn angles in a consistent order. (In general, 797 // adding up a set of numbers in a different order can change the sum due to 798 // rounding errors.) 799 // 800 // Furthermore, if we just accumulate an ordinary sum then the worst-case 801 // error is quadratic in the number of vertices. (This can happen with 802 // spiral shapes, where the partial sum of the turning angles can be linear 803 // in the number of vertices.) To avoid this we use the Kahan summation 804 // algorithm (http://en.wikipedia.org/wiki/Kahan_summation_algorithm). 805 n := len(l.vertices) 806 i, dir := l.CanonicalFirstVertex() 807 sum := TurnAngle(l.Vertex((i+n-dir)%n), l.Vertex(i), l.Vertex((i+dir)%n)) 808 809 compensation := s1.Angle(0) 810 for n-1 > 0 { 811 i += dir 812 angle := TurnAngle(l.Vertex(i-dir), l.Vertex(i), l.Vertex(i+dir)) 813 oldSum := sum 814 angle += compensation 815 sum += angle 816 compensation = (oldSum - sum) + angle 817 n-- 818 } 819 820 const maxCurvature = 2*math.Pi - 4*dblEpsilon 821 822 return math.Max(-maxCurvature, math.Min(maxCurvature, float64(dir)*float64(sum+compensation))) 823 } 824 825 // turningAngleMaxError return the maximum error in TurningAngle. The value is not 826 // constant; it depends on the loop. 827 func (l *Loop) turningAngleMaxError() float64 { 828 // The maximum error can be bounded as follows: 829 // 3.00 * dblEpsilon for RobustCrossProd(b, a) 830 // 3.00 * dblEpsilon for RobustCrossProd(c, b) 831 // 3.25 * dblEpsilon for Angle() 832 // 2.00 * dblEpsilon for each addition in the Kahan summation 833 // ------------------ 834 // 11.25 * dblEpsilon 835 maxErrorPerVertex := 11.25 * dblEpsilon 836 return maxErrorPerVertex * float64(len(l.vertices)) 837 } 838 839 // IsHole reports whether this loop represents a hole in its containing polygon. 840 func (l *Loop) IsHole() bool { return l.depth&1 != 0 } 841 842 // Sign returns -1 if this Loop represents a hole in its containing polygon, and +1 otherwise. 843 func (l *Loop) Sign() int { 844 if l.IsHole() { 845 return -1 846 } 847 return 1 848 } 849 850 // IsNormalized reports whether the loop area is at most 2*pi. Degenerate loops are 851 // handled consistently with Sign, i.e., if a loop can be 852 // expressed as the union of degenerate or nearly-degenerate CCW triangles, 853 // then it will always be considered normalized. 854 func (l *Loop) IsNormalized() bool { 855 // Optimization: if the longitude span is less than 180 degrees, then the 856 // loop covers less than half the sphere and is therefore normalized. 857 if l.bound.Lng.Length() < math.Pi { 858 return true 859 } 860 861 // We allow some error so that hemispheres are always considered normalized. 862 // TODO(roberts): This is no longer required by the Polygon implementation, 863 // so alternatively we could create the invariant that a loop is normalized 864 // if and only if its complement is not normalized. 865 return l.TurningAngle() >= -l.turningAngleMaxError() 866 } 867 868 // Normalize inverts the loop if necessary so that the area enclosed by the loop 869 // is at most 2*pi. 870 func (l *Loop) Normalize() { 871 if !l.IsNormalized() { 872 l.Invert() 873 } 874 } 875 876 // Invert reverses the order of the loop vertices, effectively complementing the 877 // region represented by the loop. For example, the loop ABCD (with edges 878 // AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD). 879 // Notice that the last edge is the same in both cases except that its 880 // direction has been reversed. 881 func (l *Loop) Invert() { 882 l.index.Reset() 883 if l.isEmptyOrFull() { 884 if l.IsFull() { 885 l.vertices[0] = emptyLoopPoint 886 } else { 887 l.vertices[0] = fullLoopPoint 888 } 889 } else { 890 // For non-special loops, reverse the slice of vertices. 891 for i := len(l.vertices)/2 - 1; i >= 0; i-- { 892 opp := len(l.vertices) - 1 - i 893 l.vertices[i], l.vertices[opp] = l.vertices[opp], l.vertices[i] 894 } 895 } 896 897 // originInside must be set correctly before building the ShapeIndex. 898 l.originInside = !l.originInside 899 if l.bound.Lat.Lo > -math.Pi/2 && l.bound.Lat.Hi < math.Pi/2 { 900 // The complement of this loop contains both poles. 901 l.bound = FullRect() 902 l.subregionBound = l.bound 903 } else { 904 l.initBound() 905 } 906 l.index.Add(l) 907 } 908 909 // findVertex returns the index of the vertex at the given Point in the range 910 // 1..numVertices, and a boolean indicating if a vertex was found. 911 func (l *Loop) findVertex(p Point) (index int, ok bool) { 912 const notFound = 0 913 if len(l.vertices) < 10 { 914 // Exhaustive search for loops below a small threshold. 915 for i := 1; i <= len(l.vertices); i++ { 916 if l.Vertex(i) == p { 917 return i, true 918 } 919 } 920 return notFound, false 921 } 922 923 it := l.index.Iterator() 924 if !it.LocatePoint(p) { 925 return notFound, false 926 } 927 928 aClipped := it.IndexCell().findByShapeID(0) 929 for i := aClipped.numEdges() - 1; i >= 0; i-- { 930 ai := aClipped.edges[i] 931 if l.Vertex(ai) == p { 932 if ai == 0 { 933 return len(l.vertices), true 934 } 935 return ai, true 936 } 937 938 if l.Vertex(ai+1) == p { 939 return ai + 1, true 940 } 941 } 942 return notFound, false 943 } 944 945 // ContainsNested reports whether the given loops is contained within this loop. 946 // This function does not test for edge intersections. The two loops must meet 947 // all of the Polygon requirements; for example this implies that their 948 // boundaries may not cross or have any shared edges (although they may have 949 // shared vertices). 950 func (l *Loop) ContainsNested(other *Loop) bool { 951 if !l.subregionBound.Contains(other.bound) { 952 return false 953 } 954 955 // Special cases to handle either loop being empty or full. Also bail out 956 // when B has no vertices to avoid heap overflow on the vertex(1) call 957 // below. (This method is called during polygon initialization before the 958 // client has an opportunity to call IsValid().) 959 if l.isEmptyOrFull() || other.NumVertices() < 2 { 960 return l.IsFull() || other.IsEmpty() 961 } 962 963 // We are given that A and B do not share any edges, and that either one 964 // loop contains the other or they do not intersect. 965 m, ok := l.findVertex(other.Vertex(1)) 966 if !ok { 967 // Since other.vertex(1) is not shared, we can check whether A contains it. 968 return l.ContainsPoint(other.Vertex(1)) 969 } 970 971 // Check whether the edge order around other.Vertex(1) is compatible with 972 // A containing B. 973 return WedgeContains(l.Vertex(m-1), l.Vertex(m), l.Vertex(m+1), other.Vertex(0), other.Vertex(2)) 974 } 975 976 // surfaceIntegralFloat64 computes the oriented surface integral of some quantity f(x) 977 // over the loop interior, given a function f(A,B,C) that returns the 978 // corresponding integral over the spherical triangle ABC. Here "oriented 979 // surface integral" means: 980 // 981 // (1) f(A,B,C) must be the integral of f if ABC is counterclockwise, 982 // and the integral of -f if ABC is clockwise. 983 // 984 // (2) The result of this function is *either* the integral of f over the 985 // loop interior, or the integral of (-f) over the loop exterior. 986 // 987 // Note that there are at least two common situations where it easy to work 988 // around property (2) above: 989 // 990 // - If the integral of f over the entire sphere is zero, then it doesn't 991 // matter which case is returned because they are always equal. 992 // 993 // - If f is non-negative, then it is easy to detect when the integral over 994 // the loop exterior has been returned, and the integral over the loop 995 // interior can be obtained by adding the integral of f over the entire 996 // unit sphere (a constant) to the result. 997 // 998 // Any changes to this method may need corresponding changes to surfaceIntegralPoint as well. 999 func (l *Loop) surfaceIntegralFloat64(f func(a, b, c Point) float64) float64 { 1000 // We sum f over a collection T of oriented triangles, possibly 1001 // overlapping. Let the sign of a triangle be +1 if it is CCW and -1 1002 // otherwise, and let the sign of a point x be the sum of the signs of the 1003 // triangles containing x. Then the collection of triangles T is chosen 1004 // such that either: 1005 // 1006 // (1) Each point in the loop interior has sign +1, and sign 0 otherwise; or 1007 // (2) Each point in the loop exterior has sign -1, and sign 0 otherwise. 1008 // 1009 // The triangles basically consist of a fan from vertex 0 to every loop 1010 // edge that does not include vertex 0. These triangles will always satisfy 1011 // either (1) or (2). However, what makes this a bit tricky is that 1012 // spherical edges become numerically unstable as their length approaches 1013 // 180 degrees. Of course there is not much we can do if the loop itself 1014 // contains such edges, but we would like to make sure that all the triangle 1015 // edges under our control (i.e., the non-loop edges) are stable. For 1016 // example, consider a loop around the equator consisting of four equally 1017 // spaced points. This is a well-defined loop, but we cannot just split it 1018 // into two triangles by connecting vertex 0 to vertex 2. 1019 // 1020 // We handle this type of situation by moving the origin of the triangle fan 1021 // whenever we are about to create an unstable edge. We choose a new 1022 // location for the origin such that all relevant edges are stable. We also 1023 // create extra triangles with the appropriate orientation so that the sum 1024 // of the triangle signs is still correct at every point. 1025 1026 // The maximum length of an edge for it to be considered numerically stable. 1027 // The exact value is fairly arbitrary since it depends on the stability of 1028 // the function f. The value below is quite conservative but could be 1029 // reduced further if desired. 1030 const maxLength = math.Pi - 1e-5 1031 1032 var sum float64 1033 origin := l.Vertex(0) 1034 for i := 1; i+1 < len(l.vertices); i++ { 1035 // Let V_i be vertex(i), let O be the current origin, and let length(A,B) 1036 // be the length of edge (A,B). At the start of each loop iteration, the 1037 // "leading edge" of the triangle fan is (O,V_i), and we want to extend 1038 // the triangle fan so that the leading edge is (O,V_i+1). 1039 // 1040 // Invariants: 1041 // 1. length(O,V_i) < maxLength for all (i > 1). 1042 // 2. Either O == V_0, or O is approximately perpendicular to V_0. 1043 // 3. "sum" is the oriented integral of f over the area defined by 1044 // (O, V_0, V_1, ..., V_i). 1045 if l.Vertex(i+1).Angle(origin.Vector) > maxLength { 1046 // We are about to create an unstable edge, so choose a new origin O' 1047 // for the triangle fan. 1048 oldOrigin := origin 1049 if origin == l.Vertex(0) { 1050 // The following point is well-separated from V_i and V_0 (and 1051 // therefore V_i+1 as well). 1052 origin = Point{l.Vertex(0).PointCross(l.Vertex(i)).Normalize()} 1053 } else if l.Vertex(i).Angle(l.Vertex(0).Vector) < maxLength { 1054 // All edges of the triangle (O, V_0, V_i) are stable, so we can 1055 // revert to using V_0 as the origin. 1056 origin = l.Vertex(0) 1057 } else { 1058 // (O, V_i+1) and (V_0, V_i) are antipodal pairs, and O and V_0 are 1059 // perpendicular. Therefore V_0.CrossProd(O) is approximately 1060 // perpendicular to all of {O, V_0, V_i, V_i+1}, and we can choose 1061 // this point O' as the new origin. 1062 origin = Point{l.Vertex(0).Cross(oldOrigin.Vector)} 1063 1064 // Advance the edge (V_0,O) to (V_0,O'). 1065 sum += f(l.Vertex(0), oldOrigin, origin) 1066 } 1067 // Advance the edge (O,V_i) to (O',V_i). 1068 sum += f(oldOrigin, l.Vertex(i), origin) 1069 } 1070 // Advance the edge (O,V_i) to (O,V_i+1). 1071 sum += f(origin, l.Vertex(i), l.Vertex(i+1)) 1072 } 1073 // If the origin is not V_0, we need to sum one more triangle. 1074 if origin != l.Vertex(0) { 1075 // Advance the edge (O,V_n-1) to (O,V_0). 1076 sum += f(origin, l.Vertex(len(l.vertices)-1), l.Vertex(0)) 1077 } 1078 return sum 1079 } 1080 1081 // surfaceIntegralPoint mirrors the surfaceIntegralFloat64 method but over Points; 1082 // see that method for commentary. The C++ version uses a templated method. 1083 // Any changes to this method may need corresponding changes to surfaceIntegralFloat64 as well. 1084 func (l *Loop) surfaceIntegralPoint(f func(a, b, c Point) Point) Point { 1085 const maxLength = math.Pi - 1e-5 1086 var sum r3.Vector 1087 1088 origin := l.Vertex(0) 1089 for i := 1; i+1 < len(l.vertices); i++ { 1090 if l.Vertex(i+1).Angle(origin.Vector) > maxLength { 1091 oldOrigin := origin 1092 if origin == l.Vertex(0) { 1093 origin = Point{l.Vertex(0).PointCross(l.Vertex(i)).Normalize()} 1094 } else if l.Vertex(i).Angle(l.Vertex(0).Vector) < maxLength { 1095 origin = l.Vertex(0) 1096 } else { 1097 origin = Point{l.Vertex(0).Cross(oldOrigin.Vector)} 1098 sum = sum.Add(f(l.Vertex(0), oldOrigin, origin).Vector) 1099 } 1100 sum = sum.Add(f(oldOrigin, l.Vertex(i), origin).Vector) 1101 } 1102 sum = sum.Add(f(origin, l.Vertex(i), l.Vertex(i+1)).Vector) 1103 } 1104 if origin != l.Vertex(0) { 1105 sum = sum.Add(f(origin, l.Vertex(len(l.vertices)-1), l.Vertex(0)).Vector) 1106 } 1107 return Point{sum} 1108 } 1109 1110 // Area returns the area of the loop interior, i.e. the region on the left side of 1111 // the loop. The return value is between 0 and 4*pi. (Note that the return 1112 // value is not affected by whether this loop is a "hole" or a "shell".) 1113 func (l *Loop) Area() float64 { 1114 // It is surprisingly difficult to compute the area of a loop robustly. The 1115 // main issues are (1) whether degenerate loops are considered to be CCW or 1116 // not (i.e., whether their area is close to 0 or 4*pi), and (2) computing 1117 // the areas of small loops with good relative accuracy. 1118 // 1119 // With respect to degeneracies, we would like Area to be consistent 1120 // with ContainsPoint in that loops that contain many points 1121 // should have large areas, and loops that contain few points should have 1122 // small areas. For example, if a degenerate triangle is considered CCW 1123 // according to s2predicates Sign, then it will contain very few points and 1124 // its area should be approximately zero. On the other hand if it is 1125 // considered clockwise, then it will contain virtually all points and so 1126 // its area should be approximately 4*pi. 1127 // 1128 // More precisely, let U be the set of Points for which IsUnitLength 1129 // is true, let P(U) be the projection of those points onto the mathematical 1130 // unit sphere, and let V(P(U)) be the Voronoi diagram of the projected 1131 // points. Then for every loop x, we would like Area to approximately 1132 // equal the sum of the areas of the Voronoi regions of the points p for 1133 // which x.ContainsPoint(p) is true. 1134 // 1135 // The second issue is that we want to compute the area of small loops 1136 // accurately. This requires having good relative precision rather than 1137 // good absolute precision. For example, if the area of a loop is 1e-12 and 1138 // the error is 1e-15, then the area only has 3 digits of accuracy. (For 1139 // reference, 1e-12 is about 40 square meters on the surface of the earth.) 1140 // We would like to have good relative accuracy even for small loops. 1141 // 1142 // To achieve these goals, we combine two different methods of computing the 1143 // area. This first method is based on the Gauss-Bonnet theorem, which says 1144 // that the area enclosed by the loop equals 2*pi minus the total geodesic 1145 // curvature of the loop (i.e., the sum of the "turning angles" at all the 1146 // loop vertices). The big advantage of this method is that as long as we 1147 // use Sign to compute the turning angle at each vertex, then 1148 // degeneracies are always handled correctly. In other words, if a 1149 // degenerate loop is CCW according to the symbolic perturbations used by 1150 // Sign, then its turning angle will be approximately 2*pi. 1151 // 1152 // The disadvantage of the Gauss-Bonnet method is that its absolute error is 1153 // about 2e-15 times the number of vertices (see turningAngleMaxError). 1154 // So, it cannot compute the area of small loops accurately. 1155 // 1156 // The second method is based on splitting the loop into triangles and 1157 // summing the area of each triangle. To avoid the difficulty and expense 1158 // of decomposing the loop into a union of non-overlapping triangles, 1159 // instead we compute a signed sum over triangles that may overlap (see the 1160 // comments for surfaceIntegral). The advantage of this method 1161 // is that the area of each triangle can be computed with much better 1162 // relative accuracy (using l'Huilier's theorem). The disadvantage is that 1163 // the result is a signed area: CCW loops may yield a small positive value, 1164 // while CW loops may yield a small negative value (which is converted to a 1165 // positive area by adding 4*pi). This means that small errors in computing 1166 // the signed area may translate into a very large error in the result (if 1167 // the sign of the sum is incorrect). 1168 // 1169 // So, our strategy is to combine these two methods as follows. First we 1170 // compute the area using the "signed sum over triangles" approach (since it 1171 // is generally more accurate). We also estimate the maximum error in this 1172 // result. If the signed area is too close to zero (i.e., zero is within 1173 // the error bounds), then we double-check the sign of the result using the 1174 // Gauss-Bonnet method. (In fact we just call IsNormalized, which is 1175 // based on this method.) If the two methods disagree, we return either 0 1176 // or 4*pi based on the result of IsNormalized. Otherwise we return the 1177 // area that we computed originally. 1178 if l.isEmptyOrFull() { 1179 if l.ContainsOrigin() { 1180 return 4 * math.Pi 1181 } 1182 return 0 1183 } 1184 area := l.surfaceIntegralFloat64(SignedArea) 1185 1186 // TODO(roberts): This error estimate is very approximate. There are two 1187 // issues: (1) SignedArea needs some improvements to ensure that its error 1188 // is actually never higher than GirardArea, and (2) although the number of 1189 // triangles in the sum is typically N-2, in theory it could be as high as 1190 // 2*N for pathological inputs. But in other respects this error bound is 1191 // very conservative since it assumes that the maximum error is achieved on 1192 // every triangle. 1193 maxError := l.turningAngleMaxError() 1194 1195 // The signed area should be between approximately -4*pi and 4*pi. 1196 if area < 0 { 1197 // We have computed the negative of the area of the loop exterior. 1198 area += 4 * math.Pi 1199 } 1200 1201 if area > 4*math.Pi { 1202 area = 4 * math.Pi 1203 } 1204 if area < 0 { 1205 area = 0 1206 } 1207 1208 // If the area is close enough to zero or 4*pi so that the loop orientation 1209 // is ambiguous, then we compute the loop orientation explicitly. 1210 if area < maxError && !l.IsNormalized() { 1211 return 4 * math.Pi 1212 } else if area > (4*math.Pi-maxError) && l.IsNormalized() { 1213 return 0 1214 } 1215 1216 return area 1217 } 1218 1219 // Centroid returns the true centroid of the loop multiplied by the area of the 1220 // loop. The result is not unit length, so you may want to normalize it. Also 1221 // note that in general, the centroid may not be contained by the loop. 1222 // 1223 // We prescale by the loop area for two reasons: (1) it is cheaper to 1224 // compute this way, and (2) it makes it easier to compute the centroid of 1225 // more complicated shapes (by splitting them into disjoint regions and 1226 // adding their centroids). 1227 // 1228 // Note that the return value is not affected by whether this loop is a 1229 // "hole" or a "shell". 1230 func (l *Loop) Centroid() Point { 1231 // surfaceIntegralPoint() returns either the integral of position over loop 1232 // interior, or the negative of the integral of position over the loop 1233 // exterior. But these two values are the same (!), because the integral of 1234 // position over the entire sphere is (0, 0, 0). 1235 return l.surfaceIntegralPoint(TrueCentroid) 1236 } 1237 1238 // Encode encodes the Loop. 1239 func (l Loop) Encode(w io.Writer) error { 1240 e := &encoder{w: w} 1241 l.encode(e) 1242 return e.err 1243 } 1244 1245 func (l Loop) encode(e *encoder) { 1246 e.writeInt8(encodingVersion) 1247 e.writeUint32(uint32(len(l.vertices))) 1248 for _, v := range l.vertices { 1249 e.writeFloat64(v.X) 1250 e.writeFloat64(v.Y) 1251 e.writeFloat64(v.Z) 1252 } 1253 1254 e.writeBool(l.originInside) 1255 e.writeInt32(int32(l.depth)) 1256 1257 // Encode the bound. 1258 l.bound.encode(e) 1259 } 1260 1261 // Decode decodes a loop. 1262 func (l *Loop) Decode(r io.Reader) error { 1263 *l = Loop{} 1264 d := &decoder{r: asByteReader(r)} 1265 l.decode(d) 1266 return d.err 1267 } 1268 1269 func (l *Loop) decode(d *decoder) { 1270 version := int8(d.readUint8()) 1271 if d.err != nil { 1272 return 1273 } 1274 if version != encodingVersion { 1275 d.err = fmt.Errorf("cannot decode version %d", version) 1276 return 1277 } 1278 1279 // Empty loops are explicitly allowed here: a newly created loop has zero vertices 1280 // and such loops encode and decode properly. 1281 nvertices := d.readUint32() 1282 if nvertices > maxEncodedVertices { 1283 if d.err == nil { 1284 d.err = fmt.Errorf("too many vertices (%d; max is %d)", nvertices, maxEncodedVertices) 1285 1286 } 1287 return 1288 } 1289 l.vertices = make([]Point, nvertices) 1290 for i := range l.vertices { 1291 l.vertices[i].X = d.readFloat64() 1292 l.vertices[i].Y = d.readFloat64() 1293 l.vertices[i].Z = d.readFloat64() 1294 } 1295 l.index = NewShapeIndex() 1296 l.originInside = d.readBool() 1297 l.depth = int(d.readUint32()) 1298 l.bound.decode(d) 1299 l.subregionBound = ExpandForSubregions(l.bound) 1300 1301 l.index.Add(l) 1302 } 1303 1304 // Bitmasks to read from properties. 1305 const ( 1306 originInside = 1 << iota 1307 boundEncoded 1308 ) 1309 1310 func (l *Loop) xyzFaceSiTiVertices() []xyzFaceSiTi { 1311 ret := make([]xyzFaceSiTi, len(l.vertices)) 1312 for i, v := range l.vertices { 1313 ret[i].xyz = v 1314 ret[i].face, ret[i].si, ret[i].ti, ret[i].level = xyzToFaceSiTi(v) 1315 } 1316 return ret 1317 } 1318 1319 func (l *Loop) encodeCompressed(e *encoder, snapLevel int, vertices []xyzFaceSiTi) { 1320 if len(l.vertices) != len(vertices) { 1321 panic("encodeCompressed: vertices must be the same length as l.vertices") 1322 } 1323 if len(vertices) > maxEncodedVertices { 1324 if e.err == nil { 1325 e.err = fmt.Errorf("too many vertices (%d; max is %d)", len(vertices), maxEncodedVertices) 1326 } 1327 return 1328 } 1329 e.writeUvarint(uint64(len(vertices))) 1330 encodePointsCompressed(e, vertices, snapLevel) 1331 1332 props := l.compressedEncodingProperties() 1333 e.writeUvarint(props) 1334 e.writeUvarint(uint64(l.depth)) 1335 if props&boundEncoded != 0 { 1336 l.bound.encode(e) 1337 } 1338 } 1339 1340 func (l *Loop) compressedEncodingProperties() uint64 { 1341 var properties uint64 1342 if l.originInside { 1343 properties |= originInside 1344 } 1345 1346 // Write whether there is a bound so we can change the threshold later. 1347 // Recomputing the bound multiplies the decode time taken per vertex 1348 // by a factor of about 3.5. Without recomputing the bound, decode 1349 // takes approximately 125 ns / vertex. A loop with 63 vertices 1350 // encoded without the bound will take ~30us to decode, which is 1351 // acceptable. At ~3.5 bytes / vertex without the bound, adding 1352 // the bound will increase the size by <15%, which is also acceptable. 1353 const minVerticesForBound = 64 1354 if len(l.vertices) >= minVerticesForBound { 1355 properties |= boundEncoded 1356 } 1357 1358 return properties 1359 } 1360 1361 func (l *Loop) decodeCompressed(d *decoder, snapLevel int) { 1362 nvertices := d.readUvarint() 1363 if d.err != nil { 1364 return 1365 } 1366 if nvertices > maxEncodedVertices { 1367 d.err = fmt.Errorf("too many vertices (%d; max is %d)", nvertices, maxEncodedVertices) 1368 return 1369 } 1370 l.vertices = make([]Point, nvertices) 1371 decodePointsCompressed(d, snapLevel, l.vertices) 1372 properties := d.readUvarint() 1373 1374 // Make sure values are valid before using. 1375 if d.err != nil { 1376 return 1377 } 1378 1379 l.index = NewShapeIndex() 1380 l.originInside = (properties & originInside) != 0 1381 1382 l.depth = int(d.readUvarint()) 1383 1384 if (properties & boundEncoded) != 0 { 1385 l.bound.decode(d) 1386 if d.err != nil { 1387 return 1388 } 1389 l.subregionBound = ExpandForSubregions(l.bound) 1390 } else { 1391 l.initBound() 1392 } 1393 1394 l.index.Add(l) 1395 } 1396 1397 // crossingTarget is an enum representing the possible crossing target cases for relations. 1398 type crossingTarget int 1399 1400 const ( 1401 crossingTargetDontCare crossingTarget = iota 1402 crossingTargetDontCross 1403 crossingTargetCross 1404 ) 1405 1406 // loopRelation defines the interface for checking a type of relationship between two loops. 1407 // Some examples of relations are Contains, Intersects, or CompareBoundary. 1408 type loopRelation interface { 1409 // Optionally, aCrossingTarget and bCrossingTarget can specify an early-exit 1410 // condition for the loop relation. If any point P is found such that 1411 // 1412 // A.ContainsPoint(P) == aCrossingTarget() && 1413 // B.ContainsPoint(P) == bCrossingTarget() 1414 // 1415 // then the loop relation is assumed to be the same as if a pair of crossing 1416 // edges were found. For example, the ContainsPoint relation has 1417 // 1418 // aCrossingTarget() == crossingTargetDontCross 1419 // bCrossingTarget() == crossingTargetCross 1420 // 1421 // because if A.ContainsPoint(P) == false and B.ContainsPoint(P) == true 1422 // for any point P, then it is equivalent to finding an edge crossing (i.e., 1423 // since Contains returns false in both cases). 1424 // 1425 // Loop relations that do not have an early-exit condition of this form 1426 // should return crossingTargetDontCare for both crossing targets. 1427 1428 // aCrossingTarget reports whether loop A crosses the target point with 1429 // the given relation type. 1430 aCrossingTarget() crossingTarget 1431 // bCrossingTarget reports whether loop B crosses the target point with 1432 // the given relation type. 1433 bCrossingTarget() crossingTarget 1434 1435 // wedgesCross reports if a shared vertex ab1 and the two associated wedges 1436 // (a0, ab1, b2) and (b0, ab1, b2) are equivalent to an edge crossing. 1437 // The loop relation is also allowed to maintain its own internal state, and 1438 // can return true if it observes any sequence of wedges that are equivalent 1439 // to an edge crossing. 1440 wedgesCross(a0, ab1, a2, b0, b2 Point) bool 1441 } 1442 1443 // loopCrosser is a helper type for determining whether two loops cross. 1444 // It is instantiated twice for each pair of loops to be tested, once for the 1445 // pair (A,B) and once for the pair (B,A), in order to be able to process 1446 // edges in either loop nesting order. 1447 type loopCrosser struct { 1448 a, b *Loop 1449 relation loopRelation 1450 swapped bool 1451 aCrossingTarget crossingTarget 1452 bCrossingTarget crossingTarget 1453 1454 // state maintained by startEdge and edgeCrossesCell. 1455 crosser *EdgeCrosser 1456 aj, bjPrev int 1457 1458 // temporary data declared here to avoid repeated memory allocations. 1459 bQuery *CrossingEdgeQuery 1460 bCells []*ShapeIndexCell 1461 } 1462 1463 // newLoopCrosser creates a loopCrosser from the given values. If swapped is true, 1464 // the loops A and B have been swapped. This affects how arguments are passed to 1465 // the given loop relation, since for example A.Contains(B) is not the same as 1466 // B.Contains(A). 1467 func newLoopCrosser(a, b *Loop, relation loopRelation, swapped bool) *loopCrosser { 1468 l := &loopCrosser{ 1469 a: a, 1470 b: b, 1471 relation: relation, 1472 swapped: swapped, 1473 aCrossingTarget: relation.aCrossingTarget(), 1474 bCrossingTarget: relation.bCrossingTarget(), 1475 bQuery: NewCrossingEdgeQuery(b.index), 1476 } 1477 if swapped { 1478 l.aCrossingTarget, l.bCrossingTarget = l.bCrossingTarget, l.aCrossingTarget 1479 } 1480 1481 return l 1482 } 1483 1484 // startEdge sets the crossers state for checking the given edge of loop A. 1485 func (l *loopCrosser) startEdge(aj int) { 1486 l.crosser = NewEdgeCrosser(l.a.Vertex(aj), l.a.Vertex(aj+1)) 1487 l.aj = aj 1488 l.bjPrev = -2 1489 } 1490 1491 // edgeCrossesCell reports whether the current edge of loop A has any crossings with 1492 // edges of the index cell of loop B. 1493 func (l *loopCrosser) edgeCrossesCell(bClipped *clippedShape) bool { 1494 // Test the current edge of A against all edges of bClipped 1495 bNumEdges := bClipped.numEdges() 1496 for j := 0; j < bNumEdges; j++ { 1497 bj := bClipped.edges[j] 1498 if bj != l.bjPrev+1 { 1499 l.crosser.RestartAt(l.b.Vertex(bj)) 1500 } 1501 l.bjPrev = bj 1502 if crossing := l.crosser.ChainCrossingSign(l.b.Vertex(bj + 1)); crossing == DoNotCross { 1503 continue 1504 } else if crossing == Cross { 1505 return true 1506 } 1507 1508 // We only need to check each shared vertex once, so we only 1509 // consider the case where l.aVertex(l.aj+1) == l.b.Vertex(bj+1). 1510 if l.a.Vertex(l.aj+1) == l.b.Vertex(bj+1) { 1511 if l.swapped { 1512 if l.relation.wedgesCross(l.b.Vertex(bj), l.b.Vertex(bj+1), l.b.Vertex(bj+2), l.a.Vertex(l.aj), l.a.Vertex(l.aj+2)) { 1513 return true 1514 } 1515 } else { 1516 if l.relation.wedgesCross(l.a.Vertex(l.aj), l.a.Vertex(l.aj+1), l.a.Vertex(l.aj+2), l.b.Vertex(bj), l.b.Vertex(bj+2)) { 1517 return true 1518 } 1519 } 1520 } 1521 } 1522 1523 return false 1524 } 1525 1526 // cellCrossesCell reports whether there are any edge crossings or wedge crossings 1527 // within the two given cells. 1528 func (l *loopCrosser) cellCrossesCell(aClipped, bClipped *clippedShape) bool { 1529 // Test all edges of aClipped against all edges of bClipped. 1530 for _, edge := range aClipped.edges { 1531 l.startEdge(edge) 1532 if l.edgeCrossesCell(bClipped) { 1533 return true 1534 } 1535 } 1536 1537 return false 1538 } 1539 1540 // cellCrossesAnySubcell reports whether given an index cell of A, if there are any 1541 // edge or wedge crossings with any index cell of B contained within bID. 1542 func (l *loopCrosser) cellCrossesAnySubcell(aClipped *clippedShape, bID CellID) bool { 1543 // Test all edges of aClipped against all edges of B. The relevant B 1544 // edges are guaranteed to be children of bID, which lets us find the 1545 // correct index cells more efficiently. 1546 bRoot := PaddedCellFromCellID(bID, 0) 1547 for _, aj := range aClipped.edges { 1548 // Use an CrossingEdgeQuery starting at bRoot to find the index cells 1549 // of B that might contain crossing edges. 1550 l.bCells = l.bQuery.getCells(l.a.Vertex(aj), l.a.Vertex(aj+1), bRoot) 1551 if len(l.bCells) == 0 { 1552 continue 1553 } 1554 l.startEdge(aj) 1555 for c := 0; c < len(l.bCells); c++ { 1556 if l.edgeCrossesCell(l.bCells[c].shapes[0]) { 1557 return true 1558 } 1559 } 1560 } 1561 1562 return false 1563 } 1564 1565 // hasCrossing reports whether given two iterators positioned such that 1566 // ai.cellID().ContainsCellID(bi.cellID()), there is an edge or wedge crossing 1567 // anywhere within ai.cellID(). This function advances bi only past ai.cellID(). 1568 func (l *loopCrosser) hasCrossing(ai, bi *rangeIterator) bool { 1569 // If ai.CellID() intersects many edges of B, then it is faster to use 1570 // CrossingEdgeQuery to narrow down the candidates. But if it intersects 1571 // only a few edges, it is faster to check all the crossings directly. 1572 // We handle this by advancing bi and keeping track of how many edges we 1573 // would need to test. 1574 const edgeQueryMinEdges = 20 // Tuned from benchmarks. 1575 var totalEdges int 1576 l.bCells = nil 1577 1578 for { 1579 if n := bi.it.IndexCell().shapes[0].numEdges(); n > 0 { 1580 totalEdges += n 1581 if totalEdges >= edgeQueryMinEdges { 1582 // There are too many edges to test them directly, so use CrossingEdgeQuery. 1583 if l.cellCrossesAnySubcell(ai.it.IndexCell().shapes[0], ai.cellID()) { 1584 return true 1585 } 1586 bi.seekBeyond(ai) 1587 return false 1588 } 1589 l.bCells = append(l.bCells, bi.indexCell()) 1590 } 1591 bi.next() 1592 if bi.cellID() > ai.rangeMax { 1593 break 1594 } 1595 } 1596 1597 // Test all the edge crossings directly. 1598 for _, c := range l.bCells { 1599 if l.cellCrossesCell(ai.it.IndexCell().shapes[0], c.shapes[0]) { 1600 return true 1601 } 1602 } 1603 1604 return false 1605 } 1606 1607 // containsCenterMatches reports if the clippedShapes containsCenter boolean corresponds 1608 // to the crossing target type given. (This is to work around C++ allowing false == 0, 1609 // true == 1 type implicit conversions and comparisons) 1610 func containsCenterMatches(a *clippedShape, target crossingTarget) bool { 1611 return (!a.containsCenter && target == crossingTargetDontCross) || 1612 (a.containsCenter && target == crossingTargetCross) 1613 } 1614 1615 // hasCrossingRelation reports whether given two iterators positioned such that 1616 // ai.cellID().ContainsCellID(bi.cellID()), there is a crossing relationship 1617 // anywhere within ai.cellID(). Specifically, this method returns true if there 1618 // is an edge crossing, a wedge crossing, or a point P that matches both relations 1619 // crossing targets. This function advances both iterators past ai.cellID. 1620 func (l *loopCrosser) hasCrossingRelation(ai, bi *rangeIterator) bool { 1621 aClipped := ai.it.IndexCell().shapes[0] 1622 if aClipped.numEdges() != 0 { 1623 // The current cell of A has at least one edge, so check for crossings. 1624 if l.hasCrossing(ai, bi) { 1625 return true 1626 } 1627 ai.next() 1628 return false 1629 } 1630 1631 if containsCenterMatches(aClipped, l.aCrossingTarget) { 1632 // The crossing target for A is not satisfied, so we skip over these cells of B. 1633 bi.seekBeyond(ai) 1634 ai.next() 1635 return false 1636 } 1637 1638 // All points within ai.cellID() satisfy the crossing target for A, so it's 1639 // worth iterating through the cells of B to see whether any cell 1640 // centers also satisfy the crossing target for B. 1641 for bi.cellID() <= ai.rangeMax { 1642 bClipped := bi.it.IndexCell().shapes[0] 1643 if containsCenterMatches(bClipped, l.bCrossingTarget) { 1644 return true 1645 } 1646 bi.next() 1647 } 1648 ai.next() 1649 return false 1650 } 1651 1652 // hasCrossingRelation checks all edges of loop A for intersection against all edges 1653 // of loop B and reports if there are any that satisfy the given relation. If there 1654 // is any shared vertex, the wedges centered at this vertex are sent to the given 1655 // relation to be tested. 1656 // 1657 // If the two loop boundaries cross, this method is guaranteed to return 1658 // true. It also returns true in certain cases if the loop relationship is 1659 // equivalent to crossing. For example, if the relation is Contains and a 1660 // point P is found such that B contains P but A does not contain P, this 1661 // method will return true to indicate that the result is the same as though 1662 // a pair of crossing edges were found (since Contains returns false in 1663 // both cases). 1664 // 1665 // See Contains, Intersects and CompareBoundary for the three uses of this function. 1666 func hasCrossingRelation(a, b *Loop, relation loopRelation) bool { 1667 // We look for CellID ranges where the indexes of A and B overlap, and 1668 // then test those edges for crossings. 1669 ai := newRangeIterator(a.index) 1670 bi := newRangeIterator(b.index) 1671 1672 ab := newLoopCrosser(a, b, relation, false) // Tests edges of A against B 1673 ba := newLoopCrosser(b, a, relation, true) // Tests edges of B against A 1674 1675 for !ai.done() || !bi.done() { 1676 if ai.rangeMax < bi.rangeMin { 1677 // The A and B cells don't overlap, and A precedes B. 1678 ai.seekTo(bi) 1679 } else if bi.rangeMax < ai.rangeMin { 1680 // The A and B cells don't overlap, and B precedes A. 1681 bi.seekTo(ai) 1682 } else { 1683 // One cell contains the other. Determine which cell is larger. 1684 abRelation := int64(ai.it.CellID().lsb() - bi.it.CellID().lsb()) 1685 if abRelation > 0 { 1686 // A's index cell is larger. 1687 if ab.hasCrossingRelation(ai, bi) { 1688 return true 1689 } 1690 } else if abRelation < 0 { 1691 // B's index cell is larger. 1692 if ba.hasCrossingRelation(bi, ai) { 1693 return true 1694 } 1695 } else { 1696 // The A and B cells are the same. Since the two cells 1697 // have the same center point P, check whether P satisfies 1698 // the crossing targets. 1699 aClipped := ai.it.IndexCell().shapes[0] 1700 bClipped := bi.it.IndexCell().shapes[0] 1701 if containsCenterMatches(aClipped, ab.aCrossingTarget) && 1702 containsCenterMatches(bClipped, ab.bCrossingTarget) { 1703 return true 1704 } 1705 // Otherwise test all the edge crossings directly. 1706 if aClipped.numEdges() > 0 && bClipped.numEdges() > 0 && ab.cellCrossesCell(aClipped, bClipped) { 1707 return true 1708 } 1709 ai.next() 1710 bi.next() 1711 } 1712 } 1713 } 1714 return false 1715 } 1716 1717 // containsRelation implements loopRelation for a contains operation. If 1718 // A.ContainsPoint(P) == false && B.ContainsPoint(P) == true, it is equivalent 1719 // to having an edge crossing (i.e., Contains returns false). 1720 type containsRelation struct { 1721 foundSharedVertex bool 1722 } 1723 1724 func (c *containsRelation) aCrossingTarget() crossingTarget { return crossingTargetDontCross } 1725 func (c *containsRelation) bCrossingTarget() crossingTarget { return crossingTargetCross } 1726 func (c *containsRelation) wedgesCross(a0, ab1, a2, b0, b2 Point) bool { 1727 c.foundSharedVertex = true 1728 return !WedgeContains(a0, ab1, a2, b0, b2) 1729 } 1730 1731 // intersectsRelation implements loopRelation for an intersects operation. Given 1732 // two loops, A and B, if A.ContainsPoint(P) == true && B.ContainsPoint(P) == true, 1733 // it is equivalent to having an edge crossing (i.e., Intersects returns true). 1734 type intersectsRelation struct { 1735 foundSharedVertex bool 1736 } 1737 1738 func (i *intersectsRelation) aCrossingTarget() crossingTarget { return crossingTargetCross } 1739 func (i *intersectsRelation) bCrossingTarget() crossingTarget { return crossingTargetCross } 1740 func (i *intersectsRelation) wedgesCross(a0, ab1, a2, b0, b2 Point) bool { 1741 i.foundSharedVertex = true 1742 return WedgeIntersects(a0, ab1, a2, b0, b2) 1743 } 1744 1745 // compareBoundaryRelation implements loopRelation for comparing boundaries. 1746 // 1747 // The compare boundary relation does not have a useful early-exit condition, 1748 // so we return crossingTargetDontCare for both crossing targets. 1749 // 1750 // Aside: A possible early exit condition could be based on the following. 1751 // If A contains a point of both B and ~B, then A intersects Boundary(B). 1752 // If ~A contains a point of both B and ~B, then ~A intersects Boundary(B). 1753 // So if the intersections of {A, ~A} with {B, ~B} are all non-empty, 1754 // the return value is 0, i.e., Boundary(A) intersects Boundary(B). 1755 // Unfortunately it isn't worth detecting this situation because by the 1756 // time we have seen a point in all four intersection regions, we are also 1757 // guaranteed to have seen at least one pair of crossing edges. 1758 type compareBoundaryRelation struct { 1759 reverse bool // True if the other loop should be reversed. 1760 foundSharedVertex bool // True if any wedge was processed. 1761 containsEdge bool // True if any edge of the other loop is contained by this loop. 1762 excludesEdge bool // True if any edge of the other loop is excluded by this loop. 1763 } 1764 1765 func newCompareBoundaryRelation(reverse bool) *compareBoundaryRelation { 1766 return &compareBoundaryRelation{reverse: reverse} 1767 } 1768 1769 func (c *compareBoundaryRelation) aCrossingTarget() crossingTarget { return crossingTargetDontCare } 1770 func (c *compareBoundaryRelation) bCrossingTarget() crossingTarget { return crossingTargetDontCare } 1771 func (c *compareBoundaryRelation) wedgesCross(a0, ab1, a2, b0, b2 Point) bool { 1772 // Because we don't care about the interior of the other, only its boundary, 1773 // it is sufficient to check whether this one contains the semiwedge (ab1, b2). 1774 c.foundSharedVertex = true 1775 if wedgeContainsSemiwedge(a0, ab1, a2, b2, c.reverse) { 1776 c.containsEdge = true 1777 } else { 1778 c.excludesEdge = true 1779 } 1780 return c.containsEdge && c.excludesEdge 1781 } 1782 1783 // wedgeContainsSemiwedge reports whether the wedge (a0, ab1, a2) contains the 1784 // "semiwedge" defined as any non-empty open set of rays immediately CCW from 1785 // the edge (ab1, b2). If reverse is true, then substitute clockwise for CCW; 1786 // this simulates what would happen if the direction of the other loop was reversed. 1787 func wedgeContainsSemiwedge(a0, ab1, a2, b2 Point, reverse bool) bool { 1788 if b2 == a0 || b2 == a2 { 1789 // We have a shared or reversed edge. 1790 return (b2 == a0) == reverse 1791 } 1792 return OrderedCCW(a0, a2, b2, ab1) 1793 } 1794 1795 // containsNonCrossingBoundary reports whether given two loops whose boundaries 1796 // do not cross (see compareBoundary), if this loop contains the boundary of the 1797 // other loop. If reverse is true, the boundary of the other loop is reversed 1798 // first (which only affects the result when there are shared edges). This method 1799 // is cheaper than compareBoundary because it does not test for edge intersections. 1800 // 1801 // This function requires that neither loop is empty, and that if the other is full, 1802 // then reverse == false. 1803 func (l *Loop) containsNonCrossingBoundary(other *Loop, reverseOther bool) bool { 1804 // The bounds must intersect for containment. 1805 if !l.bound.Intersects(other.bound) { 1806 return false 1807 } 1808 1809 // Full loops are handled as though the loop surrounded the entire sphere. 1810 if l.IsFull() { 1811 return true 1812 } 1813 if other.IsFull() { 1814 return false 1815 } 1816 1817 m, ok := l.findVertex(other.Vertex(0)) 1818 if !ok { 1819 // Since the other loops vertex 0 is not shared, we can check if this contains it. 1820 return l.ContainsPoint(other.Vertex(0)) 1821 } 1822 // Otherwise check whether the edge (b0, b1) is contained by this loop. 1823 return wedgeContainsSemiwedge(l.Vertex(m-1), l.Vertex(m), l.Vertex(m+1), 1824 other.Vertex(1), reverseOther) 1825 } 1826 1827 // TODO(roberts): Differences from the C++ version: 1828 // DistanceToPoint 1829 // DistanceToBoundary 1830 // Project 1831 // ProjectToBoundary 1832 // BoundaryApproxEqual 1833 // BoundaryNear