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/s2cell.h"
19 
20 #include <algorithm>
21 #include <cfloat>
22 #include <cmath>
23 #include <iomanip>
24 
25 #include "s2/base/logging.h"
26 #include "s2/r1interval.h"
27 #include "s2/r2.h"
28 #include "s2/s1chord_angle.h"
29 #include "s2/s1interval.h"
30 #include "s2/s2cap.h"
31 #include "s2/s2coords.h"
32 #include "s2/s2edge_crosser.h"
33 #include "s2/s2edge_distances.h"
34 #include "s2/s2latlng.h"
35 #include "s2/s2latlng_rect.h"
36 #include "s2/s2measures.h"
37 #include "s2/s2metrics.h"
38 
39 using S2::internal::kPosToIJ;
40 using S2::internal::kPosToOrientation;
41 using std::min;
42 using std::max;
43 
44 // Since S2Cells are copied by value, the following assertion is a reminder
45 // not to add fields unnecessarily.  An S2Cell currently consists of 43 data
46 // bytes, one vtable pointer, plus alignment overhead.  This works out to 48
47 // bytes on 32 bit architectures and 56 bytes on 64 bit architectures.
48 //
49 // The expression below rounds up (43 + sizeof(void*)) to the nearest
50 // multiple of sizeof(void*).
51 static_assert(sizeof(S2Cell) <= ((43+2*sizeof(void*)-1) & -sizeof(void*)),
52               "S2Cell is getting bloated");
53 
S2Cell(S2CellId id)54 S2Cell::S2Cell(S2CellId id) {
55   id_ = id;
56   int ij[2], orientation;
57   face_ = id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation);
58   orientation_ = orientation;  // Compress int to a byte.
59   level_ = id.level();
60   uv_ = S2CellId::IJLevelToBoundUV(ij, level_);
61 }
62 
GetVertexRaw(int k) const63 S2Point S2Cell::GetVertexRaw(int k) const {
64   return S2::FaceUVtoXYZ(face_, uv_.GetVertex(k));
65 }
66 
GetEdgeRaw(int k) const67 S2Point S2Cell::GetEdgeRaw(int k) const {
68   switch (k & 3) {
69     case 0:  return S2::GetVNorm(face_, uv_[1][0]);   // Bottom
70     case 1:  return S2::GetUNorm(face_, uv_[0][1]);   // Right
71     case 2:  return -S2::GetVNorm(face_, uv_[1][1]);  // Top
72     default: return -S2::GetUNorm(face_, uv_[0][0]);  // Left
73   }
74 }
75 
Subdivide(S2Cell children[4]) const76 bool S2Cell::Subdivide(S2Cell children[4]) const {
77   // This function is equivalent to just iterating over the child cell ids
78   // and calling the S2Cell constructor, but it is about 2.5 times faster.
79 
80   if (id_.is_leaf()) return false;
81 
82   // Compute the cell midpoint in uv-space.
83   R2Point uv_mid = id_.GetCenterUV();
84 
85   // Create four children with the appropriate bounds.
86   S2CellId id = id_.child_begin();
87   for (int pos = 0; pos < 4; ++pos, id = id.next()) {
88     S2Cell *child = &children[pos];
89     child->face_ = face_;
90     child->level_ = level_ + 1;
91     child->orientation_ = orientation_ ^ kPosToOrientation[pos];
92     child->id_ = id;
93     // We want to split the cell in half in "u" and "v".  To decide which
94     // side to set equal to the midpoint value, we look at cell's (i,j)
95     // position within its parent.  The index for "i" is in bit 1 of ij.
96     int ij = kPosToIJ[orientation_][pos];
97     int i = ij >> 1;
98     int j = ij & 1;
99     child->uv_[0][i] = uv_[0][i];
100     child->uv_[0][1-i] = uv_mid[0];
101     child->uv_[1][j] = uv_[1][j];
102     child->uv_[1][1-j] = uv_mid[1];
103   }
104   return true;
105 }
106 
GetCenterRaw() const107 S2Point S2Cell::GetCenterRaw() const {
108   return id_.ToPointRaw();
109 }
110 
AverageArea(int level)111 double S2Cell::AverageArea(int level) {
112   return S2::kAvgArea.GetValue(level);
113 }
114 
ApproxArea() const115 double S2Cell::ApproxArea() const {
116   // All cells at the first two levels have the same area.
117   if (level_ < 2) return AverageArea(level_);
118 
119   // First, compute the approximate area of the cell when projected
120   // perpendicular to its normal.  The cross product of its diagonals gives
121   // the normal, and the length of the normal is twice the projected area.
122   double flat_area = 0.5 * (GetVertex(2) - GetVertex(0)).
123                      CrossProd(GetVertex(3) - GetVertex(1)).Norm();
124 
125   // Now, compensate for the curvature of the cell surface by pretending
126   // that the cell is shaped like a spherical cap.  The ratio of the
127   // area of a spherical cap to the area of its projected disc turns out
128   // to be 2 / (1 + sqrt(1 - r*r)) where "r" is the radius of the disc.
129   // For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
130   // Here we set Pi*r*r == flat_area to find the equivalent disc.
131   return flat_area * 2 / (1 + sqrt(1 - min(M_1_PI * flat_area, 1.0)));
132 }
133 
ExactArea() const134 double S2Cell::ExactArea() const {
135   // There is a straightforward mathematical formula for the exact surface
136   // area (based on 4 calls to asin), but as the cell size gets small this
137   // formula has too much cancellation error.  So instead we compute the area
138   // as the sum of two triangles (which is very accurate at all cell levels).
139   S2Point v0 = GetVertex(0);
140   S2Point v1 = GetVertex(1);
141   S2Point v2 = GetVertex(2);
142   S2Point v3 = GetVertex(3);
143   return S2::Area(v0, v1, v2) + S2::Area(v0, v2, v3);
144 }
145 
Clone() const146 S2Cell* S2Cell::Clone() const {
147   return new S2Cell(*this);
148 }
149 
GetCapBound() const150 S2Cap S2Cell::GetCapBound() const {
151   // Use the cell center in (u,v)-space as the cap axis.  This vector is
152   // very close to GetCenter() and faster to compute.  Neither one of these
153   // vectors yields the bounding cap with minimal surface area, but they
154   // are both pretty close.
155   //
156   // It's possible to show that the two vertices that are furthest from
157   // the (u,v)-origin never determine the maximum cap size (this is a
158   // possible future optimization).
159 
160   S2Point center = S2::FaceUVtoXYZ(face_, uv_.GetCenter()).Normalize();
161   S2Cap cap = S2Cap::FromPoint(center);
162   for (int k = 0; k < 4; ++k) {
163     cap.AddPoint(GetVertex(k));
164   }
165   return cap;
166 }
167 
GetLatitude(int i,int j) const168 inline double S2Cell::GetLatitude(int i, int j) const {
169   S2Point p = S2::FaceUVtoXYZ(face_, uv_[0][i], uv_[1][j]);
170   return S2LatLng::Latitude(p).radians();
171 }
172 
GetLongitude(int i,int j) const173 inline double S2Cell::GetLongitude(int i, int j) const {
174   S2Point p = S2::FaceUVtoXYZ(face_, uv_[0][i], uv_[1][j]);
175   return S2LatLng::Longitude(p).radians();
176 }
177 
GetRectBound() const178 S2LatLngRect S2Cell::GetRectBound() const {
179   if (level_ > 0) {
180     // Except for cells at level 0, the latitude and longitude extremes are
181     // attained at the vertices.  Furthermore, the latitude range is
182     // determined by one pair of diagonally opposite vertices and the
183     // longitude range is determined by the other pair.
184     //
185     // We first determine which corner (i,j) of the cell has the largest
186     // absolute latitude.  To maximize latitude, we want to find the point in
187     // the cell that has the largest absolute z-coordinate and the smallest
188     // absolute x- and y-coordinates.  To do this we look at each coordinate
189     // (u and v), and determine whether we want to minimize or maximize that
190     // coordinate based on the axis direction and the cell's (u,v) quadrant.
191     double u = uv_[0][0] + uv_[0][1];
192     double v = uv_[1][0] + uv_[1][1];
193     int i = S2::GetUAxis(face_)[2] == 0 ? (u < 0) : (u > 0);
194     int j = S2::GetVAxis(face_)[2] == 0 ? (v < 0) : (v > 0);
195     R1Interval lat = R1Interval::FromPointPair(GetLatitude(i, j),
196                                                GetLatitude(1-i, 1-j));
197     S1Interval lng = S1Interval::FromPointPair(GetLongitude(i, 1-j),
198                                                GetLongitude(1-i, j));
199 
200     // We grow the bounds slightly to make sure that the bounding rectangle
201     // contains S2LatLng(P) for any point P inside the loop L defined by the
202     // four *normalized* vertices.  Note that normalization of a vector can
203     // change its direction by up to 0.5 * DBL_EPSILON radians, and it is not
204     // enough just to add Normalize() calls to the code above because the
205     // latitude/longitude ranges are not necessarily determined by diagonally
206     // opposite vertex pairs after normalization.
207     //
208     // We would like to bound the amount by which the latitude/longitude of a
209     // contained point P can exceed the bounds computed above.  In the case of
210     // longitude, the normalization error can change the direction of rounding
211     // leading to a maximum difference in longitude of 2 * DBL_EPSILON.  In
212     // the case of latitude, the normalization error can shift the latitude by
213     // up to 0.5 * DBL_EPSILON and the other sources of error can cause the
214     // two latitudes to differ by up to another 1.5 * DBL_EPSILON, which also
215     // leads to a maximum difference of 2 * DBL_EPSILON.
216     return S2LatLngRect(lat, lng).
217         Expanded(S2LatLng::FromRadians(2 * DBL_EPSILON, 2 * DBL_EPSILON)).
218         PolarClosure();
219   }
220 
221   // The 4 cells around the equator extend to +/-45 degrees latitude at the
222   // midpoints of their top and bottom edges.  The two cells covering the
223   // poles extend down to +/-35.26 degrees at their vertices.  The maximum
224   // error in this calculation is 0.5 * DBL_EPSILON.
225   static const double kPoleMinLat = asin(sqrt(1./3)) - 0.5 * DBL_EPSILON;
226 
227   // The face centers are the +X, +Y, +Z, -X, -Y, -Z axes in that order.
228   S2_DCHECK_EQ(((face_ < 3) ? 1 : -1), S2::GetNorm(face_)[face_ % 3]);
229 
230   S2LatLngRect bound;
231   switch (face_) {
232     case 0:
233       bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
234                            S1Interval(-M_PI_4, M_PI_4));
235       break;
236     case 1:
237       bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
238                            S1Interval(M_PI_4, 3*M_PI_4));
239       break;
240     case 2:
241       bound = S2LatLngRect(R1Interval(kPoleMinLat, M_PI_2),
242                            S1Interval::Full());
243       break;
244     case 3:
245       bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
246                            S1Interval(3*M_PI_4, -3*M_PI_4));
247       break;
248     case 4:
249       bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
250                            S1Interval(-3*M_PI_4, -M_PI_4));
251       break;
252     default:
253       bound = S2LatLngRect(R1Interval(-M_PI_2, -kPoleMinLat),
254                            S1Interval::Full());
255       break;
256   }
257   // Finally, we expand the bound to account for the error when a point P is
258   // converted to an S2LatLng to test for containment.  (The bound should be
259   // large enough so that it contains the computed S2LatLng of any contained
260   // point, not just the infinite-precision version.)  We don't need to expand
261   // longitude because longitude is calculated via a single call to atan2(),
262   // which is guaranteed to be semi-monotonic.  (In fact the Gnu implementation
263   // is also correctly rounded, but we don't even need that here.)
264   return bound.Expanded(S2LatLng::FromRadians(DBL_EPSILON, 0));
265 }
266 
MayIntersect(const S2Cell & cell) const267 bool S2Cell::MayIntersect(const S2Cell& cell) const {
268   return id_.intersects(cell.id_);
269 }
270 
Contains(const S2Cell & cell) const271 bool S2Cell::Contains(const S2Cell& cell) const {
272   return id_.contains(cell.id_);
273 }
274 
Contains(const S2Point & p) const275 bool S2Cell::Contains(const S2Point& p) const {
276   // We can't just call XYZtoFaceUV, because for points that lie on the
277   // boundary between two faces (i.e. u or v is +1/-1) we need to return
278   // true for both adjacent cells.
279   R2Point uv;
280   if (!S2::FaceXYZtoUV(face_, p, &uv)) return false;
281 
282   // Expand the (u,v) bound to ensure that
283   //
284   //   S2Cell(S2CellId(p)).Contains(p)
285   //
286   // is always true.  To do this, we need to account for the error when
287   // converting from (u,v) coordinates to (s,t) coordinates.  At least in the
288   // case of S2_QUADRATIC_PROJECTION, the total error is at most DBL_EPSILON.
289   return uv_.Expanded(DBL_EPSILON).Contains(uv);
290 }
291 
Encode(Encoder * const encoder) const292 void S2Cell::Encode(Encoder* const encoder) const {
293   id_.Encode(encoder);
294 }
295 
Decode(Decoder * const decoder)296 bool S2Cell::Decode(Decoder* const decoder) {
297   S2CellId id;
298   if (!id.Decode(decoder)) return false;
299   this->~S2Cell();
300   new (this) S2Cell(id);
301   return true;
302 }
303 
304 // Return the squared chord distance from point P to corner vertex (i,j).
VertexChordDist(const S2Point & p,int i,int j) const305 inline S1ChordAngle S2Cell::VertexChordDist(
306     const S2Point& p, int i, int j) const {
307   S2Point vertex = S2Point(uv_[0][i], uv_[1][j], 1).Normalize();
308   return S1ChordAngle(p, vertex);
309 }
310 
311 // Given a point P and either the lower or upper edge of the S2Cell (specified
312 // by setting "v_end" to 0 or 1 respectively), return true if P is closer to
313 // the interior of that edge than it is to either endpoint.
UEdgeIsClosest(const S2Point & p,int v_end) const314 bool S2Cell::UEdgeIsClosest(const S2Point& p, int v_end) const {
315   double u0 = uv_[0][0], u1 = uv_[0][1], v = uv_[1][v_end];
316   // These are the normals to the planes that are perpendicular to the edge
317   // and pass through one of its two endpoints.
318   S2Point dir0(v * v + 1, -u0 * v, -u0);
319   S2Point dir1(v * v + 1, -u1 * v, -u1);
320   return p.DotProd(dir0) > 0 && p.DotProd(dir1) < 0;
321 }
322 
323 // Given a point P and either the left or right edge of the S2Cell (specified
324 // by setting "u_end" to 0 or 1 respectively), return true if P is closer to
325 // the interior of that edge than it is to either endpoint.
VEdgeIsClosest(const S2Point & p,int u_end) const326 bool S2Cell::VEdgeIsClosest(const S2Point& p, int u_end) const {
327   double v0 = uv_[1][0], v1 = uv_[1][1], u = uv_[0][u_end];
328   // See comments above.
329   S2Point dir0(-u * v0, u * u + 1, -v0);
330   S2Point dir1(-u * v1, u * u + 1, -v1);
331   return p.DotProd(dir0) > 0 && p.DotProd(dir1) < 0;
332 }
333 
334 // Given the dot product of a point P with the normal of a u- or v-edge at the
335 // given coordinate value, return the distance from P to that edge.
EdgeDistance(double dirIJ,double uv)336 inline static S1ChordAngle EdgeDistance(double dirIJ, double uv) {
337   // Let P by the target point and let R be the closest point on the given
338   // edge AB.  The desired distance PR can be expressed as PR^2 = PQ^2 + QR^2
339   // where Q is the point P projected onto the plane through the great circle
340   // through AB.  We can compute the distance PQ^2 perpendicular to the plane
341   // from "dirIJ" (the dot product of the target point P with the edge
342   // normal) and the squared length the edge normal (1 + uv**2).
343   double pq2 = (dirIJ * dirIJ) / (1 + uv * uv);
344 
345   // We can compute the distance QR as (1 - OQ) where O is the sphere origin,
346   // and we can compute OQ^2 = 1 - PQ^2 using the Pythagorean theorem.
347   // (This calculation loses accuracy as angle POQ approaches Pi/2.)
348   double qr = 1 - sqrt(1 - pq2);
349   return S1ChordAngle::FromLength2(pq2 + qr * qr);
350 }
351 
GetDistanceInternal(const S2Point & target_xyz,bool to_interior) const352 S1ChordAngle S2Cell::GetDistanceInternal(const S2Point& target_xyz,
353                                          bool to_interior) const {
354   // All calculations are done in the (u,v,w) coordinates of this cell's face.
355   S2Point target = S2::FaceXYZtoUVW(face_, target_xyz);
356 
357   // Compute dot products with all four upward or rightward-facing edge
358   // normals.  "dirIJ" is the dot product for the edge corresponding to axis
359   // I, endpoint J.  For example, dir01 is the right edge of the S2Cell
360   // (corresponding to the upper endpoint of the u-axis).
361   double dir00 = target[0] - target[2] * uv_[0][0];
362   double dir01 = target[0] - target[2] * uv_[0][1];
363   double dir10 = target[1] - target[2] * uv_[1][0];
364   double dir11 = target[1] - target[2] * uv_[1][1];
365   bool inside = true;
366   if (dir00 < 0) {
367     inside = false;  // Target is to the left of the cell
368     if (VEdgeIsClosest(target, 0)) return EdgeDistance(-dir00, uv_[0][0]);
369   }
370   if (dir01 > 0) {
371     inside = false;  // Target is to the right of the cell
372     if (VEdgeIsClosest(target, 1)) return EdgeDistance(dir01, uv_[0][1]);
373   }
374   if (dir10 < 0) {
375     inside = false;  // Target is below the cell
376     if (UEdgeIsClosest(target, 0)) return EdgeDistance(-dir10, uv_[1][0]);
377   }
378   if (dir11 > 0) {
379     inside = false;  // Target is above the cell
380     if (UEdgeIsClosest(target, 1)) return EdgeDistance(dir11, uv_[1][1]);
381   }
382   if (inside) {
383     if (to_interior) return S1ChordAngle::Zero();
384     // Although you might think of S2Cells as rectangles, they are actually
385     // arbitrary quadrilaterals after they are projected onto the sphere.
386     // Therefore the simplest approach is just to find the minimum distance to
387     // any of the four edges.
388     return min(min(EdgeDistance(-dir00, uv_[0][0]),
389                    EdgeDistance(dir01, uv_[0][1])),
390                min(EdgeDistance(-dir10, uv_[1][0]),
391                    EdgeDistance(dir11, uv_[1][1])));
392   }
393   // Otherwise, the closest point is one of the four cell vertices.  Note that
394   // it is *not* trivial to narrow down the candidates based on the edge sign
395   // tests above, because (1) the edges don't meet at right angles and (2)
396   // there are points on the far side of the sphere that are both above *and*
397   // below the cell, etc.
398   return min(min(VertexChordDist(target, 0, 0),
399                  VertexChordDist(target, 1, 0)),
400              min(VertexChordDist(target, 0, 1),
401                  VertexChordDist(target, 1, 1)));
402 }
403 
GetDistance(const S2Point & target) const404 S1ChordAngle S2Cell::GetDistance(const S2Point& target) const {
405   return GetDistanceInternal(target, true /*to_interior*/);
406 }
407 
GetBoundaryDistance(const S2Point & target) const408 S1ChordAngle S2Cell::GetBoundaryDistance(const S2Point& target) const {
409   return GetDistanceInternal(target, false /*to_interior*/);
410 }
411 
GetMaxDistance(const S2Point & target) const412 S1ChordAngle S2Cell::GetMaxDistance(const S2Point& target) const {
413   // First check the 4 cell vertices.  If all are within the hemisphere
414   // centered around target, the max distance will be to one of these vertices.
415   S2Point target_uvw = S2::FaceXYZtoUVW(face_, target);
416   S1ChordAngle max_dist = max(max(VertexChordDist(target_uvw, 0, 0),
417                                          VertexChordDist(target_uvw, 1, 0)),
418                                      max(VertexChordDist(target_uvw, 0, 1),
419                                          VertexChordDist(target_uvw, 1, 1)));
420 
421   if (max_dist <= S1ChordAngle::Right()) {
422     return max_dist;
423   }
424 
425   // Otherwise, find the minimum distance d_min to the antipodal point and the
426   // maximum distance will be Pi - d_min.
427   return S1ChordAngle::Straight() - GetDistance(-target);
428 }
429 
GetDistance(const S2Point & a,const S2Point & b) const430 S1ChordAngle S2Cell::GetDistance(const S2Point& a, const S2Point& b) const {
431   // Possible optimizations:
432   //  - Currently the (cell vertex, edge endpoint) distances are computed
433   //    twice each, and the length of AB is computed 4 times.
434   //  - To fix this, refactor GetDistance(target) so that it skips calculating
435   //    the distance to each cell vertex.  Instead, compute the cell vertices
436   //    and distances in this function, and add a low-level UpdateMinDistance
437   //    that allows the XA, XB, and AB distances to be passed in.
438   //  - It might also be more efficient to do all calculations in UVW-space,
439   //    since this would involve transforming 2 points rather than 4.
440 
441   // First, check the minimum distance to the edge endpoints A and B.
442   // (This also detects whether either endpoint is inside the cell.)
443   S1ChordAngle min_dist = min(GetDistance(a), GetDistance(b));
444   if (min_dist == S1ChordAngle::Zero()) return min_dist;
445 
446   // Otherwise, check whether the edge crosses the cell boundary.
447   // Note that S2EdgeCrosser needs pointers to vertices.
448   S2Point v[4];
449   for (int i = 0; i < 4; ++i) {
450     v[i] = GetVertex(i);
451   }
452   S2EdgeCrosser crosser(&a, &b, &v[3]);
453   for (int i = 0; i < 4; ++i) {
454     if (crosser.CrossingSign(&v[i]) >= 0) {
455       return S1ChordAngle::Zero();
456     }
457   }
458   // Finally, check whether the minimum distance occurs between a cell vertex
459   // and the interior of the edge AB.  (Some of this work is redundant, since
460   // it also checks the distance to the endpoints A and B again.)
461   //
462   // Note that we don't need to check the distance from the interior of AB to
463   // the interior of a cell edge, because the only way that this distance can
464   // be minimal is if the two edges cross (already checked above).
465   for (int i = 0; i < 4; ++i) {
466     S2::UpdateMinDistance(v[i], a, b, &min_dist);
467   }
468   return min_dist;
469 }
470 
GetMaxDistance(const S2Point & a,const S2Point & b) const471 S1ChordAngle S2Cell::GetMaxDistance(const S2Point& a, const S2Point& b) const {
472   // If the maximum distance from both endpoints to the cell is less than Pi/2
473   // then the maximum distance from the edge to the cell is the maximum of the
474   // two endpoint distances.
475   S1ChordAngle max_dist = max(GetMaxDistance(a), GetMaxDistance(b));
476   if (max_dist <= S1ChordAngle::Right()) {
477     return max_dist;
478   }
479 
480   return S1ChordAngle::Straight() - GetDistance(-a, -b);
481 }
482 
GetDistance(const S2Cell & target) const483 S1ChordAngle S2Cell::GetDistance(const S2Cell& target) const {
484   // If the cells intersect, the distance is zero.  We use the (u,v) ranges
485   // rather S2CellId::intersects() so that cells that share a partial edge or
486   // corner are considered to intersect.
487   if (face_ == target.face_ && uv_.Intersects(target.uv_)) {
488     return S1ChordAngle::Zero();
489   }
490 
491   // Otherwise, the minimum distance always occurs between a vertex of one
492   // cell and an edge of the other cell (including the edge endpoints).  This
493   // represents a total of 32 possible (vertex, edge) pairs.
494   //
495   // TODO(ericv): This could be optimized to be at least 5x faster by pruning
496   // the set of possible closest vertex/edge pairs using the faces and (u,v)
497   // ranges of both cells.
498   S2Point va[4], vb[4];
499   for (int i = 0; i < 4; ++i) {
500     va[i] = GetVertex(i);
501     vb[i] = target.GetVertex(i);
502   }
503   S1ChordAngle min_dist = S1ChordAngle::Infinity();
504   for (int i = 0; i < 4; ++i) {
505     for (int j = 0; j < 4; ++j) {
506       S2::UpdateMinDistance(va[i], vb[j], vb[(j + 1) & 3], &min_dist);
507       S2::UpdateMinDistance(vb[i], va[j], va[(j + 1) & 3], &min_dist);
508     }
509   }
510   return min_dist;
511 }
512 
OppositeFace(int face)513 inline static int OppositeFace(int face) {
514   return face >= 3 ? face - 3 : face + 3;
515 }
516 
517 // The antipodal UV is the transpose of the original UV, interpreted within
518 // the opposite face.
OppositeUV(const R2Rect & uv)519 inline static R2Rect OppositeUV(const R2Rect& uv) {
520   return R2Rect(uv[1], uv[0]);
521 }
522 
GetMaxDistance(const S2Cell & target) const523 S1ChordAngle S2Cell::GetMaxDistance(const S2Cell& target) const {
524   // Need to check the antipodal target for intersection with the cell. If it
525   // intersects, the distance is S1ChordAngle::Straight().
526   if (face_ == OppositeFace(target.face_) &&
527       uv_.Intersects(OppositeUV(target.uv_))) {
528     return S1ChordAngle::Straight();
529   }
530 
531   // Otherwise, the maximum distance always occurs between a vertex of one
532   // cell and an edge of the other cell (including the edge endpoints).  This
533   // represents a total of 32 possible (vertex, edge) pairs.
534   //
535   // TODO(user): When the maximum distance is at most Pi/2, the maximum is
536   // always attained between a pair of vertices, and this could be made much
537   // faster by testing each vertex pair once rather than the current 4 times.
538   S2Point va[4], vb[4];
539   for (int i = 0; i < 4; ++i) {
540     va[i] = GetVertex(i);
541     vb[i] = target.GetVertex(i);
542   }
543   S1ChordAngle max_dist = S1ChordAngle::Negative();
544   for (int i = 0; i < 4; ++i) {
545     for (int j = 0; j < 4; ++j) {
546       S2::UpdateMaxDistance(va[i], vb[j], vb[(j + 1) & 3], &max_dist);
547       S2::UpdateMaxDistance(vb[i], va[j], va[(j + 1) & 3], &max_dist);
548     }
549   }
550   return max_dist;
551 }
552 
553