From 7bbdfb41f6c0f07f9b455e8f8f241d44b0fa377b Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:58:52 +0200 Subject: [PATCH 1/2] Return identity from Mat3::square_to_quad for a degenerate quad Found while testing crates/geom/src/lib.rs:252-264 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/geom/src/lib.rs | 3 +++ crates/geom/tests/square_to_quad.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 crates/geom/tests/square_to_quad.rs diff --git a/crates/geom/src/lib.rs b/crates/geom/src/lib.rs index a03c9d46c..cbc9c1072 100644 --- a/crates/geom/src/lib.rs +++ b/crates/geom/src/lib.rs @@ -261,6 +261,9 @@ impl Mat3 { return Mat3([[x1 - x0, x3 - x0, x0], [y1 - y0, y3 - y0, y0], [0.0, 0.0, 1.0]]); } let den = dx1 * dy2 - dx2 * dy1; + if den == 0.0 || !den.is_finite() { + return Mat3::IDENTITY; + } let g = (sx * dy2 - dx2 * sy) / den; let h = (dx1 * sy - sx * dy1) / den; Mat3([[x1 - x0 + g * x1, x3 - x0 + h * x3, x0], [y1 - y0 + g * y1, y3 - y0 + h * y3, y0], [g, h, 1.0]]) diff --git a/crates/geom/tests/square_to_quad.rs b/crates/geom/tests/square_to_quad.rs new file mode 100644 index 000000000..3dd409a58 --- /dev/null +++ b/crates/geom/tests/square_to_quad.rs @@ -0,0 +1,13 @@ +use effectcraft_geom::{Mat3, vec2}; + +#[test] +fn square_to_quad_degenerate_is_finite() { + let m = Mat3::square_to_quad([vec2(0.0, 5.0), vec2(0.0, 0.0), vec2(10.0, 0.0), vec2(20.0, 0.0)]); + assert!(m.0.iter().flatten().all(|v| v.is_finite()), "non-finite: {m:?}"); +} + +#[test] +fn square_to_quad_regular_is_finite() { + let m = Mat3::square_to_quad([vec2(0.0, 0.0), vec2(10.0, 0.0), vec2(8.0, 6.0), vec2(0.0, 5.0)]); + assert!(m.0.iter().flatten().all(|v| v.is_finite())); +} From 3311f02559ed79e569a4524fc79cec2c6ab4dd9a Mon Sep 17 00:00:00 2001 From: bflatastic Date: Thu, 29 Oct 2026 10:22:02 +0700 Subject: [PATCH 2/2] Geom: square_to_quad tests live in the crate's own test module Co-Authored-By: Claude Opus 5.5 --- crates/geom/src/lib.rs | 13 +++++++++++++ crates/geom/tests/square_to_quad.rs | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 crates/geom/tests/square_to_quad.rs diff --git a/crates/geom/src/lib.rs b/crates/geom/src/lib.rs index cbc9c1072..24abcfe39 100644 --- a/crates/geom/src/lib.rs +++ b/crates/geom/src/lib.rs @@ -449,6 +449,19 @@ pub fn look_at(pos: Vec3, poi: Vec3, extra: Mat4) -> Mat4 { mod tests { use super::*; + /// A quad with three collinear corners has no projective map: identity, not NaN. + #[test] + fn square_to_quad_degenerate_is_finite() { + let m = Mat3::square_to_quad([vec2(0.0, 5.0), vec2(0.0, 0.0), vec2(10.0, 0.0), vec2(20.0, 0.0)]); + assert!(m.0.iter().flatten().all(|v| v.is_finite()), "non-finite: {m:?}"); + } + + #[test] + fn square_to_quad_regular_is_finite() { + let m = Mat3::square_to_quad([vec2(0.0, 0.0), vec2(10.0, 0.0), vec2(8.0, 6.0), vec2(0.0, 5.0)]); + assert!(m.0.iter().flatten().all(|v| v.is_finite())); + } + fn close(a: Vec2, b: Vec2) -> bool { (a - b).length() < 1e-9 } diff --git a/crates/geom/tests/square_to_quad.rs b/crates/geom/tests/square_to_quad.rs deleted file mode 100644 index 3dd409a58..000000000 --- a/crates/geom/tests/square_to_quad.rs +++ /dev/null @@ -1,13 +0,0 @@ -use effectcraft_geom::{Mat3, vec2}; - -#[test] -fn square_to_quad_degenerate_is_finite() { - let m = Mat3::square_to_quad([vec2(0.0, 5.0), vec2(0.0, 0.0), vec2(10.0, 0.0), vec2(20.0, 0.0)]); - assert!(m.0.iter().flatten().all(|v| v.is_finite()), "non-finite: {m:?}"); -} - -#[test] -fn square_to_quad_regular_is_finite() { - let m = Mat3::square_to_quad([vec2(0.0, 0.0), vec2(10.0, 0.0), vec2(8.0, 6.0), vec2(0.0, 5.0)]); - assert!(m.0.iter().flatten().all(|v| v.is_finite())); -}