-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmpr_incremental_penetration.hpp
338 lines (303 loc) · 12.9 KB
/
mpr_incremental_penetration.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//
// Created by mech-mind_gw on 3/28/2022.
//
#pragma once
namespace fcl {
namespace cvx_collide {
template <typename T>
void computeExploredDistanceLowerUpperBound(
const Vector3<T>& ray_direction, const Vector3<T>& v1_arg,
const Vector3<T>& v2_arg, const Vector3<T>& v3_arg,
const Vector3<T>& v123_normal_arg, T o_to_v1_dot_v123_normal,
T o_to_v4_dot_v123_normal, T& lower_bound, T& upper_bound) {
// The input must be a UNIT direction
const Vector3<T>& d = ray_direction;
assert(std::abs(d.norm() - T(1.)) < T(1e-3));
assert(std::abs(v123_normal_arg.norm() - T(1.)) < T(1e-3));
const T v123_normal_dot_d = d.dot(v123_normal_arg);
if (std::abs(v123_normal_dot_d) <= 0) {
const T o_to_v1_dot_d = v1_arg.dot(d);
const T o_to_v2_dot_d = v2_arg.dot(d);
const T o_to_v3_dot_d = v3_arg.dot(d);
lower_bound =
std::max(o_to_v1_dot_d, std::max(o_to_v2_dot_d, o_to_v3_dot_d));
upper_bound = lower_bound;
return;
}
// The case we can make a division
lower_bound = o_to_v1_dot_v123_normal / v123_normal_dot_d;
upper_bound = o_to_v4_dot_v123_normal / v123_normal_dot_d;
// assert(upper_bound >= lower_bound);
}
// The incremental minimum penetration query
template <typename T>
typename MPR<T>::IncrementalMinDistanceSubroutineStatus
MPR<T>::incrementalMinimumDistanceExploreDirection(
const MinkowskiDiff<T>& shape, const Vector3<T>& direction_to_explore,
// Both input/output
Vector3<T>& v1, Vector3<T>& v2, Vector3<T>& v3,
Vector3<T>& v1_dir_in_support, Vector3<T>& v2_dir_in_support,
Vector3<T>& v3_dir_in_support,
// Output
T& distance_on_direction_lb, T& distance_on_direction_ub,
Vector3<T>& new_direction,
// Parameters
bool v123_valid, int max_iterations, T tolerance) {
using std::swap;
const Vector3<T>& d = direction_to_explore;
// v0_to_O is the direction. This v0 is actually a mock
// It may NOT lie in the MinkowskiDiff shape
const Vector3<T>& v0 = -d;
// Compute the upper bound
Vector3<T> init_support_dir = d;
Vector3<T> init_support = computeShapeSupport(shape, init_support_dir);
const T distance_ub_from_init_supporting = d.dot(init_support);
// No intersect case
if (distance_ub_from_init_supporting < 0) {
return IncrementalMinDistanceSubroutineStatus::FailedNoIntersect;
}
// Do we need to re-init v123
bool reinit_v123 = !v123_valid;
// Init v1, v2 and v3
if (reinit_v123) {
v1_dir_in_support = d;
v1 = init_support;
v2_dir_in_support = v0.cross(v1);
// o_to_v0 and o_to_v1 can be co-linear, check it
// Note that v0 MUST have norm one
if (computeAbsNorm(v2_dir_in_support) <= computeAbsNorm(v1) * tolerance) {
// Refer to the note in RunIntersect
distance_on_direction_lb = v1.dot(d);
distance_on_direction_ub = distance_on_direction_lb;
new_direction = d;
return IncrementalMinDistanceSubroutineStatus::Degenerated;
}
v2 = computeShapeSupport(shape, v2_dir_in_support);
if (v2_dir_in_support.dot(v2) < 0) {
return IncrementalMinDistanceSubroutineStatus::FailedNoIntersect;
}
// it is better to form portal faces to be oriented "outside" origin
// Here O must be an interior point in penetration query
v3_dir_in_support = v1.cross(v2);
if (v3_dir_in_support.dot(v0) > 0) {
// swap v1/v2
swap(v1, v2);
swap(v1_dir_in_support, v2_dir_in_support);
v3_dir_in_support *= -1;
}
v3 = computeShapeSupport(shape, v3_dir_in_support);
if (v3_dir_in_support.dot(v3) < 0) {
return IncrementalMinDistanceSubroutineStatus::FailedNoIntersect;
}
}
// Scale the v0 to the max of v1/v2/v3
Vector3<T> v0_scaled = v0;
{
const T v1_squared_norm = v1.squaredNorm();
const T v2_squared_norm = v2.squaredNorm();
const T v3_squared_norm = v3.squaredNorm();
const T max_squared_norm =
std::max(v1_squared_norm, std::max(v2_squared_norm, v3_squared_norm));
v0_scaled *= std::sqrt(max_squared_norm);
}
// The loop to find the portal
std::array<Vector3<T>*, 3> v1_v2_v3_dir_in_support{
&v1_dir_in_support, &v2_dir_in_support, &v3_dir_in_support};
auto find_portal_status = findPortal(shape, v0_scaled, v1, v2, v3,
v1_v2_v3_dir_in_support, max_iterations);
if (find_portal_status == FindPortalStatus::IterationLimit) {
return IncrementalMinDistanceSubroutineStatus::Failed;
} else if (find_portal_status == FindPortalStatus::DetectSeperated) {
return IncrementalMinDistanceSubroutineStatus::FailedNoIntersect;
} else {
assert(find_portal_status == FindPortalStatus::PortalFound);
}
// Portal refinement
int portal_refinement_iteration = 0;
while (portal_refinement_iteration < max_iterations) {
// Update iteration data
portal_refinement_iteration += 1;
// Compute the normal
// The v123_normal must be oriented in the same side with O
Vector3<T> v123_normal = (v2 - v1).cross(v3 - v1);
if (v123_normal.dot(d) < 0) {
swap(v2, v3);
swap(v2_dir_in_support, v3_dir_in_support);
v123_normal *= -1;
}
// A new point v4 on that direction
// v123_normal would be normalized in computeShapeSupport
const Vector3<T> v4 = computeShapeSupport(shape, v123_normal);
assert(std::abs(v123_normal.norm() - T(1.)) < T(1e-3));
const T distance_ub_on_new_supporting_plane = v4.dot(v123_normal);
if (distance_ub_on_new_supporting_plane < 0) {
return IncrementalMinDistanceSubroutineStatus::FailedNoIntersect;
}
// Compute some distance
// const Vector3<T> v1_to_v4 = v4 - v1;
// const T v1_v4_distance_on_n123 = v1_to_v4.dot(v123_normal);
const T v1_dot_v123_normal = v1.dot(v123_normal);
const T v4_dot_v123_normal = v4.dot(v123_normal);
const T v1_v4_distance_on_n123 = v4_dot_v123_normal - v1_dot_v123_normal;
computeExploredDistanceLowerUpperBound(
d, v1, v2, v3, v123_normal, v1_dot_v123_normal, v4_dot_v123_normal,
distance_on_direction_lb, distance_on_direction_ub);
// Compute the shortcut
if (distance_on_direction_lb >
distance_ub_on_new_supporting_plane + tolerance) {
new_direction = v123_normal;
return IncrementalMinDistanceSubroutineStatus::NewDirection;
}
// Separation plane very close to the new (candidate) portal
// Note that v123_normal is normalized
// We must should stop the subroutine
if (std::abs(v1_v4_distance_on_n123) <= tolerance) {
new_direction = v123_normal;
return IncrementalMinDistanceSubroutineStatus::SubroutineConverge;
}
// Update the portal
updatePortal(v0_scaled, v4, v123_normal, v1, v2, v3,
v1_v2_v3_dir_in_support);
}
// Failure
return IncrementalMinDistanceSubroutineStatus::Failed;
}
template <typename T>
typename MPR<T>::IncrementalPenetrationStatus
MPR<T>::RunIncrementalMinimumPenetrationDistance(
const MinkowskiDiff<T>& shape, const Vector3<T>& init_direction,
IncrementalMinimumPenetrationData& refine_data, int max_iteration,
T tolerance, bool return_on_subroutine_converge) {
// The local variable
Vector3<T> d = init_direction;
assert(std::abs(d.norm() - T(1.0)) <= T(1e-3));
Vector3<T> v1, v2, v3;
Vector3<T> v1_dir_in_support, v2_dir_in_support, v3_dir_in_support;
// The main loop
Vector3<T> prev_direction = init_direction;
T prev_distance_lb{-1};
T prev_distance_ub{-1};
for (auto outer_iteration = 0; outer_iteration < max_iteration;
outer_iteration++) {
// Invoke subroutine
Vector3<T> new_d;
T distance_on_d_lb{0};
T distance_on_d_ub{0};
const bool portal_valid = (outer_iteration >= 1);
auto subroutine_status = incrementalMinimumDistanceExploreDirection(
shape, d, v1, v2, v3, v1_dir_in_support, v2_dir_in_support,
v3_dir_in_support, distance_on_d_lb, distance_on_d_ub, new_d,
portal_valid, max_iteration, tolerance);
// Update the output
switch (subroutine_status) {
case IncrementalMinDistanceSubroutineStatus::NewDirection: {
d = new_d;
break;
}
case IncrementalMinDistanceSubroutineStatus::SubroutineConverge: {
// Directly return without further checking.
if (return_on_subroutine_converge) {
finalizeIncrementalPenetrationResult(
shape, d, v1, v2, v3, v1_dir_in_support, v2_dir_in_support,
v3_dir_in_support, new_d, distance_on_d_lb, distance_on_d_ub,
refine_data);
return IncrementalPenetrationStatus::OK;
}
// Prev-data is valid, use that to check the convergence
if (outer_iteration >= 1) {
assert(prev_distance_lb >= 0);
assert(prev_distance_ub >= 0);
if (std::abs(distance_on_d_ub - prev_distance_ub) <= tolerance) {
// Done as not much improvement
finalizeIncrementalPenetrationResult(
shape, d, v1, v2, v3, v1_dir_in_support, v2_dir_in_support,
v3_dir_in_support, new_d, distance_on_d_lb, distance_on_d_ub,
refine_data);
return IncrementalPenetrationStatus::OK;
}
}
// Check direction
constexpr bool check_direction_termination = true;
constexpr T direction_tolerance = T(1e-3);
if (check_direction_termination &&
((new_d - prev_direction).norm() < direction_tolerance)) {
finalizeIncrementalPenetrationResult(
shape, d, v1, v2, v3, v1_dir_in_support, v2_dir_in_support,
v3_dir_in_support, new_d, distance_on_d_lb, distance_on_d_ub,
refine_data);
return IncrementalPenetrationStatus::OK;
}
// Move to next, as the subroutine converge
// The portal can be rather small here
// portal_valid = false;
d = new_d;
break;
}
case IncrementalMinDistanceSubroutineStatus::Degenerated: {
// Done on degenerate
refine_data.minimum_penetration = distance_on_d_ub;
refine_data.penetration_direction = new_d;
refine_data.p0_in_shape0_frame = shape.support0(new_d);
refine_data.p1_in_shape0_frame = shape.support1(-new_d);
return IncrementalPenetrationStatus::OK;
}
case IncrementalMinDistanceSubroutineStatus::FailedNoIntersect: {
return IncrementalPenetrationStatus::FailedNoIntersect;
}
default:
return IncrementalPenetrationStatus::Failed;
}
// Move to next iteration, book keeping
prev_direction = new_d;
prev_distance_lb = distance_on_d_lb;
prev_distance_ub = distance_on_d_ub;
}
// Too many iterations
finalizeIncrementalPenetrationResult(shape, d, v1, v2, v3, v1_dir_in_support,
v2_dir_in_support, v3_dir_in_support,
prev_direction, prev_distance_lb,
prev_distance_ub, refine_data);
return IncrementalPenetrationStatus::IterationLimit;
}
template <typename T>
typename MPR<T>::IncrementalPenetrationStatus
MPR<T>::IncrementalMinimumPenetrationDistance(
const MinkowskiDiff<T>& shape, const Vector3<T>& init_direction,
IncrementalMinimumPenetrationData& refine_data,
bool return_on_subroutine_converge) const {
return RunIncrementalMinimumPenetrationDistance(
shape, init_direction, refine_data, max_iterations, tolerance,
return_on_subroutine_converge);
}
template <typename T>
void MPR<T>::finalizeIncrementalPenetrationResult(
const MinkowskiDiff<T>& shape, const Vector3<T>& ray_direction,
const Vector3<T>& v1, const Vector3<T>& v2, const Vector3<T>& v3,
const Vector3<T>& v1_dir, const Vector3<T>& v2_dir,
const Vector3<T>& v3_dir, const Vector3<T>& v123_normal_arg, T distance_lb,
T distance_ub,
typename cvx_collide::MPR<T>::IncrementalMinimumPenetrationData&
penetration_data) {
const Vector3<T>& d = ray_direction;
penetration_data.minimum_penetration = distance_ub;
penetration_data.penetration_direction = d;
// Point in triangle distance is just lb
(void)(v123_normal_arg);
assert(std::abs(d.dot(v123_normal_arg)) > 0);
Vector3<T> o_projected = distance_lb * d;
const Vector3<T> v1_to_v2 = v2 - v1;
const Vector3<T> v1_to_v3 = v3 - v1;
const Vector3<T> s1s2s3_plane_normal = v1_to_v2.cross(v1_to_v3);
const T area = s1s2s3_plane_normal.norm();
const T s2_weight = (v1_to_v3.cross(v1 - o_projected)).norm() / area;
const T s3_weight = (v1_to_v2.cross(v1 - o_projected)).norm() / area;
const T s1_weight = T(1.0) - s2_weight - s3_weight;
penetration_data.p0_in_shape0_frame = shape.support0(v1_dir) * s1_weight +
shape.support0(v2_dir) * s2_weight +
shape.support0(v3_dir) * s3_weight;
penetration_data.p1_in_shape0_frame = shape.support1(-v1_dir) * s1_weight +
shape.support1(-v2_dir) * s2_weight +
shape.support1(-v3_dir) * s3_weight;
}
} // namespace cvx_collide
} // namespace fcl