edge_crossings.go (15780B)
1 // Copyright 2017 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 "math" 20 21 "github.com/golang/geo/r3" 22 "github.com/golang/geo/s1" 23 ) 24 25 const ( 26 // intersectionError can be set somewhat arbitrarily, because the algorithm 27 // uses more precision if necessary in order to achieve the specified error. 28 // The only strict requirement is that intersectionError >= dblEpsilon 29 // radians. However, using a larger error tolerance makes the algorithm more 30 // efficient because it reduces the number of cases where exact arithmetic is 31 // needed. 32 intersectionError = s1.Angle(8 * dblError) 33 34 // intersectionMergeRadius is used to ensure that intersection points that 35 // are supposed to be coincident are merged back together into a single 36 // vertex. This is required in order for various polygon operations (union, 37 // intersection, etc) to work correctly. It is twice the intersection error 38 // because two coincident intersection points might have errors in 39 // opposite directions. 40 intersectionMergeRadius = 2 * intersectionError 41 ) 42 43 // A Crossing indicates how edges cross. 44 type Crossing int 45 46 const ( 47 // Cross means the edges cross. 48 Cross Crossing = iota 49 // MaybeCross means two vertices from different edges are the same. 50 MaybeCross 51 // DoNotCross means the edges do not cross. 52 DoNotCross 53 ) 54 55 func (c Crossing) String() string { 56 switch c { 57 case Cross: 58 return "Cross" 59 case MaybeCross: 60 return "MaybeCross" 61 case DoNotCross: 62 return "DoNotCross" 63 default: 64 return fmt.Sprintf("(BAD CROSSING %d)", c) 65 } 66 } 67 68 // CrossingSign reports whether the edge AB intersects the edge CD. 69 // If AB crosses CD at a point that is interior to both edges, Cross is returned. 70 // If any two vertices from different edges are the same it returns MaybeCross. 71 // Otherwise it returns DoNotCross. 72 // If either edge is degenerate (A == B or C == D), the return value is MaybeCross 73 // if two vertices from different edges are the same and DoNotCross otherwise. 74 // 75 // Properties of CrossingSign: 76 // 77 // (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d) 78 // (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d) 79 // (3) CrossingSign(a,b,c,d) == MaybeCross if a==c, a==d, b==c, b==d 80 // (3) CrossingSign(a,b,c,d) == DoNotCross or MaybeCross if a==b or c==d 81 // 82 // This method implements an exact, consistent perturbation model such 83 // that no three points are ever considered to be collinear. This means 84 // that even if you have 4 points A, B, C, D that lie exactly in a line 85 // (say, around the equator), C and D will be treated as being slightly to 86 // one side or the other of AB. This is done in a way such that the 87 // results are always consistent (see RobustSign). 88 func CrossingSign(a, b, c, d Point) Crossing { 89 crosser := NewChainEdgeCrosser(a, b, c) 90 return crosser.ChainCrossingSign(d) 91 } 92 93 // VertexCrossing reports whether two edges "cross" in such a way that point-in-polygon 94 // containment tests can be implemented by counting the number of edge crossings. 95 // 96 // Given two edges AB and CD where at least two vertices are identical 97 // (i.e. CrossingSign(a,b,c,d) == 0), the basic rule is that a "crossing" 98 // occurs if AB is encountered after CD during a CCW sweep around the shared 99 // vertex starting from a fixed reference point. 100 // 101 // Note that according to this rule, if AB crosses CD then in general CD 102 // does not cross AB. However, this leads to the correct result when 103 // counting polygon edge crossings. For example, suppose that A,B,C are 104 // three consecutive vertices of a CCW polygon. If we now consider the edge 105 // crossings of a segment BP as P sweeps around B, the crossing number 106 // changes parity exactly when BP crosses BA or BC. 107 // 108 // Useful properties of VertexCrossing (VC): 109 // 110 // (1) VC(a,a,c,d) == VC(a,b,c,c) == false 111 // (2) VC(a,b,a,b) == VC(a,b,b,a) == true 112 // (3) VC(a,b,c,d) == VC(a,b,d,c) == VC(b,a,c,d) == VC(b,a,d,c) 113 // (3) If exactly one of a,b equals one of c,d, then exactly one of 114 // VC(a,b,c,d) and VC(c,d,a,b) is true 115 // 116 // It is an error to call this method with 4 distinct vertices. 117 func VertexCrossing(a, b, c, d Point) bool { 118 // If A == B or C == D there is no intersection. We need to check this 119 // case first in case 3 or more input points are identical. 120 if a == b || c == d { 121 return false 122 } 123 124 // If any other pair of vertices is equal, there is a crossing if and only 125 // if OrderedCCW indicates that the edge AB is further CCW around the 126 // shared vertex O (either A or B) than the edge CD, starting from an 127 // arbitrary fixed reference point. 128 129 // Optimization: if AB=CD or AB=DC, we can avoid most of the calculations. 130 switch { 131 case a == c: 132 return (b == d) || OrderedCCW(Point{a.Ortho()}, d, b, a) 133 case b == d: 134 return OrderedCCW(Point{b.Ortho()}, c, a, b) 135 case a == d: 136 return (b == c) || OrderedCCW(Point{a.Ortho()}, c, b, a) 137 case b == c: 138 return OrderedCCW(Point{b.Ortho()}, d, a, b) 139 } 140 141 return false 142 } 143 144 // EdgeOrVertexCrossing is a convenience function that calls CrossingSign to 145 // handle cases where all four vertices are distinct, and VertexCrossing to 146 // handle cases where two or more vertices are the same. This defines a crossing 147 // function such that point-in-polygon containment tests can be implemented 148 // by simply counting edge crossings. 149 func EdgeOrVertexCrossing(a, b, c, d Point) bool { 150 switch CrossingSign(a, b, c, d) { 151 case DoNotCross: 152 return false 153 case Cross: 154 return true 155 default: 156 return VertexCrossing(a, b, c, d) 157 } 158 } 159 160 // Intersection returns the intersection point of two edges AB and CD that cross 161 // (CrossingSign(a,b,c,d) == Crossing). 162 // 163 // Useful properties of Intersection: 164 // 165 // (1) Intersection(b,a,c,d) == Intersection(a,b,d,c) == Intersection(a,b,c,d) 166 // (2) Intersection(c,d,a,b) == Intersection(a,b,c,d) 167 // 168 // The returned intersection point X is guaranteed to be very close to the 169 // true intersection point of AB and CD, even if the edges intersect at a 170 // very small angle. 171 func Intersection(a0, a1, b0, b1 Point) Point { 172 // It is difficult to compute the intersection point of two edges accurately 173 // when the angle between the edges is very small. Previously we handled 174 // this by only guaranteeing that the returned intersection point is within 175 // intersectionError of each edge. However, this means that when the edges 176 // cross at a very small angle, the computed result may be very far from the 177 // true intersection point. 178 // 179 // Instead this function now guarantees that the result is always within 180 // intersectionError of the true intersection. This requires using more 181 // sophisticated techniques and in some cases extended precision. 182 // 183 // - intersectionStable computes the intersection point using 184 // projection and interpolation, taking care to minimize cancellation 185 // error. 186 // 187 // - intersectionExact computes the intersection point using precision 188 // arithmetic and converts the final result back to an Point. 189 pt, ok := intersectionStable(a0, a1, b0, b1) 190 if !ok { 191 pt = intersectionExact(a0, a1, b0, b1) 192 } 193 194 // Make sure the intersection point is on the correct side of the sphere. 195 // Since all vertices are unit length, and edges are less than 180 degrees, 196 // (a0 + a1) and (b0 + b1) both have positive dot product with the 197 // intersection point. We use the sum of all vertices to make sure that the 198 // result is unchanged when the edges are swapped or reversed. 199 if pt.Dot((a0.Add(a1.Vector)).Add(b0.Add(b1.Vector))) < 0 { 200 pt = Point{pt.Mul(-1)} 201 } 202 203 return pt 204 } 205 206 // Computes the cross product of two vectors, normalized to be unit length. 207 // Also returns the length of the cross 208 // product before normalization, which is useful for estimating the amount of 209 // error in the result. For numerical stability, the vectors should both be 210 // approximately unit length. 211 func robustNormalWithLength(x, y r3.Vector) (r3.Vector, float64) { 212 var pt r3.Vector 213 // This computes 2 * (x.Cross(y)), but has much better numerical 214 // stability when x and y are unit length. 215 tmp := x.Sub(y).Cross(x.Add(y)) 216 length := tmp.Norm() 217 if length != 0 { 218 pt = tmp.Mul(1 / length) 219 } 220 return pt, 0.5 * length // Since tmp == 2 * (x.Cross(y)) 221 } 222 223 /* 224 // intersectionSimple is not used by the C++ so it is skipped here. 225 */ 226 227 // projection returns the projection of aNorm onto X (x.Dot(aNorm)), and a bound 228 // on the error in the result. aNorm is not necessarily unit length. 229 // 230 // The remaining parameters (the length of aNorm (aNormLen) and the edge endpoints 231 // a0 and a1) allow this dot product to be computed more accurately and efficiently. 232 func projection(x, aNorm r3.Vector, aNormLen float64, a0, a1 Point) (proj, bound float64) { 233 // The error in the dot product is proportional to the lengths of the input 234 // vectors, so rather than using x itself (a unit-length vector) we use 235 // the vectors from x to the closer of the two edge endpoints. This 236 // typically reduces the error by a huge factor. 237 x0 := x.Sub(a0.Vector) 238 x1 := x.Sub(a1.Vector) 239 x0Dist2 := x0.Norm2() 240 x1Dist2 := x1.Norm2() 241 242 // If both distances are the same, we need to be careful to choose one 243 // endpoint deterministically so that the result does not change if the 244 // order of the endpoints is reversed. 245 var dist float64 246 if x0Dist2 < x1Dist2 || (x0Dist2 == x1Dist2 && x0.Cmp(x1) == -1) { 247 dist = math.Sqrt(x0Dist2) 248 proj = x0.Dot(aNorm) 249 } else { 250 dist = math.Sqrt(x1Dist2) 251 proj = x1.Dot(aNorm) 252 } 253 254 // This calculation bounds the error from all sources: the computation of 255 // the normal, the subtraction of one endpoint, and the dot product itself. 256 // dblError appears because the input points are assumed to be 257 // normalized in double precision. 258 // 259 // For reference, the bounds that went into this calculation are: 260 // ||N'-N|| <= ((1 + 2 * sqrt(3))||N|| + 32 * sqrt(3) * dblError) * epsilon 261 // |(A.B)'-(A.B)| <= (1.5 * (A.B) + 1.5 * ||A|| * ||B||) * epsilon 262 // ||(X-Y)'-(X-Y)|| <= ||X-Y|| * epsilon 263 bound = (((3.5+2*math.Sqrt(3))*aNormLen+32*math.Sqrt(3)*dblError)*dist + 1.5*math.Abs(proj)) * epsilon 264 return proj, bound 265 } 266 267 // compareEdges reports whether (a0,a1) is less than (b0,b1) with respect to a total 268 // ordering on edges that is invariant under edge reversals. 269 func compareEdges(a0, a1, b0, b1 Point) bool { 270 if a0.Cmp(a1.Vector) != -1 { 271 a0, a1 = a1, a0 272 } 273 if b0.Cmp(b1.Vector) != -1 { 274 b0, b1 = b1, b0 275 } 276 return a0.Cmp(b0.Vector) == -1 || (a0 == b0 && b0.Cmp(b1.Vector) == -1) 277 } 278 279 // intersectionStable returns the intersection point of the edges (a0,a1) and 280 // (b0,b1) if it can be computed to within an error of at most intersectionError 281 // by this function. 282 // 283 // The intersection point is not guaranteed to have the correct sign because we 284 // choose to use the longest of the two edges first. The sign is corrected by 285 // Intersection. 286 func intersectionStable(a0, a1, b0, b1 Point) (Point, bool) { 287 // Sort the two edges so that (a0,a1) is longer, breaking ties in a 288 // deterministic way that does not depend on the ordering of the endpoints. 289 // This is desirable for two reasons: 290 // - So that the result doesn't change when edges are swapped or reversed. 291 // - It reduces error, since the first edge is used to compute the edge 292 // normal (where a longer edge means less error), and the second edge 293 // is used for interpolation (where a shorter edge means less error). 294 aLen2 := a1.Sub(a0.Vector).Norm2() 295 bLen2 := b1.Sub(b0.Vector).Norm2() 296 if aLen2 < bLen2 || (aLen2 == bLen2 && compareEdges(a0, a1, b0, b1)) { 297 return intersectionStableSorted(b0, b1, a0, a1) 298 } 299 return intersectionStableSorted(a0, a1, b0, b1) 300 } 301 302 // intersectionStableSorted is a helper function for intersectionStable. 303 // It expects that the edges (a0,a1) and (b0,b1) have been sorted so that 304 // the first edge passed in is longer. 305 func intersectionStableSorted(a0, a1, b0, b1 Point) (Point, bool) { 306 var pt Point 307 308 // Compute the normal of the plane through (a0, a1) in a stable way. 309 aNorm := a0.Sub(a1.Vector).Cross(a0.Add(a1.Vector)) 310 aNormLen := aNorm.Norm() 311 bLen := b1.Sub(b0.Vector).Norm() 312 313 // Compute the projection (i.e., signed distance) of b0 and b1 onto the 314 // plane through (a0, a1). Distances are scaled by the length of aNorm. 315 b0Dist, b0Error := projection(b0.Vector, aNorm, aNormLen, a0, a1) 316 b1Dist, b1Error := projection(b1.Vector, aNorm, aNormLen, a0, a1) 317 318 // The total distance from b0 to b1 measured perpendicularly to (a0,a1) is 319 // |b0Dist - b1Dist|. Note that b0Dist and b1Dist generally have 320 // opposite signs because b0 and b1 are on opposite sides of (a0, a1). The 321 // code below finds the intersection point by interpolating along the edge 322 // (b0, b1) to a fractional distance of b0Dist / (b0Dist - b1Dist). 323 // 324 // It can be shown that the maximum error in the interpolation fraction is 325 // 326 // (b0Dist * b1Error - b1Dist * b0Error) / (distSum * (distSum - errorSum)) 327 // 328 // We save ourselves some work by scaling the result and the error bound by 329 // "distSum", since the result is normalized to be unit length anyway. 330 distSum := math.Abs(b0Dist - b1Dist) 331 errorSum := b0Error + b1Error 332 if distSum <= errorSum { 333 return pt, false // Error is unbounded in this case. 334 } 335 336 x := b1.Mul(b0Dist).Sub(b0.Mul(b1Dist)) 337 err := bLen*math.Abs(b0Dist*b1Error-b1Dist*b0Error)/ 338 (distSum-errorSum) + 2*distSum*epsilon 339 340 // Finally we normalize the result, compute the corresponding error, and 341 // check whether the total error is acceptable. 342 xLen := x.Norm() 343 maxError := intersectionError 344 if err > (float64(maxError)-epsilon)*xLen { 345 return pt, false 346 } 347 348 return Point{x.Mul(1 / xLen)}, true 349 } 350 351 // intersectionExact returns the intersection point of (a0, a1) and (b0, b1) 352 // using precise arithmetic. Note that the result is not exact because it is 353 // rounded down to double precision at the end. Also, the intersection point 354 // is not guaranteed to have the correct sign (i.e., the return value may need 355 // to be negated). 356 func intersectionExact(a0, a1, b0, b1 Point) Point { 357 // Since we are using presice arithmetic, we don't need to worry about 358 // numerical stability. 359 a0P := r3.PreciseVectorFromVector(a0.Vector) 360 a1P := r3.PreciseVectorFromVector(a1.Vector) 361 b0P := r3.PreciseVectorFromVector(b0.Vector) 362 b1P := r3.PreciseVectorFromVector(b1.Vector) 363 aNormP := a0P.Cross(a1P) 364 bNormP := b0P.Cross(b1P) 365 xP := aNormP.Cross(bNormP) 366 367 // The final Normalize() call is done in double precision, which creates a 368 // directional error of up to 2*dblError. (Precise conversion and Normalize() 369 // each contribute up to dblError of directional error.) 370 x := xP.Vector() 371 372 if x == (r3.Vector{}) { 373 // The two edges are exactly collinear, but we still consider them to be 374 // "crossing" because of simulation of simplicity. Out of the four 375 // endpoints, exactly two lie in the interior of the other edge. Of 376 // those two we return the one that is lexicographically smallest. 377 x = r3.Vector{10, 10, 10} // Greater than any valid S2Point 378 379 aNorm := Point{aNormP.Vector()} 380 bNorm := Point{bNormP.Vector()} 381 if OrderedCCW(b0, a0, b1, bNorm) && a0.Cmp(x) == -1 { 382 return a0 383 } 384 if OrderedCCW(b0, a1, b1, bNorm) && a1.Cmp(x) == -1 { 385 return a1 386 } 387 if OrderedCCW(a0, b0, a1, aNorm) && b0.Cmp(x) == -1 { 388 return b0 389 } 390 if OrderedCCW(a0, b1, a1, aNorm) && b1.Cmp(x) == -1 { 391 return b1 392 } 393 } 394 395 return Point{x} 396 }