/** * A slightly more efficient version of getDistance() where the cross product * of the two endpoints has been precomputed. The cross product does not need * to be normalized, but should be computed using S2.robustCrossProd() for the * most accurate results. */ public static S1Angle GetDistance(S2Point x, S2Point a, S2Point b, S2Point aCrossB) { Preconditions.CheckArgument(S2.IsUnitLength(x)); Preconditions.CheckArgument(S2.IsUnitLength(a)); Preconditions.CheckArgument(S2.IsUnitLength(b)); // There are three cases. If X is located in the spherical wedge defined by // A, B, and the axis A x B, then the closest point is on the segment AB. // Otherwise the closest point is either A or B; the dividing line between // these two cases is the great circle passing through (A x B) and the // midpoint of AB. if (S2.SimpleCcw(aCrossB, a, x) && S2.SimpleCcw(x, b, aCrossB)) { // The closest point to X lies on the segment AB. We compute the distance // to the corresponding great circle. The result is accurate for small // distances but not necessarily for large distances (approaching Pi/2). var sinDist = Math.Abs(x.DotProd(aCrossB)) / aCrossB.Norm; return(S1Angle.FromRadians(Math.Asin(Math.Min(1.0, sinDist)))); } // Otherwise, the closest point is either A or B. The cheapest method is // just to compute the minimum of the two linear (as opposed to spherical) // distances and convert the result to an angle. Again, this method is // accurate for small but not large distances (approaching Pi). var linearDist2 = Math.Min((x - a).Norm2, (x - b).Norm2); return(S1Angle.FromRadians(2 * Math.Asin(Math.Min(1.0, 0.5 * Math.Sqrt(linearDist2))))); }
/** * Returns the point on edge AB closest to X. x, a and b must be of unit * length. Throws IllegalArgumentException if this is not the case. * */ public static S2Point GetClosestPoint(S2Point x, S2Point a, S2Point b) { Preconditions.CheckArgument(S2.IsUnitLength(x)); Preconditions.CheckArgument(S2.IsUnitLength(a)); Preconditions.CheckArgument(S2.IsUnitLength(b)); var crossProd = S2.RobustCrossProd(a, b); // Find the closest point to X along the great circle through AB. var p = x - (crossProd * x.DotProd(crossProd) / crossProd.Norm2); // If p is on the edge AB, then it's the closest point. if (S2.SimpleCcw(crossProd, a, p) && S2.SimpleCcw(p, b, crossProd)) { return(S2Point.Normalize(p)); } // Otherwise, the closest point is either A or B. return((x - a).Norm2 <= (x - b).Norm2 ? a : b); }