Dynamic dimensions: constant screen size, transform keeps the second constraint point

Every dynamic dimension gets a DIMSCALE override for 12 px text, refreshed
per zoom band, at creation and after load. A SCALE/ROTATE of an entity whose
own end points a driving dimension measures now pins its second constraint
point so the dimension moves the first end, as the reference does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ramox81andClaude Fable 5.1 committed 2026-09-21 13:36:01 +03:00
1 parent 971abe64a4
commit 0b5c686371
6 files changed
+75 -4

No files matched your search

+1
View File
@@ -3688,6 +3688,7 @@ impl OpenCADStudio {
);
// DYNCONSTRAINTDISPLAY 0 also keeps a new dynamic dimension off screen.
self.tabs[i].scene.refresh_hidden_dynamic_dimensions();
self.tabs[i].scene.refresh_dynamic_dimension_scales(true);
self.tabs[i].dirty = true;
self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None;
+2
View File
@@ -785,6 +785,8 @@ impl OpenCADStudio {
pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
let i = self.active_tab;
self.tabs[i].scene.update(t - self.start);
// Dynamic dimensions keep their screen size across zoom bands.
self.tabs[i].scene.refresh_dynamic_dimension_scales(false);
// If the camera moved since we last synced, write it back to
// the document and mark the file dirty.
+3
View File
@@ -2810,6 +2810,9 @@ impl Scene {
pub(crate) fn load_parametric_constraints_from_document(&mut self) {
self.parametric_constraints.clear();
// Dynamic dimensions read from the file get their screen size on the
// first tick after the view is known.
self.dynamic_dimension_band = 0.0;
let owners: Vec<_> = self
.document
.block_records
+4
View File
@@ -2302,6 +2302,9 @@ pub struct Scene {
pub dynamic_constraint_display: bool,
/// Dynamic dimensions DCHIDE or DYNCONSTRAINTDISPLAY 0 keep off screen.
hidden_dynamic_dimensions: HashSet<Handle>,
/// The zoom band (`quantize_wpp`) the dynamic dimensions were last
/// scaled for; 0 until the first scaling.
dynamic_dimension_band: f32,
/// Session-only visibility overrides for constraint glyphs.
hidden_parametric_constraints: HashSet<(
parametric_constraints::ParametricScope,
@@ -2635,6 +2638,7 @@ impl Scene {
constraint_name_format: 2,
dynamic_constraint_display: true,
hidden_dynamic_dimensions: HashSet::default(),
dynamic_dimension_band: 0.0,
hidden_parametric_constraints: HashSet::default(),
shown_parametric_constraints: HashSet::default(),
named_parameters: named_parameters::ParameterTable::new(),
+60
View File
@@ -364,6 +364,9 @@ pub(crate) fn next_dimensional_parameter_name(
/// The constraint-bar label that draws a dynamic dimension's lock mark.
pub const DYNAMIC_DIMENSION_GLYPH: &str = "\u{1F512}";
/// The on-screen text height of a dynamic dimension, in pixels.
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 {
@@ -1575,6 +1578,63 @@ impl super::Scene {
.any(|set| set.dimensions.values().any(|dimension| *dimension == handle))
}
/// Gives every dynamic dimension a DIMSCALE override that keeps its
/// text, arrows and offsets at a screen size (the reference draws them
/// at a constant pixel size whatever the zoom). Runs once per zoom band;
/// `force` re-applies after a dimension was created or loaded.
pub fn refresh_dynamic_dimension_scales(&mut self, force: bool) {
let Some(band) = super::Scene::quantize_wpp(self.world_per_pixel()) else {
return;
};
if !force && band == self.dynamic_dimension_band {
return;
}
self.dynamic_dimension_band = band;
let handles: Vec<Handle> = self
.parametric_constraints
.iter()
.flat_map(|set| set.dimensions.values().copied())
.collect();
let mut changes = Vec::new();
for handle in handles {
let Some(acadrust::EntityType::Dimension(dimension)) = self.document.get_entity(handle)
else {
continue;
};
let style_name = dimension.base().style_name.clone();
let text_height = self
.document
.dim_styles
.iter()
.find(|style| {
style.name.eq_ignore_ascii_case(&style_name)
|| (style_name.trim().is_empty()
&& style.name.eq_ignore_ascii_case("Standard"))
})
.map(|style| style.dimtxt)
.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) {
continue;
}
crate::entities::dim_override::set(
&mut self.document,
handle,
crate::entities::dim_override::DIMSCALE,
Some(acadrust::xdata::XDataValue::Real(scale)),
);
changes.push((handle, super::ChangeKind::Modified));
}
if !changes.is_empty() {
self.bump_entities(&changes);
}
}
/// Re-derives which dynamic dimensions stay off screen (DCHIDE, or
/// DYNCONSTRAINTDISPLAY 0 for all of them) and redraws the ones that
/// changed.
+5 -4
View File
@@ -2442,9 +2442,10 @@ fn solve_scope(
})
.flatten();
// A transformed entity whose own end points a driving dimension
// measures keeps only the first constraint point where the transform
// put it; the dimension pulls the other end back to its value (a
// scaled line keeps its d1), as in the reference.
// measures keeps only its second constraint point where the transform
// put it; the dimension pulls the first end back to its value (a line
// scaled 2x about its start keeps its d1 by moving that start), as in
// the reference.
let dimension_pin = (reference.marker.is_none() && axis_pin.is_none())
.then(|| {
constraints.iter().find_map(|c| {
@@ -2461,7 +2462,7 @@ fn solve_scope(
&& c.refs
.iter()
.all(|r| r.entity == reference.entity && r.marker.is_some()))
.then(|| c.refs[0])
.then(|| c.refs[1])
})
})
.flatten();