From 5d0390bcdf0bdc5205ebbb156d6f0189c74273e5 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Tue, 13 Oct 2026 10:41:00 +0300 Subject: [PATCH] fix(annotative): start new drawings at the unit-aware annotation scale A new drawing is metric with metre insertion units, but its annotation multiplier stayed at Scene::new()'s unitless 1 while opened files and CANNOSCALE convert paper millimetres into drawing units (0.001 at 1:1). Annotative text and dimensions were drawn 1000x too large until the first scale change, then shrank to their real size and looked gone, also after switching back to 1:1. One helper now derives the multiplier from the header; new drawings, file open and every INSUNITS change (UNITS, the units popup, DWGUNITS, SETVAR) go through it. Fixes #1674 Co-Authored-By: Claude Opus 5.5 --- src/app/commands/layers.rs | 1 + src/app/commands/styleprops.rs | 4 ++++ src/app/update/file.rs | 17 +---------------- src/app/update/mod.rs | 4 ++++ src/scene/entity.rs | 4 ++++ src/scene/mod.rs | 32 +++++++++++++++++--------------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/app/commands/layers.rs b/src/app/commands/layers.rs index 7560af332..d444b05c2 100644 --- a/src/app/commands/layers.rs +++ b/src/app/commands/layers.rs @@ -893,6 +893,7 @@ impl OpenCADStudio { self.push_undo_snapshot(i, "DWGUNITS"); self.tabs[i].scene.document.header.insertion_units = to; + self.tabs[i].scene.sync_annotation_scale_from_header(); let factor = units::conversion_factor(from, to).filter(|f| (f - 1.0).abs() > 1e-12); let (from_label, to_label) = (units::label(from), units::label(to)); diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index 0599bdb89..a31daad7d 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -3088,6 +3088,10 @@ impl OpenCADStudio { self.delete_objects = value; } if changed { + if name == "INSUNITS" { + self.tabs[i].scene.sync_annotation_scale_from_header(); + self.tabs[i].scene.invalidate_annotation_dependencies(); + } if matches!( name.as_str(), "ZOOMWHEEL" diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 5d5200ead..837c1a765 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -2115,22 +2115,7 @@ impl OpenCADStudio { ) { crate::scene::text::ttf_glyph::clear_fallback_cache(); } - // Current model-space annotation scale comes from the drawing's - // CANNOSCALEVALUE (paper/drawing factor). Convert its inverse into - // drawing units as well: metric annotation sizes are paper millimetres - // and imperial annotation sizes are paper inches. - // Current model-space annotation scale comes from the drawing's - // CANNOSCALEVALUE (paper/drawing factor). Convert its paper unit into - // the drawing's INSUNITS as well, so e.g. a metre drawing uses - // 0.001 model units for 1 mm of paper at 1:1. - let cannoscale_value = self.tabs[i].scene.document.header.annotation_scale_value; - let unit_factor = self.tabs[i].scene.annotation_scale_unit_factor(); - - self.tabs[i].scene.annotation_scale = if cannoscale_value > 1e-9 { - ((1.0 / cannoscale_value) / unit_factor) as f32 - } else { - (1.0 / unit_factor) as f32 - }; + self.tabs[i].scene.sync_annotation_scale_from_header(); // Open-time breakdown so regressions are visible immediately. // `total` is wall time from the Open click to here (post-xref, diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 2526cd8de..23f1743fd 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -5442,6 +5442,8 @@ impl OpenCADStudio { header.angle_base = degrees.to_radians(); } header.insertion_units = state.insertion_units; + self.tabs[i].scene.sync_annotation_scale_from_header(); + self.tabs[i].scene.invalidate_annotation_dependencies(); self.tabs[i].dirty = true; self.tabs[i].scene.bump_geometry(); self.refresh_properties(); @@ -6020,6 +6022,8 @@ impl OpenCADStudio { // menu having done nothing. (#668) self.push_undo_snapshot(i, "UNITS"); self.tabs[i].scene.document.header.insertion_units = code; + self.tabs[i].scene.sync_annotation_scale_from_header(); + self.tabs[i].scene.invalidate_annotation_dependencies(); self.tabs[i].dirty = true; let label = crate::modules::draw::units::label(code); self.command_line.push_output( diff --git a/src/scene/entity.rs b/src/scene/entity.rs index 6d49abf79..df09813c2 100644 --- a/src/scene/entity.rs +++ b/src/scene/entity.rs @@ -3090,6 +3090,10 @@ impl Scene { // The name alone leaves the header pointing at the previous style, and // a DWG writes the current style by handle only. self.document.header.current_dimstyle_handle = iso; + // The annotation multiplier depends on those units; Scene::new() + // left it at the unitless 1, which drew annotative objects 1000x too + // large until the first CANNOSCALE change shrank them. (#1674) + self.sync_annotation_scale_from_header(); for obj in self.document.objects.values_mut() { if let codec::objects::ObjectType::Layout(l) = obj { if l.name != "Model" { diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 3a96d43e7..918b58757 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1448,14 +1448,7 @@ pub fn prepare_open_geometry( scene.local_extent_max = caches.local_extent_max; scene.local_center = caches.local_center; scene.bg_color = model_bg; - let cannoscale_value = scene.document.header.annotation_scale_value; - let unit_factor = scene.annotation_scale_unit_factor(); - - scene.annotation_scale = if cannoscale_value > 1e-9 { - ((1.0 / cannoscale_value) / unit_factor) as f32 - } else { - (1.0 / unit_factor) as f32 - }; + scene.sync_annotation_scale_from_header(); scene.current_layout = "Model".to_string(); let camera = scene.camera.borrow().clone(); let perf = crate::perf::enabled(); @@ -5373,6 +5366,21 @@ impl Scene { /// The built-in scale factors are defined assuming model and paper use /// the same base unit. A drawing in metres therefore needs an extra /// factor of 1000 when its paper side is measured in millimetres. + /// Model-space annotation multiplier from the header's CANNOSCALEVALUE + /// (paper/drawing factor), converted from paper units into the drawing's + /// INSUNITS, so a metre drawing uses 0.001 model units per paper + /// millimetre at 1:1. Every path that starts or loads a drawing, or + /// changes its units, goes through here so it agrees with CANNOSCALE. + pub(crate) fn sync_annotation_scale_from_header(&mut self) { + let cannoscale_value = self.document.header.annotation_scale_value; + let unit_factor = self.annotation_scale_unit_factor(); + self.annotation_scale = if cannoscale_value > 1e-9 { + ((1.0 / cannoscale_value) / unit_factor) as f32 + } else { + (1.0 / unit_factor) as f32 + }; + } + pub(crate) fn annotation_scale_unit_factor(&self) -> f64 { let paper_unit = if self.prefers_imperial_scales() == Some(true) { 1 // Inches @@ -13384,13 +13392,7 @@ mod journal_tests { scene.local_center = caches.local_center; scene.bg_color = model_bg; scene.current_layout = "Model".to_string(); - let scale = scene.document.header.annotation_scale_value; - let unit_factor = scene.annotation_scale_unit_factor(); - scene.annotation_scale = if scale > 1e-9 { - ((1.0 / scale) / unit_factor) as f32 - } else { - (1.0 / unit_factor) as f32 - }; + scene.sync_annotation_scale_from_header(); scene.install_prepared_open_geometry(prepared); let hits = scene.resident_patch_hits.get();