Perspective: a stored projection whose determinant overflows reads back at a w row of 1

A stored projection whose determinant is not finite is read back as the
same map divided by the largest magnitude in its w row, the scale the
overflow branch of conjugated already uses, so points that overflowed at
the stored scale draw: the identity with every entry 1e308 maps (2, 2)
to (2, 2). The overflow branch of inverse scales the map so that the
largest of the products its determinant sums is near 1, so a map with
large entries and a small w row, such as a scale by 1e305 with a move
by -3.9e307, is still inverted and read back.

The two overflow tests in persp_text.rs build their stored projections
with a w row of 1, so they still overflow after the read-back.
This commit is contained in:
dotCooCoo
2026-11-01 04:55:11 +07:00
committed by bflatastic
parent d098d33188
commit bbcb886a40
3 changed files with 66 additions and 17 deletions
+9 -7
View File
@@ -119,13 +119,15 @@ fn moving_type_whose_projection_would_overflow_is_refused() {
let id = persp_text(&mut s);
let NodeKind::Text(t) = &node(&s, id).kind else { panic!("type") };
let flat = t.bounds().unwrap();
// A stored projection that draws the type flat where it is, with every entry scaled by `k`: a
// document can hold it (its entries are finite) and the type draws, but its product with a
// scale by 2 in perspective overflows.
// A stored projection that maps each point p of the type to k·(p − 0.9·q), with q its top-left
// corner: a document can hold it (its entries are finite, and its w row is already 1) and the
// type draws, but its product with a scale by 2 in perspective overflows, since its move,
// 0.9·k·q, is larger than any point it draws.
let step = grid(&s).transform_map(Plane::Right, 0.0, vectorcraft_geom::Affine::scale(2.0), 0.0).unwrap();
let q = flat.origin();
let scaled = |e: i32| {
let k = 2f64.powf(f64::from(e) / 16.0);
vectorcraft_geom::Homography([[k, 0.0, 0.0], [0.0, k, 0.0], [0.0, 0.0, k]])
vectorcraft_geom::Homography([[k, 0.0, -0.9 * k * q.x], [0.0, k, -0.9 * k * q.y], [0.0, 0.0, 1.0]])
};
let finite = |m: &vectorcraft_geom::Homography| m.to_array().iter().all(|v| v.is_finite());
let draws = |m: &vectorcraft_geom::Homography| {
@@ -180,12 +182,12 @@ fn edit_text_whose_flat_view_would_overflow_is_refused() {
let id = persp_text(&mut s);
let NodeKind::Text(t) = &node(&s, id).kind else { panic!("type") };
let flat = t.bounds().unwrap();
// A stored projection that draws the type 4 times its size, with every entry scaled by `k`: a
// document can hold it and the type draws, but the projection that shows it flat where it is
// A stored projection that draws the type `4k` times its size: a document can hold it (its w
// row is already 1) and the type draws, but the projection that shows it flat where it is
// drawn overflows.
let scaled = |e: i32| {
let k = 2f64.powf(f64::from(e) / 16.0);
vectorcraft_geom::Homography([[4.0 * k, 0.0, 0.0], [0.0, 4.0 * k, 0.0], [0.0, 0.0, k]])
vectorcraft_geom::Homography([[4.0 * k, 0.0, 0.0], [0.0, 4.0 * k, 0.0], [0.0, 0.0, 1.0]])
};
let overflows = |m: &vectorcraft_geom::Homography| {
let Some(drawn) = m.map_rect_bbox(flat) else { return false };