From fff2ecd76b675a91b94213832d5ac9859d9e499b Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 7 Nov 2026 19:45:38 -0800 Subject: [PATCH] Symbols: Reset Transformation tests cover a mixed selection and negative stored sizes An instance in perspective selected with a turned instance stays as it was while the other is reset and returned in one undo step. Stored natural sizes that are negative on either side give the art its own size, unmirrored, as a zero size does. --- crates/engine/src/tests/brushsym.rs | 46 ++++++++++++++++++----------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/crates/engine/src/tests/brushsym.rs b/crates/engine/src/tests/brushsym.rs index 46daae352..638b7a1f4 100644 --- a/crates/engine/src/tests/brushsym.rs +++ b/crates/engine/src/tests/brushsym.rs @@ -1422,25 +1422,29 @@ fn reset_transformation_keeps_the_center_of_art_away_from_the_origin() { assert!((after.width() - 20.0).abs() < 1e-9 && (after.height() - 20.0).abs() < 1e-9, "{after:?}"); } -/// A stored natural size that is not positive (from a damaged file) does not flatten the instance: -/// Reset Transformation gives it the art's own size, as drawing the symbol does. +/// A stored natural size that is not positive (from a damaged file: zero or negative on either +/// side) does not flatten or mirror the instance: Reset Transformation gives it the art's own +/// size, as drawing the symbol does. #[test] fn reset_transformation_ignores_a_stored_size_that_is_not_positive() { - let mut s = session(); - let r = rect(&mut s, 0.0, 0.0, 20.0, 20.0); - let art = node(&s, r); - s.execute("edit.clear", &json!({"ids": [r.0]})).unwrap(); - let st = s.doc_mut().unwrap(); - let d = std::sync::Arc::make_mut(&mut st.doc); - d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Flat".into(), art: std::sync::Arc::new(art) }); - d.unknown.insert(vectorcraft_doc::SYMBOL_SIZES.to_string(), json!({"Flat": [0.0, 20.0]})); - let layer = d.layers[0].id; - let id = d.alloc_id(); - let xf = vectorcraft_geom::Affine::translate((100.0, 100.0)) * vectorcraft_geom::Affine::rotate(0.5); - d.insert(Some(layer), usize::MAX, Node::new(id, NodeKind::SymbolInstance { symbol: "Flat".into(), xf })).unwrap(); - assert_eq!(s.execute("symbol.instance.resetTransform", &json!({"ids": [id.0]})).unwrap()["ids"], json!([id.0])); - let after = s.doc().unwrap().doc.bounds_of(&[id], false).unwrap(); - assert!((after.width() - 20.0).abs() < 1e-9 && (after.height() - 20.0).abs() < 1e-9, "{after:?}"); + for size in [json!([0.0, 20.0]), json!([-20.0, 20.0]), json!([20.0, -5.0]), json!([-20.0, -20.0])] { + let mut s = session(); + let r = rect(&mut s, 0.0, 0.0, 20.0, 20.0); + let art = node(&s, r); + s.execute("edit.clear", &json!({"ids": [r.0]})).unwrap(); + let st = s.doc_mut().unwrap(); + let d = std::sync::Arc::make_mut(&mut st.doc); + d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Flat".into(), art: std::sync::Arc::new(art) }); + d.unknown.insert(vectorcraft_doc::SYMBOL_SIZES.to_string(), json!({ "Flat": size })); + let layer = d.layers[0].id; + let id = d.alloc_id(); + let xf = vectorcraft_geom::Affine::translate((100.0, 100.0)) * vectorcraft_geom::Affine::rotate(0.5); + d.insert(Some(layer), usize::MAX, Node::new(id, NodeKind::SymbolInstance { symbol: "Flat".into(), xf })).unwrap(); + assert_eq!(s.execute("symbol.instance.resetTransform", &json!({"ids": [id.0]})).unwrap()["ids"], json!([id.0]), "{size}"); + let after = s.doc().unwrap().doc.bounds_of(&[id], false).unwrap(); + assert!((after.width() - 20.0).abs() < 1e-9 && (after.height() - 20.0).abs() < 1e-9, "{size}: {after:?}"); + assert!(close_xf(xf_of(&s, id).with_translation(vectorcraft_geom::Vec2::ZERO), vectorcraft_geom::Affine::IDENTITY), "{size}: not mirrored"); + } } /// Reset Transformation adds no undo step for an instance already at its symbol's scale or for @@ -1473,6 +1477,14 @@ fn reset_transformation_leaves_unchanged_instances_and_refuses_bad_ids() { let before = undo_len(&s); assert_eq!(s.execute("symbol.instance.resetTransform", &json!({"ids": [projected.0]})).unwrap()["ids"], json!([])); assert!(undo_len(&s) == before && close_xf(xf_of(&s, projected), xf), "an instance in perspective stays as it was"); + // Selected with a turned instance, the instance in perspective still stays as it was, and only + // the other one is reset and returned, in one undo step. + select(&mut s, &[inst, projected]); + assert_eq!(s.execute("symbol.instance.resetTransform", &json!({})).unwrap()["ids"], json!([inst.0])); + assert_eq!(undo_len(&s), before + 1, "one undo step"); + assert!(close_xf(xf_of(&s, projected), xf), "the instance in perspective stays as it was"); + let reset = xf_of(&s, inst).with_translation(vectorcraft_geom::Vec2::ZERO); + assert!(close_xf(reset, s.doc().unwrap().doc.symbol_natural_xf(&name)), "the turned instance is reset: {reset:?}"); } /// New Symbol and Duplicate Symbol trim the name they are given, as Symbol Options trims