feat(annotation): integrate object contexts
This commit is contained in:
parent
f39481ac49
commit
e03e091b3c
37 changed files with 2820 additions and 581 deletions
|
|
@ -1491,15 +1491,19 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
// take the resolved value directly (None = the default
|
||||
// "Closed filled" / "ByBlock" option).
|
||||
let doc = &self.tabs[i].scene.document;
|
||||
let resolved_mleader_style = (field == "mleader_style")
|
||||
.then(|| {
|
||||
doc.objects.values().find_map(|object| match object {
|
||||
acadrust::objects::ObjectType::MultiLeaderStyle(style)
|
||||
if style.name == value => Some(style.clone()),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
let resolved: Option<acadrust::Handle> = match field {
|
||||
"mleader_style" => doc.objects.iter().find_map(|(h, o)| match o {
|
||||
acadrust::objects::ObjectType::MultiLeaderStyle(s)
|
||||
if s.name == value =>
|
||||
{
|
||||
Some(*h)
|
||||
}
|
||||
_ => None,
|
||||
}),
|
||||
"mleader_style" => {
|
||||
resolved_mleader_style.as_ref().map(|style| style.handle)
|
||||
}
|
||||
"text_style_handle" => doc
|
||||
.text_styles
|
||||
.iter()
|
||||
|
|
@ -1531,15 +1535,20 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
if self.tabs[i].scene.is_layer_locked(handle) {
|
||||
continue;
|
||||
}
|
||||
if let Some(acadrust::EntityType::MultiLeader(ml)) =
|
||||
let mut style_annotation = None;
|
||||
if field == "mleader_style" {
|
||||
if let Some(style) = &resolved_mleader_style {
|
||||
crate::scene::annotative::apply_mleader_style_to_object(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
style,
|
||||
);
|
||||
style_annotation = Some(style.is_annotative);
|
||||
}
|
||||
} else if let Some(acadrust::EntityType::MultiLeader(ml)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
match field {
|
||||
"mleader_style" => {
|
||||
if let Some(h) = resolved {
|
||||
ml.style_handle = Some(h);
|
||||
}
|
||||
}
|
||||
"text_style_handle" => {
|
||||
if let Some(h) = resolved {
|
||||
ml.text_style_handle = Some(h);
|
||||
|
|
@ -1550,6 +1559,24 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(annotative) = style_annotation {
|
||||
if annotative {
|
||||
if let Some(scale) =
|
||||
self.tabs[i].scene.creation_annotation_scale_handle()
|
||||
{
|
||||
crate::scene::annotative::create_annotation_context(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
scale,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
crate::scene::annotative::clear_annotation_context(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if matches!(field, "arrow_block" | "dim_line_lw" | "text_pos_vert") {
|
||||
// Leader dim-var overrides picked from a dropdown. The
|
||||
|
|
|
|||
|
|
@ -516,6 +516,7 @@ impl OpenCADStudio {
|
|||
section: self.start_section,
|
||||
},
|
||||
statusbar: self.statusbar_config.clone(),
|
||||
annotation_auto_scale: self.annotation_auto_scale,
|
||||
ribbon: crate::app::config::RibbonConfig {
|
||||
collapse: self.ribbon.collapse_mode(),
|
||||
},
|
||||
|
|
@ -545,6 +546,7 @@ impl OpenCADStudio {
|
|||
// (`refresh_recent_thumbs`) — never here on the boot path.
|
||||
self.start_section = cfg.start.section;
|
||||
self.statusbar_config = cfg.statusbar;
|
||||
self.annotation_auto_scale = cfg.annotation_auto_scale.clamp(-4, 4);
|
||||
self.ribbon.set_collapse_mode(cfg.ribbon.collapse);
|
||||
self.plot_dialog = cfg.plot;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2589,17 +2589,60 @@ impl OpenCADStudio {
|
|||
}
|
||||
Message::SetAnnotationScale(scale) => {
|
||||
self.scale_popup_open = false;
|
||||
let auto_scale = self.annotation_auto_scale;
|
||||
if let Some(tab) = self.tabs.get_mut(self.active_tab) {
|
||||
tab.scene.annotation_scale = scale;
|
||||
util::sync_annotation_scale_header(&mut tab.scene);
|
||||
tab.scene.invalidate_annotation_dependencies();
|
||||
let previous = tab.scene.displayed_annotation_scale_handle();
|
||||
if let Some(handle) = tab.scene.set_annotation_scale_named(&scale) {
|
||||
if auto_scale > 0 {
|
||||
tab.scene.add_annotation_scale_to_objects(
|
||||
handle,
|
||||
previous,
|
||||
auto_scale as u8,
|
||||
);
|
||||
}
|
||||
tab.dirty = true;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::SetViewportScale(scale) => {
|
||||
self.scale_popup_open = false;
|
||||
let auto_scale = self.annotation_auto_scale;
|
||||
if let Some(tab) = self.tabs.get_mut(self.active_tab) {
|
||||
tab.scene.set_viewport_scale(scale);
|
||||
let previous = tab.scene.displayed_annotation_scale_handle();
|
||||
if let Some(handle) = tab.scene.set_viewport_scale_named(&scale) {
|
||||
if auto_scale > 0 {
|
||||
tab.scene.add_annotation_scale_to_objects(
|
||||
handle,
|
||||
previous,
|
||||
auto_scale as u8,
|
||||
);
|
||||
}
|
||||
tab.dirty = true;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::ToggleAnnotationVisibility => {
|
||||
if let Some(tab) = self.tabs.get_mut(self.active_tab) {
|
||||
let value = !tab.scene.annotation_all_visible();
|
||||
tab.scene.set_annotation_all_visible(value);
|
||||
tab.dirty = true;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::ToggleAnnotationAutoAdd => {
|
||||
self.annotation_auto_scale = match self.annotation_auto_scale {
|
||||
0 => 4,
|
||||
value => -value,
|
||||
};
|
||||
Task::none()
|
||||
}
|
||||
Message::SyncViewportAnnotationScale => {
|
||||
if let Some(tab) = self.tabs.get_mut(self.active_tab) {
|
||||
if tab.scene.sync_viewport_annotation_scale() {
|
||||
tab.dirty = true;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -2647,22 +2690,17 @@ impl OpenCADStudio {
|
|||
let i = self.active_tab;
|
||||
let handles = self.property_target_handles(i);
|
||||
if handles.len() == 1 {
|
||||
// Only object types that carry a per-object context.
|
||||
let ok = matches!(
|
||||
self.tabs[i].scene.document.get_entity(handles[0]),
|
||||
Some(
|
||||
acadrust::EntityType::Text(_)
|
||||
| acadrust::EntityType::MText(_)
|
||||
| acadrust::EntityType::Insert(_)
|
||||
)
|
||||
);
|
||||
let ok = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handles[0])
|
||||
.is_some_and(crate::scene::annotative::supports_annotation_context);
|
||||
if ok {
|
||||
self.anno_object_scale_target = Some(handles[0]);
|
||||
self.active_modal = Some(crate::app::ModalKind::AnnoObjectScale);
|
||||
} else {
|
||||
self.command_line.push_info(
|
||||
"OBJECTSCALE applies to a single Text, MText or block reference.",
|
||||
);
|
||||
self.command_line
|
||||
.push_info("The selected object does not support annotation scales.");
|
||||
}
|
||||
} else {
|
||||
self.command_line
|
||||
|
|
@ -2816,21 +2854,16 @@ impl OpenCADStudio {
|
|||
// never rolled back when the manager closes.
|
||||
let i = self.active_tab;
|
||||
let sel = self.scale_manager_selected.clone();
|
||||
if let Some((_, anno, _)) = self
|
||||
.tabs[i]
|
||||
.scene
|
||||
.scale_list()
|
||||
.into_iter()
|
||||
.find(|(n, _, _)| n.eq_ignore_ascii_case(&sel))
|
||||
{
|
||||
self.tabs[i].scene.annotation_scale = anno;
|
||||
self.tabs[i].scene.document.header.current_annotation_scale = sel.clone();
|
||||
if let Some((p, d)) = self.tabs[i].scene.scale_paper_drawing(&sel) {
|
||||
if d != 0.0 {
|
||||
self.tabs[i].scene.document.header.annotation_scale_value = p / d;
|
||||
}
|
||||
let previous = self.tabs[i].scene.displayed_annotation_scale_handle();
|
||||
if let Some(scale) = self.tabs[i].scene.set_annotation_scale_named(&sel) {
|
||||
if self.annotation_auto_scale > 0 {
|
||||
self.tabs[i].scene.add_annotation_scale_to_objects(
|
||||
scale,
|
||||
previous,
|
||||
self.annotation_auto_scale as u8,
|
||||
);
|
||||
}
|
||||
self.tabs[i].scene.invalidate_annotation_dependencies();
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -3657,7 +3690,7 @@ impl OpenCADStudio {
|
|||
// object. Off is handled inside set_entity_*.
|
||||
if !cur {
|
||||
if let Some(sh) =
|
||||
self.tabs[i].scene.current_annotation_scale_handle()
|
||||
self.tabs[i].scene.creation_annotation_scale_handle()
|
||||
{
|
||||
crate::scene::annotative::create_annotation_context(
|
||||
&mut self.tabs[i].scene.document,
|
||||
|
|
|
|||
|
|
@ -299,7 +299,12 @@ impl OpenCADStudio {
|
|||
set_f64!(dimcen, self.ds_dimcen);
|
||||
set_f64!(dimtsz, self.ds_dimtsz);
|
||||
set_f64!(dimtxt, self.ds_dimtxt);
|
||||
set_f64!(dimscale, self.ds_dimscale);
|
||||
if self.ds_annotative {
|
||||
ds.dimscale = 0.0;
|
||||
self.ds_dimscale = "0".to_string();
|
||||
} else {
|
||||
set_f64!(dimscale, self.ds_dimscale);
|
||||
}
|
||||
set_f64!(dimlfac, self.ds_dimlfac);
|
||||
set_f64!(dimtp, self.ds_dimtp);
|
||||
set_f64!(dimtm, self.ds_dimtm);
|
||||
|
|
@ -442,7 +447,12 @@ impl OpenCADStudio {
|
|||
Dimtoh => self.ds_dimtoh = !self.ds_dimtoh,
|
||||
Dimtol => self.ds_dimtol = !self.ds_dimtol,
|
||||
Dimlim => self.ds_dimlim = !self.ds_dimlim,
|
||||
Annotative => self.ds_annotative = !self.ds_annotative,
|
||||
Annotative => {
|
||||
self.ds_annotative = !self.ds_annotative;
|
||||
if self.ds_annotative {
|
||||
self.ds_dimscale = "0".to_string();
|
||||
}
|
||||
}
|
||||
Dimfxlon => self.ds_dimfxlon = !self.ds_dimfxlon,
|
||||
Dimsah => self.ds_dimsah = !self.ds_dimsah,
|
||||
Dimtxtdirection => self.ds_dimtxtdirection = !self.ds_dimtxtdirection,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ use crate::scene::Scene;
|
|||
|
||||
/// Parse a scale string like "1:50" or "2:1" into (numerator, denominator).
|
||||
/// Returns (1.0, 1.0) for "Fit" or unknown formats.
|
||||
/// Sync the model-space annotation scale into the standard CANNOSCALE /
|
||||
/// CANNOSCALEVALUE header variables before a save, so the scale round-trips
|
||||
/// through the file (and is read correctly by other CAD applications).
|
||||
/// Sync the model-space annotation scale into its named drawing variable and
|
||||
/// numeric header mirror before a save.
|
||||
pub(super) fn sync_annotation_scale_header(scene: &mut Scene) {
|
||||
let anno = scene.annotation_scale;
|
||||
let value = if anno.abs() > 1e-9 {
|
||||
|
|
@ -14,17 +13,25 @@ pub(super) fn sync_annotation_scale_header(scene: &mut Scene) {
|
|||
} else {
|
||||
1.0
|
||||
};
|
||||
// Prefer the name of a matching scale already in the drawing's list;
|
||||
// fall back to a formatted ratio when none matches.
|
||||
let name = scene
|
||||
.scale_list()
|
||||
.into_iter()
|
||||
.find(|(_, a, _)| (a - anno).abs() < 0.001 * anno.max(0.001))
|
||||
.map(|(n, _, _)| n)
|
||||
.unwrap_or_else(|| format_annotation_scale_name(anno));
|
||||
let current = scene.document.header.current_annotation_scale.clone();
|
||||
let current_matches = scene.scale_list().into_iter().any(|(name, factor, _)| {
|
||||
name.eq_ignore_ascii_case(¤t)
|
||||
&& (factor - anno).abs() < 0.001 * anno.max(0.001)
|
||||
});
|
||||
let name = if current_matches {
|
||||
current
|
||||
} else {
|
||||
scene
|
||||
.scale_list()
|
||||
.into_iter()
|
||||
.find(|(_, factor, _)| (factor - anno).abs() < 0.001 * anno.max(0.001))
|
||||
.map(|(name, _, _)| name)
|
||||
.unwrap_or_else(|| format_annotation_scale_name(anno))
|
||||
};
|
||||
let hdr = &mut scene.document.header;
|
||||
hdr.current_annotation_scale = name;
|
||||
hdr.current_annotation_scale = name.clone();
|
||||
hdr.annotation_scale_value = value;
|
||||
crate::io::set_drawing_variable(&mut scene.document, "CANNOSCALE", &name);
|
||||
}
|
||||
|
||||
/// Format an annotation-scale multiplier as a ratio name: 50.0 -> "1:50",
|
||||
|
|
@ -101,4 +108,3 @@ pub(super) fn f4_to_u3([r, g, b, _]: [f32; 4]) -> [u8; 3] {
|
|||
pub(super) fn u3_to_f4([r, g, b]: [u8; 3]) -> [f32; 4] {
|
||||
[r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue