forked from 1b-agent/opencadstudio-upstream
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 <[email protected]>
This commit is contained in:
1 parent
971abe64a4
commit
0b5c686371
6 files changed
+75
-4
No files matched your search
@@ -3688,6 +3688,7 @@ impl OpenCADStudio {
|
|||||||
);
|
);
|
||||||
// DYNCONSTRAINTDISPLAY 0 also keeps a new dynamic dimension off screen.
|
// DYNCONSTRAINTDISPLAY 0 also keeps a new dynamic dimension off screen.
|
||||||
self.tabs[i].scene.refresh_hidden_dynamic_dimensions();
|
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].dirty = true;
|
||||||
self.tabs[i].active_cmd = None;
|
self.tabs[i].active_cmd = None;
|
||||||
self.tabs[i].snap_result = None;
|
self.tabs[i].snap_result = None;
|
||||||
|
|||||||
@@ -785,6 +785,8 @@ impl OpenCADStudio {
|
|||||||
pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.tabs[i].scene.update(t - self.start);
|
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
|
// If the camera moved since we last synced, write it back to
|
||||||
// the document and mark the file dirty.
|
// the document and mark the file dirty.
|
||||||
|
|||||||
@@ -2810,6 +2810,9 @@ impl Scene {
|
|||||||
|
|
||||||
pub(crate) fn load_parametric_constraints_from_document(&mut self) {
|
pub(crate) fn load_parametric_constraints_from_document(&mut self) {
|
||||||
self.parametric_constraints.clear();
|
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
|
let owners: Vec<_> = self
|
||||||
.document
|
.document
|
||||||
.block_records
|
.block_records
|
||||||
|
|||||||
@@ -2302,6 +2302,9 @@ pub struct Scene {
|
|||||||
pub dynamic_constraint_display: bool,
|
pub dynamic_constraint_display: bool,
|
||||||
/// Dynamic dimensions DCHIDE or DYNCONSTRAINTDISPLAY 0 keep off screen.
|
/// Dynamic dimensions DCHIDE or DYNCONSTRAINTDISPLAY 0 keep off screen.
|
||||||
hidden_dynamic_dimensions: HashSet<Handle>,
|
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.
|
/// Session-only visibility overrides for constraint glyphs.
|
||||||
hidden_parametric_constraints: HashSet<(
|
hidden_parametric_constraints: HashSet<(
|
||||||
parametric_constraints::ParametricScope,
|
parametric_constraints::ParametricScope,
|
||||||
@@ -2635,6 +2638,7 @@ impl Scene {
|
|||||||
constraint_name_format: 2,
|
constraint_name_format: 2,
|
||||||
dynamic_constraint_display: true,
|
dynamic_constraint_display: true,
|
||||||
hidden_dynamic_dimensions: HashSet::default(),
|
hidden_dynamic_dimensions: HashSet::default(),
|
||||||
|
dynamic_dimension_band: 0.0,
|
||||||
hidden_parametric_constraints: HashSet::default(),
|
hidden_parametric_constraints: HashSet::default(),
|
||||||
shown_parametric_constraints: HashSet::default(),
|
shown_parametric_constraints: HashSet::default(),
|
||||||
named_parameters: named_parameters::ParameterTable::new(),
|
named_parameters: named_parameters::ParameterTable::new(),
|
||||||
|
|||||||
@@ -364,6 +364,9 @@ pub(crate) fn next_dimensional_parameter_name(
|
|||||||
/// The constraint-bar label that draws a dynamic dimension's lock mark.
|
/// The constraint-bar label that draws a dynamic dimension's lock mark.
|
||||||
pub const DYNAMIC_DIMENSION_GLYPH: &str = "\u{1F512}";
|
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,
|
/// The text a dynamic dimension shows: CONSTRAINTNAMEFORMAT 0 = name,
|
||||||
/// 1 = value, 2 = name=value.
|
/// 1 = value, 2 = name=value.
|
||||||
pub(crate) fn dynamic_dimension_text(name: &str, value: f64, format: u8) -> String {
|
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))
|
.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
|
/// Re-derives which dynamic dimensions stay off screen (DCHIDE, or
|
||||||
/// DYNCONSTRAINTDISPLAY 0 for all of them) and redraws the ones that
|
/// DYNCONSTRAINTDISPLAY 0 for all of them) and redraws the ones that
|
||||||
/// changed.
|
/// changed.
|
||||||
|
|||||||
@@ -2442,9 +2442,10 @@ fn solve_scope(
|
|||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
// A transformed entity whose own end points a driving dimension
|
// A transformed entity whose own end points a driving dimension
|
||||||
// measures keeps only the first constraint point where the transform
|
// measures keeps only its second constraint point where the transform
|
||||||
// put it; the dimension pulls the other end back to its value (a
|
// put it; the dimension pulls the first end back to its value (a line
|
||||||
// scaled line keeps its d1), as in the reference.
|
// 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())
|
let dimension_pin = (reference.marker.is_none() && axis_pin.is_none())
|
||||||
.then(|| {
|
.then(|| {
|
||||||
constraints.iter().find_map(|c| {
|
constraints.iter().find_map(|c| {
|
||||||
@@ -2461,7 +2462,7 @@ fn solve_scope(
|
|||||||
&& c.refs
|
&& c.refs
|
||||||
.iter()
|
.iter()
|
||||||
.all(|r| r.entity == reference.entity && r.marker.is_some()))
|
.all(|r| r.entity == reference.entity && r.marker.is_some()))
|
||||||
.then(|| c.refs[0])
|
.then(|| c.refs[1])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|||||||
Reference in new issue
Block a user