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