Lagrange
segment_segment_squared_distance.h
1/*
2 * Copyright 2022 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/common.h>
15#include <Eigen/Dense>
16#include <algorithm>
17#include <cmath>
18
19namespace lagrange {
20
43template <typename PointType>
45 const Eigen::MatrixBase<PointType>& U0,
46 const Eigen::MatrixBase<PointType>& U1,
47 const Eigen::MatrixBase<PointType>& V0,
48 const Eigen::MatrixBase<PointType>& V1,
49 Eigen::PlainObjectBase<PointType>& closest_pointU,
50 Eigen::PlainObjectBase<PointType>& closest_pointV,
51 ScalarOf<PointType>& lambdaU,
52 ScalarOf<PointType>& lambdaV) -> ScalarOf<PointType>
53{
54 using Scalar = ScalarOf<PointType>;
55 using Vector = typename PointType::PlainObject;
56
57 constexpr Scalar ZERO = static_cast<Scalar>(0);
58 constexpr Scalar ONE = static_cast<Scalar>(1);
59 constexpr Scalar EPS = std::numeric_limits<Scalar>::epsilon(); // is this the best choice?
60
61 // Expose these in the function signature if we need to support unbounded lines as well
62 constexpr bool is_lineU = false;
63 constexpr bool is_lineV = false;
64
65 Vector d1 = U1 - U0; // Direction vector of segment U
66 Vector d2 = V1 - V0; // Direction vector of segment V
67 Vector r = U0 - V0;
68 Scalar a = d1.squaredNorm(); // Squared length of segment U, always nonnegative
69 Scalar e = d2.squaredNorm(); // Squared length of segment V, always nonnegative
70 Scalar f = d2.dot(r);
71
72 // Check if either or both segments degenerate into points
73 if (std::abs(a) < EPS) {
74 if (std::abs(e) < EPS) {
75 // Both segments degenerate into points
76 lambdaU = lambdaV = 0;
77 closest_pointU = U0;
78 closest_pointV = V0;
79 return (closest_pointU - closest_pointV).dot(closest_pointU - closest_pointV);
80 } else {
81 // First segment degenerates into a point
82 lambdaU = 0;
83 lambdaV = f / e; // lambdaU = 0 => lambdaV = (b*lambdaU + f) / e = f / e
84
85 if (!is_lineV) lambdaV = std::clamp(lambdaV, ZERO, ONE);
86 }
87 } else {
88 Scalar c = d1.dot(r);
89 if (std::abs(e) < EPS) {
90 // Second segment degenerates into a point
91 lambdaV = 0;
92 lambdaU = -c / a;
93
94 if (!is_lineU) // lambdaV = 0 => lambdaU = (b*lambdaV - c) / a = -c / a
95 lambdaU = std::clamp(lambdaU, ZERO, ONE);
96 } else {
97 // The general nondegenerate case starts here
98 Scalar b = d1.dot(d2);
99 Scalar denom = a * e - b * b; // Always nonnegative
100
101 // If segments not parallel, compute closest point on L1 to L2, and clamp to segment U.
102 // Else pick arbitrary lambdaU (here 0)
103 if (std::abs(denom) >= EPS) {
104 lambdaU = (b * f - c * e) / denom;
105
106 if (!is_lineU) lambdaU = std::clamp(lambdaU, ZERO, ONE);
107 } else
108 lambdaU = 0;
109
110 // Compute point on L2 closest to U(lambdaU) using:
111 // lambdaV = Dot((P1+D1*lambdaU)-P2,D2) / Dot(D2,D2) = (b*lambdaU + f) / e
112 lambdaV = (b * lambdaU + f) / e;
113
114 if (!is_lineV) {
115 // If lambdaV in [0,1] done. Else clamp lambdaV, recompute lambdaU for the new value
116 // of lambdaV using:
117 // lambdaU = Dot((P2+D2*lambdaV)-P1,D1) / Dot(D1,D1)= (lambdaV*b - c) / a
118 // and clamp lambdaU to [0, 1]
119 if (lambdaV < 0) {
120 lambdaV = 0;
121 lambdaU = -c / a;
122
123 if (!is_lineU) lambdaU = std::clamp(lambdaU, ZERO, ONE);
124 } else if (lambdaV > 1) {
125 lambdaV = 1;
126 lambdaU = (b - c) / a;
127
128 if (!is_lineU) lambdaU = std::clamp(lambdaU, ZERO, ONE);
129 }
130 }
131 }
132 }
133
134 closest_pointU = U0 + lambdaU * d1;
135 closest_pointV = V0 + lambdaV * d2;
136 return (closest_pointU - closest_pointV).squaredNorm();
137}
138
139} // namespace lagrange
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Type alias for one-dimensional column Eigen vectors.
Definition: views.h:79
Main namespace for Lagrange.
Definition: AABBIGL.h:30
auto segment_segment_squared_distance(const Eigen::MatrixBase< PointType > &U0, const Eigen::MatrixBase< PointType > &U1, const Eigen::MatrixBase< PointType > &V0, const Eigen::MatrixBase< PointType > &V1, Eigen::PlainObjectBase< PointType > &closest_pointU, Eigen::PlainObjectBase< PointType > &closest_pointV, ScalarOf< PointType > &lambdaU, ScalarOf< PointType > &lambdaV) -> ScalarOf< PointType >
Computes the squared distance between two N-d line segments, and the closest pair of points whose sep...
Definition: segment_segment_squared_distance.h:44