/src/s2geometry/src/s2/s2cap.cc
Line | Count | Source |
1 | | // Copyright 2005 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS-IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | |
16 | | // Author: ericv@google.com (Eric Veach) |
17 | | |
18 | | #include "s2/s2cap.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cfloat> |
22 | | #include <cmath> |
23 | | #include <ostream> |
24 | | #include <vector> |
25 | | |
26 | | #include "absl/flags/flag.h" |
27 | | #include "absl/log/absl_check.h" |
28 | | #include "s2/util/coding/coder.h" |
29 | | #include "s2/r1interval.h" |
30 | | #include "s2/s1angle.h" |
31 | | #include "s2/s1chord_angle.h" |
32 | | #include "s2/s1interval.h" |
33 | | #include "s2/s2cell.h" |
34 | | #include "s2/s2cell_id.h" |
35 | | #include "s2/s2debug.h" |
36 | | #include "s2/s2edge_distances.h" |
37 | | #include "s2/s2latlng.h" |
38 | | #include "s2/s2latlng_rect.h" |
39 | | #include "s2/s2metrics.h" |
40 | | #include "s2/s2point.h" |
41 | | #include "s2/s2pointutil.h" |
42 | | #include "s2/s2region.h" |
43 | | |
44 | | using std::fabs; |
45 | | using std::max; |
46 | | using std::vector; |
47 | | |
48 | 0 | double S2Cap::GetArea() const { |
49 | 0 | return 2 * M_PI * max(0.0, height()); |
50 | 0 | } |
51 | | |
52 | 0 | S2Point S2Cap::GetCentroid() const { |
53 | | // From symmetry, the centroid of the cap must be somewhere on the line |
54 | | // from the origin to the center of the cap on the surface of the sphere. |
55 | | // When a sphere is divided into slices of constant thickness by a set of |
56 | | // parallel planes, all slices have the same surface area. This implies |
57 | | // that the radial component of the centroid is simply the midpoint of the |
58 | | // range of radial distances spanned by the cap. That is easily computed |
59 | | // from the cap height. |
60 | 0 | if (is_empty()) return S2Point(); |
61 | 0 | double r = 1.0 - 0.5 * height(); |
62 | 0 | return r * GetArea() * center_; |
63 | 0 | } |
64 | | |
65 | 0 | S2Cap S2Cap::Complement() const { |
66 | | // The complement of a full cap is an empty cap, not a singleton. |
67 | | // Also make sure that the complement of an empty cap is full. |
68 | 0 | if (is_full()) return Empty(); |
69 | 0 | if (is_empty()) return Full(); |
70 | 0 | return S2Cap(-center_, S1ChordAngle::FromLength2(4 - radius_.length2())); |
71 | 0 | } |
72 | | |
73 | 0 | bool S2Cap::Contains(const S2Cap& other) const { |
74 | 0 | if (is_full() || other.is_empty()) return true; |
75 | 0 | return radius_ >= S1ChordAngle(center_, other.center_) + other.radius_; |
76 | 0 | } |
77 | | |
78 | 0 | bool S2Cap::Intersects(const S2Cap& other) const { |
79 | 0 | if (is_empty() || other.is_empty()) return false; |
80 | 0 | return radius_ + other.radius_ >= S1ChordAngle(center_, other.center_); |
81 | 0 | } |
82 | | |
83 | 0 | bool S2Cap::InteriorIntersects(const S2Cap& other) const { |
84 | | // Make sure this cap has an interior and the other cap is non-empty. |
85 | 0 | if (radius_.length2() <= 0 || other.is_empty()) return false; |
86 | 0 | return radius_ + other.radius_ > S1ChordAngle(center_, other.center_); |
87 | 0 | } |
88 | | |
89 | 0 | void S2Cap::AddPoint(const S2Point& p) { |
90 | | // Compute the squared chord length, then convert it into a height. |
91 | 0 | ABSL_DCHECK(S2::IsUnitLength(p)); |
92 | 0 | if (is_empty()) { |
93 | 0 | center_ = p; |
94 | 0 | radius_ = S1ChordAngle::Zero(); |
95 | 0 | } else { |
96 | | // After calling cap.AddPoint(p), cap.Contains(p) must be true. However |
97 | | // we don't need to do anything special to achieve this because Contains() |
98 | | // does exactly the same distance calculation that we do here. |
99 | 0 | radius_ = max(radius_, S1ChordAngle(center_, p)); |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | 0 | void S2Cap::AddCap(const S2Cap& other) { |
104 | 0 | if (is_empty()) { |
105 | 0 | *this = other; |
106 | 0 | } else if (!other.is_empty()) { |
107 | | // We round up the distance to ensure that the cap is actually contained. |
108 | 0 | S1ChordAngle dist = S1ChordAngle(center_, other.center_) + other.radius_; |
109 | 0 | dist = dist.PlusError( // |
110 | 0 | (2 * DBL_EPSILON + S1ChordAngle::kRelativeSumError) * dist.length2()); |
111 | 0 | radius_ = max(radius_, dist); |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | 0 | S2Cap S2Cap::Expanded(S1Angle distance) const { |
116 | 0 | ABSL_DCHECK_GE(distance.radians(), 0); |
117 | 0 | if (is_empty()) return Empty(); |
118 | 0 | return S2Cap(center_, radius_ + S1ChordAngle(distance)); |
119 | 0 | } |
120 | | |
121 | 0 | S2Cap S2Cap::Union(const S2Cap& other) const { |
122 | 0 | if (radius_ < other.radius_) { |
123 | 0 | return other.Union(*this); |
124 | 0 | } |
125 | 0 | if (is_full() || other.is_empty()) { |
126 | 0 | return *this; |
127 | 0 | } |
128 | | // This calculation would be more efficient using S1ChordAngles. |
129 | 0 | S1Angle this_radius = GetRadius(); |
130 | 0 | S1Angle other_radius = other.GetRadius(); |
131 | 0 | S1Angle distance(center(), other.center()); |
132 | 0 | if (this_radius >= distance + other_radius) { |
133 | 0 | return *this; |
134 | 0 | } else { |
135 | 0 | S1Angle result_radius = 0.5 * (distance + this_radius + other_radius); |
136 | 0 | S2Point result_center = |
137 | 0 | S2::GetPointOnLine(center(), other.center(), |
138 | 0 | 0.5 * (distance - this_radius + other_radius)); |
139 | 0 | return S2Cap(result_center, result_radius); |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | 0 | S2Region* S2Cap::Clone() const { |
144 | 0 | return new S2Cap(*this); |
145 | 0 | } |
146 | | |
147 | 0 | S2Cap S2Cap::GetCapBound() const { |
148 | 0 | return *this; |
149 | 0 | } |
150 | | |
151 | 0 | S2LatLngRect S2Cap::GetRectBound() const { |
152 | 0 | if (is_empty()) return S2LatLngRect::Empty(); |
153 | | |
154 | | // Convert the center to a (lat,lng) pair, and compute the cap angle. |
155 | 0 | S2LatLng center_ll(center_); |
156 | 0 | double cap_angle = GetRadius().radians(); |
157 | |
|
158 | 0 | bool all_longitudes = false; |
159 | 0 | double lat[2], lng[2]; |
160 | 0 | lng[0] = -M_PI; |
161 | 0 | lng[1] = M_PI; |
162 | | |
163 | | // Check whether cap includes the south pole. |
164 | 0 | lat[0] = center_ll.lat().radians() - cap_angle; |
165 | 0 | if (lat[0] <= -M_PI_2) { |
166 | 0 | lat[0] = -M_PI_2; |
167 | 0 | all_longitudes = true; |
168 | 0 | } |
169 | | // Check whether cap includes the north pole. |
170 | 0 | lat[1] = center_ll.lat().radians() + cap_angle; |
171 | 0 | if (lat[1] >= M_PI_2) { |
172 | 0 | lat[1] = M_PI_2; |
173 | 0 | all_longitudes = true; |
174 | 0 | } |
175 | 0 | if (!all_longitudes) { |
176 | | // Compute the range of longitudes covered by the cap. We use the law |
177 | | // of sines for spherical triangles. Consider the triangle ABC where |
178 | | // A is the north pole, B is the center of the cap, and C is the point |
179 | | // of tangency between the cap boundary and a line of longitude. Then |
180 | | // C is a right angle, and letting a,b,c denote the sides opposite A,B,C, |
181 | | // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c). |
182 | | // Here "a" is the cap angle, and "c" is the colatitude (90 degrees |
183 | | // minus the latitude). This formula also works for negative latitudes. |
184 | | // |
185 | | // The formula for sin(a) follows from the relationship h = 1 - cos(a). |
186 | |
|
187 | 0 | double sin_a = sin(radius_); |
188 | 0 | double sin_c = cos(center_ll.lat().radians()); |
189 | 0 | if (sin_a <= sin_c) { |
190 | 0 | double angle_A = asin(sin_a / sin_c); |
191 | 0 | lng[0] = remainder(center_ll.lng().radians() - angle_A, 2 * M_PI); |
192 | 0 | lng[1] = remainder(center_ll.lng().radians() + angle_A, 2 * M_PI); |
193 | 0 | } |
194 | 0 | } |
195 | 0 | return S2LatLngRect(R1Interval(lat[0], lat[1]), |
196 | 0 | S1Interval(lng[0], lng[1])); |
197 | 0 | } |
198 | | |
199 | | // Computes a covering of the S2Cap. In general the covering consists of at |
200 | | // most 4 cells except for very large caps, which may need up to 6 cells. |
201 | | // The output is not sorted. |
202 | 0 | void S2Cap::GetCellUnionBound(vector<S2CellId>* cell_ids) const { |
203 | | // TODO(ericv): The covering could be made quite a bit tighter by mapping |
204 | | // the cap to a rectangle in (i,j)-space and finding a covering for that. |
205 | 0 | cell_ids->clear(); |
206 | | |
207 | | // Find the maximum level such that the cap contains at most one cell vertex |
208 | | // and such that S2CellId::AppendVertexNeighbors() can be called. |
209 | 0 | int level = S2::kMinWidth.GetLevelForMinValue(GetRadius().radians()) - 1; |
210 | | |
211 | | // If level < 0, then more than three face cells are required. |
212 | 0 | if (level < 0) { |
213 | 0 | cell_ids->reserve(6); |
214 | 0 | for (int face = 0; face < 6; ++face) { |
215 | 0 | cell_ids->push_back(S2CellId::FromFace(face)); |
216 | 0 | } |
217 | 0 | } else { |
218 | | // The covering consists of the 4 cells at the given level that share the |
219 | | // cell vertex that is closest to the cap center. |
220 | 0 | cell_ids->reserve(4); |
221 | 0 | S2CellId(center_).AppendVertexNeighbors(level, cell_ids); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | 0 | bool S2Cap::Intersects(const S2Cell& cell, const S2Point* vertices) const { |
226 | | // Return true if this cap intersects any point of 'cell' excluding its |
227 | | // vertices (which are assumed to already have been checked). |
228 | | |
229 | | // If the cap is a hemisphere or larger, the cell and the complement of the |
230 | | // cap are both convex. Therefore since no vertex of the cell is contained, |
231 | | // no other interior point of the cell is contained either. |
232 | 0 | if (radius_ >= S1ChordAngle::Right()) return false; |
233 | | |
234 | | // We need to check for empty caps due to the center check just below. |
235 | 0 | if (is_empty()) return false; |
236 | | |
237 | | // Optimization: return true if the cell contains the cap center. (This |
238 | | // allows half of the edge checks below to be skipped.) |
239 | 0 | if (cell.Contains(center_)) return true; |
240 | | |
241 | | // At this point we know that the cell does not contain the cap center, |
242 | | // and the cap does not contain any cell vertex. The only way that they |
243 | | // can intersect is if the cap intersects the interior of some edge. |
244 | | |
245 | 0 | double sin2_angle = sin2(radius_); |
246 | 0 | for (int k = 0; k < 4; ++k) { |
247 | 0 | S2Point edge = cell.GetEdgeRaw(k); |
248 | 0 | double dot = center_.DotProd(edge); |
249 | 0 | if (dot > 0) { |
250 | | // The center is in the interior half-space defined by the edge. We don't |
251 | | // need to consider these edges, since if the cap intersects this edge |
252 | | // then it also intersects the edge on the opposite side of the cell |
253 | | // (because we know the center is not contained with the cell). |
254 | 0 | continue; |
255 | 0 | } |
256 | | // The Norm2() factor is necessary because "edge" is not normalized. |
257 | 0 | if (dot * dot > sin2_angle * edge.Norm2()) { |
258 | 0 | return false; // Entire cap is on the exterior side of this edge. |
259 | 0 | } |
260 | | // Otherwise, the great circle containing this edge intersects |
261 | | // the interior of the cap. We just need to check whether the point |
262 | | // of closest approach occurs between the two edge endpoints. |
263 | 0 | Vector3_d dir = edge.CrossProd(center_); |
264 | 0 | if (dir.DotProd(vertices[k]) < 0 && dir.DotProd(vertices[(k+1)&3]) > 0) |
265 | 0 | return true; |
266 | 0 | } |
267 | 0 | return false; |
268 | 0 | } |
269 | | |
270 | 0 | bool S2Cap::Contains(const S2Cell& cell) const { |
271 | | // If the cap does not contain all cell vertices, return false. |
272 | | // We check the vertices before taking the Complement() because we can't |
273 | | // accurately represent the complement of a very small cap (a height |
274 | | // of 2-epsilon is rounded off to 2). |
275 | 0 | S2Point vertices[4]; |
276 | 0 | for (int k = 0; k < 4; ++k) { |
277 | 0 | vertices[k] = cell.GetVertex(k); |
278 | 0 | if (!Contains(vertices[k])) return false; |
279 | 0 | } |
280 | | // Otherwise, return true if the complement of the cap does not intersect |
281 | | // the cell. (This test is slightly conservative, because technically we |
282 | | // want Complement().InteriorIntersects() here.) |
283 | 0 | return !Complement().Intersects(cell, vertices); |
284 | 0 | } |
285 | | |
286 | 0 | bool S2Cap::MayIntersect(const S2Cell& cell) const { |
287 | | // If the cap contains any cell vertex, return true. |
288 | 0 | S2Point vertices[4]; |
289 | 0 | for (int k = 0; k < 4; ++k) { |
290 | 0 | vertices[k] = cell.GetVertex(k); |
291 | 0 | if (Contains(vertices[k])) return true; |
292 | 0 | } |
293 | 0 | return Intersects(cell, vertices); |
294 | 0 | } |
295 | | |
296 | 0 | bool S2Cap::Contains(const S2Point& p) const { |
297 | 0 | ABSL_DCHECK(S2::IsUnitLength(p)); |
298 | 0 | return S1ChordAngle(center_, p) <= radius_; |
299 | 0 | } |
300 | | |
301 | 0 | bool S2Cap::InteriorContains(const S2Point& p) const { |
302 | 0 | ABSL_DCHECK(S2::IsUnitLength(p)); |
303 | 0 | return is_full() || S1ChordAngle(center_, p) < radius_; |
304 | 0 | } |
305 | | |
306 | 0 | bool S2Cap::operator==(const S2Cap& other) const { |
307 | 0 | return (center_ == other.center_ && radius_ == other.radius_) || |
308 | 0 | (is_empty() && other.is_empty()) || |
309 | 0 | (is_full() && other.is_full()); |
310 | 0 | } |
311 | | |
312 | 0 | bool S2Cap::ApproxEquals(const S2Cap& other, S1Angle max_error_angle) const { |
313 | 0 | const double max_error = max_error_angle.radians(); |
314 | 0 | const double r2 = radius_.length2(); |
315 | 0 | const double other_r2 = other.radius_.length2(); |
316 | 0 | return (S2::ApproxEquals(center_, other.center_, max_error_angle) && |
317 | 0 | fabs(r2 - other_r2) <= max_error) || |
318 | 0 | (is_empty() && other_r2 <= max_error) || |
319 | 0 | (other.is_empty() && r2 <= max_error) || |
320 | 0 | (is_full() && other_r2 >= 2 - max_error) || |
321 | 0 | (other.is_full() && r2 >= 2 - max_error); |
322 | 0 | } |
323 | | |
324 | 0 | std::ostream& operator<<(std::ostream& os, const S2Cap& cap) { |
325 | 0 | return os << "[Center=" << cap.center() |
326 | 0 | << ", Radius=" << cap.GetRadius() << "]"; |
327 | 0 | } |
328 | | |
329 | 0 | void S2Cap::Encode(Encoder* encoder) const { |
330 | 0 | encoder->Ensure(4 * sizeof(double)); |
331 | |
|
332 | 0 | encoder->putdouble(center_.x()); |
333 | 0 | encoder->putdouble(center_.y()); |
334 | 0 | encoder->putdouble(center_.z()); |
335 | 0 | encoder->putdouble(radius_.length2()); |
336 | |
|
337 | 0 | ABSL_DCHECK_GE(encoder->avail(), 0); |
338 | 0 | } |
339 | | |
340 | 0 | bool S2Cap::Decode(Decoder* decoder) { |
341 | 0 | if (decoder->avail() < 4 * sizeof(double)) return false; |
342 | | |
343 | 0 | double x = decoder->getdouble(); |
344 | 0 | double y = decoder->getdouble(); |
345 | 0 | double z = decoder->getdouble(); |
346 | 0 | center_ = S2Point(x, y, z); |
347 | 0 | radius_ = S1ChordAngle::FromLength2(decoder->getdouble()); |
348 | |
|
349 | 0 | if (absl::GetFlag(FLAGS_s2debug)) { |
350 | 0 | ABSL_CHECK(is_valid()) << "Invalid S2Cap: " << *this; |
351 | 0 | } |
352 | 0 | return true; |
353 | 0 | } |