1 // Copyright 2005 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 16 // Original author: ericv@google.com (Eric Veach) 17 // Converted to D: madric@gmail.com (Vijay Nayar) 18 19 module s2.s2edge_crosser; 20 21 import math = std.math; 22 import s2.logger; 23 import s2.s2edge_crossings; 24 import s2.s2point; 25 import s2.s2pointutil; 26 import s2.s2predicates; 27 import s2.util.math.vector; 28 29 /** 30 * This class allows edges to be efficiently tested for intersection with a 31 * given fixed edge AB. It is especially efficient when testing for 32 * intersection with an edge chain connecting vertices v0, v1, v2, ... 33 * 34 * Example usage: 35 * 36 * void CountIntersections(const S2Point& a, const S2Point& b, 37 * const vector<pair<S2Point, S2Point>>& edges) { 38 * int count = 0; 39 * S2EdgeCrosser crosser(&a, &b); 40 * for (const auto& edge : edges) { 41 * if (crosser.CrossingSign(&edge.first, &edge.second) >= 0) { 42 * ++count; 43 * } 44 * } 45 * return count; 46 * } 47 * 48 * This class expects that the client already has all the necessary vertices 49 * stored in memory, so that this class can refer to them with pointers and 50 * does not need to make its own copies. If this is not the case (e.g., you 51 * want to pass temporary objects as vertices), see S2CopyingEdgeCrosser. 52 */ 53 class S2EdgeCrosser { 54 public: 55 // Default constructor; must be followed by a call to Init(). 56 this() {} 57 58 // Convenience constructor that calls Init() with the given fixed edge AB. 59 // The arguments "a" and "b" must point to values that persist for the 60 // lifetime of the S2EdgeCrosser object (or until the next Init() call). 61 this(ref in S2Point a, ref in S2Point b) { 62 if (!isUnitLength(a)) logger.logWarn("Invalid"); 63 if (!isUnitLength(b)) logger.logWarn("Invalid"); 64 65 _a = &a; 66 _b = &b; 67 _aCrossB = _a.crossProd(*_b); 68 _haveTangents = false; 69 _c = null; 70 } 71 72 // Accessors for the constructor arguments. 73 const (S2Point*) a() { 74 return _a; 75 } 76 77 const (S2Point*) b() { 78 return _b; 79 } 80 81 // Initialize the S2EdgeCrosser with the given fixed edge AB. The arguments 82 // "a" and "b" must point to values that persist for the lifetime of the 83 // S2EdgeCrosser object (or until the next Init() call). 84 void initialize(ref in S2Point a, ref in S2Point b) { 85 _a = &a; 86 _b = &b; 87 _aCrossB = a.crossProd(*_b); 88 _haveTangents = false; 89 _c = null; 90 } 91 92 /** 93 * This function determines whether the edge AB intersects the edge CD. 94 * Returns +1 if AB crosses CD at a point that is interior to both edges. 95 * Returns 0 if any two vertices from different edges are the same. 96 * Returns -1 otherwise. 97 * 98 * Note that if an edge is degenerate (A == B or C == D), the return value 99 * is 0 if two vertices from different edges are the same and -1 otherwise. 100 * 101 * Properties of CrossingSign: 102 * 103 * (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d) 104 * (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d) 105 * (3) CrossingSign(a,b,c,d) == 0 if a==c, a==d, b==c, b==d 106 * (3) CrossingSign(a,b,c,d) <= 0 if a==b or c==d (see above) 107 * 108 * This function implements an exact, consistent perturbation model such 109 * that no three points are ever considered to be collinear. This means 110 * that even if you have 4 points A, B, C, D that lie exactly in a line 111 * (say, around the equator), C and D will be treated as being slightly to 112 * one side or the other of AB. This is done in a way such that the 113 * results are always consistent (see s2pred::Sign). 114 * 115 * Note that if you want to check an edge against a chain of other edges, 116 * it is slightly more efficient to use the single-argument version of 117 * CrossingSign below. 118 * 119 * The arguments must point to values that persist until the next call. 120 */ 121 int crossingSign(ref in S2Point c, ref in S2Point d) { 122 if (_c != &c) { 123 restartAt(c); 124 } 125 return crossingSign(d); 126 } 127 128 /** 129 * This method extends the concept of a "crossing" to the case where AB 130 * and CD have a vertex in common. The two edges may or may not cross, 131 * according to the rules defined in VertexCrossing() below. The rules 132 * are designed so that point containment tests can be implemented simply 133 * by counting edge crossings. Similarly, determining whether one edge 134 * chain crosses another edge chain can be implemented by counting. 135 * 136 * Returns true if CrossingSign(c, d) > 0, or AB and CD share a vertex 137 * and VertexCrossing(a, b, c, d) returns true. 138 * 139 * The arguments must point to values that persist until the next call. 140 */ 141 bool edgeOrVertexCrossing(ref in S2Point c, ref in S2Point d) { 142 if (_c != &c) { 143 restartAt(c); 144 } 145 return edgeOrVertexCrossing(d); 146 } 147 148 ///////////////////////// Edge Chain Methods /////////////////////////// 149 // 150 // You don't need to use these unless you're trying to squeeze out every 151 // last drop of performance. Essentially all you are saving is a test 152 // whether the first vertex of the current edge is the same as the second 153 // vertex of the previous edge. Example usage: 154 // 155 // vector<S2Point> chain; 156 // crosser.RestartAt(&chain[0]); 157 // for (int i = 1; i < chain.size(); ++i) { 158 // if (crosser.EdgeOrVertexCrossing(&chain[i])) { ++count; } 159 // } 160 161 /** 162 * Convenience constructor that uses AB as the fixed edge, and C as the 163 * first vertex of the vertex chain (equivalent to calling RestartAt(c)). 164 * 165 * The arguments must point to values that persist until the next call. 166 */ 167 this(ref in S2Point a, ref in S2Point b, ref in S2Point c) { 168 if (!isUnitLength(a)) logger.logWarn("Invalid"); 169 if (!isUnitLength(b)) logger.logWarn("Invalid"); 170 171 _a = &a; 172 _b = &b; 173 _aCrossB = _a.crossProd(*_b); 174 _haveTangents = false; 175 restartAt(c); 176 } 177 178 179 /** 180 * Call this method when your chain 'jumps' to a new place. 181 * The argument must point to a value that persists until the next call. 182 */ 183 void restartAt(ref in S2Point c) { 184 if (!isUnitLength(c)) logger.logWarn("Invalid"); 185 _c = &c; 186 _acb = -triageSign(*_a, *_b, *_c, _aCrossB); 187 } 188 189 190 /** 191 * Like CrossingSign above, but uses the last vertex passed to one of 192 * the crossing methods (or RestartAt) as the first vertex of the current 193 * edge. 194 * 195 * The argument must point to a value that persists until the next call. 196 */ 197 int crossingSign(ref in S2Point d) { 198 if (!isUnitLength(d)) logger.logWarn("Invalid"); 199 // For there to be an edge crossing, the triangles ACB, CBD, BDA, DAC must 200 // all be oriented the same way (CW or CCW). We keep the orientation of ACB 201 // as part of our state. When each new point D arrives, we compute the 202 // orientation of BDA and check whether it matches ACB. This checks whether 203 // the points C and D are on opposite sides of the great circle through AB. 204 205 // Recall that TriageSign is invariant with respect to rotating its 206 // arguments, i.e. ABD has the same orientation as BDA. 207 int bda = triageSign(*_a, *_b, d, _aCrossB); 208 if (_acb == -bda && bda != 0) { 209 // The most common case -- triangles have opposite orientations. Save the 210 // current vertex D as the next vertex C, and also save the orientation of 211 // the new triangle ACB (which is opposite to the current triangle BDA). 212 _c = &d; 213 _acb = -bda; 214 return -1; 215 } 216 _bda = bda; 217 return crossingSignInternal(d); 218 } 219 220 /** 221 * Like EdgeOrVertexCrossing above, but uses the last vertex passed to one 222 * of the crossing methods (or RestartAt) as the first vertex of the 223 * current edge. 224 * 225 * The argument must point to a value that persists until the next call. 226 */ 227 bool edgeOrVertexCrossing(ref in S2Point d) { 228 // We need to copy c_ since it is clobbered by CrossingSign(). 229 const S2Point c = *_c; 230 int crossing = crossingSign(d); 231 if (crossing < 0) { 232 return false; 233 } 234 if (crossing > 0) { 235 return true; 236 } 237 return vertexCrossing(*_a, *_b, c, d); 238 } 239 240 /** 241 * Returns the last vertex of the current edge chain being tested, i.e. the 242 * C vertex that will be used to construct the edge CD when one of the 243 * methods above is called. 244 */ 245 @property 246 const(S2Point)* c() { 247 return _c; 248 } 249 250 private: 251 /** These functions handle the "slow path" of CrossingSign(). */ 252 int crossingSignInternal(ref in S2Point d) { 253 // Compute the actual result, and then save the current vertex D as the next 254 // vertex C, and save the orientation of the next triangle ACB (which is 255 // opposite to the current triangle BDA). 256 int result = crossingSignInternal2(d); 257 _c = &d; 258 _acb = -_bda; 259 return result; 260 } 261 262 int crossingSignInternal2(ref in S2Point d) { 263 // At this point, a very common situation is that A,B,C,D are four points on 264 // a line such that AB does not overlap CD. (For example, this happens when 265 // a line or curve is sampled finely, or when geometry is constructed by 266 // computing the union of S2CellIds.) Most of the time, we can determine 267 // that AB and CD do not intersect by computing the two outward-facing 268 // tangents at A and B (parallel to AB) and testing whether AB and CD are on 269 // opposite sides of the plane perpendicular to one of these tangents. This 270 // is moderately expensive but still much cheaper than s2pred::ExpensiveSign. 271 if (!_haveTangents) { 272 S2Point norm = robustCrossProd(*_a, *_b).normalize(); 273 _aTangent = _a.crossProd(norm); 274 _bTangent = norm.crossProd(*_b); 275 _haveTangents = true; 276 } 277 // The error in robustCrossProd() is insignificant. The maximum error in 278 // the call to crossProd() (i.e., the maximum norm of the error vector) is 279 // (0.5 + 1/sqrt(3)) * double.epsilon. The maximum error in each call to 280 // DotProd() below is double.epsilon. (There is also a small relative error 281 // term that is insignificant because we are comparing the result against a 282 // constant that is very close to zero.) 283 static const double kError = (1.5 + 1 / math.sqrt(3.0)) * double.epsilon; 284 if ((_c.dotProd(_aTangent) > kError && d.dotProd(_aTangent) > kError) || 285 (_c.dotProd(_bTangent) > kError && d.dotProd(_bTangent) > kError)) { 286 return -1; 287 } 288 289 // Otherwise, eliminate the cases where two vertices from different edges 290 // are equal. (These cases could be handled in the code below, but we would 291 // rather avoid calling ExpensiveSign whenever possible.) 292 if (*_a == *_c || *_a == d || *_b == *_c || *_b == d) { 293 return 0; 294 } 295 296 // Eliminate cases where an input edge is degenerate. (Note that in most 297 // cases, if CD is degenerate then this method is not even called because 298 // _acb and bda have different signs.) 299 if (*_a == *_b || *_c == d) { 300 return -1; 301 } 302 303 // Otherwise it's time to break out the big guns. 304 if (_acb == 0) { 305 _acb = -expensiveSign(*_a, *_b, *_c); 306 } 307 assert(_acb != 0); 308 if (_bda == 0) { 309 _bda = expensiveSign(*_a, *_b, d); 310 } 311 assert(_bda != 0); 312 if (_bda != _acb) { 313 return -1; 314 } 315 316 Vector3_d c_cross_d = _c.crossProd(d); 317 int cbd = -sign(*_c, d, *_b, c_cross_d); 318 assert(cbd != 0); 319 if (cbd != _acb) { 320 return -1; 321 } 322 int dac = sign(*_c, d, *_a, c_cross_d); 323 assert(dac != 0); 324 return (dac != _acb) ? -1 : 1; 325 } 326 327 /** Used internally by S2CopyingEdgeCrosser. Updates "_c" only. */ 328 void setC(ref S2Point c) { 329 _c = &c; 330 } 331 332 // The fields below are constant after the call to Init(). 333 const(S2Point)* _a; 334 const(S2Point)* _b; 335 Vector3_d _aCrossB; 336 337 // To reduce the number of calls to s2pred::ExpensiveSign(), we compute an 338 // outward-facing tangent at A and B if necessary. If the plane 339 // perpendicular to one of these tangents separates AB from CD (i.e., one 340 // edge on each side) then there is no intersection. 341 bool _haveTangents; // True if the tangents have been computed. 342 S2Point _aTangent; // Outward-facing tangent at A. 343 S2Point _bTangent; // Outward-facing tangent at B. 344 345 // The fields below are updated for each vertex in the chain. 346 const(S2Point)* _c; // Previous vertex in the vertex chain. 347 int _acb; // The orientation of triangle ACB. 348 349 // The field below is a temporary used by CrossingSignInternal(). 350 int _bda; // The orientation of triangle BDA. 351 } 352 353 /** 354 * S2CopyingEdgeCrosser is exactly like S2EdgeCrosser, except that it makes its 355 * own copy of all arguments so that they do not need to persist between 356 * calls. This is less efficient, but makes it possible to use points that 357 * are generated on demand and cannot conveniently be stored by the client. 358 */ 359 class S2CopyingEdgeCrosser { 360 public: 361 // These methods are all exactly like S2EdgeCrosser, except that the 362 // arguments can be temporaries. 363 this() { 364 _crosser = new S2EdgeCrosser(); 365 } 366 367 this(in S2Point a, in S2Point b) { 368 _a = a; 369 _b = b; 370 _c = S2Point(); 371 _crosser = new S2EdgeCrosser(_a, _b); 372 } 373 374 S2Point a() { 375 return _a; 376 } 377 378 S2Point b() { 379 return _b; 380 } 381 382 S2Point c() { 383 return _c; 384 } 385 386 void initialize(in S2Point a, in S2Point b) { 387 _a = a; 388 _b = b; 389 _c = S2Point(); 390 _crosser.initialize(_a, _b); 391 } 392 393 int crossingSign(in S2Point c, in S2Point d) { 394 if (c != _c || _crosser._c == null) { 395 restartAt(c); 396 } 397 return crossingSign(d); 398 } 399 400 bool edgeOrVertexCrossing(in S2Point c, in S2Point d) { 401 if (c != _c || _crosser._c == null) { 402 restartAt(c); 403 } 404 return edgeOrVertexCrossing(d); 405 } 406 407 this(in S2Point a, in S2Point b, in S2Point c) { 408 _a = a; 409 _b = b; 410 _c = c; 411 _crosser = new S2EdgeCrosser(_a, _b, _c); 412 } 413 414 void restartAt(in S2Point c) { 415 _c = c; 416 _crosser.restartAt(_c); 417 } 418 419 int crossingSign(in S2Point d) { 420 int result = _crosser.crossingSign(d); 421 _c = d; 422 _crosser.setC(_c); 423 return result; 424 } 425 426 bool edgeOrVertexCrossing(in S2Point d) { 427 bool result = _crosser.edgeOrVertexCrossing(d); 428 _c = d; 429 _crosser.setC(_c); 430 return result; 431 } 432 433 private: 434 S2Point _a; 435 S2Point _b; 436 S2Point _c; 437 // TODO(ericv): It would be more efficient to implement S2CopyingEdgeCrosser 438 // directly rather than as a wrapper around S2EdgeCrosser. 439 S2EdgeCrosser _crosser; 440 }