fix: honor drawing units in annotation and viewport scales
This commit is contained in:
parent
622da9095e
commit
cc7894cd92
4 changed files with 125 additions and 42 deletions
|
|
@ -876,6 +876,28 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
reframed += 1;
|
reframed += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep the active model-space annotation scale in sync as well.
|
||||||
|
// annotation_scale is drawing/paper, while CANNOSCALEVALUE stores
|
||||||
|
// the reciprocal paper/drawing factor.
|
||||||
|
if self.tabs[i].scene.annotation_scale.abs() > 1.0e-12 {
|
||||||
|
self.tabs[i].scene.annotation_scale *= factor as f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.header
|
||||||
|
.annotation_scale_value
|
||||||
|
.abs()
|
||||||
|
> 1.0e-12
|
||||||
|
{
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.header
|
||||||
|
.annotation_scale_value /= factor;
|
||||||
|
}
|
||||||
// Sizes the drawing keeps as settings rather than as geometry: dash
|
// Sizes the drawing keeps as settings rather than as geometry: dash
|
||||||
// lengths, default heights and widths, the radii the fillet and chamfer
|
// lengths, default heights and widths, the radii the fillet and chamfer
|
||||||
// commands start from. They are all lengths in the unit that just
|
// commands start from. They are all lengths in the unit that just
|
||||||
|
|
|
||||||
|
|
@ -1720,12 +1720,29 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn on_prop_geom_choice_changed(&mut self, field: &'static str, value: String) -> Task<Message> {
|
pub(super) fn on_prop_geom_choice_changed(
|
||||||
let i = self.active_tab;
|
&mut self,
|
||||||
let handles = self.property_target_handles(i);
|
field: &'static str,
|
||||||
if !handles.is_empty() {
|
value: String,
|
||||||
self.push_undo_snapshot(i, "CHPROP");
|
) -> Task<Message> {
|
||||||
if field == "vp_ucs_name" {
|
let i = self.active_tab;
|
||||||
|
let handles = self.property_target_handles(i);
|
||||||
|
|
||||||
|
if !handles.is_empty() {
|
||||||
|
self.push_undo_snapshot(i, "CHPROP");
|
||||||
|
|
||||||
|
if field == "vscale_std" {
|
||||||
|
for &handle in &handles {
|
||||||
|
if matches!(
|
||||||
|
self.tabs[i].scene.document.get_entity(handle),
|
||||||
|
Some(acadrust::EntityType::Viewport(_))
|
||||||
|
) {
|
||||||
|
let _ = self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.set_viewport_scale_named_for(handle, &value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if field == "vp_ucs_name" {
|
||||||
// Resolve UCS name → cloned data, then mutate viewports.
|
// Resolve UCS name → cloned data, then mutate viewports.
|
||||||
let ucs_data = self.tabs[i]
|
let ucs_data = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,6 @@ const STANDARD_SCALES: &[(&str, f64)] = &[
|
||||||
("10:1", 10.0),
|
("10:1", 10.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Parse a scale ratio name to its paper/drawing factor: "1:50" -> 0.02,
|
|
||||||
/// "2:1" -> 2.0. Also accepts a plain decimal ("0.02").
|
|
||||||
fn parse_scale_ratio(label: &str) -> Option<f64> {
|
|
||||||
if let Some((a, b)) = label.split_once(':') {
|
|
||||||
let n: f64 = a.trim().parse().ok()?;
|
|
||||||
let d: f64 = b.trim().parse().ok()?;
|
|
||||||
if d.abs() > 1e-12 {
|
|
||||||
return Some(n / d);
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
label.trim().parse::<f64>().ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scale_label(scale: f64) -> String {
|
fn scale_label(scale: f64) -> String {
|
||||||
for (label, val) in STANDARD_SCALES {
|
for (label, val) in STANDARD_SCALES {
|
||||||
|
|
@ -248,19 +235,6 @@ fn apply_geom_prop(vp: &mut Viewport, field: &str, value: &str) {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale picker. The label is a ratio name from the drawing's scale list
|
|
||||||
// ("1:50", "2:1"); parse it to the paper/drawing factor so any named
|
|
||||||
// scale resolves, not just the built-in set.
|
|
||||||
if field == "vscale_std" {
|
|
||||||
if let Some(scale) = parse_scale_ratio(value) {
|
|
||||||
vp.custom_scale = scale;
|
|
||||||
if scale > 1e-9 {
|
|
||||||
vp.view_height = vp.height / scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render mode picker.
|
// Render mode picker.
|
||||||
if field == "vp_render" {
|
if field == "vp_render" {
|
||||||
if let Some(&(_, mode)) = RENDER_MODES.iter().find(|(label, _)| *label == value) {
|
if let Some(&(_, mode)) = RENDER_MODES.iter().find(|(label, _)| *label == value) {
|
||||||
|
|
|
||||||
|
|
@ -3584,6 +3584,26 @@ impl Scene {
|
||||||
Self::DEFAULT_METRIC_SCALES
|
Self::DEFAULT_METRIC_SCALES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Conversion from one drawing unit to the paper unit used by the
|
||||||
|
/// standard annotation-scale family: millimetres for metric drawings
|
||||||
|
/// and inches for imperial drawings.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
fn annotation_scale_unit_factor(&self) -> f64 {
|
||||||
|
let paper_unit = if self.prefers_imperial_scales() == Some(true) {
|
||||||
|
1 // Inches
|
||||||
|
} else {
|
||||||
|
4 // Millimeters
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::modules::draw::units::conversion_factor(
|
||||||
|
self.document.header.insertion_units,
|
||||||
|
paper_unit,
|
||||||
|
)
|
||||||
|
.unwrap_or(1.0)
|
||||||
|
}
|
||||||
|
|
||||||
fn is_architectural_scale_name(name: &str) -> bool {
|
fn is_architectural_scale_name(name: &str) -> bool {
|
||||||
name.contains('=') && name.contains('"') && name.contains('\'')
|
name.contains('=') && name.contains('"') && name.contains('\'')
|
||||||
|
|
@ -3642,6 +3662,7 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scale_list(&self) -> Vec<(String, f32, f64)> {
|
pub fn scale_list(&self) -> Vec<(String, f32, f64)> {
|
||||||
|
let unit_factor = self.annotation_scale_unit_factor();
|
||||||
let mut list: Vec<(String, f32, f64)> = self
|
let mut list: Vec<(String, f32, f64)> = self
|
||||||
.document
|
.document
|
||||||
.objects
|
.objects
|
||||||
|
|
@ -3656,7 +3677,11 @@ impl Scene {
|
||||||
&& !s.name.contains('|')
|
&& !s.name.contains('|')
|
||||||
&& !s.name.to_ascii_uppercase().ends_with("_XREF") =>
|
&& !s.name.to_ascii_uppercase().ends_with("_XREF") =>
|
||||||
{
|
{
|
||||||
Some((s.name.clone(), s.inverse_factor() as f32, s.factor()))
|
Some((
|
||||||
|
s.name.clone(),
|
||||||
|
(s.inverse_factor() / unit_factor) as f32,
|
||||||
|
s.factor() * unit_factor,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
|
@ -3668,10 +3693,14 @@ impl Scene {
|
||||||
// annotation / viewport scale picker would be empty and so appear
|
// annotation / viewport scale picker would be empty and so appear
|
||||||
// broken. Substitute the standard ratio set — file scales still
|
// broken. Substitute the standard ratio set — file scales still
|
||||||
// win whenever the drawing actually defines any. (#154)
|
// win whenever the drawing actually defines any. (#154)
|
||||||
|
|
||||||
list = self
|
list = self
|
||||||
.default_scales()
|
.default_scales()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(label, vp)| (label.to_string(), (1.0 / vp) as f32, vp))
|
.map(|&(label, base_vp)| {
|
||||||
|
let vp = base_vp * unit_factor;
|
||||||
|
(label.to_string(), (1.0 / vp) as f32, vp)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
list
|
list
|
||||||
|
|
@ -3709,10 +3738,15 @@ impl Scene {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if !has_matching_family {
|
if !has_matching_family {
|
||||||
|
let unit_factor = self.annotation_scale_unit_factor();
|
||||||
|
|
||||||
visible.extend(
|
visible.extend(
|
||||||
self.default_scales()
|
self.default_scales()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(label, vp)| (label.to_string(), (1.0 / vp) as f32, vp)),
|
.map(|&(label, base_vp)| {
|
||||||
|
let vp = base_vp * unit_factor;
|
||||||
|
(label.to_string(), (1.0 / vp) as f32, vp)
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4005,6 +4039,7 @@ impl Scene {
|
||||||
return Some(h);
|
return Some(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fallback = self
|
let fallback = self
|
||||||
.default_scales()
|
.default_scales()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -4454,9 +4489,13 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn viewport_annotation_multiplier(&self, viewport: Handle) -> f32 {
|
fn viewport_annotation_multiplier(&self, viewport: Handle) -> f32 {
|
||||||
|
let unit_factor = self.annotation_scale_unit_factor();
|
||||||
|
|
||||||
self.viewport_scale_handle(viewport)
|
self.viewport_scale_handle(viewport)
|
||||||
.and_then(|handle| match self.document.objects.get(&handle) {
|
.and_then(|handle| match self.document.objects.get(&handle) {
|
||||||
Some(ObjectType::Scale(scale)) => Some(scale.inverse_factor() as f32),
|
Some(ObjectType::Scale(scale)) => {
|
||||||
|
Some((scale.inverse_factor() / unit_factor) as f32)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.unwrap_or(self.annotation_scale)
|
.unwrap_or(self.annotation_scale)
|
||||||
|
|
@ -4483,27 +4522,45 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_viewport_scale_named(&mut self, name: &str) -> Option<Handle> {
|
pub fn set_viewport_scale_named(&mut self, name: &str) -> Option<Handle> {
|
||||||
|
let viewport = self.explicit_viewport_handle()?;
|
||||||
|
self.set_viewport_scale_named_for(viewport, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_viewport_scale_named_for(
|
||||||
|
&mut self,
|
||||||
|
viewport: Handle,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<Handle> {
|
||||||
let scale_handle = self.scale_handle_ensuring(name)?;
|
let scale_handle = self.scale_handle_ensuring(name)?;
|
||||||
|
let unit_factor = self.annotation_scale_unit_factor();
|
||||||
|
|
||||||
let factor = match self.document.objects.get(&scale_handle) {
|
let factor = match self.document.objects.get(&scale_handle) {
|
||||||
Some(ObjectType::Scale(scale)) => scale.factor(),
|
Some(ObjectType::Scale(scale)) => scale.factor() * unit_factor,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let viewport = self.explicit_viewport_handle()?;
|
|
||||||
let locked = matches!(
|
let locked = matches!(
|
||||||
self.document.get_entity(viewport),
|
self.document.get_entity(viewport),
|
||||||
Some(EntityType::Viewport(vp)) if vp.status.locked
|
Some(EntityType::Viewport(vp)) if vp.status.locked
|
||||||
);
|
);
|
||||||
|
|
||||||
if locked || factor <= 1.0e-9 {
|
if locked || factor <= 1.0e-9 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(EntityType::Viewport(vp)) = self.document.get_entity_mut(viewport) {
|
if let Some(EntityType::Viewport(vp)) = self.document.get_entity_mut(viewport) {
|
||||||
vp.custom_scale = factor;
|
vp.custom_scale = factor;
|
||||||
vp.view_height = vp.height / factor;
|
vp.view_height = vp.height / factor;
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.document
|
self.document
|
||||||
.set_viewport_annotation_scale(viewport, scale_handle);
|
.set_viewport_annotation_scale(viewport, scale_handle);
|
||||||
|
|
||||||
self.resident_wire_sets.borrow_mut().clear();
|
self.resident_wire_sets.borrow_mut().clear();
|
||||||
self.bump_geometry();
|
self.bump_geometry();
|
||||||
|
|
||||||
Some(scale_handle)
|
Some(scale_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4512,14 +4569,25 @@ impl Scene {
|
||||||
let EntityType::Viewport(vp) = self.document.get_entity(viewport)? else {
|
let EntityType::Viewport(vp) = self.document.get_entity(viewport)? else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale = self.viewport_scale_handle(viewport)?;
|
let scale = self.viewport_scale_handle(viewport)?;
|
||||||
let ObjectType::Scale(scale) = self.document.objects.get(&scale)? else {
|
let ObjectType::Scale(scale) = self.document.objects.get(&scale)? else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let effective = vp_effective_scale(vp.custom_scale, vp.view_height, vp.height);
|
|
||||||
Some((effective - scale.factor()).abs() <= 1.0e-6 * scale.factor().max(1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let effective = vp_effective_scale(
|
||||||
|
vp.custom_scale,
|
||||||
|
vp.view_height,
|
||||||
|
vp.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
let expected = scale.factor() * self.annotation_scale_unit_factor();
|
||||||
|
|
||||||
|
Some(
|
||||||
|
(effective - expected).abs()
|
||||||
|
<= 1.0e-6 * expected.max(1.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
pub fn sync_viewport_annotation_scale(&mut self) -> bool {
|
pub fn sync_viewport_annotation_scale(&mut self) -> bool {
|
||||||
let Some(viewport) = self.explicit_viewport_handle() else {
|
let Some(viewport) = self.explicit_viewport_handle() else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -4527,8 +4595,10 @@ impl Scene {
|
||||||
let Some(scale_handle) = self.viewport_scale_handle(viewport) else {
|
let Some(scale_handle) = self.viewport_scale_handle(viewport) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
let unit_factor = self.annotation_scale_unit_factor();
|
||||||
|
|
||||||
let factor = match self.document.objects.get(&scale_handle) {
|
let factor = match self.document.objects.get(&scale_handle) {
|
||||||
Some(ObjectType::Scale(scale)) => scale.factor(),
|
Some(ObjectType::Scale(scale)) => scale.factor() * unit_factor,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
let Some(EntityType::Viewport(vp)) = self.document.get_entity_mut(viewport) else {
|
let Some(EntityType::Viewport(vp)) = self.document.get_entity_mut(viewport) else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue