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 // Original author: ericv@google.com (Eric Veach)
17 // Converted to D:  madric@gmail.com (Vijay Nayar)
18 //
19 // Defines a collection of functions for:
20 //
21 //   (1) Robustly clipping geodesic edges to the faces of the S2 biunit cube
22 //       (see s2coords.h), and
23 //
24 //   (2) Robustly clipping 2D edges against 2D rectangles.
25 //
26 // These functions can be used to efficiently find the set of S2CellIds that
27 // are intersected by a geodesic edge (e.g., see S2CrossingEdgeQuery).
28 
29 module s2.s2edge_clipping;
30 
31 import s2.r1interval;
32 import s2.r2point;
33 import s2.r2rect;
34 import s2.s2point;
35 
36 import s2.s2pointutil : isUnitLength, robustCrossProd;
37 import s2.s2coords
38     : GetFace, GetUVWFace, FaceUVtoXYZ, FaceXYZtoUVW, ValidFaceXYZtoUV, XYZtoFaceUV;
39 
40 import std.exception : enforce;
41 import std.algorithm : min, max;
42 import std.math : fabs, ldexp, signbit, SQRT1_2, SQRT2;
43 
44 // FaceSegment represents an edge AB clipped to an S2 cube face.  It is
45 // represented by a face index and a pair of (u,v) coordinates.
46 struct FaceSegment {
47   int face;
48   R2Point a, b;
49 }
50 
51 alias FaceSegmentVector = FaceSegment[];
52 
53 // S2PointUVW is used to document that a given S2Point is expressed in the
54 // (u,v,w) coordinates of some cube face.
55 alias S2PointUVW = S2Point;
56 
57 // Subdivides the given edge AB at every point where it crosses the boundary
58 // between two S2 cube faces and returns the corresponding FaceSegments.  The
59 // segments are returned in order from A toward B.  The input points must be
60 // unit length.
61 //
62 // This method guarantees that the returned segments form a continuous path
63 // from A to B, and that all vertices are within kFaceClipErrorUVDist of the
64 // line AB.  All vertices lie within the [-1,1]x[-1,1] cube face rectangles.
65 // The results are consistent with s2pred::Sign(), i.e. the edge is
66 // well-defined even its endpoints are antipodal.  [TODO(ericv): Extend the
67 // implementation of S2::RobustCrossProd so that this statement is true.]
68 void getFaceSegments(in S2Point a, in S2Point b, out FaceSegmentVector segments)
69 in {
70   assert(isUnitLength(a));
71   assert(isUnitLength(b));
72 } do {
73   // Fast path: both endpoints are on the same face.
74   FaceSegment segment;
75   int a_face = XYZtoFaceUV(a, segment.a);
76   int b_face = XYZtoFaceUV(b, segment.b);
77   if (a_face == b_face) {
78     segment.face = a_face;
79     segments ~= segment;
80     return;
81   }
82 
83   // Starting at A, we follow AB from face to face until we reach the face
84   // containing B.  The following code is designed to ensure that we always
85   // reach B, even in the presence of numerical errors.
86   //
87   // First we compute the normal to the plane containing A and B.  This normal
88   // becomes the ultimate definition of the line AB; it is used to resolve all
89   // questions regarding where exactly the line goes.  Unfortunately due to
90   // numerical errors, the line may not quite intersect the faces containing
91   // the original endpoints.  We handle this by moving A and/or B slightly if
92   // necessary so that they are on faces intersected by the line AB.
93   S2Point ab = robustCrossProd(a, b);
94   a_face = moveOriginToValidFace(a_face, a, ab, segment.a);
95   b_face = moveOriginToValidFace(b_face, b, -ab, segment.b);
96 
97   // Now we simply follow AB from face to face until we reach B.
98   segment.face = a_face;
99   R2Point b_saved = segment.b;
100   for (int face = a_face; face != b_face; ) {
101     // Complete the current segment by finding the point where AB exits the
102     // current face.
103     S2PointUVW n = FaceXYZtoUVW(face, ab);
104     int exit_axis = getExitAxis(n);
105     segment.b = getExitPoint(n, exit_axis);
106     segments ~= segment;
107 
108     // Compute the next face intersected by AB, and translate the exit point
109     // of the current segment into the (u,v) coordinates of the next face.
110     // This becomes the first point of the next segment.
111     S2Point exit_xyz = FaceUVtoXYZ(face, segment.b);
112     face = getNextFace(face, segment.b, exit_axis, n, b_face);
113     S2PointUVW exit_uvw = FaceXYZtoUVW(face, exit_xyz);
114     segment.face = face;
115     segment.a = R2Point(exit_uvw[0], exit_uvw[1]);
116   }
117   // Finish the last segment.
118   segment.b = b_saved;
119   segments ~= segment;
120 }
121 
122 // This helper function does two things.  First, it clips the line segment AB
123 // to find the clipped destination B' on a given face.  (The face is specified
124 // implicitly by expressing *all arguments* in the (u,v,w) coordinates of that
125 // face.)  Second, it partially computes whether the segment AB intersects
126 // this face at all.  The actual condition is fairly complicated, but it turns
127 // out that it can be expressed as a "score" that can be computed
128 // independently when clipping the two endpoints A and B.  This function
129 // returns the score for the given endpoint, which is an integer ranging from
130 // 0 to 3.  If the sum of the two scores is 3 or more, then AB does not
131 // intersect this face.  See the calling function for the meaning of the
132 // various parameters.
133 private int clipDestination(
134     in S2PointUVW a, in S2PointUVW b, in S2PointUVW scaled_n,
135     in S2PointUVW a_tangent, in S2PointUVW b_tangent, double scale_uv,
136     out R2Point uv)
137 in {
138   assert(intersectsFace(scaled_n));
139 } do {
140 
141   // Optimization: if B is within the safe region of the face, use it.
142   const double kMaxSafeUVCoord = 1 - FACE_CLIP_ERROR_UV_COORD;
143   if (b[2] > 0) {
144     uv = R2Point(b[0] / b[2], b[1] / b[2]);
145     if (max(fabs(uv[0]), fabs(uv[1])) <= kMaxSafeUVCoord)
146       return 0;
147   }
148   // Otherwise find the point B' where the line AB exits the face.
149   uv = scale_uv * getExitPoint(scaled_n, getExitAxis(scaled_n));
150   auto p = S2PointUVW(uv[0], uv[1], 1.0);
151 
152   // Determine if the exit point B' is contained within the segment.  We do this
153   // by computing the dot products with two inward-facing tangent vectors at A
154   // and B.  If either dot product is negative, we say that B' is on the "wrong
155   // side" of that point.  As the point B' moves around the great circle AB past
156   // the segment endpoint B, it is initially on the wrong side of B only; as it
157   // moves further it is on the wrong side of both endpoints; and then it is on
158   // the wrong side of A only.  If the exit point B' is on the wrong side of
159   // either endpoint, we can't use it; instead the segment is clipped at the
160   // original endpoint B.
161   //
162   // We reject the segment if the sum of the scores of the two endpoints is 3
163   // or more.  Here is what that rule encodes:
164   //  - If B' is on the wrong side of A, then the other clipped endpoint A'
165   //    must be in the interior of AB (otherwise AB' would go the wrong way
166   //    around the circle).  There is a similar rule for A'.
167   //  - If B' is on the wrong side of either endpoint (and therefore we must
168   //    use the original endpoint B instead), then it must be possible to
169   //    project B onto this face (i.e., its w-coordinate must be positive).
170   //    This rule is only necessary to handle certain zero-length edges (A=B).
171   int score = 0;
172   if ((p - a).dotProd(a_tangent) < 0) {
173     score = 2;  // B' is on wrong side of A.
174   } else if ((p - b).dotProd(b_tangent) < 0) {
175     score = 1;  // B' is on wrong side of B.
176   }
177   if (score > 0) {  // B' is not in the interior of AB.
178     if (b[2] <= 0) {
179       score = 3;    // B cannot be projected onto this face.
180     } else {
181       uv = R2Point(b[0] / b[2], b[1] / b[2]);
182     }
183   }
184   return score;
185 }
186 
187 // Given an edge AB and a face, returns the (u,v) coordinates for the portion
188 // of AB that intersects that face.  This method guarantees that the clipped
189 // vertices lie within the [-1,1]x[-1,1] cube face rectangle and are within
190 // kFaceClipErrorUVDist of the line AB, but the results may differ from
191 // those produced by GetFaceSegments.  Returns false if AB does not
192 // intersect the given face.
193 bool clipToFace(in S2Point a, in S2Point b, int face, out R2Point a_uv, out R2Point b_uv) {
194   return clipToPaddedFace(a, b, face, 0.0, a_uv, b_uv);
195 }
196 
197 // Like ClipToFace, but rather than clipping to the square [-1,1]x[-1,1]
198 // in (u,v) space, this method clips to [-R,R]x[-R,R] where R=(1+padding).
199 bool clipToPaddedFace(
200     in S2Point a_xyz, in S2Point b_xyz, int face, double padding,
201     out R2Point a_uv, out R2Point b_uv)
202 in {
203   assert(padding >= 0);
204 } do {
205   // Fast path: both endpoints are on the given face.
206   if (GetFace(a_xyz) == face && GetFace(b_xyz) == face) {
207     ValidFaceXYZtoUV(face, a_xyz, a_uv);
208     ValidFaceXYZtoUV(face, b_xyz, b_uv);
209     return true;
210   }
211   // Convert everything into the (u,v,w) coordinates of the given face.  Note
212   // that the cross product *must* be computed in the original (x,y,z)
213   // coordinate system because RobustCrossProd (unlike the mathematical cross
214   // product) can produce different results in different coordinate systems
215   // when one argument is a linear multiple of the other, due to the use of
216   // symbolic perturbations.
217   S2PointUVW n = FaceXYZtoUVW(face, robustCrossProd(a_xyz, b_xyz));
218   S2PointUVW a = FaceXYZtoUVW(face, a_xyz);
219   S2PointUVW b = FaceXYZtoUVW(face, b_xyz);
220 
221   // Padding is handled by scaling the u- and v-components of the normal.
222   // Letting R=1+padding, this means that when we compute the dot product of
223   // the normal with a cube face vertex (such as (-1,-1,1)), we will actually
224   // compute the dot product with the scaled vertex (-R,-R,1).  This allows
225   // methods such as IntersectsFace(), GetExitAxis(), etc, to handle padding
226   // with no further modifications.
227   const double scale_uv = 1 + padding;
228   auto scaled_n = S2PointUVW(scale_uv * n[0], scale_uv * n[1], n[2]);
229   if (!intersectsFace(scaled_n)) return false;
230 
231   // TODO(ericv): This is a temporary hack until I rewrite S2::RobustCrossProd;
232   // it avoids loss of precision in Normalize() when the vector is so small
233   // that it underflows.
234   if (max(fabs(n[0]), max(fabs(n[1]), fabs(n[2]))) < ldexp(1.0, -511)) {
235     n *= ldexp(1.0, 563);
236   }  // END OF HACK
237   n = n.normalize();
238   S2PointUVW a_tangent = n.crossProd(a);
239   S2PointUVW b_tangent = b.crossProd(n);
240   // As described above, if the sum of the scores from clipping the two
241   // endpoints is 3 or more, then the segment does not intersect this face.
242   int a_score = clipDestination(b, a, -scaled_n, b_tangent, a_tangent, scale_uv, a_uv);
243   int b_score = clipDestination(a, b, scaled_n, a_tangent, b_tangent, scale_uv, b_uv);
244   return a_score + b_score < 3;
245 }
246 
247 // The maximum error in the vertices returned by GetFaceSegments and
248 // ClipToFace (compared to an exact calculation):
249 //
250 //  - kFaceClipErrorRadians is the maximum angle between a returned vertex
251 //    and the nearest point on the exact edge AB.  It is equal to the
252 //    maximum directional error in S2::RobustCrossProd, plus the error when
253 //    projecting points onto a cube face.
254 //
255 //  - kFaceClipErrorDist is the same angle expressed as a maximum distance
256 //    in (u,v)-space.  In other words, a returned vertex is at most this far
257 //    from the exact edge AB projected into (u,v)-space.
258 
259 //  - kFaceClipErrorUVCoord is the same angle expressed as the maximum error
260 //    in an individual u- or v-coordinate.  In other words, for each
261 //    returned vertex there is a point on the exact edge AB whose u- and
262 //    v-coordinates differ from the vertex by at most this amount.
263 
264 enum double FACE_CLIP_ERROR_RADIANS = 3.0 * double.epsilon;
265 enum double FACE_CLIP_ERROR_UV_DIST = 9.0 * double.epsilon;
266 enum double FACE_CLIP_ERROR_UV_COORD = 9.0 * SQRT1_2 * double.epsilon;
267 
268 // Returns true if the edge AB intersects the given (closed) rectangle to
269 // within the error bound below.
270 bool intersectsRect(in R2Point a, in R2Point b, in R2Rect rect) {
271   // First check whether the bound of AB intersects "rect".
272   R2Rect bound = R2Rect.fromPointPair(a, b);
273   if (!rect.intersects(bound)) return false;
274 
275   // Otherwise AB intersects "rect" if and only if all four vertices of "rect"
276   // do not lie on the same side of the extended line AB.  We test this by
277   // finding the two vertices of "rect" with minimum and maximum projections
278   // onto the normal of AB, and computing their dot products with the edge
279   // normal.
280   R2Point n = (b - a).ortho();
281   int i = (n[0] >= 0) ? 1 : 0;
282   int j = (n[1] >= 0) ? 1 : 0;
283   double max = n.dotProd(rect.getVertex(i, j) - a);
284   double min = n.dotProd(rect.getVertex(1-i, 1-j) - a);
285   return (max >= 0) && (min <= 0);
286 }
287 
288 private bool updateEndpoint(ref R1Interval bound, int end, double value) {
289   if (end == 0) {
290     if (bound.hi() < value) return false;
291     if (bound.lo() < value) bound.setLo(value);
292   } else {
293     if (bound.lo() > value) return false;
294     if (bound.hi() > value) bound.setHi(value);
295   }
296   return true;
297 }
298 
299 // The maximum error in IntersectRect.  If some point of AB is inside the
300 // rectangle by at least this distance, the result is guaranteed to be true;
301 // if all points of AB are outside the rectangle by at least this distance,
302 // the result is guaranteed to be false.  This bound assumes that "rect" is
303 // a subset of the rectangle [-1,1]x[-1,1] or extends slightly outside it
304 // (e.g., by 1e-10 or less).
305 enum double INTERSECTS_RECT_ERROR_UV_DIST = 3 * SQRT2 * double.epsilon;
306 
307 // Given an edge AB, returns the portion of AB that is contained by the given
308 // rectangle "clip".  Returns false if there is no intersection.
309 bool clipEdge(
310     in R2Point a, in R2Point b, in R2Rect clip, out R2Point a_clipped, out R2Point b_clipped) {
311   // Compute the bounding rectangle of AB, clip it, and then extract the new
312   // endpoints from the clipped bound.
313   R2Rect bound = R2Rect.fromPointPair(a, b);
314   if (clipEdgeBound(a, b, clip, bound)) {
315     int ai = (a[0] > b[0]), aj = (a[1] > b[1]);
316     a_clipped = bound.getVertex(ai, aj);
317     b_clipped = bound.getVertex(1 - ai, 1 - aj);
318     return true;
319   }
320   return false;
321 }
322 
323 // Given an edge AB and a rectangle "clip", returns the bounding rectangle of
324 // the portion of AB intersected by "clip".  The resulting bound may be
325 // empty.  This is a convenience function built on top of ClipEdgeBound.
326 R2Rect getClippedEdgeBound(in R2Point a, in R2Point b, in R2Rect clip) {
327   R2Rect bound = R2Rect.fromPointPair(a, b);
328   if (clipEdgeBound(a, b, clip, bound)) return bound;
329   return R2Rect.empty();
330 }
331 
332 // This function can be used to clip an edge AB to sequence of rectangles
333 // efficiently.  It represents the clipped edges by their bounding boxes
334 // rather than as a pair of endpoints.  Specifically, let A'B' be some
335 // portion of an edge AB, and let "bound" be a tight bound of A'B'.  This
336 // function updates "bound" (in place) to be a tight bound of A'B'
337 // intersected with a given rectangle "clip".  If A'B' does not intersect
338 // "clip", returns false and does not necessarily update "bound".
339 //
340 // REQUIRES: "bound" is a tight bounding rectangle for some portion of AB.
341 // (This condition is automatically satisfied if you start with the bounding
342 // box of AB and clip to a sequence of rectangles, stopping when the method
343 // returns false.)
344 bool clipEdgeBound(in R2Point a, in R2Point b, in R2Rect clip, ref R2Rect bound) {
345   // "diag" indicates which diagonal of the bounding box is spanned by AB: it
346   // is 0 if AB has positive slope, and 1 if AB has negative slope.  This is
347   // used to determine which interval endpoints need to be updated each time
348   // the edge is clipped.
349   int diag = (a[0] > b[0]) != (a[1] > b[1]);
350   return clipBoundAxis(a[0], b[0], bound[0], a[1], b[1], bound[1], diag, clip[0])
351       && clipBoundAxis(a[1], b[1], bound[1], a[0], b[0], bound[0], diag, clip[1]);
352 }
353 
354 // Given a line segment from (a0,a1) to (b0,b1) and a bounding interval for
355 // each axis, clip the segment further if necessary so that "bound0" does not
356 // extend outside the given interval "clip".  "diag" is a a precomputed helper
357 // variable that indicates which diagonal of the bounding box is spanned by AB:
358 // it is 0 if AB has positive slope, and 1 if AB has negative slope.
359 private bool clipBoundAxis(
360     double a0, double b0, ref R1Interval bound0, double a1, double b1, ref R1Interval bound1,
361     int diag, in R1Interval clip0) {
362   if (bound0.lo() < clip0.lo()) {
363     if (bound0.hi() < clip0.lo()) return false;
364     bound0[0] = clip0.lo();
365     if (!updateEndpoint(bound1, diag, interpolateDouble(clip0.lo(), a0, b0, a1, b1)))
366       return false;
367   }
368   if (bound0.hi() > clip0.hi()) {
369     if (bound0.lo() > clip0.hi()) return false;
370     bound0[1] = clip0.hi();
371     if (!updateEndpoint(bound1, 1 - diag, interpolateDouble(clip0.hi(), a0, b0, a1, b1)))
372       return false;
373   }
374   return true;
375 }
376 
377 
378 // The maximum error in the vertices generated by ClipEdge and the bounds
379 // generated by ClipEdgeBound (compared to an exact calculation):
380 //
381 //  - kEdgeClipErrorUVCoord is the maximum error in a u- or v-coordinate
382 //    compared to the exact result, assuming that the points A and B are in
383 //    the rectangle [-1,1]x[1,1] or slightly outside it (by 1e-10 or less).
384 //
385 //  - kEdgeClipErrorUVDist is the maximum distance from a clipped point to
386 //    the corresponding exact result.  It is equal to the error in a single
387 //    coordinate because at most one coordinate is subject to error.
388 
389 enum double EDGE_CLIP_ERROR_UV_COORD = 2.25 * double.epsilon;
390 enum double EDGE_CLIP_ERROR_UV_DIST = 2.25 * double.epsilon;
391 
392 // Given a value x that is some linear combination of a and b, returns the
393 // value x1 that is the same linear combination of a1 and b1.  This function
394 // makes the following guarantees:
395 //  - If x == a, then x1 = a1 (exactly).
396 //  - If x == b, then x1 = b1 (exactly).
397 //  - If a <= x <= b, then a1 <= x1 <= b1 (even if a1 == b1).
398 // REQUIRES: a != b
399 double interpolateDouble(double x, double a, double b, double a1, double b1)
400 in {
401   assert(a != b);
402 } do {
403   // To get results that are accurate near both A and B, we interpolate
404   // starting from the closer of the two points.
405   if (fabs(a - x) <= fabs(b - x)) {
406     return a1 + (b1 - a1) * (x - a) / (b - a);
407   } else {
408     return b1 + (a1 - b1) * (x - b) / (a - b);
409   }
410 }
411 
412 
413 // Given a line segment AB whose origin A has been projected onto a given cube
414 // face, determine whether it is necessary to project A onto a different face
415 // instead.  This can happen because the normal of the line AB is not computed
416 // exactly, so that the line AB (defined as the set of points perpendicular to
417 // the normal) may not intersect the cube face containing A.  Even if it does
418 // intersect the face, the "exit point" of the line from that face may be on
419 // the wrong side of A (i.e., in the direction away from B).  If this happens,
420 // we reproject A onto the adjacent face where the line AB approaches A most
421 // closely.  This moves the origin by a small amount, but never more than the
422 // error tolerances documented in the header file.
423 private int moveOriginToValidFace(int face, in S2Point a, in S2Point ab, ref R2Point a_uv) {
424   // Fast path: if the origin is sufficiently far inside the face, it is
425   // always safe to use it.
426   const double kMaxSafeUVCoord = 1 - FACE_CLIP_ERROR_UV_COORD;
427   if (max(fabs(a_uv[0]), fabs(a_uv[1])) <= kMaxSafeUVCoord) {
428     return face;
429   }
430   // Otherwise check whether the normal AB even intersects this face.
431   S2PointUVW n = FaceXYZtoUVW(face, ab);
432   if (intersectsFace(n)) {
433     // Check whether the point where the line AB exits this face is on the
434     // wrong side of A (by more than the acceptable error tolerance).
435     S2Point exit = FaceUVtoXYZ(face, getExitPoint(n, getExitAxis(n)));
436     S2Point a_tangent = ab.normalize().crossProd(a);
437     if ((exit - a).dotProd(a_tangent) >= -FACE_CLIP_ERROR_RADIANS) {
438       return face;  // We can use the given face.
439     }
440   }
441   // Otherwise we reproject A to the nearest adjacent face.  (If line AB does
442   // not pass through a given face, it must pass through all adjacent faces.)
443   if (fabs(a_uv[0]) >= fabs(a_uv[1])) {
444     face = GetUVWFace(face, 0 /*U axis*/, a_uv[0] > 0);
445   } else {
446     face = GetUVWFace(face, 1 /*V axis*/, a_uv[1] > 0);
447   }
448   enforce(intersectsFace(FaceXYZtoUVW(face, ab)));
449   ValidFaceXYZtoUV(face, a, a_uv);
450   a_uv[0] = max(-1.0, min(1.0, a_uv[0]));
451   a_uv[1] = max(-1.0, min(1.0, a_uv[1]));
452   return face;
453 }
454 
455 // Given cube face F and a directed line L (represented by its CCW normal N in
456 // the (u,v,w) coordinates of F), compute the axis of the cube face edge where
457 // L exits the face: return 0 if L exits through the u=-1 or u=+1 edge, and 1
458 // if L exits through the v=-1 or v=+1 edge.  Either result is acceptable if L
459 // exits exactly through a corner vertex of the cube face.
460 private int getExitAxis(in S2PointUVW n)
461 in {
462   assert(intersectsFace(n));
463 } do {
464   if (intersectsOppositeEdges(n)) {
465     // The line passes through through opposite edges of the face.
466     // It exits through the v=+1 or v=-1 edge if the u-component of N has a
467     // larger absolute magnitude than the v-component.
468     return (fabs(n[0]) >= fabs(n[1])) ? 1 : 0;
469   } else {
470     // The line passes through through two adjacent edges of the face.
471     // It exits the v=+1 or v=-1 edge if an even number of the components of N
472     // are negative.  We test this using signbit() rather than multiplication
473     // to avoid the possibility of underflow.
474     enforce(n[0] != 0 && n[1] != 0  && n[2] != 0);
475     return ((signbit(n[0]) ^ signbit(n[1]) ^ signbit(n[2])) == 0) ? 1 : 0;
476   }
477 }
478 
479 // Given a cube face F, a directed line L (represented by its CCW normal N in
480 // the (u,v,w) coordinates of F), and result of GetExitAxis(N), return the
481 // (u,v) coordinates of the point where L exits the cube face.
482 private R2Point getExitPoint(in S2PointUVW n, int axis) {
483   if (axis == 0) {
484     double u = (n[1] > 0) ? 1.0 : -1.0;
485     return R2Point(u, (-u * n[0] - n[2]) / n[1]);
486   } else {
487     double v = (n[0] < 0) ? 1.0 : -1.0;
488     return R2Point((-v * n[1] - n[2]) / n[0], v);
489   }
490 }
491 
492 // Return the next face that should be visited by GetFaceSegments, given that
493 // we have just visited "face" and we are following the line AB (represented
494 // by its normal N in the (u,v,w) coordinates of that face).  The other
495 // arguments include the point where AB exits "face", the corresponding
496 // exit axis, and the "target face" containing the destination point B.
497 private int getNextFace(int face, in R2Point exit, int axis, in S2PointUVW n, int target_face) {
498   // We return the face that is adjacent to the exit point along the given
499   // axis.  If line AB exits *exactly* through a corner of the face, there are
500   // two possible next faces.  If one is the "target face" containing B, then
501   // we guarantee that we advance to that face directly.
502   //
503   // The three conditions below check that (1) AB exits approximately through
504   // a corner, (2) the adjacent face along the non-exit axis is the target
505   // face, and (3) AB exits *exactly* through the corner.  (The SumEquals()
506   // code checks whether the dot product of (u,v,1) and "n" is exactly zero.)
507   if (fabs(exit[1 - axis]) == 1
508       && GetUVWFace(face, 1 - axis, exit[1 - axis] > 0) == target_face
509       && sumEquals(exit[0] * n[0], exit[1] * n[1], -n[2])) {
510     return target_face;
511   }
512   // Otherwise return the face that is adjacent to the exit point in the
513   // direction of the exit axis.
514   return GetUVWFace(face, axis, exit[axis] > 0);
515 }
516 
517 // The three functions below all compare a sum (u + v) to a third value w.
518 // They are implemented in such a way that they produce an exact result even
519 // though all calculations are done with ordinary floating-point operations.
520 // Here are the principles on which these functions are based:
521 //
522 // A. If u + v < w in floating-point, then u + v < w in exact arithmetic.
523 //
524 // B. If u + v < w in exact arithmetic, then at least one of the following
525 //    expressions is true in floating-point:
526 //       u + v < w
527 //       u < w - v
528 //       v < w - u
529 //
530 //    Proof: By rearranging terms and substituting ">" for "<", we can assume
531 //    that all values are non-negative.  Now clearly "w" is not the smallest
532 //    value, so assume WLOG that "u" is the smallest.  We want to show that
533 //    u < w - v in floating-point.  If v >= w/2, the calculation of w - v is
534 //    exact since the result is smaller in magnitude than either input value,
535 //    so the result holds.  Otherwise we have u <= v < w/2 and w - v >= w/2
536 //    (even in floating point), so the result also holds.
537 
538 // Return true if u + v == w exactly.
539 private bool sumEquals(double u, double v, double w) {
540   return (u + v == w) && (u == w - v) && (v == w - u);
541 }
542 
543 // Return true if a given directed line L intersects the cube face F.  The
544 // line L is defined by its normal N in the (u,v,w) coordinates of F.
545 private bool intersectsFace(in S2PointUVW n) {
546   // L intersects the [-1,1]x[-1,1] square in (u,v) if and only if the dot
547   // products of N with the four corner vertices (-1,-1,1), (1,-1,1), (1,1,1),
548   // and (-1,1,1) do not all have the same sign.  This is true exactly when
549   // |Nu| + |Nv| >= |Nw|.  The code below evaluates this expression exactly
550   // (see comments above).
551   double u = fabs(n[0]), v = fabs(n[1]), w = fabs(n[2]);
552   // We only need to consider the cases where u or v is the smallest value,
553   // since if w is the smallest then both expressions below will have a
554   // positive LHS and a negative RHS.
555   return (v >= w - u) && (u >= w - v);
556 }
557 
558 // Given a directed line L intersecting a cube face F, return true if L
559 // intersects two opposite edges of F (including the case where L passes
560 // exactly through a corner vertex of F).  The line L is defined by its
561 // normal N in the (u,v,w) coordinates of F.
562 private bool intersectsOppositeEdges(in S2PointUVW n) {
563   // The line L intersects opposite edges of the [-1,1]x[-1,1] (u,v) square if
564   // and only exactly two of the corner vertices lie on each side of L.  This
565   // is true exactly when ||Nu| - |Nv|| >= |Nw|.  The code below evaluates this
566   // expression exactly (see comments above).
567   double u = fabs(n[0]), v = fabs(n[1]), w = fabs(n[2]);
568   // If w is the smallest, the following line returns an exact result.
569   if (fabs(u - v) != w) return fabs(u - v) >= w;
570   // Otherwise u - v = w exactly, or w is not the smallest value.  In either
571   // case the following line returns the correct result.
572   return (u >= v) ? (u - w >= v) : (v - w >= u);
573 }