/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 |