Coverage Report

Created: 2025-08-25 06:55

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