Moving a body that a boolean made no longer hangs: a boolean between a cylinder and a tilted plane (an elliptical edge) froze the app at 100 % CPU, because its retries move both bodies and the result back
Moving a body transforms its intersection-curve edges, and truck-geometry 0.5.0 scales each curve's tolerance by the operator norm of the matrix. matext4cgmath 0.1.0 computes it from eigenvalues through a quartic whose Newton refinement loops while the residual is above a tolerance and never gives up. For a translation matrix all four eigenvalues are 1, and near that quadruple root the iteration can cycle forever (about one translation in eight of the boolean retries' shifts, by a scan of sizes 0.1-2000 mm). matext4cgmath is vendored with both Newton loops stopped after 100 steps (vendor/README.md, ATTRIBUTION.md). truck master no longer computes the norm in transform_by, so the vendored copy can go once a truck release after 0.5.0 is adopted. Tests: moving_a_body_with_an_intersection_curve_edge_ends (kernel) cuts a cylinder with a tilted box and moves the result by one of the shifts that hung (hangs on main). examples/regressions/oblique_cut_of_cylinder_hang.json is the variant of the oblique cut that hung the app; it hangs on main, and since the previous commit it passes without reaching the shifted retries, so it is replayed by the engine test as a scenario only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f3a689f284
commit
5249ae84ef
14 files changed
+920
-3
No files matched your search
+106
@@ -0,0 +1,106 @@
|
||||
use crate::*;
|
||||
|
||||
impl<F: BaseFloat> Exponential for Matrix2<F> {}
|
||||
impl<F: BaseFloat> Exponential for Matrix3<F> {}
|
||||
impl<F: BaseFloat> Exponential for Matrix4<F> {}
|
||||
|
||||
impl<F: BaseFloat> Decomposition for Matrix2<F> {
|
||||
fn iwasawa_decomposition(self) -> Option<(Self, Self, Self)> {
|
||||
let v0 = self[0];
|
||||
let mag0 = dot(v0, v0);
|
||||
if mag0 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let n0 = dot(self[1], v0) / mag0;
|
||||
let v1 = self[1] - v0 * n0;
|
||||
let mag1 = dot(v1, v1);
|
||||
if mag1 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let (a0, a1) = (F::sqrt(mag0), F::sqrt(mag1));
|
||||
Some((
|
||||
Matrix2::from_cols(v0 / a0, v1 / a1),
|
||||
Matrix2::from_diagonal(Vector2::new(a0, a1)),
|
||||
Matrix2::new(F::one(), F::zero(), n0, F::one()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: BaseFloat> Decomposition for Matrix3<F> {
|
||||
fn iwasawa_decomposition(self) -> Option<(Self, Self, Self)> {
|
||||
let v0 = self[0];
|
||||
let mag0 = dot(v0, v0);
|
||||
if mag0 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let n0 = dot(self[1], v0) / mag0;
|
||||
let v1 = self[1] - v0 * n0;
|
||||
let mag1 = dot(v1, v1);
|
||||
if mag1 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let n1 = dot(self[2], v0) / mag0;
|
||||
let n2 = dot(self[2], v1) / mag1;
|
||||
let v2 = self[2] - v0 * n1 - v1 * n2;
|
||||
let mag2 = dot(v2, v2);
|
||||
if mag2 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let (a0, a1, a2) = (F::sqrt(mag0), F::sqrt(mag1), F::sqrt(mag2));
|
||||
#[rustfmt::skip]
|
||||
let res = Some((
|
||||
Matrix3::from_cols(v0 / a0, v1 / a1, v2 / a2),
|
||||
Matrix3::from_diagonal(Vector3::new(a0, a1, a2)),
|
||||
Matrix3::new(
|
||||
F::one(), F::zero(), F::zero(),
|
||||
n0, F::one(), F::zero(),
|
||||
n1, n2, F::one(),
|
||||
),
|
||||
));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: BaseFloat> Decomposition for Matrix4<F> {
|
||||
fn iwasawa_decomposition(self) -> Option<(Self, Self, Self)> {
|
||||
let v0 = self[0];
|
||||
let mag0 = dot(v0, v0);
|
||||
if mag0 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let n0 = dot(self[1], v0) / mag0;
|
||||
let v1 = self[1] - v0 * n0;
|
||||
let mag1 = dot(v1, v1);
|
||||
if mag1 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let n1 = dot(self[2], v0) / mag0;
|
||||
let n2 = dot(self[2], v1) / mag1;
|
||||
let v2 = self[2] - v0 * n1 - v1 * n2;
|
||||
let mag2 = dot(v2, v2);
|
||||
if mag2 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let n3 = dot(self[3], v0) / mag0;
|
||||
let n4 = dot(self[3], v1) / mag1;
|
||||
let n5 = dot(self[3], v2) / mag2;
|
||||
let v3 = self[3] - v0 * n3 - v1 * n4 - v2 * n5;
|
||||
let mag3 = dot(v3, v3);
|
||||
if mag3 <= F::zero() {
|
||||
return None;
|
||||
}
|
||||
let (a0, a1, a2, a3) = (F::sqrt(mag0), F::sqrt(mag1), F::sqrt(mag2), F::sqrt(mag3));
|
||||
#[rustfmt::skip]
|
||||
let res = Some((
|
||||
Matrix4::from_cols(v0 / a0, v1 / a1, v2 / a2, v3 / a3),
|
||||
Matrix4::from_diagonal(Vector4::new(a0, a1, a2, a3)),
|
||||
Matrix4::new(
|
||||
F::one(), F::zero(), F::zero(), F::zero(),
|
||||
n0, F::one(), F::zero(), F::zero(),
|
||||
n1, n2, F::one(), F::zero(),
|
||||
n3, n4, n5, F::one(),
|
||||
),
|
||||
));
|
||||
res
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user