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 <noreply@anthropic.com>
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.
|
||||
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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user