Lagrange
Loading...
Searching...
No Matches
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
25
48template <typename PointType>
50 const Eigen::MatrixBase<PointType>& U0,
51 const Eigen::MatrixBase<PointType>& U1,
52 const Eigen::MatrixBase<PointType>& V0,
53 const Eigen::MatrixBase<PointType>& V1,
54 Eigen::PlainObjectBase<PointType>& closest_pointU,
55 Eigen::PlainObjectBase<PointType>& closest_pointV,
56 ScalarOf<PointType>& lambdaU,
57 ScalarOf<PointType>& lambdaV) -> ScalarOf<PointType>
58{
59 using Scalar = ScalarOf<PointType>;
60 using Vector = typename PointType::PlainObject;
61
62 constexpr Scalar ZERO = static_cast<Scalar>(0);
63 constexpr Scalar ONE = static_cast<Scalar>(1);
64 constexpr Scalar EPS = std::numeric_limits<Scalar>::epsilon(); // is this the best choice?
65
66 // Expose these in the function signature if we need to support unbounded lines as well
67 constexpr bool is_lineU = false;
68 constexpr bool is_lineV = false;
69
70 Vector d1 = U1 - U0; // Direction vector of segment U
71 Vector d2 = V1 - V0; // Direction vector of segment V
72 Vector r = U0 - V0;
73 Scalar a = d1.squaredNorm(); // Squared length of segment U, always nonnegative
74 Scalar e = d2.squaredNorm(); // Squared length of segment V, always nonnegative
75 Scalar f = d2.dot(r);
76
77 // Check if either or both segments degenerate into points
78 if (std::abs(a) < EPS) {
79 if (std::abs(e) < EPS) {
80 // Both segments degenerate into points
81 lambdaU = lambdaV = 0;
82 closest_pointU = U0;
83 closest_pointV = V0;
84 return (closest_pointU - closest_pointV).dot(closest_pointU - closest_pointV);
85 } else {
86 // First segment degenerates into a point
87 lambdaU = 0;
88 lambdaV = f / e; // lambdaU = 0 => lambdaV = (b*lambdaU + f) / e = f / e
89
90 if (!is_lineV) lambdaV = std::clamp(lambdaV, ZERO, ONE);
91 }
92 } else {
93 Scalar c = d1.dot(r);
94 if (std::abs(e) < EPS) {
95 // Second segment degenerates into a point
96 lambdaV = 0;
97 lambdaU = -c / a;
98
99 if (!is_lineU) // lambdaV = 0 => lambdaU = (b*lambdaV - c) / a = -c / a
100 lambdaU = std::clamp(lambdaU, ZERO, ONE);
101 } else {
102 // The general nondegenerate case starts here
103 Scalar b = d1.dot(d2);
104 Scalar denom = a * e - b * b; // Always nonnegative
105
106 // If segments not parallel, compute closest point on L1 to L2, and clamp to segment U.
107 // Else pick arbitrary lambdaU (here 0)
108 if (std::abs(denom) >= EPS) {
109 lambdaU = (b * f - c * e) / denom;
110
111 if (!is_lineU) lambdaU = std::clamp(lambdaU, ZERO, ONE);
112 } else
113 lambdaU = 0;
114
115 // Compute point on L2 closest to U(lambdaU) using:
116 // lambdaV = Dot((P1+D1*lambdaU)-P2,D2) / Dot(D2,D2) = (b*lambdaU + f) / e
117 lambdaV = (b * lambdaU + f) / e;
118
119 if (!is_lineV) {
120 // If lambdaV in [0,1] done. Else clamp lambdaV, recompute lambdaU for the new value
121 // of lambdaV using:
122 // lambdaU = Dot((P2+D2*lambdaV)-P1,D1) / Dot(D1,D1)= (lambdaV*b - c) / a
123 // and clamp lambdaU to [0, 1]
124 if (lambdaV < 0) {
125 lambdaV = 0;
126 lambdaU = -c / a;
127
128 if (!is_lineU) lambdaU = std::clamp(lambdaU, ZERO, ONE);
129 } else if (lambdaV > 1) {
130 lambdaV = 1;
131 lambdaU = (b - c) / a;
132
133 if (!is_lineU) lambdaU = std::clamp(lambdaU, ZERO, ONE);
134 }
135 }
136 }
137 }
138
139 closest_pointU = U0 + lambdaU * d1;
140 closest_pointV = V0 + lambdaV * d2;
141 return (closest_pointU - closest_pointV).squaredNorm();
142}
143
145
146} // namespace lagrange
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
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:49
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Type alias for one-dimensional column Eigen vectors.
Definition views.h:79
Main namespace for Lagrange.