Bound complete member displacement across large origins
This commit is contained in:
@@ -70,10 +70,12 @@ fn center(
|
|||||||
Ok(center)
|
Ok(center)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate(value: f32, delta: f32) -> Result<f32, ModelError> {
|
fn translate(value: f32, old_origin: f32, new_origin: f32) -> Result<f32, ModelError> {
|
||||||
let next = value + delta;
|
// Bound the complete displacement, including origin subtraction. A rounded
|
||||||
if !next.is_finite() || ((f64::from(next) - f64::from(value)) - f64::from(delta)).abs() > 0.001
|
// 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"));
|
return Err(error("boundary member translation precision exceeded"));
|
||||||
}
|
}
|
||||||
Ok(next)
|
Ok(next)
|
||||||
@@ -355,18 +357,17 @@ pub(super) fn reorder(
|
|||||||
for (&c, y) in after.iter().zip(ys) {
|
for (&c, y) in after.iter().zip(ys) {
|
||||||
charge(work, groups[g].blocks[c].members.len() + 1)?;
|
charge(work, groups[g].blocks[c].members.len() + 1)?;
|
||||||
let old = groups[g].blocks[c].position.y;
|
let old = groups[g].blocks[c].position.y;
|
||||||
let delta = y - old;
|
if !y.is_finite() {
|
||||||
if !y.is_finite() || !delta.is_finite() {
|
|
||||||
return Err(error("boundary packing overflow"));
|
return Err(error("boundary packing overflow"));
|
||||||
}
|
}
|
||||||
if delta != 0.0 {
|
if y != old {
|
||||||
changed.push(c);
|
changed.push(c);
|
||||||
}
|
}
|
||||||
for id in &groups[g].blocks[c].members {
|
for id in &groups[g].blocks[c].members {
|
||||||
let p = offsets
|
let p = offsets
|
||||||
.get_mut(id)
|
.get_mut(id)
|
||||||
.ok_or_else(|| error("boundary lost member offset"))?;
|
.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;
|
groups[g].blocks[c].position.y = y;
|
||||||
}
|
}
|
||||||
@@ -666,7 +667,20 @@ mod tests {
|
|||||||
center(id(3), &parents, &offsets, &sizes, &mut 0).unwrap(),
|
center(id(3), &parents, &offsets, &sizes, &mut 0).unwrap(),
|
||||||
45.1015625
|
45.1015625
|
||||||
);
|
);
|
||||||
assert_eq!(translate(10.125, 0.375).unwrap(), 10.5);
|
assert_eq!(translate(10.125, 0.0, 0.375).unwrap(), 10.5);
|
||||||
assert!(translate(32768.0, 0.002).is_err());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user