/src/s2geometry/src/s2/s2edge_distances.h
Line | Count | Source |
1 | | // Copyright 2005 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS-IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | |
16 | | // Author: ericv@google.com (Eric Veach) |
17 | | // |
18 | | // Defines a collection of functions for computing the distance to an edge, |
19 | | // interpolating along an edge, projecting points onto edges, etc. |
20 | | |
21 | | #ifndef S2_S2EDGE_DISTANCES_H_ |
22 | | #define S2_S2EDGE_DISTANCES_H_ |
23 | | |
24 | | #include <cfloat> |
25 | | #include <utility> |
26 | | |
27 | | #include "absl/base/macros.h" |
28 | | #include "absl/log/absl_check.h" |
29 | | #include "s2/_fp_contract_off.h" // IWYU pragma: keep |
30 | | #include "s2/s1angle.h" |
31 | | #include "s2/s1chord_angle.h" |
32 | | #include "s2/s2edge_crossings.h" |
33 | | #include "s2/s2point.h" |
34 | | #include "s2/s2pointutil.h" |
35 | | #include "s2/s2predicates_internal.h" |
36 | | |
37 | | namespace S2 { |
38 | | |
39 | | ///////////////////////////////////////////////////////////////////////////// |
40 | | /////////////// (point, edge) functions /////////////// |
41 | | |
42 | | // Returns the minimum distance from X to any point on the edge AB. All |
43 | | // arguments should be unit length. The result is very accurate for small |
44 | | // distances but may have some numerical error if the distance is large |
45 | | // (approximately Pi/2 or greater). The case A == B is handled correctly. |
46 | | // |
47 | | // If you want to compare a distance against a fixed threshold, e.g. |
48 | | // if (S2::GetDistance(x, a, b) < limit) |
49 | | // then it is significantly faster to use UpdateMinDistance() below. |
50 | | S1Angle GetDistance(const S2Point& x, const S2Point& a, const S2Point& b); |
51 | | |
52 | | // Returns true if the distance from X to the edge AB is less than "limit". |
53 | | // (Specify limit.Successor() for "less than or equal to".) This method is |
54 | | // significantly faster than GetDistance(). If you want to compare against a |
55 | | // fixed S1Angle, you should convert it to an S1ChordAngle once and save the |
56 | | // value, since this step is relatively expensive. |
57 | | // |
58 | | // See s2pred::CompareEdgeDistance() for an exact version of this predicate. |
59 | | bool IsDistanceLess(const S2Point& x, const S2Point& a, const S2Point& b, |
60 | | S1ChordAngle limit); |
61 | | |
62 | | // If the distance from X to the edge AB is less than "min_dist", this |
63 | | // method updates "min_dist" and returns true. Otherwise it returns false. |
64 | | // The case A == B is handled correctly. |
65 | | // |
66 | | // Use this method when you want to compute many distances and keep track of |
67 | | // the minimum. It is significantly faster than using GetDistance(), |
68 | | // because (1) using S1ChordAngle is much faster than S1Angle, and (2) it |
69 | | // can save a lot of work by not actually computing the distance when it is |
70 | | // obviously larger than the current minimum. |
71 | | bool UpdateMinDistance(const S2Point& x, const S2Point& a, const S2Point& b, |
72 | | S1ChordAngle* min_dist); |
73 | | |
74 | | // If the maximum distance from X to the edge AB is greater than "max_dist", |
75 | | // this method updates "max_dist" and returns true. Otherwise it returns false. |
76 | | // The case A == B is handled correctly. |
77 | | bool UpdateMaxDistance(const S2Point& x, const S2Point& a, const S2Point& b, |
78 | | S1ChordAngle* max_dist); |
79 | | |
80 | | // Returns the maximum error in the result of UpdateMinDistance (and |
81 | | // associated functions such as UpdateMinInteriorDistance, IsDistanceLess, |
82 | | // etc), assuming that all input points are normalized to within the bounds |
83 | | // guaranteed by S2Point::Normalize(). The error can be added or subtracted |
84 | | // from an S1ChordAngle "x" using x.PlusError(error). |
85 | | // |
86 | | // Note that accuracy goes down as the distance approaches 0 degrees or 180 |
87 | | // degrees (for different reasons). Near 0 degrees the error is acceptable |
88 | | // for all practical purposes (about 1.2e-15 radians ~= 8 nanometers). For |
89 | | // exactly antipodal points the maximum error is quite high (0.5 meters), but |
90 | | // this error drops rapidly as the points move away from antipodality |
91 | | // (approximately 1 millimeter for points that are 50 meters from antipodal, |
92 | | // and 1 micrometer for points that are 50km from antipodal). |
93 | | double GetUpdateMinDistanceMaxError(S1ChordAngle dist); |
94 | | |
95 | | // Returns true if the minimum distance from X to the edge AB is attained at |
96 | | // an interior point of AB (i.e., not an endpoint), and that distance is less |
97 | | // than "limit". (Specify limit.Successor() for "less than or equal to".) |
98 | | bool IsInteriorDistanceLess(const S2Point& x, |
99 | | const S2Point& a, const S2Point& b, |
100 | | S1ChordAngle limit); |
101 | | |
102 | | // If the minimum distance from X to AB is attained at an interior point of AB |
103 | | // (i.e., not an endpoint), and that distance is less than "min_dist", then |
104 | | // this method updates "min_dist" and returns true. Otherwise returns false. |
105 | | bool UpdateMinInteriorDistance(const S2Point& x, |
106 | | const S2Point& a, const S2Point& b, |
107 | | S1ChordAngle* min_dist); |
108 | | |
109 | | // Returns the point along the edge AB that is closest to the point X. |
110 | | // The fractional distance of this point along the edge AB can be obtained |
111 | | // using GetDistanceFraction() above. Requires that all vectors have |
112 | | // unit length. |
113 | | S2Point Project(const S2Point& x, const S2Point& a, const S2Point& b); |
114 | | |
115 | | // A slightly more efficient version of Project() where the cross product of |
116 | | // the two endpoints has been precomputed. The cross product does not need to |
117 | | // be normalized, but should be computed using S2::RobustCrossProd() for the |
118 | | // most accurate results. Requires that x, a, and b have unit length. |
119 | | S2Point Project(const S2Point& x, const S2Point& a, const S2Point& b, |
120 | | const Vector3_d& a_cross_b); |
121 | | |
122 | | // kProjectPerpendicularError is an upper bound on the distance from the point |
123 | | // returned by Project() to the edge AB. Note that it only bounds the error |
124 | | // perpendicular to the edge, not the error parallel to it. |
125 | | constexpr S1Angle kProjectPerpendicularError = S1Angle::Radians( |
126 | | (2 + (2 / s2pred::kSqrt3)) * s2pred::DBL_ERR) + S2::kRobustCrossProdError; |
127 | | |
128 | | ///////////////////////////////////////////////////////////////////////////// |
129 | | /////////////// (point along edge) functions /////////////// |
130 | | |
131 | | // Given a point X and an edge AB, returns the distance ratio AX / (AX + BX). |
132 | | // If X happens to be on the line segment AB, this is the fraction "t" such |
133 | | // that X == Interpolate(A, B, t). Requires that A and B are distinct. |
134 | | double GetDistanceFraction(const S2Point& x, |
135 | | const S2Point& a, const S2Point& b); |
136 | | |
137 | | // Returns the point X along the line segment AB whose distance from A is the |
138 | | // given fraction "t" of the distance AB (where "t" is not necessarily in the |
139 | | // range [0, 1]). Note that all distances are measured on the surface of the |
140 | | // sphere, so this is more complicated than just computing (1-t)*a + t*b and |
141 | | // normalizing the result. |
142 | | // |
143 | | // Note that the line AB has a well-defined direction even when A and B are |
144 | | // antipodal or nearly so (see S2::RobustCrossProd). |
145 | | S2Point Interpolate(const S2Point& a, const S2Point& b, double t); |
146 | | |
147 | | // Returns the point at distance "r" from A along the line AB. |
148 | | // |
149 | | // Note that the line AB has a well-defined direction even when A and B are |
150 | | // antipodal or nearly so. If A == B then an arbitrary direction is chosen. |
151 | | S2Point GetPointOnLine(const S2Point& a, const S2Point& b, S1Angle r); |
152 | | |
153 | | // Faster than the function above, but cannot accurately represent distances |
154 | | // near 180 degrees due to the limitations of S1ChordAngle. |
155 | | S2Point GetPointOnLine(const S2Point& a, const S2Point& b, S1ChordAngle r); |
156 | | |
157 | | // Returns S2Point to the left of the edge from `a` to `b` which |
158 | | // is distance 'r' away from `a` orthogonal to the specified edge. |
159 | | // c (result) |
160 | | // | |
161 | | // | |
162 | | // a --------> b |
163 | | S2Point GetPointToLeft(const S2Point& a, const S2Point& b, S1Angle r); |
164 | | |
165 | | // Faster than the function above due to the use of S1ChordAngle. |
166 | | S2Point GetPointToLeft(const S2Point& a, const S2Point& b, S1ChordAngle r); |
167 | | |
168 | | // Returns S2Point to the right of the edge from `a` to `b` which |
169 | | // is distance 'r' away from `a` orthogonal to the specified edge. |
170 | | // a --------> b |
171 | | // | |
172 | | // | |
173 | | // c (result) |
174 | | S2Point GetPointToRight(const S2Point& a, const S2Point& b, S1Angle r); |
175 | | |
176 | | // Faster than the function above due to the use of S1ChordAngle. |
177 | | S2Point GetPointToRight(const S2Point& a, const S2Point& b, S1ChordAngle r); |
178 | | |
179 | | // kGetPointOnLineError is an upper bound on the distance between the point |
180 | | // returned by GetPointOnLine() and the corresponding true infinite-precision |
181 | | // result. |
182 | | constexpr S1Angle kGetPointOnLineError = S1Angle::Radians( |
183 | | (4 + (2 / s2pred::kSqrt3)) * s2pred::DBL_ERR) + S2::kRobustCrossProdError; |
184 | | |
185 | | // Returns the point at distance "r" along the ray with the given origin and |
186 | | // direction. "dir" is required to be perpendicular to "origin" (since this |
187 | | // is how directions on the sphere are represented). |
188 | | // |
189 | | // This function is similar to S2::GetPointOnLine() except that (1) the first |
190 | | // two arguments are required to be perpendicular and (2) it is much faster. |
191 | | // It can be used as an alternative to repeatedly calling GetPointOnLine() by |
192 | | // computing "dir" as |
193 | | // |
194 | | // S2Point dir = S2::RobustCrossProd(a, b).CrossProd(a).Normalize(); |
195 | | // |
196 | | // REQUIRES: "origin" and "dir" are perpendicular to within the tolerance |
197 | | // of the calculation above. |
198 | | S2Point GetPointOnRay(const S2Point& origin, const S2Point& dir, S1Angle r); |
199 | | |
200 | | // Faster than the function above, but cannot accurately represent distances |
201 | | // near 180 degrees due to the limitations of S1ChordAngle. |
202 | | S2Point GetPointOnRay(const S2Point& origin, const S2Point& dir, |
203 | | S1ChordAngle r); |
204 | | |
205 | | // kGetPointOnRayPerpendicularError is an upper bound on the distance from the |
206 | | // point returned by GetPointOnRay() to the ray itself. Note that it only |
207 | | // bounds the error perpendicular to the ray, not the error parallel to it. |
208 | | constexpr S1Angle kGetPointOnRayPerpendicularError = S1Angle::Radians( |
209 | | 3 * s2pred::DBL_ERR); |
210 | | |
211 | | ///////////////////////////////////////////////////////////////////////////// |
212 | | /////////////// (edge, edge) functions /////////////// |
213 | | |
214 | | |
215 | | // Like UpdateMinDistance(), but computes the minimum distance between the |
216 | | // given pair of edges. (If the two edges cross, the distance is zero.) |
217 | | // The cases a0 == a1 and b0 == b1 are handled correctly. |
218 | | bool UpdateEdgePairMinDistance(const S2Point& a0, const S2Point& a1, |
219 | | const S2Point& b0, const S2Point& b1, |
220 | | S1ChordAngle* min_dist); |
221 | | |
222 | | // As above, but for maximum distances. If one edge crosses the antipodal |
223 | | // reflection of the other, the distance is Pi. |
224 | | bool UpdateEdgePairMaxDistance(const S2Point& a0, const S2Point& a1, |
225 | | const S2Point& b0, const S2Point& b1, |
226 | | S1ChordAngle* max_dist); |
227 | | |
228 | | // Returns true if the minimum distance between two edges is less than distance. |
229 | | // |
230 | | // See s2pred::CompareEdgePairDistance() for an exact version of this predicate. |
231 | | bool IsEdgePairDistanceLess(const S2Point& a0, const S2Point& a1, |
232 | | const S2Point& b0, const S2Point& b1, |
233 | | S1ChordAngle distance); |
234 | | |
235 | | // Returns the pair of points (a, b) that achieves the minimum distance |
236 | | // between edges a0a1 and b0b1, where "a" is a point on a0a1 and "b" is a |
237 | | // point on b0b1. If the two edges intersect, "a" and "b" are both equal to |
238 | | // the intersection point. Handles a0 == a1 and b0 == b1 correctly. |
239 | | std::pair<S2Point, S2Point> GetEdgePairClosestPoints( |
240 | | const S2Point& a0, const S2Point& a1, |
241 | | const S2Point& b0, const S2Point& b1); |
242 | | |
243 | | // Returns true if every point on edge B=b0b1 is no further than "tolerance" |
244 | | // from some point on edge A=a0a1. Equivalently, returns true if the directed |
245 | | // Hausdorff distance from B to A is no more than "tolerance". |
246 | | // Requires that tolerance is less than 90 degrees. |
247 | | bool IsEdgeBNearEdgeA(const S2Point& a0, const S2Point& a1, |
248 | | const S2Point& b0, const S2Point& b1, |
249 | | S1Angle tolerance); |
250 | | |
251 | | |
252 | | ////////////////// Implementation details follow //////////////////// |
253 | | |
254 | | // TODO(user): Remove this in favor of s2pred::CompareEdgeDistance()? |
255 | | inline bool IsDistanceLess(const S2Point& x, const S2Point& a, |
256 | 0 | const S2Point& b, S1ChordAngle limit) { |
257 | 0 | return UpdateMinDistance(x, a, b, &limit); |
258 | 0 | } |
259 | | |
260 | | inline bool IsInteriorDistanceLess(const S2Point& x, const S2Point& a, |
261 | 0 | const S2Point& b, S1ChordAngle limit) { |
262 | 0 | return UpdateMinInteriorDistance(x, a, b, &limit); |
263 | 0 | } |
264 | | |
265 | | inline S2Point GetPointOnRay(const S2Point& origin, const S2Point& dir, |
266 | 0 | S1ChordAngle r) { |
267 | 0 | ABSL_DCHECK(S2::IsUnitLength(origin)); |
268 | 0 | ABSL_DCHECK(S2::IsUnitLength(dir)); |
269 | | // The error bound below includes the error in computing the dot product. |
270 | 0 | ABSL_DCHECK_LE(origin.DotProd(dir), |
271 | 0 | S2::kRobustCrossProdError.radians() + 0.75 * DBL_EPSILON); |
272 | | |
273 | | // Mathematically the result should already be unit length, but we normalize |
274 | | // it anyway to ensure that the error is within acceptable bounds. |
275 | | // (Otherwise errors can build up when the result of one interpolation is |
276 | | // fed into another interpolation.) |
277 | | // |
278 | | // Note that it is much cheaper to compute the sine and cosine of an |
279 | | // S1ChordAngle than an S1Angle. |
280 | 0 | return (cos(r) * origin + sin(r) * dir).Normalize(); |
281 | 0 | } |
282 | | |
283 | | inline S2Point GetPointOnRay(const S2Point& origin, const S2Point& dir, |
284 | 0 | const S1Angle r) { |
285 | | // See comments above. |
286 | 0 | ABSL_DCHECK(S2::IsUnitLength(origin)); |
287 | 0 | ABSL_DCHECK(S2::IsUnitLength(dir)); |
288 | 0 | ABSL_DCHECK_LE(origin.DotProd(dir), |
289 | 0 | S2::kRobustCrossProdError.radians() + 0.75 * DBL_EPSILON); |
290 | |
|
291 | 0 | const S1Angle::SinCosPair trig_r = r.SinCos(); |
292 | 0 | return (trig_r.cos * origin + trig_r.sin * dir).Normalize(); |
293 | 0 | } |
294 | | |
295 | | } // namespace S2 |
296 | | |
297 | | #endif // S2_S2EDGE_DISTANCES_H_ |