diff --git a/crates/oa-core/examples/auto_layout_probe/condensed/boundary.rs b/crates/oa-core/examples/auto_layout_probe/condensed/boundary.rs index 6952bed2..0e60ca01 100644 --- a/crates/oa-core/examples/auto_layout_probe/condensed/boundary.rs +++ b/crates/oa-core/examples/auto_layout_probe/condensed/boundary.rs @@ -70,10 +70,12 @@ fn center( Ok(center) } -fn translate(value: f32, delta: f32) -> Result { - 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 { + // 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); } }