Coverage Report

Created: 2026-07-15 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2edge_crossings.cc
Line
Count
Source
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
20
#include <algorithm>
21
#include <cmath>
22
#include <cstdint>
23
#include <ios>
24
#include <limits>
25
#include <utility>
26
27
#include "absl/base/casts.h"
28
#include "absl/base/optimization.h"
29
#include "absl/log/absl_check.h"
30
#include "absl/log/absl_log.h"
31
#include "absl/strings/string_view.h"
32
33
#include "s2/s1angle.h"
34
#include "s2/s2edge_crosser.h"
35
#include "s2/s2edge_crossings_internal.h"
36
#include "s2/s2point.h"
37
#include "s2/s2pointutil.h"
38
#include "s2/s2predicates.h"
39
#include "s2/s2predicates_internal.h"
40
#include "s2/util/math/exactfloat/exactfloat.h"
41
42
namespace S2 {
43
44
using absl::string_view;
45
using exactfloat::ExactFloat;
46
using internal::GetIntersectionExact;
47
using internal::intersection_method_tally_;
48
using internal::IntersectionMethod;
49
using S2::internal::GetStableCrossProd;
50
using s2pred::DBL_ERR;
51
using s2pred::kSqrt3;
52
using s2pred::rounding_epsilon;
53
using s2pred::ToExact;
54
using s2pred::ToLD;
55
using std::fabs;
56
using std::max;
57
using std::sqrt;
58
59
using Vector3_ld = s2pred::Vector3_ld;
60
using Vector3_xf = s2pred::Vector3_xf;
61
62
// kRobustCrossProdError can be set somewhat arbitrarily because the algorithm
63
// uses more precision as needed in order to achieve the specified error.  The
64
// only strict requirement is that kRobustCrossProdError >= DBL_ERR, since
65
// this is the minimum error even when using exact arithmetic.  We set the
66
// error somewhat larger than this so that virtually all cases can be handled
67
// using ordinary double-precision arithmetic.
68
static_assert(kRobustCrossProdError.radians() == 6 * DBL_ERR, "update comment");
69
70
// kIntersectionError can also be set somewhat arbitrarily (see above) except
71
// that in this case the error using exact arithmetic is up to 2 * DBL_ERR,
72
// and the error limit is set to 8 * DBL_ERR so that virtually all cases can
73
// be handled using ordinary double-precision arithmetic.
74
static_assert(kIntersectionError.radians() == 8 * DBL_ERR, "update comment");
75
76
namespace internal {
77
78
const S1Angle kExactCrossProdError = S1Angle::Radians(DBL_ERR);
79
const S1Angle kIntersectionExactError = S1Angle::Radians(2 * DBL_ERR);
80
81
int* intersection_method_tally_ = nullptr;
82
83
0
string_view GetIntersectionMethodName(IntersectionMethod method) {
84
0
  switch (method) {
85
0
    case IntersectionMethod::SIMPLE:    return "Simple";
86
0
    case IntersectionMethod::SIMPLE_LD: return "Simple_ld";
87
0
    case IntersectionMethod::STABLE:    return "Stable";
88
0
    case IntersectionMethod::STABLE_LD: return "Stable_ld";
89
0
    case IntersectionMethod::EXACT:     return "Exact";
90
0
    default:                            return "Unknown";
91
0
  }
92
0
}
93
94
// Evaluates the cross product of unit-length vectors "a" and "b" in a
95
// numerically stable way, returning true if the error in the result is
96
// guaranteed to be at most kRobustCrossProdError.
97
template <class T>
98
inline bool GetStableCrossProd(const Vector3<T>& a, const Vector3<T>& b,
99
0
                               Vector3<T>* result) {
100
  // We compute the cross product (a - b) x (a + b).  Mathematically this is
101
  // exactly twice the cross product of "a" and "b", but it has the numerical
102
  // advantage that (a - b) and (a + b) are nearly perpendicular (since "a" and
103
  // "b" are unit length).  This yields a result that is nearly orthogonal to
104
  // both "a" and "b" even if these two values differ only very slightly.
105
  //
106
  // The maximum directional error in radians when this calculation is done in
107
  // precision T (where T is a floating-point type) is:
108
  //
109
  //   (1 + 2 * sqrt(3) + 32 * sqrt(3) * DBL_ERR / ||N||) * T_ERR
110
  //
111
  // where ||N|| is the norm of the result.  To keep this error to at most
112
  // kRobustCrossProdError, assuming this value is much less than 1, we need
113
  //
114
  //   (1 + 2 * sqrt(3) + 32 * sqrt(3) * DBL_ERR / ||N||) * T_ERR <= kErr
115
  //
116
  //   ||N|| >= 32 * sqrt(3) * DBL_ERR / (kErr / T_ERR - (1 + 2 * sqrt(3)))
117
  //
118
  // From this you can see that in order for this calculation to ever succeed in
119
  // double precision, we must have kErr > (1 + 2 * sqrt(3)) * DBL_ERR, which is
120
  // about 4.46 * DBL_ERR.  We actually set kRobustCrossProdError == 6 * DBL_ERR
121
  // (== 3 * DBL_EPSILON) in order to minimize the number of cases where higher
122
  // precision is needed; in particular, higher precision is only necessary when
123
  // "a" and "b" are closer than about 18 * DBL_ERR == 9 * DBL_EPSILON.
124
  // (80-bit precision can handle inputs as close as 2.5 * LDBL_EPSILON.)
125
0
  constexpr T T_ERR = rounding_epsilon<T>();
126
  // `kMinNorm` should be `constexpr`, but we make it only `const` to work
127
  // around a gcc ppc64el bug with `long double`s.
128
  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107745
129
  // https://github.com/google/s2geometry/issues/279
130
0
  const T kMinNorm =
131
0
      (32 * kSqrt3 * DBL_ERR) /
132
0
      (kRobustCrossProdError.radians() / T_ERR - (1 + 2 * kSqrt3));
133
134
0
  *result = (a - b).CrossProd(a + b);
135
0
  return result->Norm2() >= kMinNorm * kMinNorm;
136
0
}
Unexecuted instantiation: bool S2::internal::GetStableCrossProd<double>(Vector3<double> const&, Vector3<double> const&, Vector3<double>*)
Unexecuted instantiation: bool S2::internal::GetStableCrossProd<long double>(Vector3<long double> const&, Vector3<long double> const&, Vector3<long double>*)
137
138
// Explicitly instantiate this function so that we can use it in tests without
139
// putting its definition in a header file.
140
template bool GetStableCrossProd<double>(
141
  const Vector3_d&, const Vector3_d&, Vector3_d*);
142
template bool GetStableCrossProd<long double>(
143
    const Vector3_ld&, const Vector3_ld&, Vector3_ld*);
144
145
}  // namespace internal
146
147
0
S2Point RobustCrossProd(const S2Point& a, const S2Point& b) {
148
0
  ABSL_DCHECK(IsUnitLength(a));
149
0
  ABSL_DCHECK(IsUnitLength(b));
150
151
  // The direction of a.CrossProd(b) becomes unstable as (a + b) or (a - b)
152
  // approaches zero.  This leads to situations where a.CrossProd(b) is not
153
  // very orthogonal to "a" and/or "b".  To solve this problem robustly requires
154
  // falling back to extended precision, arbitrary precision, and even symbolic
155
  // perturbations to handle the case when "a" and "b" are exactly
156
  // proportional, e.g. a == -b (see s2predicates.cc for details).
157
0
  Vector3_d result;
158
0
  if (GetStableCrossProd(a, b, &result)) {
159
0
    return result;
160
0
  }
161
  // Handle the (a == b) case now, before doing expensive arithmetic.  The only
162
  // result that makes sense mathematically is to return zero, but it turns out
163
  // to reduce the number of special cases in client code if we instead return
164
  // an arbitrary orthogonal vector.
165
0
  if (a == b) {
166
0
    return Ortho(a);
167
0
  }
168
0
  constexpr bool kUseLongDoubleInRobustCrossProd = s2pred::kHasLongDouble;
169
  // Next we try using "long double" precision (if available).
170
0
  Vector3_ld result_ld;
171
0
  if (kUseLongDoubleInRobustCrossProd &&
172
0
      GetStableCrossProd(ToLD(a), ToLD(b), &result_ld)) {
173
0
    return Vector3_d::Cast(result_ld);
174
0
  }
175
  // Otherwise we fall back to exact arithmetic, then symbolic perturbations.
176
0
  return internal::ExactCrossProd(a, b);
177
0
}
178
179
// Returns the cross product of "a" and "b" after symbolic perturbations.
180
// (These perturbations only affect the result if "a" and "b" are exactly
181
// collinear, e.g. if a == -b or a == (1+eps) * b.)  The result may not be
182
// normalizable (i.e., EnsureNormalizable() should be called on the result).
183
0
static Vector3_d SymbolicCrossProdSorted(const S2Point& a, const S2Point& b) {
184
0
  ABSL_DCHECK(a < b);
185
0
  ABSL_DCHECK(s2pred::IsZero(ToExact(a).CrossProd(ToExact(b))));
186
187
  // The following code uses the same symbolic perturbation model as S2::Sign.
188
  // The particular sequence of tests below was obtained using Mathematica
189
  // (although it would be easy to do it by hand for this simple case).
190
  //
191
  // Just like the function SymbolicallyPerturbedSign() in s2predicates.cc,
192
  // every input coordinate x[i] is assigned a symbolic perturbation dx[i].  We
193
  // then compute the cross product
194
  //
195
  //     (a + da).CrossProd(b + db) .
196
  //
197
  // The result is a polynomial in the perturbation symbols.  For example if we
198
  // did this in one dimension, the result would be
199
  //
200
  //     a * b + b * da + a * db + da * db
201
  //
202
  // where "a" and "b" have numerical values and "da" and "db" are symbols.
203
  // In 3 dimensions the result is similar except that the coefficients are
204
  // 3-vectors rather than scalars.
205
  //
206
  // Every possible S2Point has its own symbolic perturbation in each coordinate
207
  // (i.e., there are about 3 * 2**192 symbols).  The magnitudes of the
208
  // perturbations are chosen such that if x < y lexicographically, the
209
  // perturbations for "y" are much smaller than the perturbations for "x".
210
  // Similarly, the perturbations for the coordinates of a given point x are
211
  // chosen such that dx[0] is much smaller than dx[1] which is much smaller
212
  // than dx[2].  Putting this together with fact the inputs to this function
213
  // have been sorted so that a < b lexicographically, this tells us that
214
  //
215
  //     da[2] > da[1] > da[0] > db[2] > db[1] > db[0]
216
  //
217
  // where each perturbation is so much smaller than the previous one that we
218
  // don't even need to consider it unless the coefficients of all previous
219
  // perturbations are zero.  In fact, each succeeding perturbation is so small
220
  // that we don't need to consider it unless the coefficient of all products of
221
  // the previous perturbations are zero.  For example, we don't need to
222
  // consider the coefficient of db[1] unless the coefficient of db[2]*da[0] is
223
  // zero.
224
  //
225
  // The follow code simply enumerates the coefficients of the perturbations
226
  // (and products of perturbations) that appear in the cross product above, in
227
  // order of decreasing perturbation magnitude.  The first non-zero
228
  // coefficient determines the result.  The easiest way to enumerate the
229
  // coefficients in the correct order is to pretend that each perturbation is
230
  // some tiny value "eps" raised to a power of two:
231
  //
232
  // eps**    1      2      4      8     16     32
233
  //        da[2]  da[1]  da[0]  db[2]  db[1]  db[0]
234
  //
235
  // Essentially we can then just count in binary and test the corresponding
236
  // subset of perturbations at each step.  So for example, we must test the
237
  // coefficient of db[2]*da[0] before db[1] because eps**12 > eps**16.
238
239
0
  if (b[0] != 0 || b[1] != 0) {           // da[2]
240
0
    return Vector3_d(-b[1], b[0], 0);
241
0
  }
242
0
  if (b[2] != 0) {                        // da[1]
243
0
    return Vector3_d(b[2], 0, 0);         // Note that b[0] == 0.
244
0
  }
245
246
  // None of the remaining cases can occur in practice, because we can only get
247
  // to this point if b = (0, 0, 0).  Nevertheless, even (0, 0, 0) has a
248
  // well-defined direction under the symbolic perturbation model.
249
0
  ABSL_DCHECK(b[1] == 0 && b[2] == 0);  // da[0] coefficients (always zero)
250
251
0
  if (a[0] != 0 || a[1] != 0) {          // db[2]
252
0
    return Vector3_d(a[1], -a[0], 0);
253
0
  }
254
255
  // The following coefficient is always non-zero, so we can stop here.
256
  //
257
  // It may seem strange that we are returning (1, 0, 0) as the cross product
258
  // without even looking at the sign of a[2].  (Wouldn't you expect
259
  // (0, 0, -1) x (0, 0, 0) and (0, 0, 1) x (0, 0, 0) to point in opposite
260
  // directions?)  It's worth pointing out that in this function there is *no
261
  // relationship whatsoever* between the vectors "a" and "-a", because the
262
  // perturbations applied to these vectors may be entirely different.  This is
263
  // why the identity "RobustCrossProd(-a, b) == -RobustCrossProd(a, b)" does
264
  // not hold whenever "a" and "b" are linearly dependent (i.e., proportional).
265
  // [As it happens the two cross products above actually do point in opposite
266
  // directions, but for example (1, 1, 1) x (2, 2, 2) = (-2, 2, 0) and
267
  // (-1, -1, -1) x (2, 2, 2) = (-2, 2, 0) do not.]
268
0
  return Vector3_d(1, 0, 0);                   // db[2] * da[1]
269
0
}
270
271
// Returns true if the given vector's magnitude is large enough such that the
272
// angle to another vector of the same magnitude can be measured using Angle()
273
// without loss of precision due to floating-point underflow.  (This requirement
274
// is also sufficient to ensure that Normalize() can be called without risk of
275
// precision loss.)
276
0
inline static bool IsNormalizable(const Vector3_d& p) {
277
  // Let ab = RobustCrossProd(a, b) and cd = RobustCrossProd(cd).  In order for
278
  // ab.Angle(cd) to not lose precision, the squared magnitudes of ab and cd
279
  // must each be at least 2**-484.  This ensures that the sum of the squared
280
  // magnitudes of ab.CrossProd(cd) and ab.DotProd(cd) is at least 2**-968,
281
  // which ensures that any denormalized terms in these two calculations do
282
  // not affect the accuracy of the result (since all denormalized numbers are
283
  // smaller than 2**-1022, which is less than DBL_ERR * 2**-968).
284
  //
285
  // The fastest way to ensure this is to test whether the largest component of
286
  // the result has a magnitude of at least 2**-242.
287
0
  return max(fabs(p[0]), max(fabs(p[1]), fabs(p[2]))) >= ldexp(1, -242);
288
0
}
289
290
// Scales a 3-vector as necessary to ensure that the result can be normalized
291
// without loss of precision due to floating-point underflow.
292
//
293
// REQUIRES: p != (0, 0, 0)
294
0
inline static Vector3_d EnsureNormalizable(const Vector3_d& p) {
295
0
  ABSL_DCHECK_NE(p, Vector3_d(0, 0, 0));
296
0
  if (!IsNormalizable(p)) {
297
    // We can't just scale by a fixed factor because the smallest representable
298
    // double is 2**-1074, so if we multiplied by 2**(1074 - 242) then the
299
    // result might be so large that we couldn't square it without overflow.
300
    //
301
    // Note that we must scale by a power of two to avoid rounding errors,
302
    // and that the calculation of "pmax" is free because IsNormalizable()
303
    // is inline.  The code below scales "p" such that the largest component is
304
    // in the range [1, 2).
305
0
    double p_max = max(fabs(p[0]), max(fabs(p[1]), fabs(p[2])));
306
307
    // The expression below avoids signed overflow for any value of ilogb().
308
0
    return ldexp(2, -1 - ilogb(p_max)) * p;
309
0
  }
310
0
  return p;
311
0
}
312
313
// Converts an ExactFloat vector to a double-precision vector, scaling the
314
// result as necessary to ensure that the result can be normalized without loss
315
// of precision due to floating-point underflow.  (This method doesn't actually
316
// call Normalize() since that would create additional error in situations
317
// where normalization is not necessary.)
318
0
static Vector3_d NormalizableFromExact(const Vector3_xf& xf) {
319
0
  Vector3_d x = Vector3_d::Cast(xf);
320
0
  if (IsNormalizable(x)) {
321
0
    return x;
322
0
  }
323
  // Scale so that the largest component magnitude is in the range [0.5, 1).
324
  // Note that the exponents involved could be much smaller than those
325
  // representable by an IEEE double precision float.
326
0
  int exp = ExactFloat::kMinExp - 1;
327
0
  for (int i = 0; i < 3; ++i) {
328
0
    if (isnormal(xf[i])) exp = std::max(exp, xf[i].exp());
329
0
  }
330
0
  if (exp < ExactFloat::kMinExp) {
331
0
    return Vector3_d(0, 0, 0);  // The exact result is (0, 0, 0).
332
0
  }
333
0
  return Vector3_d(static_cast<double>(ldexp(xf[0], -exp)),
334
0
                   static_cast<double>(ldexp(xf[1], -exp)),
335
0
                   static_cast<double>(ldexp(xf[2], -exp)));
336
0
}
337
338
namespace internal {
339
340
0
Vector3_d SymbolicCrossProd(const S2Point& a, const S2Point& b) {
341
0
  ABSL_DCHECK_NE(a, b);
342
  // SymbolicCrossProdSorted() requires that a < b.
343
0
  if (a < b) {
344
0
    return EnsureNormalizable(SymbolicCrossProdSorted(a, b));
345
0
  } else {
346
0
    return -EnsureNormalizable(SymbolicCrossProdSorted(b, a));
347
0
  }
348
0
}
349
0
Vector3_d ExactCrossProd(const S2Point& a, const S2Point& b) {
350
0
  ABSL_DCHECK_NE(a, b);
351
0
  Vector3_xf result_xf = ToExact(a).CrossProd(ToExact(b));
352
0
  if (!s2pred::IsZero(result_xf)) {
353
0
    return NormalizableFromExact(result_xf);
354
0
  }
355
  // SymbolicCrossProd() requires that a < b.
356
0
  if (a < b) {
357
0
    return EnsureNormalizable(SymbolicCrossProd(a, b));
358
0
  } else {
359
0
    return -EnsureNormalizable(SymbolicCrossProd(b, a));
360
0
  }
361
0
}
362
363
}  // namespace internal
364
365
int CrossingSign(const S2Point& a, const S2Point& b,
366
0
                 const S2Point& c, const S2Point& d) {
367
0
  S2EdgeCrosser crosser(&a, &b, &c);
368
0
  return crosser.CrossingSign(&d);
369
0
}
370
371
bool VertexCrossing(const S2Point& a, const S2Point& b,
372
0
                    const S2Point& c, const S2Point& d) {
373
  // If A == B or C == D there is no intersection.  We need to check this
374
  // case first in case 3 or more input points are identical.
375
0
  if (a == b || c == d) return false;
376
377
  // If any other pair of vertices is equal, there is a crossing if and only
378
  // if OrderedCCW() indicates that the edge AB is further CCW around the
379
  // shared vertex O (either A or B) than the edge CD, starting from an
380
  // arbitrary fixed reference point.
381
  //
382
  // Optimization: if AB=CD or AB=DC, we can avoid most of the calculations.
383
0
  if (a == c) return (b == d) || s2pred::OrderedCCW(S2::RefDir(a), d, b, a);
384
0
  if (b == d) return s2pred::OrderedCCW(S2::RefDir(b), c, a, b);
385
386
0
  if (a == d) return (b == c) || s2pred::OrderedCCW(S2::RefDir(a), c, b, a);
387
0
  if (b == c) return s2pred::OrderedCCW(S2::RefDir(b), d, a, b);
388
389
0
  ABSL_LOG(DFATAL) << "VertexCrossing called with 4 distinct vertices";
390
0
  return false;
391
0
}
392
393
int SignedVertexCrossing(const S2Point& a, const S2Point& b,
394
0
                         const S2Point& c, const S2Point& d) {
395
0
  if (a == b || c == d) return 0;
396
397
  // See VertexCrossing.  The sign of the crossing is +1 if both edges are
398
  // outgoing or both edges are incoming with respect to the common vertex
399
  // and -1 otherwise.
400
0
  if (a == c) {
401
0
    return ((b == d) || s2pred::OrderedCCW(S2::RefDir(a), d, b, a)) ? 1 : 0;
402
0
  }
403
0
  if (b == d) return s2pred::OrderedCCW(S2::RefDir(b), c, a, b) ? 1 : 0;
404
405
0
  if (a == d) {
406
0
    return ((b == c) || s2pred::OrderedCCW(S2::RefDir(a), c, b, a)) ? -1 : 0;
407
0
  }
408
0
  if (b == c) return s2pred::OrderedCCW(S2::RefDir(b), d, a, b) ? -1 : 0;
409
410
0
  ABSL_LOG(DFATAL) << "SignedVertexCrossing called with 4 distinct vertices";
411
0
  return 0;
412
0
}
413
414
bool EdgeOrVertexCrossing(const S2Point& a, const S2Point& b,
415
0
                          const S2Point& c, const S2Point& d) {
416
0
  int crossing = CrossingSign(a, b, c, d);
417
0
  if (crossing < 0) return false;
418
0
  if (crossing > 0) return true;
419
0
  return VertexCrossing(a, b, c, d);
420
0
}
421
422
// Computes the cross product of "x" and "y", normalizes it to be unit length,
423
// and stores the result in "result".  Also returns the length of the cross
424
// product before normalization, which is useful for estimating the amount of
425
// error in the result.  For numerical stability, "x" and "y" should both be
426
// approximately unit length.
427
template <class T>
428
static T RobustNormalWithLength(const Vector3<T>& x, const Vector3<T>& y,
429
0
                                Vector3<T>* result) {
430
0
  // This computes 2 * (x.CrossProd(y)), but has much better numerical
431
0
  // stability when "x" and "y" are unit length.
432
0
  Vector3<T> tmp = (x - y).CrossProd(x + y);
433
0
  T length = tmp.Norm();
434
0
  if (length != 0) {
435
0
    *result = (1 / length) * tmp;
436
0
  }
437
0
  return 0.5 * length;  // Since tmp == 2 * (x.CrossProd(y))
438
0
}
Unexecuted instantiation: s2edge_crossings.cc:long double S2::RobustNormalWithLength<long double>(Vector3<long double> const&, Vector3<long double> const&, Vector3<long double>*)
Unexecuted instantiation: s2edge_crossings.cc:double S2::RobustNormalWithLength<double>(Vector3<double> const&, Vector3<double> const&, Vector3<double>*)
439
440
// If the intersection point of the edges (a0,a1) and (b0,b1) can be computed
441
// to within an error of at most kIntersectionError by this function, then set
442
// "result" to the intersection point and return true.
443
template <class T>
444
static bool GetIntersectionSimple(const Vector3<T>& a0, const Vector3<T>& a1,
445
                                  const Vector3<T>& b0, const Vector3<T>& b1,
446
0
                                  Vector3<T>* result) {
447
0
  // The code below computes the intersection point as
448
0
  //
449
0
  //    (a0.CrossProd(a1)).CrossProd(b0.CrossProd(b1))
450
0
  //
451
0
  // except that it has better numerical stability and also computes a
452
0
  // guaranteed error bound.
453
0
  //
454
0
  // Each cross product is computed as (X-Y).CrossProd(X+Y) using unit-length
455
0
  // input vectors, which eliminates most of the cancellation error.  However
456
0
  // the error in the direction of the cross product can still become large if
457
0
  // the two points are extremely close together.  We can show that as long as
458
0
  // the length of the cross product is at least (16 * sqrt(3) + 24) * DBL_ERR
459
0
  // (about 6e-15), then the directional error is at most 5 * T_ERR (about
460
0
  // 3e-19 when T == "long double").  (DBL_ERR appears in the first formula
461
0
  // because the inputs are assumed to be normalized in double precision
462
0
  // rather than in the given type T.)
463
0
  //
464
0
  // The third cross product is different because its inputs already have some
465
0
  // error.  Letting "result_len" be the length of the cross product, it can
466
0
  // be shown that the error is at most
467
0
  //
468
0
  //   (2 + 2 * sqrt(3) + 12 / result_len) * T_ERR
469
0
  //
470
0
  // We want this error to be at most kIntersectionError, which is true as
471
0
  // long as "result_len" is at least kMinResultLen defined below.
472
0
473
0
  constexpr T T_ERR = rounding_epsilon<T>();
474
0
  constexpr T kMinNormalLength = (16 * kSqrt3 + 24) * DBL_ERR;
475
0
  constexpr T kMinResultLen =
476
0
      12 / (kIntersectionError.radians() / T_ERR - (2 + 2 * kSqrt3));
477
0
478
0
  // On some platforms "long double" is the same as "double", and on these
479
0
  // platforms this method always returns false (e.g. ARM32, Win32).  Rather
480
0
  // than testing this directly, instead we look at kMinResultLen since this
481
0
  // is a direct measure of whether "long double" has sufficient accuracy to
482
0
  // be useful.  If kMinResultLen >= 0.5, it means that this method will fail
483
0
  // even for edges that meet at an angle of 30 degrees.  (On Intel platforms
484
0
  // kMinResultLen corresponds to an intersection angle of about 0.04
485
0
  // degrees.)
486
0
  if (kMinResultLen >= 0.5) return false;
487
0
488
0
  Vector3<T> a_norm, b_norm;
489
0
  if (RobustNormalWithLength(a0, a1, &a_norm) >= kMinNormalLength &&
490
0
      RobustNormalWithLength(b0, b1, &b_norm) >= kMinNormalLength &&
491
0
      RobustNormalWithLength(a_norm, b_norm, result) >= kMinResultLen) {
492
0
    // Make sure that we return the intersection point rather than its antipode.
493
0
    *result *= (a_norm.DotProd(b1 - b0) < 0) ? -1 : 1;
494
0
    return true;
495
0
  }
496
0
  return false;
497
0
}
Unexecuted instantiation: s2edge_crossings.cc:bool S2::GetIntersectionSimple<long double>(Vector3<long double> const&, Vector3<long double> const&, Vector3<long double> const&, Vector3<long double> const&, Vector3<long double>*)
Unexecuted instantiation: s2edge_crossings.cc:bool S2::GetIntersectionSimple<double>(Vector3<double> const&, Vector3<double> const&, Vector3<double> const&, Vector3<double> const&, Vector3<double>*)
498
499
static bool GetIntersectionSimpleLD(const S2Point& a0, const S2Point& a1,
500
                                    const S2Point& b0, const S2Point& b1,
501
0
                                    S2Point* result) {
502
0
  Vector3_ld result_ld;
503
0
  if (GetIntersectionSimple(ToLD(a0), ToLD(a1), ToLD(b0), ToLD(b1),
504
0
                            &result_ld)) {
505
0
    *result = S2Point::Cast(result_ld);
506
0
    return true;
507
0
  }
508
0
  return false;
509
0
}
510
511
// Given a point X and a vector "a_norm" (not necessarily unit length),
512
// compute x.DotProd(a_norm) and return a bound on the error in the result.
513
// The remaining parameters allow this dot product to be computed more
514
// accurately and efficiently.  They include the length of "a_norm"
515
// ("a_norm_len") and the edge endpoints "a0" and "a1".
516
template <class T>
517
static T GetProjection(const Vector3<T>& x,
518
                       const Vector3<T>& a_norm, T a_norm_len,
519
                       const Vector3<T>& a0, const Vector3<T>& a1,
520
0
                       T* error) {
521
  // The error in the dot product is proportional to the lengths of the input
522
  // vectors, so rather than using "x" itself (a unit-length vector) we use
523
  // the vectors from "x" to the closer of the two edge endpoints.  This
524
  // typically reduces the error by a huge factor.
525
0
  Vector3<T> x0 = x - a0;
526
0
  Vector3<T> x1 = x - a1;
527
0
  T x0_dist2 = x0.Norm2();
528
0
  T x1_dist2 = x1.Norm2();
529
530
  // If both distances are the same, we need to be careful to choose one
531
  // endpoint deterministically so that the result does not change if the
532
  // order of the endpoints is reversed.
533
0
  T dist, result;
534
0
  if (x0_dist2 < x1_dist2 || (x0_dist2 == x1_dist2 && x0 < x1)) {
535
0
    dist = sqrt(x0_dist2);
536
0
    result = x0.DotProd(a_norm);
537
0
  } else {
538
0
    dist = sqrt(x1_dist2);
539
0
    result = x1.DotProd(a_norm);
540
0
  }
541
  // This calculation bounds the error from all sources: the computation of
542
  // the normal, the subtraction of one endpoint, and the dot product itself.
543
  // (DBL_ERR appears because the input points are assumed to be normalized in
544
  // double precision rather than in the given type T.)
545
  //
546
  // For reference, the bounds that went into this calculation are:
547
  // ||N'-N|| <= ((1 + 2 * sqrt(3))||N|| + 32 * sqrt(3) * DBL_ERR) * T_ERR
548
  // |(A.B)'-(A.B)| <= (1.5 * (A.B) + 1.5 * ||A|| * ||B||) * T_ERR
549
  // ||(X-Y)'-(X-Y)|| <= ||X-Y|| * T_ERR
550
0
  constexpr T T_ERR = rounding_epsilon<T>();
551
0
  *error = (((3.5 + 2 * kSqrt3) * a_norm_len + 32 * kSqrt3 * DBL_ERR)
552
0
            * dist + 1.5 * fabs(result)) * T_ERR;
553
0
  return result;
554
0
}
Unexecuted instantiation: s2edge_crossings.cc:long double S2::GetProjection<long double>(Vector3<long double> const&, Vector3<long double> const&, long double, Vector3<long double> const&, Vector3<long double> const&, long double*)
Unexecuted instantiation: s2edge_crossings.cc:double S2::GetProjection<double>(Vector3<double> const&, Vector3<double> const&, double, Vector3<double> const&, Vector3<double> const&, double*)
555
556
// Helper function for GetIntersectionStable().  It expects that the edges
557
// (a0,a1) and (b0,b1) have been sorted so that the first edge is longer.
558
template <class T>
559
static bool GetIntersectionStableSorted(
560
    const Vector3<T>& a0, const Vector3<T>& a1,
561
0
    const Vector3<T>& b0, const Vector3<T>& b1, Vector3<T>* result) {
562
0
  ABSL_DCHECK_GE((a1 - a0).Norm2(), (b1 - b0).Norm2());
563
564
  // Compute the normal of the plane through (a0, a1) in a stable way.
565
0
  Vector3<T> a_norm = (a0 - a1).CrossProd(a0 + a1);
566
0
  T a_norm_len = a_norm.Norm();
567
0
  T b_len = (b1 - b0).Norm();
568
569
  // Compute the projection (i.e., signed distance) of b0 and b1 onto the
570
  // plane through (a0, a1).  Distances are scaled by the length of a_norm.
571
0
  T b0_error, b1_error;
572
0
  T b0_dist = GetProjection(b0, a_norm, a_norm_len, a0, a1, &b0_error);
573
0
  T b1_dist = GetProjection(b1, a_norm, a_norm_len, a0, a1, &b1_error);
574
575
  // The total distance from b0 to b1 measured perpendicularly to (a0,a1) is
576
  // |b0_dist - b1_dist|.  Note that b0_dist and b1_dist generally have
577
  // opposite signs because b0 and b1 are on opposite sides of (a0, a1).  The
578
  // code below finds the intersection point by interpolating along the edge
579
  // (b0, b1) to a fractional distance of b0_dist / (b0_dist - b1_dist).
580
  //
581
  // It can be shown that the maximum error in the interpolation fraction is
582
  //
583
  //     (b0_dist * b1_error - b1_dist * b0_error) /
584
  //        (dist_sum * (dist_sum - error_sum))
585
  //
586
  // We save ourselves some work by scaling the result and the error bound by
587
  // "dist_sum", since the result is normalized to be unit length anyway.
588
  //
589
  // Make sure that we return the intersection point rather than its antipode.
590
  // It is sufficient to ensure that (b0_dist - b1_dist) is non-negative.
591
0
  if (b0_dist < b1_dist) {
592
0
    b0_dist = -b0_dist;
593
0
    b1_dist = -b1_dist;
594
0
  }
595
0
  T dist_sum = b0_dist - b1_dist;
596
0
  T error_sum = b0_error + b1_error;
597
0
  if (dist_sum <= error_sum) {
598
0
    return false;  // Error is unbounded in this case.
599
0
  }
600
0
  Vector3<T> x = b0_dist * b1 - b1_dist * b0;
601
0
  constexpr T T_ERR = rounding_epsilon<T>();
602
0
  T error = b_len * fabs(b0_dist * b1_error - b1_dist * b0_error) /
603
0
      (dist_sum - error_sum) + 2 * T_ERR * dist_sum;
604
605
  // Finally we normalize the result, compute the corresponding error, and
606
  // check whether the total error is acceptable.
607
0
  T x_len2 = x.Norm2();
608
0
  if (x_len2 < std::numeric_limits<T>::min()) {
609
    // If x.Norm2() is less than the minimum normalized value of T, x_len might
610
    // lose precision and the result might fail to satisfy S2::IsUnitLength().
611
    // TODO(ericv): Implement S2::RobustNormalize().
612
0
    return false;
613
0
  }
614
0
  T x_len = sqrt(x_len2);
615
0
  const T kMaxError = kIntersectionError.radians();
616
0
  if (error > (kMaxError - T_ERR) * x_len) {
617
0
    return false;
618
0
  }
619
0
  *result = (1 / x_len) * x;
620
0
  return true;
621
0
}
Unexecuted instantiation: s2edge_crossings.cc:bool S2::GetIntersectionStableSorted<long double>(Vector3<long double> const&, Vector3<long double> const&, Vector3<long double> const&, Vector3<long double> const&, Vector3<long double>*)
Unexecuted instantiation: s2edge_crossings.cc:bool S2::GetIntersectionStableSorted<double>(Vector3<double> const&, Vector3<double> const&, Vector3<double> const&, Vector3<double> const&, Vector3<double>*)
622
623
// If the intersection point of the edges (a0,a1) and (b0,b1) can be computed
624
// to within an error of at most kIntersectionError by this function, then set
625
// "result" to the intersection point and return true.
626
template <class T>
627
static bool GetIntersectionStable(const Vector3<T>& a0, const Vector3<T>& a1,
628
                                  const Vector3<T>& b0, const Vector3<T>& b1,
629
0
                                  Vector3<T>* result) {
630
  // Sort the two edges so that (a0,a1) is longer, breaking ties in a
631
  // deterministic way that does not depend on the ordering of the endpoints.
632
  // This is desirable for two reasons:
633
  //  - So that the result doesn't change when edges are swapped or reversed.
634
  //  - It reduces error, since the first edge is used to compute the edge
635
  //    normal (where a longer edge means less error), and the second edge
636
  //    is used for interpolation (where a shorter edge means less error).
637
0
  T a_len2 = (a1 - a0).Norm2();
638
0
  T b_len2 = (b1 - b0).Norm2();
639
0
  if (a_len2 < b_len2 ||
640
0
      (a_len2 == b_len2 && internal::CompareEdges(a0, a1, b0, b1))) {
641
0
    return GetIntersectionStableSorted(b0, b1, a0, a1, result);
642
0
  } else {
643
0
    return GetIntersectionStableSorted(a0, a1, b0, b1, result);
644
0
  }
645
0
}
Unexecuted instantiation: s2edge_crossings.cc:bool S2::GetIntersectionStable<long double>(Vector3<long double> const&, Vector3<long double> const&, Vector3<long double> const&, Vector3<long double> const&, Vector3<long double>*)
Unexecuted instantiation: s2edge_crossings.cc:bool S2::GetIntersectionStable<double>(Vector3<double> const&, Vector3<double> const&, Vector3<double> const&, Vector3<double> const&, Vector3<double>*)
646
647
0
inline static S2Point ToS2Point(const Vector3_xf& xf) {
648
0
  return NormalizableFromExact(xf).Normalize();
649
0
}
650
651
namespace internal {
652
653
bool GetIntersectionStableLD(const S2Point& a0, const S2Point& a1,
654
                             const S2Point& b0, const S2Point& b1,
655
0
                             S2Point* result) {
656
0
  Vector3_ld result_ld;
657
0
  if (GetIntersectionStable(ToLD(a0), ToLD(a1), ToLD(b0), ToLD(b1),
658
0
                            &result_ld)) {
659
0
    *result = S2Point::Cast(result_ld);
660
0
    return true;
661
0
  }
662
0
  return false;
663
0
}
664
665
// Compute the intersection point of (a0, a1) and (b0, b1) using exact
666
// arithmetic.  Note that the result is not exact because it is rounded to
667
// double precision.
668
S2Point GetIntersectionExact(const S2Point& a0, const S2Point& a1,
669
0
                             const S2Point& b0, const S2Point& b1) {
670
  // Since we are using exact arithmetic, we don't need to worry about
671
  // numerical stability.
672
0
  Vector3_xf a_norm_xf = ToExact(a0).CrossProd(ToExact(a1));
673
0
  Vector3_xf b_norm_xf = ToExact(b0).CrossProd(ToExact(b1));
674
0
  Vector3_xf x_xf = a_norm_xf.CrossProd(b_norm_xf);
675
676
  // The final Normalize() call is done in double precision, which creates a
677
  // directional error of up to 2 * DBL_ERR.  (NormalizableFromExact() and
678
  // Normalize() each contribute up to DBL_ERR of directional error.)
679
0
  if (!s2pred::IsZero(x_xf)) {
680
    // Make sure that we return the intersection point rather than its antipode.
681
0
    return s2pred::Sign(a0, a1, b1) * ToS2Point(x_xf);
682
0
  }
683
684
  // The two edges are exactly collinear, but we still consider them to be
685
  // "crossing" because of simulation of simplicity.  The most principled way to
686
  // handle this situation is to use symbolic perturbations, similar to what
687
  // S2::RobustCrossProd and s2pred::Sign do.  This is certainly possible, but
688
  // it turns out that there are approximately 18 cases to consider (compared to
689
  // the 4 cases for RobustCrossProd and 13 for s2pred::Sign).
690
  //
691
  // For now we use a heuristic that simply chooses a plausible intersection
692
  // point.  Out of the four endpoints, exactly two lie in the interior of the
693
  // other edge.  Of those two we return the one that is lexicographically
694
  // smallest.
695
0
  S2Point a_norm = ToS2Point(a_norm_xf);
696
0
  S2Point b_norm = ToS2Point(b_norm_xf);
697
0
  if (a_norm == S2Point(0, 0, 0)) a_norm = SymbolicCrossProd(a0, a1);
698
0
  if (b_norm == S2Point(0, 0, 0)) b_norm = SymbolicCrossProd(b0, b1);
699
700
0
  S2Point x(10, 10, 10);  // Greater than any valid S2Point.
701
0
  if (s2pred::OrderedCCW(b0, a0, b1, b_norm) && a0 < x) x = a0;
702
0
  if (s2pred::OrderedCCW(b0, a1, b1, b_norm) && a1 < x) x = a1;
703
0
  if (s2pred::OrderedCCW(a0, b0, a1, a_norm) && b0 < x) x = b0;
704
0
  if (s2pred::OrderedCCW(a0, b1, a1, a_norm) && b1 < x) x = b1;
705
706
0
  ABSL_DCHECK(S2::IsUnitLength(x));
707
0
  return x;
708
0
}
709
710
}  // namespace internal
711
712
// Given three points "a", "x", "b", returns true if these three points occur
713
// in the given order along the edge (a,b) to within the given tolerance.
714
// More precisely, either "x" must be within "tolerance" of "a" or "b", or
715
// when "x" is projected onto the great circle through "a" and "b" it must lie
716
// along the edge (a,b) (i.e., the shortest path from "a" to "b").
717
static bool ApproximatelyOrdered(const S2Point& a, const S2Point& x,
718
0
                                 const S2Point& b, double tolerance) {
719
0
  if ((x - a).Norm2() <= tolerance * tolerance) return true;
720
0
  if ((x - b).Norm2() <= tolerance * tolerance) return true;
721
0
  return s2pred::OrderedCCW(a, x, b, S2::RobustCrossProd(a, b).Normalize());
722
0
}
723
724
S2Point GetIntersection(const S2Point& a0, const S2Point& a1,
725
0
                        const S2Point& b0, const S2Point& b1) {
726
0
  ABSL_DCHECK_GT(CrossingSign(a0, a1, b0, b1), 0);
727
728
  // It is difficult to compute the intersection point of two edges accurately
729
  // when the angle between the edges is very small.  Previously we handled
730
  // this by only guaranteeing that the returned intersection point is within
731
  // kIntersectionError of each edge.  However, this means that when the edges
732
  // cross at a very small angle, the computed result may be very far from the
733
  // true intersection point.
734
  //
735
  // Instead this function now guarantees that the result is always within
736
  // kIntersectionError of the true intersection.  This requires using more
737
  // sophisticated techniques and in some cases extended precision.
738
  //
739
  // Three different techniques are implemented, but only two are used:
740
  //
741
  //  - GetIntersectionSimple() computes the intersection point using
742
  //    numerically stable cross products in "long double" precision.
743
  //
744
  //  - GetIntersectionStable() computes the intersection point using
745
  //    projection and interpolation, taking care to minimize cancellation
746
  //    error.  This method exists in "double" and "long double" versions.
747
  //
748
  //  - GetIntersectionExact() computes the intersection point using exact
749
  //    arithmetic and converts the final result back to an S2Point.
750
  //
751
  // We don't actually use the first method (GetIntersectionSimple) because it
752
  // turns out that GetIntersectionStable() is twice as fast and also much
753
  // more accurate (even in double precision). The "long double" version
754
  // (only available on some platforms) uses 80-bit precision on x64 and
755
  // 128-bit precision on ARM64, and is ~7x slower on x86. The exact arithmetic
756
  // version is about 140x slower than "double" on x86.
757
  //
758
  // So our strategy is to first call GetIntersectionStable() in double
759
  // precision; if that doesn't work then we fall back to exact arithmetic.
760
  // Because "long double" version gives different results depending on the
761
  // platform, it is not used in this function.
762
763
  // TODO(b/365080041): consider moving the unused implementations to the test
764
  // so they can be benchmarked and compared for accuracy.
765
0
  constexpr bool kUseSimpleMethod = false;
766
  // Long double version produces different results on x86-64 and ARM64
767
  // platforms, and is not used in this function.
768
0
  constexpr bool kUseLongDoubleInIntersection = s2pred::kHasLongDouble && false;
769
0
  S2Point result;
770
0
  IntersectionMethod method;
771
0
  if (kUseSimpleMethod && GetIntersectionSimple(a0, a1, b0, b1, &result)) {
772
0
    method = IntersectionMethod::SIMPLE;
773
0
  } else if (kUseSimpleMethod && kUseLongDoubleInIntersection &&
774
0
             GetIntersectionSimpleLD(a0, a1, b0, b1, &result)) {
775
0
    method = IntersectionMethod::SIMPLE_LD;
776
0
  } else if (GetIntersectionStable(a0, a1, b0, b1, &result)) {
777
0
    method = IntersectionMethod::STABLE;
778
0
  } else if (kUseLongDoubleInIntersection &&
779
0
             internal::GetIntersectionStableLD(a0, a1, b0, b1, &result)) {
780
0
    method = IntersectionMethod::STABLE_LD;
781
0
  } else {
782
0
    result = GetIntersectionExact(a0, a1, b0, b1);
783
0
    method = IntersectionMethod::EXACT;
784
0
  }
785
0
  if (intersection_method_tally_) {
786
0
    ++intersection_method_tally_[static_cast<int>(method)];
787
0
  }
788
789
  // Make sure that the intersection point lies on both edges.
790
0
  ABSL_DCHECK(
791
0
      ApproximatelyOrdered(a0, result, a1, kIntersectionError.radians()))
792
0
      << "Using method " << static_cast<int>(method) << " for intersection of "
793
0
      << std::hexfloat << a0 << ", " << a1 << ", " << b0 << ", " << b1;
794
0
  ABSL_DCHECK(
795
0
      ApproximatelyOrdered(b0, result, b1, kIntersectionError.radians()))
796
0
      << "Using method " << static_cast<int>(method) << " for intersection of "
797
0
      << std::hexfloat << a0 << ", " << a1 << ", " << b0 << ", " << b1;
798
799
0
  return result;
800
0
}
801
802
}  // namespace S2