/src/s2geometry/src/s2/s1chord_angle.cc
Line | Count | Source |
1 | | // Copyright 2013 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/s1chord_angle.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cfloat> |
22 | | #include <cmath> |
23 | | #include <ostream> |
24 | | |
25 | | #include "absl/log/absl_check.h" |
26 | | #include "s2/s1angle.h" |
27 | | |
28 | | using std::max; |
29 | | using std::min; |
30 | | using std::nextafter; |
31 | | |
32 | | static constexpr double kMaxLength2 = 4.0; |
33 | | |
34 | 0 | S1Angle S1ChordAngle::ToAngle() const { |
35 | 0 | if (is_negative()) return S1Angle::Radians(-1); |
36 | 0 | if (is_infinity()) return S1Angle::Infinity(); |
37 | 0 | return S1Angle::Radians(2 * asin(0.5 * sqrt(length2_))); |
38 | 0 | } |
39 | | |
40 | 0 | S1ChordAngle S1ChordAngle::Successor() const { |
41 | 0 | if (length2_ >= kMaxLength2) return Infinity(); |
42 | 0 | if (length2_ < 0.0) return Zero(); |
43 | 0 | return S1ChordAngle(nextafter(length2_, 10.0)); |
44 | 0 | } |
45 | | |
46 | 0 | S1ChordAngle S1ChordAngle::Predecessor() const { |
47 | 0 | if (length2_ <= 0.0) return Negative(); |
48 | 0 | if (length2_ > kMaxLength2) return Straight(); |
49 | 0 | return S1ChordAngle(nextafter(length2_, -10.0)); |
50 | 0 | } |
51 | | |
52 | 0 | S1ChordAngle S1ChordAngle::PlusError(double error) const { |
53 | | // If angle is Negative() or Infinity(), don't change it. |
54 | | // Otherwise clamp it to the valid range. |
55 | 0 | if (is_special()) return *this; |
56 | 0 | return S1ChordAngle(max(0.0, min(kMaxLength2, length2_ + error))); |
57 | 0 | } |
58 | | |
59 | 0 | double S1ChordAngle::GetS2PointConstructorMaxError() const { |
60 | | // There is a relative error of 2.5 * DBL_EPSILON when computing the squared |
61 | | // distance, plus a relative error of 2 * DBL_EPSILON and an absolute error |
62 | | // of (16 * DBL_EPSILON**2) because the lengths of the input points may |
63 | | // differ from 1 by up to (2 * DBL_EPSILON) each. (This is the maximum |
64 | | // length error in S2Point::Normalize.) |
65 | 0 | return 4.5 * DBL_EPSILON * length2_ + 16 * DBL_EPSILON * DBL_EPSILON; |
66 | 0 | } |
67 | | |
68 | 0 | double S1ChordAngle::GetS1AngleConstructorMaxError() const { |
69 | | // Assuming that an accurate math library is being used, the sin() call and |
70 | | // the multiply each have a relative error of 0.5 * DBL_EPSILON. However |
71 | | // the sin() error is squared. |
72 | 0 | return 1.5 * DBL_EPSILON * length2_; |
73 | 0 | } |
74 | | |
75 | 0 | S1ChordAngle operator+(S1ChordAngle a, S1ChordAngle b) { |
76 | | // Error Analysis: |
77 | | // |
78 | | // u is the unit round-off, equal to ε/2 = 2^-53 |
79 | | // x and y below are both computed with (1+u)² error. So we have |
80 | | // length2 = x + y + 2*sqrt(x * y) |
81 | | // length2 = ((x + y)(1+u)³ + 2*sqrt((x * y)((1+u)³))(1+u))(1+u) |
82 | | // |
83 | | // Taking (1+u)^1.5 out of the square root and rounding (1+u)^2.5 to (1+u)³: |
84 | | // length2 = (x + y + 2*sqrt(x * y))(1+u)⁴ |
85 | | // |
86 | | // Bounding (1+u)⁴ as 4*1.01u = 4.04*ε/2 = 2.02ε, total relative error is: |
87 | | // length2()*2.02ε. |
88 | | |
89 | | // Note that this method is much more efficient than converting the chord |
90 | | // angles to S1Angles and adding those. It requires only one square root |
91 | | // plus a few additions and multiplications. |
92 | 0 | ABSL_DCHECK(!a.is_special()) << a; |
93 | 0 | ABSL_DCHECK(!b.is_special()) << b; |
94 | | |
95 | | // Optimization for the common case where "b" is an error tolerance |
96 | | // parameter that happens to be set to zero. |
97 | 0 | double a2 = a.length2(), b2 = b.length2(); |
98 | 0 | if (b2 == 0) return a; |
99 | | |
100 | | // Clamp the angle sum to at most 180 degrees. |
101 | 0 | if (a2 + b2 >= kMaxLength2) return S1ChordAngle::Straight(); |
102 | | |
103 | | // Let "a" and "b" be the (non-squared) chord lengths, and let c = a+b. |
104 | | // Let A, B, and C be the corresponding half-angles (a = 2*sin(A), etc). |
105 | | // Then the formula below can be derived from c = 2 * sin(A+B) and the |
106 | | // relationships sin(A+B) = sin(A)*cos(B) + sin(B)*cos(A) |
107 | | // cos(X) = sqrt(1 - sin^2(X)) . |
108 | | |
109 | 0 | double x = a2 * (1 - 0.25 * b2); // is_valid() => non-negative |
110 | 0 | double y = b2 * (1 - 0.25 * a2); // is_valid() => non-negative |
111 | 0 | return S1ChordAngle(min(kMaxLength2, x + y + 2 * sqrt(x * y))); |
112 | 0 | } |
113 | | |
114 | 0 | S1ChordAngle operator-(S1ChordAngle a, S1ChordAngle b) { |
115 | | // See comments in operator+(). |
116 | 0 | ABSL_DCHECK(!a.is_special()) << a; |
117 | 0 | ABSL_DCHECK(!b.is_special()) << b; |
118 | 0 | double a2 = a.length2(), b2 = b.length2(); |
119 | 0 | if (b2 == 0) return a; |
120 | 0 | if (a2 <= b2) return S1ChordAngle::Zero(); |
121 | 0 | double x = a2 * (1 - 0.25 * b2); |
122 | 0 | double y = b2 * (1 - 0.25 * a2); |
123 | | |
124 | | // The calculation below is formulated differently (with two square roots |
125 | | // rather than one) to avoid excessive cancellation error when two nearly |
126 | | // equal values are subtracted. |
127 | 0 | double c = max(0.0, sqrt(x) - sqrt(y)); |
128 | 0 | return S1ChordAngle(c * c); |
129 | 0 | } |
130 | | |
131 | 0 | double sin2(S1ChordAngle a) { |
132 | 0 | ABSL_DCHECK(!a.is_special()); |
133 | | // Let "a" be the (non-squared) chord length, and let A be the corresponding |
134 | | // half-angle (a = 2*sin(A)). The formula below can be derived from: |
135 | | // sin(2*A) = 2 * sin(A) * cos(A) |
136 | | // cos^2(A) = 1 - sin^2(A) |
137 | | // This is much faster than converting to an angle and computing its sine. |
138 | 0 | return a.length2() * (1 - 0.25 * a.length2()); |
139 | 0 | } |
140 | | |
141 | 0 | double sin(S1ChordAngle a) { |
142 | 0 | return sqrt(sin2(a)); |
143 | 0 | } |
144 | | |
145 | 0 | double cos(S1ChordAngle a) { |
146 | | // cos(2*A) = cos^2(A) - sin^2(A) = 1 - 2*sin^2(A) |
147 | 0 | ABSL_DCHECK(!a.is_special()); |
148 | 0 | return 1 - 0.5 * a.length2(); |
149 | 0 | } |
150 | | |
151 | 0 | double tan(S1ChordAngle a) { |
152 | 0 | return sin(a) / cos(a); |
153 | 0 | } |
154 | | |
155 | 0 | std::ostream& operator<<(std::ostream& os, S1ChordAngle a) { |
156 | 0 | return os << a.ToAngle(); |
157 | 0 | } |