Bound complete member displacement across large origins

This commit is contained in:
2026-09-24 21:52:38 -07:00
parent 42f595a5d6
commit 3e21c1bca2
@@ -70,10 +70,12 @@ fn center(
Ok(center)
}
fn translate(value: f32, delta: f32) -> Result<f32, ModelError> {
let next = value + delta;
if !next.is_finite() || ((f64::from(next) - f64::from(value)) - f64::from(delta)).abs() > 0.001
{
fn translate(value: f32, old_origin: f32, new_origin: f32) -> Result<f32, ModelError> {
// Bound the complete displacement, including origin subtraction. A rounded
// f32 delta can hide lost relative precision even when its addition is exact.
let intended = f64::from(value) + (f64::from(new_origin) - f64::from(old_origin));
let next = intended as f32;
if !next.is_finite() || (f64::from(next) - intended).abs() > 0.001 {
return Err(error("boundary member translation precision exceeded"));
}
Ok(next)
@@ -355,18 +357,17 @@ pub(super) fn reorder(
for (&c, y) in after.iter().zip(ys) {
charge(work, groups[g].blocks[c].members.len() + 1)?;
let old = groups[g].blocks[c].position.y;
let delta = y - old;
if !y.is_finite() || !delta.is_finite() {
if !y.is_finite() {
return Err(error("boundary packing overflow"));
}
if delta != 0.0 {
if y != old {
changed.push(c);
}
for id in &groups[g].blocks[c].members {
let p = offsets
.get_mut(id)
.ok_or_else(|| error("boundary lost member offset"))?;
p.y = translate(p.y, delta)?;
p.y = translate(p.y, old, y)?;
}
groups[g].blocks[c].position.y = y;
}
@@ -666,7 +667,20 @@ mod tests {
center(id(3), &parents, &offsets, &sizes, &mut 0).unwrap(),
45.1015625
);
assert_eq!(translate(10.125, 0.375).unwrap(), 10.5);
assert!(translate(32768.0, 0.002).is_err());
assert_eq!(translate(10.125, 0.0, 0.375).unwrap(), 10.5);
assert!(translate(32768.0, 0.0, 0.002).is_err());
}
#[test]
fn member_offset_survives_large_origin_subtraction() {
let blocks = vec![
block(1, 0.0, 65404.0),
block(2, 65436.0, 68.0),
block(3, 65536.0, 100.0),
block(4, 65668.0, 68.002),
];
let ys = packing(&blocks, &[0, 1, 2, 3], &[3, 2, 1, 0], 32.0).unwrap();
let member = translate(65568.0, 65536.0, ys[1]).unwrap();
assert!((f64::from(member) - f64::from(ys[1]) - 32.0).abs() <= 0.001);
}
}