From 283d46c1ccdd384f425444899193dc98c8a691fa Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 7 Nov 2026 01:25:11 -0800 Subject: [PATCH] Symbols: Reset Transformation's enabled check does not measure the symbol's art --- crates/engine/src/cmd.rs | 6 +++--- crates/engine/src/cmd/brushsym.rs | 4 ++-- crates/engine/src/cmd/brushsym/symbols.rs | 26 ++++++++++++++++------- crates/ui-egui/src/panels/symbols.rs | 3 +-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/engine/src/cmd.rs b/crates/engine/src/cmd.rs index 4648b8dd1..98dfe3aaa 100644 --- a/crates/engine/src/cmd.rs +++ b/crates/engine/src/cmd.rs @@ -722,9 +722,9 @@ pub use pathops::shape_count; /// The gaps View › Show Live Paint Gaps shows on a Live Paint group. pub use buildcmds::live_paint_gaps; -/// The transform Reset Transformation gives a symbol instance (the Symbols panel menu and the -/// Control bar enable it when there is one). -pub use brushsym::reset_transformation; +/// Whether Reset Transformation changes a symbol instance (the Symbols panel menu and the +/// Control bar enable it when it changes one). +pub use brushsym::reset_transformation_changes; /// Command ids kept only so older scripts keep working: `(alias, the command it duplicates)`. /// They run as before but the command palette leaves them out (it lists the command once). diff --git a/crates/engine/src/cmd/brushsym.rs b/crates/engine/src/cmd/brushsym.rs index 9bdae569f..0fa03042c 100644 --- a/crates/engine/src/cmd/brushsym.rs +++ b/crates/engine/src/cmd/brushsym.rs @@ -24,8 +24,8 @@ use super::{ }; use crate::EngineError; -/// The transform Reset Transformation gives a symbol instance. -pub use symbols::reset_transformation; +/// Whether Reset Transformation changes a symbol instance. +pub use symbols::reset_transformation_changes; mod brushes; mod dynamic; diff --git a/crates/engine/src/cmd/brushsym/symbols.rs b/crates/engine/src/cmd/brushsym/symbols.rs index 318acbe97..69bfcb66e 100644 --- a/crates/engine/src/cmd/brushsym/symbols.rs +++ b/crates/engine/src/cmd/brushsym/symbols.rs @@ -702,22 +702,32 @@ const RESET_TOLERANCE: f64 = 1e-9; /// The transform Reset Transformation gives instance `n` of the document of `boxes`: its symbol's /// own scale, without rotation, shear or reflection, with the center of its art's bounding box -/// where it is. `None` when `n` is not an instance of a symbol the document holds, is in -/// perspective, or already has that transform. -pub fn reset_transformation(boxes: &vectorcraft_doc::SymbolBoxes, n: &Node) -> Option { +/// where it is. `None` when [`reset_transformation_changes`] is false for `n`. +fn reset_transformation(boxes: &vectorcraft_doc::SymbolBoxes, n: &Node) -> Option { let d = boxes.document(); - let NodeKind::SymbolInstance { symbol, xf } = &n.kind else { return None }; - if n.extras().perspective.is_some() || !d.symbols.iter().any(|s| s.name == *symbol) { + if !reset_transformation_changes(d, n) { return None; } - // The symbol's stored natural size, or the art unscaled when that size is not positive and finite. + let NodeKind::SymbolInstance { symbol, xf } = &n.kind else { return None }; let scale = d.symbol_natural_xf(symbol); // The art's box in symbol space (the ±HALF square when the art cannot be measured): its // center keeps its place on the page. let art = boxes.art_bounds(symbol).unwrap_or_else(|| vectorcraft_geom::Rect::new(-HALF, -HALF, HALF, HALF)); let center = xf.transform_rect_bbox(art).center(); - let new = Affine::translate(center - scale * art.center()) * scale; - xf.as_coeffs().iter().zip(new.as_coeffs()).any(|(a, b)| (a - b).abs() > RESET_TOLERANCE).then_some(new) + Some(Affine::translate(center - scale * art.center()) * scale) +} + +/// Does Reset Transformation change `n`? True for an instance of a symbol `d` holds, not in +/// perspective, whose scale, rotation, shear or reflection differs from the symbol's own scale +/// (its stored natural size, or the art unscaled when that size is not positive and finite). +/// It does not measure the art. +pub fn reset_transformation_changes(d: &Document, n: &Node) -> bool { + let NodeKind::SymbolInstance { symbol, xf } = &n.kind else { return false }; + if n.extras().perspective.is_some() || !d.symbols.iter().any(|s| s.name == *symbol) { + return false; + } + let natural = d.symbol_natural_xf(symbol).as_coeffs(); + xf.as_coeffs()[..4].iter().zip(&natural[..4]).any(|(a, b)| (a - b).abs() > RESET_TOLERANCE) } pub(super) fn symbol_instance_reset_transform(s: &mut Session, p: &Value) -> Result { diff --git a/crates/ui-egui/src/panels/symbols.rs b/crates/ui-egui/src/panels/symbols.rs index a1ecaf6c5..f42c47470 100644 --- a/crates/ui-egui/src/panels/symbols.rs +++ b/crates/ui-egui/src/panels/symbols.rs @@ -164,8 +164,7 @@ pub(crate) fn changed_instances_selected(app: &VectorcraftApp) -> bool { /// symbol's, which Reset Transformation restores? fn transformed_instances_selected(app: &VectorcraftApp) -> bool { let Some(doc) = app.session.active().map(|st| st.doc.clone()) else { return false }; - let boxes = doc.symbol_boxes(); - instance_selected(app, |c| vectorcraft_engine::cmd::reset_transformation(&boxes, c).is_some()) + instance_selected(app, |c| vectorcraft_engine::cmd::reset_transformation_changes(&doc, c)) } /// The symbols of the selected objects when every one is a symbol instance; `None` otherwise.