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 // Author: ericv@google.com (Eric Veach)
17
18 #include "s2/s2edge_crossings.h"
19 #include "s2/s2edge_crossings_internal.h"
20
21 #include <cmath>
22
23 #include "s2/base/logging.h"
24 #include "s2/s1angle.h"
25 #include "s2/s2edge_crosser.h"
26 #include "s2/s2pointutil.h"
27 #include "s2/s2predicates.h"
28 #include "s2/s2predicates_internal.h"
29 #include "s2/util/math/exactfloat/exactfloat.h"
30
31 namespace S2 {
32
33 using internal::GetIntersectionExact;
34 using internal::IntersectionMethod;
35 using internal::intersection_method_tally_;
36 using std::fabs;
37 using std::sqrt;
38
39 // All error bounds in this file are expressed in terms of the maximum
40 // rounding error for a floating-point type. The rounding error is half of
41 // the numeric_limits<T>::epsilon() value.
42 static constexpr double DBL_ERR = s2pred::rounding_epsilon<double>();
43
44 // kIntersectionError can be set somewhat arbitrarily, because the algorithm
45 // uses more precision when necessary in order to achieve the specified error.
46 // The only strict requirement is that kIntersectionError >= 2 * DBL_ERR
47 // radians. However, using a larger error tolerance makes the algorithm more
48 // efficient because it reduces the number of cases where exact arithmetic is
49 // needed.
50 const S1Angle kIntersectionError = S1Angle::Radians(8 * DBL_ERR);
51
52 const S1Angle kIntersectionMergeRadius = 2 * kIntersectionError;
53
54 namespace internal {
55
56 const S1Angle kIntersectionExactError = S1Angle::Radians(2 * DBL_ERR);
57
58 int* intersection_method_tally_ = nullptr;
59
GetIntersectionMethodName(IntersectionMethod method)60 const char* GetIntersectionMethodName(IntersectionMethod method) {
61 switch (method) {
62 case IntersectionMethod::SIMPLE: return "Simple";
63 case IntersectionMethod::SIMPLE_LD: return "Simple_ld";
64 case IntersectionMethod::STABLE: return "Stable";
65 case IntersectionMethod::STABLE_LD: return "Stable_ld";
66 case IntersectionMethod::EXACT: return "Exact";
67 default: return "Unknown";
68 }
69 }
70
71 } // namespace internal
72
CrossingSign(const S2Point & a,const S2Point & b,const S2Point & c,const S2Point & d)73 int CrossingSign(const S2Point& a, const S2Point& b,
74 const S2Point& c, const S2Point& d) {
75 S2EdgeCrosser crosser(&a, &b, &c);
76 return crosser.CrossingSign(&d);
77 }
78
VertexCrossing(const S2Point & a,const S2Point & b,const S2Point & c,const S2Point & d)79 bool VertexCrossing(const S2Point& a, const S2Point& b,
80 const S2Point& c, const S2Point& d) {
81 // If A == B or C == D there is no intersection. We need to check this
82 // case first in case 3 or more input points are identical.
83 if (a == b || c == d) return false;
84
85 // If any other pair of vertices is equal, there is a crossing if and only
86 // if OrderedCCW() indicates that the edge AB is further CCW around the
87 // shared vertex O (either A or B) than the edge CD, starting from an
88 // arbitrary fixed reference point.
89 //
90 // Optimization: if AB=CD or AB=DC, we can avoid most of the calculations.
91 if (a == c) return (b == d) || s2pred::OrderedCCW(S2::Ortho(a), d, b, a);
92 if (b == d) return s2pred::OrderedCCW(S2::Ortho(b), c, a, b);
93
94 if (a == d) return (b == c) || s2pred::OrderedCCW(S2::Ortho(a), c, b, a);
95 if (b == c) return s2pred::OrderedCCW(S2::Ortho(b), d, a, b);
96
97 S2_LOG(DFATAL) << "VertexCrossing called with 4 distinct vertices";
98 return false;
99 }
100
EdgeOrVertexCrossing(const S2Point & a,const S2Point & b,const S2Point & c,const S2Point & d)101 bool EdgeOrVertexCrossing(const S2Point& a, const S2Point& b,
102 const S2Point& c, const S2Point& d) {
103 int crossing = CrossingSign(a, b, c, d);
104 if (crossing < 0) return false;
105 if (crossing > 0) return true;
106 return VertexCrossing(a, b, c, d);
107 }
108
109 using Vector3_ld = Vector3<long double>;
110 using Vector3_xf = Vector3<ExactFloat>;
111
112 // Computes the cross product of "x" and "y", normalizes it to be unit length,
113 // and stores the result in "result". Also returns the length of the cross
114 // product before normalization, which is useful for estimating the amount of
115 // error in the result. For numerical stability, "x" and "y" should both be
116 // approximately unit length.
117 template <class T>
RobustNormalWithLength(const Vector3<T> & x,const Vector3<T> & y,Vector3<T> * result)118 static T RobustNormalWithLength(const Vector3<T>& x, const Vector3<T>& y,
119 Vector3<T>* result) {
120 // This computes 2 * (x.CrossProd(y)), but has much better numerical
121 // stability when "x" and "y" are unit length.
122 Vector3<T> tmp = (x - y).CrossProd(x + y);
123 T length = tmp.Norm();
124 if (length != 0) {
125 *result = (1 / length) * tmp;
126 }
127 return 0.5 * length; // Since tmp == 2 * (x.CrossProd(y))
128 }
129
130 // If the intersection point of the edges (a0,a1) and (b0,b1) can be computed
131 // to within an error of at most kIntersectionError by this function, then set
132 // "result" to the intersection point and return true.
133 //
134 // The intersection point is not guaranteed to have the correct sign
135 // (i.e., it may be either "result" or "-result").
136 template <class T>
GetIntersectionSimple(const Vector3<T> & a0,const Vector3<T> & a1,const Vector3<T> & b0,const Vector3<T> & b1,Vector3<T> * result)137 static bool GetIntersectionSimple(const Vector3<T>& a0, const Vector3<T>& a1,
138 const Vector3<T>& b0, const Vector3<T>& b1,
139 Vector3<T>* result) {
140 // The code below computes the intersection point as
141 //
142 // (a0.CrossProd(a1)).CrossProd(b0.CrossProd(b1))
143 //
144 // except that it has better numerical stability and also computes a
145 // guaranteed error bound.
146 //
147 // Each cross product is computed as (X-Y).CrossProd(X+Y) using unit-length
148 // input vectors, which eliminates most of the cancellation error. However
149 // the error in the direction of the cross product can still become large if
150 // the two points are extremely close together. We can show that as long as
151 // the length of the cross product is at least (16 * sqrt(3) + 24) * DBL_ERR
152 // (about 6e-15), then the directional error is at most 5 * T_ERR (about
153 // 3e-19 when T == "long double"). (DBL_ERR appears in the first formula
154 // because the inputs are assumed to be normalized in double precision
155 // rather than in the given type T.)
156 //
157 // The third cross product is different because its inputs already have some
158 // error. Letting "result_len" be the length of the cross product, it can
159 // be shown that the error is at most
160 //
161 // (2 + 2 * sqrt(3) + 12 / result_len) * T_ERR
162 //
163 // We want this error to be at most kIntersectionError, which is true as
164 // long as "result_len" is at least kMinResultLen defined below.
165
166 constexpr T T_ERR = s2pred::rounding_epsilon<T>();
167 static const T kMinNormalLength = (16 * sqrt(3) + 24) * DBL_ERR;
168 static const T kMinResultLen =
169 12 / (kIntersectionError.radians() / T_ERR - (2 + 2 * sqrt(3)));
170
171 // On some platforms "long double" is the same as "double", and on these
172 // platforms this method always returns false (e.g. ARM, Win32). Rather
173 // than testing this directly, instead we look at kMinResultLen since this
174 // is a direct measure of whether "long double" has sufficient accuracy to
175 // be useful. If kMinResultLen > 0.5, it means that this method will fail
176 // even for edges that meet at an angle of 30 degrees. (On Intel platforms
177 // kMinResultLen corresponds to an intersection angle of about 0.04
178 // degrees.)
179 S2_DCHECK_LE(kMinResultLen, 0.5);
180
181 Vector3<T> a_norm, b_norm;
182 if (RobustNormalWithLength(a0, a1, &a_norm) >= kMinNormalLength &&
183 RobustNormalWithLength(b0, b1, &b_norm) >= kMinNormalLength &&
184 RobustNormalWithLength(a_norm, b_norm, result) >= kMinResultLen) {
185 return true;
186 }
187 return false;
188 }
189
GetIntersectionSimpleLD(const S2Point & a0,const S2Point & a1,const S2Point & b0,const S2Point & b1,S2Point * result)190 static bool GetIntersectionSimpleLD(const S2Point& a0, const S2Point& a1,
191 const S2Point& b0, const S2Point& b1,
192 S2Point* result) {
193 Vector3_ld result_ld;
194 if (GetIntersectionSimple(Vector3_ld::Cast(a0), Vector3_ld::Cast(a1),
195 Vector3_ld::Cast(b0), Vector3_ld::Cast(b1),
196 &result_ld)) {
197 *result = S2Point::Cast(result_ld);
198 return true;
199 }
200 return false;
201 }
202
203 // Given a point X and a vector "a_norm" (not necessarily unit length),
204 // compute x.DotProd(a_norm) and return a bound on the error in the result.
205 // The remaining parameters allow this dot product to be computed more
206 // accurately and efficiently. They include the length of "a_norm"
207 // ("a_norm_len") and the edge endpoints "a0" and "a1".
208 template <class T>
GetProjection(const Vector3<T> & x,const Vector3<T> & a_norm,T a_norm_len,const Vector3<T> & a0,const Vector3<T> & a1,T * error)209 static T GetProjection(const Vector3<T>& x,
210 const Vector3<T>& a_norm, T a_norm_len,
211 const Vector3<T>& a0, const Vector3<T>& a1,
212 T* error) {
213 // The error in the dot product is proportional to the lengths of the input
214 // vectors, so rather than using "x" itself (a unit-length vector) we use
215 // the vectors from "x" to the closer of the two edge endpoints. This
216 // typically reduces the error by a huge factor.
217 Vector3<T> x0 = x - a0;
218 Vector3<T> x1 = x - a1;
219 T x0_dist2 = x0.Norm2();
220 T x1_dist2 = x1.Norm2();
221
222 // If both distances are the same, we need to be careful to choose one
223 // endpoint deterministically so that the result does not change if the
224 // order of the endpoints is reversed.
225 T dist, result;
226 if (x0_dist2 < x1_dist2 || (x0_dist2 == x1_dist2 && x0 < x1)) {
227 dist = sqrt(x0_dist2);
228 result = x0.DotProd(a_norm);
229 } else {
230 dist = sqrt(x1_dist2);
231 result = x1.DotProd(a_norm);
232 }
233 // This calculation bounds the error from all sources: the computation of
234 // the normal, the subtraction of one endpoint, and the dot product itself.
235 // (DBL_ERR appears because the input points are assumed to be normalized in
236 // double precision rather than in the given type T.)
237 //
238 // For reference, the bounds that went into this calculation are:
239 // ||N'-N|| <= ((1 + 2 * sqrt(3))||N|| + 32 * sqrt(3) * DBL_ERR) * T_ERR
240 // |(A.B)'-(A.B)| <= (1.5 * (A.B) + 1.5 * ||A|| * ||B||) * T_ERR
241 // ||(X-Y)'-(X-Y)|| <= ||X-Y|| * T_ERR
242 constexpr T T_ERR = s2pred::rounding_epsilon<T>();
243 *error = (((3.5 + 2 * sqrt(3)) * a_norm_len + 32 * sqrt(3) * DBL_ERR)
244 * dist + 1.5 * fabs(result)) * T_ERR;
245 return result;
246 }
247
248 // Helper function for GetIntersectionStable(). It expects that the edges
249 // (a0,a1) and (b0,b1) have been sorted so that the first edge is longer.
250 template <class T>
GetIntersectionStableSorted(const Vector3<T> & a0,const Vector3<T> & a1,const Vector3<T> & b0,const Vector3<T> & b1,Vector3<T> * result)251 static bool GetIntersectionStableSorted(
252 const Vector3<T>& a0, const Vector3<T>& a1,
253 const Vector3<T>& b0, const Vector3<T>& b1, Vector3<T>* result) {
254 S2_DCHECK_GE((a1 - a0).Norm2(), (b1 - b0).Norm2());
255
256 // Compute the normal of the plane through (a0, a1) in a stable way.
257 Vector3<T> a_norm = (a0 - a1).CrossProd(a0 + a1);
258 T a_norm_len = a_norm.Norm();
259 T b_len = (b1 - b0).Norm();
260
261 // Compute the projection (i.e., signed distance) of b0 and b1 onto the
262 // plane through (a0, a1). Distances are scaled by the length of a_norm.
263 T b0_error, b1_error;
264 T b0_dist = GetProjection(b0, a_norm, a_norm_len, a0, a1, &b0_error);
265 T b1_dist = GetProjection(b1, a_norm, a_norm_len, a0, a1, &b1_error);
266
267 // The total distance from b0 to b1 measured perpendicularly to (a0,a1) is
268 // |b0_dist - b1_dist|. Note that b0_dist and b1_dist generally have
269 // opposite signs because b0 and b1 are on opposite sides of (a0, a1). The
270 // code below finds the intersection point by interpolating along the edge
271 // (b0, b1) to a fractional distance of b0_dist / (b0_dist - b1_dist).
272 //
273 // It can be shown that the maximum error in the interpolation fraction is
274 //
275 // (b0_dist * b1_error - b1_dist * b0_error) /
276 // (dist_sum * (dist_sum - error_sum))
277 //
278 // We save ourselves some work by scaling the result and the error bound by
279 // "dist_sum", since the result is normalized to be unit length anyway.
280 T dist_sum = fabs(b0_dist - b1_dist);
281 T error_sum = b0_error + b1_error;
282 if (dist_sum <= error_sum) {
283 return false; // Error is unbounded in this case.
284 }
285 Vector3<T> x = b0_dist * b1 - b1_dist * b0;
286 constexpr T T_ERR = s2pred::rounding_epsilon<T>();
287 T error = b_len * fabs(b0_dist * b1_error - b1_dist * b0_error) /
288 (dist_sum - error_sum) + 2 * T_ERR * dist_sum;
289
290 // Finally we normalize the result, compute the corresponding error, and
291 // check whether the total error is acceptable.
292 T x_len2 = x.Norm2();
293 if (x_len2 < std::numeric_limits<T>::min()) {
294 // If x.Norm2() is less than the minimum normalized value of T, x_len might
295 // lose precision and the result might fail to satisfy S2::IsUnitLength().
296 // TODO(ericv): Implement S2::RobustNormalize().
297 return false;
298 }
299 T x_len = sqrt(x_len2);
300 const T kMaxError = kIntersectionError.radians();
301 if (error > (kMaxError - T_ERR) * x_len) {
302 return false;
303 }
304 *result = (1 / x_len) * x;
305 return true;
306 }
307
308 // Returns whether (a0,a1) is less than (b0,b1) with respect to a total
309 // ordering on edges that is invariant under edge reversals.
310 template <class T>
CompareEdges(const Vector3<T> & a0,const Vector3<T> & a1,const Vector3<T> & b0,const Vector3<T> & b1)311 static bool CompareEdges(const Vector3<T>& a0, const Vector3<T>& a1,
312 const Vector3<T>& b0, const Vector3<T>& b1) {
313 const Vector3<T> *pa0 = &a0, *pa1 = &a1;
314 const Vector3<T> *pb0 = &b0, *pb1 = &b1;
315 if (*pa0 >= *pa1) std::swap(pa0, pa1);
316 if (*pb0 >= *pb1) std::swap(pb0, pb1);
317 return *pa0 < *pb0 || (*pa0 == *pb0 && *pb0 < *pb1);
318 }
319
320 // If the intersection point of the edges (a0,a1) and (b0,b1) can be computed
321 // to within an error of at most kIntersectionError by this function, then set
322 // "result" to the intersection point and return true.
323 //
324 // The intersection point is not guaranteed to have the correct sign
325 // (i.e., it may be either "result" or "-result").
326 template <class T>
GetIntersectionStable(const Vector3<T> & a0,const Vector3<T> & a1,const Vector3<T> & b0,const Vector3<T> & b1,Vector3<T> * result)327 static bool GetIntersectionStable(const Vector3<T>& a0, const Vector3<T>& a1,
328 const Vector3<T>& b0, const Vector3<T>& b1,
329 Vector3<T>* result) {
330 // Sort the two edges so that (a0,a1) is longer, breaking ties in a
331 // deterministic way that does not depend on the ordering of the endpoints.
332 // This is desirable for two reasons:
333 // - So that the result doesn't change when edges are swapped or reversed.
334 // - It reduces error, since the first edge is used to compute the edge
335 // normal (where a longer edge means less error), and the second edge
336 // is used for interpolation (where a shorter edge means less error).
337 T a_len2 = (a1 - a0).Norm2();
338 T b_len2 = (b1 - b0).Norm2();
339 if (a_len2 < b_len2 || (a_len2 == b_len2 && CompareEdges(a0, a1, b0, b1))) {
340 return GetIntersectionStableSorted(b0, b1, a0, a1, result);
341 } else {
342 return GetIntersectionStableSorted(a0, a1, b0, b1, result);
343 }
344 }
345
GetIntersectionStableLD(const S2Point & a0,const S2Point & a1,const S2Point & b0,const S2Point & b1,S2Point * result)346 static bool GetIntersectionStableLD(const S2Point& a0, const S2Point& a1,
347 const S2Point& b0, const S2Point& b1,
348 S2Point* result) {
349 Vector3_ld result_ld;
350 if (GetIntersectionStable(Vector3_ld::Cast(a0), Vector3_ld::Cast(a1),
351 Vector3_ld::Cast(b0), Vector3_ld::Cast(b1),
352 &result_ld)) {
353 *result = S2Point::Cast(result_ld);
354 return true;
355 }
356 return false;
357 }
358
S2PointFromExact(const Vector3_xf & xf)359 static S2Point S2PointFromExact(const Vector3_xf& xf) {
360 // If all components of "x" have absolute value less than about 1e-154,
361 // then x.Norm2() is zero in double precision due to underflow. Therefore
362 // we need to scale "x" by an appropriate power of 2 before the conversion.
363 S2Point x(xf[0].ToDouble(), xf[1].ToDouble(), xf[2].ToDouble());
364 if (x.Norm2() > 0) return x.Normalize();
365
366 // Scale so that the largest component magnitude is in the range [0.5, 1).
367 int exp = ExactFloat::kMinExp - 1;
368 for (int i = 0; i < 3; ++i) {
369 if (xf[i].is_normal()) exp = std::max(exp, xf[i].exp());
370 }
371 if (exp < ExactFloat::kMinExp) {
372 return S2Point(0, 0, 0);
373 }
374 return S2Point(ldexp(xf[0], -exp).ToDouble(),
375 ldexp(xf[1], -exp).ToDouble(),
376 ldexp(xf[2], -exp).ToDouble()).Normalize();
377 }
378
379 namespace internal {
380
381 // Compute the intersection point of (a0, a1) and (b0, b1) using exact
382 // arithmetic. Note that the result is not exact because it is rounded to
383 // double precision. Also, the intersection point is not guaranteed to have
384 // the correct sign (i.e., the return value may need to be negated).
GetIntersectionExact(const S2Point & a0,const S2Point & a1,const S2Point & b0,const S2Point & b1)385 S2Point GetIntersectionExact(const S2Point& a0, const S2Point& a1,
386 const S2Point& b0, const S2Point& b1) {
387 // Since we are using exact arithmetic, we don't need to worry about
388 // numerical stability.
389 Vector3_xf a0_xf = Vector3_xf::Cast(a0);
390 Vector3_xf a1_xf = Vector3_xf::Cast(a1);
391 Vector3_xf b0_xf = Vector3_xf::Cast(b0);
392 Vector3_xf b1_xf = Vector3_xf::Cast(b1);
393 Vector3_xf a_norm_xf = a0_xf.CrossProd(a1_xf);
394 Vector3_xf b_norm_xf = b0_xf.CrossProd(b1_xf);
395 Vector3_xf x_xf = a_norm_xf.CrossProd(b_norm_xf);
396
397 // The final Normalize() call is done in double precision, which creates a
398 // directional error of up to 2 * DBL_ERR. (ToDouble() and Normalize() each
399 // contribute up to DBL_ERR of directional error.)
400 S2Point x = S2PointFromExact(x_xf);
401
402 if (x == S2Point(0, 0, 0)) {
403 // The two edges are exactly collinear, but we still consider them to be
404 // "crossing" because of simulation of simplicity. Out of the four
405 // endpoints, exactly two lie in the interior of the other edge. Of
406 // those two we return the one that is lexicographically smallest.
407 x = S2Point(10, 10, 10); // Greater than any valid S2Point
408 S2Point a_norm = S2PointFromExact(a_norm_xf);
409 S2Point b_norm = S2PointFromExact(b_norm_xf);
410 if (a_norm == S2Point(0, 0, 0) || b_norm == S2Point(0, 0, 0)) {
411 // TODO(ericv): To support antipodal edges properly, we would need to
412 // add an s2pred::CrossProd() function that computes the cross product
413 // using simulation of simplicity and rounds the result to the nearest
414 // floating-point representation.
415 S2_LOG(DFATAL) << "Exactly antipodal edges not supported by GetIntersection";
416 }
417 if (s2pred::OrderedCCW(b0, a0, b1, b_norm) && a0 < x) x = a0;
418 if (s2pred::OrderedCCW(b0, a1, b1, b_norm) && a1 < x) x = a1;
419 if (s2pred::OrderedCCW(a0, b0, a1, a_norm) && b0 < x) x = b0;
420 if (s2pred::OrderedCCW(a0, b1, a1, a_norm) && b1 < x) x = b1;
421 }
422 S2_DCHECK(S2::IsUnitLength(x));
423 return x;
424 }
425
426 } // namespace internal
427
428 // Given three points "a", "x", "b", returns true if these three points occur
429 // in the given order along the edge (a,b) to within the given tolerance.
430 // More precisely, either "x" must be within "tolerance" of "a" or "b", or
431 // when "x" is projected onto the great circle through "a" and "b" it must lie
432 // along the edge (a,b) (i.e., the shortest path from "a" to "b").
ApproximatelyOrdered(const S2Point & a,const S2Point & x,const S2Point & b,double tolerance)433 static bool ApproximatelyOrdered(const S2Point& a, const S2Point& x,
434 const S2Point& b, double tolerance) {
435 if ((x - a).Norm2() <= tolerance * tolerance) return true;
436 if ((x - b).Norm2() <= tolerance * tolerance) return true;
437 return s2pred::OrderedCCW(a, x, b, S2::RobustCrossProd(a, b).Normalize());
438 }
439
GetIntersection(const S2Point & a0,const S2Point & a1,const S2Point & b0,const S2Point & b1)440 S2Point GetIntersection(const S2Point& a0, const S2Point& a1,
441 const S2Point& b0, const S2Point& b1) {
442 S2_DCHECK_GT(CrossingSign(a0, a1, b0, b1), 0);
443
444 // It is difficult to compute the intersection point of two edges accurately
445 // when the angle between the edges is very small. Previously we handled
446 // this by only guaranteeing that the returned intersection point is within
447 // kIntersectionError of each edge. However, this means that when the edges
448 // cross at a very small angle, the computed result may be very far from the
449 // true intersection point.
450 //
451 // Instead this function now guarantees that the result is always within
452 // kIntersectionError of the true intersection. This requires using more
453 // sophisticated techniques and in some cases extended precision.
454 //
455 // Three different techniques are implemented, but only two are used:
456 //
457 // - GetIntersectionSimple() computes the intersection point using
458 // numerically stable cross products in "long double" precision.
459 //
460 // - GetIntersectionStable() computes the intersection point using
461 // projection and interpolation, taking care to minimize cancellation
462 // error. This method exists in "double" and "long double" versions.
463 //
464 // - GetIntersectionExact() computes the intersection point using exact
465 // arithmetic and converts the final result back to an S2Point.
466 //
467 // We don't actually use the first method (GetIntersectionSimple) because it
468 // turns out that GetIntersectionStable() is twice as fast and also much
469 // more accurate (even in double precision). The "long double" version
470 // (only available on Intel platforms) uses 80-bit precision and is about
471 // twice as slow. The exact arithmetic version is about 100x slower.
472 //
473 // So our strategy is to first call GetIntersectionStable() in double
474 // precision; if that doesn't work and this platform supports "long double",
475 // then we try again in "long double"; if that doesn't work then we fall
476 // back to exact arithmetic.
477
478 static const bool kUseSimpleMethod = false;
479 static const bool kHasLongDouble = (s2pred::rounding_epsilon<long double>() <
480 s2pred::rounding_epsilon<double>());
481 S2Point result;
482 IntersectionMethod method;
483 if (kUseSimpleMethod && GetIntersectionSimple(a0, a1, b0, b1, &result)) {
484 method = IntersectionMethod::SIMPLE;
485 } else if (kUseSimpleMethod && kHasLongDouble &&
486 GetIntersectionSimpleLD(a0, a1, b0, b1, &result)) {
487 method = IntersectionMethod::SIMPLE_LD;
488 } else if (GetIntersectionStable(a0, a1, b0, b1, &result)) {
489 method = IntersectionMethod::STABLE;
490 } else if (kHasLongDouble &&
491 GetIntersectionStableLD(a0, a1, b0, b1, &result)) {
492 method = IntersectionMethod::STABLE_LD;
493 } else {
494 result = GetIntersectionExact(a0, a1, b0, b1);
495 method = IntersectionMethod::EXACT;
496 }
497 if (intersection_method_tally_) {
498 ++intersection_method_tally_[static_cast<int>(method)];
499 }
500
501 // Make sure the intersection point is on the correct side of the sphere.
502 // Since all vertices are unit length, and edges are less than 180 degrees,
503 // (a0 + a1) and (b0 + b1) both have positive dot product with the
504 // intersection point. We use the sum of all vertices to make sure that the
505 // result is unchanged when the edges are swapped or reversed.
506 if (result.DotProd((a0 + a1) + (b0 + b1)) < 0) result = -result;
507
508 // Make sure that the intersection point lies on both edges.
509 S2_DCHECK(ApproximatelyOrdered(a0, result, a1, kIntersectionError.radians()));
510 S2_DCHECK(ApproximatelyOrdered(b0, result, b1, kIntersectionError.radians()));
511
512 return result;
513 }
514
515 } // namespace S2
516