Mirror the rotation when flipping a rotated shape

Found while testing crates/engine/src/cmd/shape.rs:400-412 (flip; used by arrange.flipHorizontal / arrange.flipVertical)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-18 12:17:00 +02:00
co-authored by Claude Haiku 5.5
parent 22db9261f9
commit 3f973b0d87
2 changed files with 33 additions and 0 deletions
+2
View File
@@ -406,6 +406,8 @@ fn flip(s: &mut Session, p: &Value) -> Result<Value> {
} else {
x.flip_h = !x.flip_h;
}
// `Xfrm::affine` flips first and rotates second; a true mirror also negates the rotation.
x.rot = (360.0 - x.rot_norm()) % 360.0;
}
Ok(())
})
+31
View File
@@ -3623,3 +3623,34 @@ fn table_column_width_of_a_huge_column_is_ignored() {
assert_eq!(table_of(&s, id).cols, vec![100.0, 100.0], "no column changes");
}
}
#[test]
fn flipping_a_rotated_shape_mirrors_its_outline() {
let corners = |s: &Session, id: deckcraft_model::ShapeId| {
let x = s.doc().unwrap().shape(id).unwrap().xfrm.unwrap();
let a = x.affine();
[(0.0, 0.0), (x.w, 0.0), (x.w, x.h), (0.0, x.h)].map(|(px, py)| {
let p = a * deckcraft_geom::Point::new(px, py);
((p.x * 100.0).round() as i64, (p.y * 100.0).round() as i64)
})
};
let sorted = |mut c: [(i64, i64); 4]| {
c.sort_unstable();
c
};
for (cmd, horizontal) in [("arrange.flipHorizontal", true), ("arrange.flipVertical", false)] {
let mut s = session();
let r = s.execute("shape.insert", &json!({"preset": "rect", "rect": [100, 100, 200, 50]})).unwrap();
let id = deckcraft_model::ShapeId(r["id"].as_u64().unwrap() as u32);
s.execute("shape.rotate", &json!({"deg": 30})).unwrap();
let before = corners(&s, id);
// Mirror about the box's centre line (centre is 200, 125).
let mirrored = before.map(|(x, y)| if horizontal { (40000 - x, y) } else { (x, 25000 - y) });
s.execute(cmd, &json!({})).unwrap();
assert_eq!(sorted(corners(&s, id)), sorted(mirrored), "{cmd} of a 30° rotated rectangle is not its mirror image");
let rot = s.doc().unwrap().shape(id).unwrap().xfrm.unwrap().rot;
assert!((rot - 330.0).abs() < 1e-9, "{cmd} negates the rotation, got {rot}");
s.execute(cmd, &json!({})).unwrap();
assert_eq!(sorted(corners(&s, id)), sorted(before), "{cmd} twice restores the outline");
}
}