1 | // Copyright 2005 Google Inc. All Rights Reserved. |
2 | |
3 | #include "base/integral_types.h" |
4 | #include "base/logging.h" |
5 | #include "s2.h" |
6 | #include "s2cap.h" |
7 | #include "s2cell.h" |
8 | #include "s2latlngrect.h" |
9 | |
10 | namespace { |
11 | |
12 | // Multiply a positive number by this constant to ensure that the result |
13 | // of a floating point operation is at least as large as the true |
14 | // infinite-precision result. |
15 | double const kRoundUp = 1.0 + 1.0 / (uint64(1) << 52); |
16 | |
17 | // Return the cap height corresponding to the given non-negative cap angle in |
18 | // radians. Cap angles of Pi radians or larger yield a full cap. |
19 | double GetHeightForAngle(double radians) { |
20 | DCHECK_GE(radians, 0); |
21 | |
22 | // Caps of Pi radians or more are full. |
23 | if (radians >= M_PI) return 2; |
24 | |
25 | // The height of the cap can be computed as 1 - cos(radians), but this isn't |
26 | // very accurate for angles close to zero (where cos(radians) is almost 1). |
27 | // Computing it as 2 * (sin(radians / 2) ** 2) gives much better precision. |
28 | double d = sin(0.5 * radians); |
29 | return 2 * d * d; |
30 | } |
31 | |
32 | } // namespace |
33 | |
34 | S2Cap S2Cap::FromAxisAngle(S2Point const& axis, S1Angle const& angle) { |
35 | DCHECK(S2::IsUnitLength(axis)); |
36 | DCHECK_GE(angle.radians(), 0); |
37 | return S2Cap(axis, GetHeightForAngle(angle.radians())); |
38 | } |
39 | |
40 | S1Angle S2Cap::angle() const { |
41 | // This could also be computed as acos(1 - height_), but the following |
42 | // formula is much more accurate when the cap height is small. It |
43 | // follows from the relationship h = 1 - cos(theta) = 2 sin^2(theta/2). |
44 | if (is_empty()) return S1Angle::Radians(-1); |
45 | return S1Angle::Radians(2 * asin(sqrt(0.5 * height_))); |
46 | } |
47 | |
48 | S2Cap S2Cap::Complement() const { |
49 | // The complement of a full cap is an empty cap, not a singleton. |
50 | // Also make sure that the complement of an empty cap has height 2. |
51 | double height = is_full() ? -1 : 2 - max(height_, 0.0); |
52 | return S2Cap::FromAxisHeight(-axis_, height); |
53 | } |
54 | |
55 | bool S2Cap::Contains(S2Cap const& other) const { |
56 | if (is_full() || other.is_empty()) return true; |
57 | return angle().radians() >= axis_.Angle(other.axis_) + |
58 | other.angle().radians(); |
59 | } |
60 | |
61 | bool S2Cap::Intersects(S2Cap const& other) const { |
62 | if (is_empty() || other.is_empty()) return false; |
63 | |
64 | return (angle().radians() + other.angle().radians() >= |
65 | axis_.Angle(other.axis_)); |
66 | } |
67 | |
68 | bool S2Cap::InteriorIntersects(S2Cap const& other) const { |
69 | // Make sure this cap has an interior and the other cap is non-empty. |
70 | if (height_ <= 0 || other.is_empty()) return false; |
71 | |
72 | return (angle().radians() + other.angle().radians() > |
73 | axis_.Angle(other.axis_)); |
74 | } |
75 | |
76 | void S2Cap::AddPoint(S2Point const& p) { |
77 | // Compute the squared chord length, then convert it into a height. |
78 | DCHECK(S2::IsUnitLength(p)); |
79 | if (is_empty()) { |
80 | axis_ = p; |
81 | height_ = 0; |
82 | } else { |
83 | // To make sure that the resulting cap actually includes this point, |
84 | // we need to round up the distance calculation. That is, after |
85 | // calling cap.AddPoint(p), cap.Contains(p) should be true. |
86 | double dist2 = (axis_ - p).Norm2(); |
87 | height_ = max(height_, kRoundUp * 0.5 * dist2); |
88 | } |
89 | } |
90 | |
91 | void S2Cap::AddCap(S2Cap const& other) { |
92 | if (is_empty()) { |
93 | *this = other; |
94 | } else { |
95 | // See comments for AddPoint(). This could be optimized by doing the |
96 | // calculation in terms of cap heights rather than cap opening angles. |
97 | double radians = axis_.Angle(other.axis_) + other.angle().radians(); |
98 | height_ = max(height_, kRoundUp * GetHeightForAngle(radians)); |
99 | } |
100 | } |
101 | |
102 | S2Cap S2Cap::Expanded(S1Angle const& distance) const { |
103 | DCHECK_GE(distance.radians(), 0); |
104 | if (is_empty()) return Empty(); |
105 | return FromAxisAngle(axis_, angle() + distance); |
106 | } |
107 | |
108 | S2Cap* S2Cap::Clone() const { |
109 | return new S2Cap(*this); |
110 | } |
111 | |
112 | S2Cap S2Cap::GetCapBound() const { |
113 | return *this; |
114 | } |
115 | |
116 | S2LatLngRect S2Cap::GetRectBound() const { |
117 | if (is_empty()) return S2LatLngRect::Empty(); |
118 | |
119 | // Convert the axis to a (lat,lng) pair, and compute the cap angle. |
120 | S2LatLng axis_ll(axis_); |
121 | double cap_angle = angle().radians(); |
122 | |
123 | bool all_longitudes = false; |
124 | double lat[2], lng[2]; |
125 | lng[0] = -M_PI; |
126 | lng[1] = M_PI; |
127 | |
128 | // Check whether cap includes the south pole. |
129 | lat[0] = axis_ll.lat().radians() - cap_angle; |
130 | if (lat[0] <= -M_PI_2) { |
131 | lat[0] = -M_PI_2; |
132 | all_longitudes = true; |
133 | } |
134 | // Check whether cap includes the north pole. |
135 | lat[1] = axis_ll.lat().radians() + cap_angle; |
136 | if (lat[1] >= M_PI_2) { |
137 | lat[1] = M_PI_2; |
138 | all_longitudes = true; |
139 | } |
140 | if (!all_longitudes) { |
141 | // Compute the range of longitudes covered by the cap. We use the law |
142 | // of sines for spherical triangles. Consider the triangle ABC where |
143 | // A is the north pole, B is the center of the cap, and C is the point |
144 | // of tangency between the cap boundary and a line of longitude. Then |
145 | // C is a right angle, and letting a,b,c denote the sides opposite A,B,C, |
146 | // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c). |
147 | // Here "a" is the cap angle, and "c" is the colatitude (90 degrees |
148 | // minus the latitude). This formula also works for negative latitudes. |
149 | // |
150 | // The formula for sin(a) follows from the relationship h = 1 - cos(a). |
151 | |
152 | double sin_a = sqrt(height_ * (2 - height_)); |
153 | double sin_c = cos(axis_ll.lat().radians()); |
154 | if (sin_a <= sin_c) { |
155 | double angle_A = asin(sin_a / sin_c); |
156 | lng[0] = drem(axis_ll.lng().radians() - angle_A, 2 * M_PI); |
157 | lng[1] = drem(axis_ll.lng().radians() + angle_A, 2 * M_PI); |
158 | } |
159 | } |
160 | return S2LatLngRect(R1Interval(lat[0], lat[1]), |
161 | S1Interval(lng[0], lng[1])); |
162 | } |
163 | |
164 | bool S2Cap::Intersects(S2Cell const& cell, S2Point const* vertices) const { |
165 | // Return true if this cap intersects any point of 'cell' excluding its |
166 | // vertices (which are assumed to already have been checked). |
167 | |
168 | // If the cap is a hemisphere or larger, the cell and the complement of the |
169 | // cap are both convex. Therefore since no vertex of the cell is contained, |
170 | // no other interior point of the cell is contained either. |
171 | if (height_ >= 1) return false; |
172 | |
173 | // We need to check for empty caps due to the axis check just below. |
174 | if (is_empty()) return false; |
175 | |
176 | // Optimization: return true if the cell contains the cap axis. (This |
177 | // allows half of the edge checks below to be skipped.) |
178 | if (cell.Contains(axis_)) return true; |
179 | |
180 | // At this point we know that the cell does not contain the cap axis, |
181 | // and the cap does not contain any cell vertex. The only way that they |
182 | // can intersect is if the cap intersects the interior of some edge. |
183 | |
184 | double sin2_angle = height_ * (2 - height_); // sin^2(cap_angle) |
185 | for (int k = 0; k < 4; ++k) { |
186 | S2Point edge = cell.GetEdgeRaw(k); |
187 | double dot = axis_.DotProd(edge); |
188 | if (dot > 0) { |
189 | // The axis is in the interior half-space defined by the edge. We don't |
190 | // need to consider these edges, since if the cap intersects this edge |
191 | // then it also intersects the edge on the opposite side of the cell |
192 | // (because we know the axis is not contained with the cell). |
193 | continue; |
194 | } |
195 | // The Norm2() factor is necessary because "edge" is not normalized. |
196 | if (dot * dot > sin2_angle * edge.Norm2()) { |
197 | return false; // Entire cap is on the exterior side of this edge. |
198 | } |
199 | // Otherwise, the great circle containing this edge intersects |
200 | // the interior of the cap. We just need to check whether the point |
201 | // of closest approach occurs between the two edge endpoints. |
202 | S2Point dir = edge.CrossProd(axis_); |
203 | if (dir.DotProd(vertices[k]) < 0 && dir.DotProd(vertices[(k+1)&3]) > 0) |
204 | return true; |
205 | } |
206 | return false; |
207 | } |
208 | |
209 | bool S2Cap::Contains(S2Cell const& cell) const { |
210 | // If the cap does not contain all cell vertices, return false. |
211 | // We check the vertices before taking the Complement() because we can't |
212 | // accurately represent the complement of a very small cap (a height |
213 | // of 2-epsilon is rounded off to 2). |
214 | S2Point vertices[4]; |
215 | for (int k = 0; k < 4; ++k) { |
216 | vertices[k] = cell.GetVertex(k); |
217 | if (!Contains(vertices[k])) return false; |
218 | } |
219 | // Otherwise, return true if the complement of the cap does not intersect |
220 | // the cell. (This test is slightly conservative, because technically we |
221 | // want Complement().InteriorIntersects() here.) |
222 | return !Complement().Intersects(cell, vertices); |
223 | } |
224 | |
225 | bool S2Cap::MayIntersect(S2Cell const& cell) const { |
226 | // If the cap contains any cell vertex, return true. |
227 | S2Point vertices[4]; |
228 | for (int k = 0; k < 4; ++k) { |
229 | vertices[k] = cell.GetVertex(k); |
230 | if (Contains(vertices[k])) return true; |
231 | } |
232 | return Intersects(cell, vertices); |
233 | } |
234 | |
235 | bool S2Cap::Contains(S2Point const& p) const { |
236 | DCHECK(S2::IsUnitLength(p)); |
237 | return (axis_ - p).Norm2() <= 2 * height_; |
238 | } |
239 | |
240 | bool S2Cap::InteriorContains(S2Point const& p) const { |
241 | DCHECK(S2::IsUnitLength(p)); |
242 | return is_full() || (axis_ - p).Norm2() < 2 * height_; |
243 | } |
244 | |
245 | bool S2Cap::operator==(S2Cap const& other) const { |
246 | return (axis_ == other.axis_ && height_ == other.height_) || |
247 | (is_empty() && other.is_empty()) || |
248 | (is_full() && other.is_full()); |
249 | } |
250 | |
251 | bool S2Cap::ApproxEquals(S2Cap const& other, double max_error) { |
252 | return (S2::ApproxEquals(axis_, other.axis_, max_error) && |
253 | fabs(height_ - other.height_) <= max_error) || |
254 | (is_empty() && other.height_ <= max_error) || |
255 | (other.is_empty() && height_ <= max_error) || |
256 | (is_full() && other.height_ >= 2 - max_error) || |
257 | (other.is_full() && height_ >= 2 - max_error); |
258 | } |
259 | |
260 | ostream& operator<<(ostream& os, S2Cap const& cap) { |
261 | return os << "[Axis=" << cap.axis() << ", Angle=" << cap.angle() << "]" ; |
262 | } |
263 | |