From aa51765a62224eefe0af2ee650f374b594cb25b9 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Mon, 21 Sep 2026 13:57:05 +0300 Subject: [PATCH] Dynamic dimensions: reference text format, horizontal text, lock after the text A plain number shows as typed (d1=100), an expression under the fx: prefix (fx: d2=d1*2); the text stays horizontal through DIMTIH/DIMTOH overrides and the lock mark follows the text. Co-Authored-By: Claude Fable 5.1 --- src/app/command_driver.rs | 1 + src/entities/dimension.rs | 12 ++---- src/scene/parametric_constraints.rs | 64 +++++++++++++++++++++-------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index a1a093528..84b3f3b6d 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -3611,6 +3611,7 @@ impl OpenCADStudio { }; let text = dynamic_dimension_text( &name, + &expression, value, self.tabs[i].scene.constraint_name_format, ); diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index 906dea36d..6f7efb264 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -6295,8 +6295,8 @@ fn dimension_text_is_outside(dim: &Dimension, style: Option<&DimStyle>) -> bool } } -/// Where a dynamic dimension's lock mark sits: just before the text on its -/// baseline, with the direction that points away from the text. +/// Where a dynamic dimension's lock mark sits: just after the (horizontal) +/// text, with the direction that points away from the text. pub(crate) fn dynamic_dimension_lock_anchor( document: &CadDocument, dim: &Dimension, @@ -6317,13 +6317,7 @@ pub(crate) fn dynamic_dimension_lock_anchor( let stack_scale = style.map(dimtfac_or_one).unwrap_or(1.0); let half_width = text_cells(&value, stack_scale) * text_height * CELL_WIDTH * 0.5; let pos = dimension_text_pos_f64(dim, style, text_height, dim_scale); - let rot = if dim.base().text_rotation.abs() > 1e-9 { - dim.base().text_rotation - } else { - dimension_text_natural_rotation(dim) - }; - let (sr, cr) = rot.sin_cos(); - let outward = Vector3::new(-cr, -sr, 0.0); + let outward = Vector3::new(1.0, 0.0, 0.0); Some(( Vector3::new( pos.x + outward.x * half_width, diff --git a/src/scene/parametric_constraints.rs b/src/scene/parametric_constraints.rs index a34b1989b..579eaa90b 100644 --- a/src/scene/parametric_constraints.rs +++ b/src/scene/parametric_constraints.rs @@ -368,12 +368,23 @@ pub const DYNAMIC_DIMENSION_GLYPH: &str = "\u{1F512}"; pub const DYNAMIC_DIMENSION_TEXT_PX: f32 = 12.0; /// The text a dynamic dimension shows: CONSTRAINTNAMEFORMAT 0 = name, -/// 1 = value, 2 = name=value. -pub(crate) fn dynamic_dimension_text(name: &str, value: f64, format: u8) -> String { +/// 1 = value, 2 = name=value. A plain number shows as typed (`d1=100`); an +/// expression shows the expression under the reference's `fx:` prefix +/// (`fx: d2=d1*2`). +pub(crate) fn dynamic_dimension_text(name: &str, source: &str, value: f64, format: u8) -> String { + let source = source.trim(); + let literal = source.parse::().is_ok(); + let shown = if literal { + source.to_string() + } else { + measured_expression(value) + }; match format { 0 => name.to_string(), - 1 => format!("{value:.4}"), - _ => format!("{name}={value:.4}"), + 1 if literal => shown, + 1 => format!("fx: {shown}"), + _ if literal => format!("{name}={shown}"), + _ => format!("fx: {name}={source}"), } } @@ -1477,14 +1488,24 @@ impl super::Scene { let Some(constraint) = set.get(*id) else { continue; }; - let (name, value) = match &constraint.driving_param { - Some(DrivingValue::Named(name)) => { - (name.clone(), table.resolve(name).unwrap_or(f64::NAN)) + let (name, source, value) = match &constraint.driving_param { + Some(DrivingValue::Named(name)) => ( + name.clone(), + table + .get(name) + .map(|parameter| parameter.source.clone()) + .unwrap_or_default(), + table.resolve(name).unwrap_or(f64::NAN), + ), + Some(DrivingValue::Literal(value)) => { + (String::new(), measured_expression(*value), *value) } - Some(DrivingValue::Literal(value)) => (String::new(), *value), None => continue, }; - updates.push((*dimension, dynamic_dimension_text(&name, value, format))); + updates.push(( + *dimension, + dynamic_dimension_text(&name, &source, value, format), + )); } } for (handle, text) in updates { @@ -1615,19 +1636,30 @@ impl super::Scene { .filter(|height| *height > 1e-9) .unwrap_or(0.18); let scale = f64::from(band) * f64::from(DYNAMIC_DIMENSION_TEXT_PX) / text_height; - let current = crate::entities::dim_override::real( - &dimension.base().common.extended_data, - crate::entities::dim_override::DIMSCALE, - ); - if current.is_some_and(|value| (value - scale).abs() < 1e-9) { + use crate::entities::dim_override as ov; + let xdata = &dimension.base().common.extended_data; + let current = ov::real(xdata, ov::DIMSCALE); + // The reference draws dynamic dimension text horizontally. + let horizontal = ov::int(xdata, ov::DIMTIH) == Some(1) && ov::int(xdata, ov::DIMTOH) == Some(1); + if current.is_some_and(|value| (value - scale).abs() < 1e-9) && horizontal { continue; } - crate::entities::dim_override::set( + ov::set( &mut self.document, handle, - crate::entities::dim_override::DIMSCALE, + ov::DIMSCALE, Some(acadrust::xdata::XDataValue::Real(scale)), ); + if !horizontal { + for code in [ov::DIMTIH, ov::DIMTOH] { + ov::set( + &mut self.document, + handle, + code, + Some(acadrust::xdata::XDataValue::Integer16(1)), + ); + } + } changes.push((handle, super::ChangeKind::Modified)); } if !changes.is_empty() {