diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index 3dbc3b3a2..42cceb508 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -165,6 +165,12 @@ impl OpenCADStudio { self.tabs[i].scene.deselect_all(); self.refresh_properties(); } + // A command just ended (any terminal result) — if it was the draw + // command an ADDSELECTED launched, revert the template-property override + // so CLAYER / CECOLOR / … are left unchanged (#239). No-op otherwise. + if was_active && self.tabs[i].active_cmd.is_none() { + self.restore_add_selected_defaults(); + } task } @@ -1385,7 +1391,7 @@ impl OpenCADStudio { verts_low, normals, indices, - } => Some(crate::scene::model::mesh_model::MeshModel { + } => Some((crate::scene::model::mesh_model::MeshModel { name: String::new(), verts, verts_low, @@ -1393,19 +1399,22 @@ impl OpenCADStudio { indices, color, selected: false, - }), + }, solid)), _ => None, } } _ => None, } }); - if let Some(mut mesh) = result { + if let Some((mut mesh, solid)) = result { self.push_undo_snapshot(i, "EXTRUDE"); let new_entity = empty_solid3d(); let new_handle = self.tabs[i].scene.add_entity(new_entity); mesh.name = format!("{}", new_handle.value()); self.tabs[i].scene.meshes.insert(new_handle, crate::scene::MeshLodSet::from_single(mesh)); + // Keep the truck B-rep so the save path can export exact + // ACIS geometry (else the solid is dropped by other CAD apps). + self.tabs[i].scene.solid_models.insert(new_handle, solid); self.tabs[i].dirty = true; self.command_line.push_output("EXTRUDE: solid created."); } else { diff --git a/src/app/commands/inquiry.rs b/src/app/commands/inquiry.rs index f0ad22fd8..d4f7326fe 100644 --- a/src/app/commands/inquiry.rs +++ b/src/app/commands/inquiry.rs @@ -813,9 +813,16 @@ impl OpenCADStudio { c.linetype.clone(), c.linetype_scale, c.line_weight, + // A dimension template also carries its dimension style, adopted + // as the current DIMSTYLE so the cloned dimension matches (#239). + match e { + acadrust::EntityType::Dimension(d) => Some(d.base().style_name.clone()), + _ => None, + }, ) }); - let Some((verb, kind, layer, color, linetype, lt_scale, lw)) = info else { + let Some((verb, kind, layer, color, linetype, lt_scale, lw, template_dimstyle)) = info + else { self.command_line .push_error("ADDSELECTED: selected object not found."); return Task::none(); @@ -830,6 +837,29 @@ impl OpenCADStudio { // rewrite the template, and so the draw starts on a clean slate. self.tabs[i].scene.deselect_all(); + // Snapshot the current drawing defaults so the override below reverts + // once the launched draw command ends — ADDSELECTED must adopt the + // template's properties for the new object without permanently changing + // CLAYER / CECOLOR / CELTYPE / CELWEIGHT (issue #239). + let restore = crate::app::AddSelectedRestore { + layer_name: self.tabs[i].scene.document.header.current_layer_name.clone(), + layer_handle: self.tabs[i].scene.document.header.current_layer_handle, + color: self.tabs[i].scene.document.header.current_entity_color, + linetype_name: self.tabs[i].scene.document.header.current_linetype_name.clone(), + linetype_handle: self.tabs[i].scene.document.header.current_linetype_handle, + line_weight: self.tabs[i].scene.document.header.current_line_weight, + lt_scale: self.tabs[i].scene.document.header.current_entity_linetype_scale, + dimstyle_name: self.tabs[i].scene.document.header.current_dimstyle_name.clone(), + dimstyle_handle: self.tabs[i].scene.document.header.current_dimstyle_handle, + tab_active_layer: self.tabs[i].active_layer.clone(), + tab_layers_current: self.tabs[i].layers.current_layer.clone(), + ribbon_layer: self.ribbon.active_layer.clone(), + ribbon_color: self.ribbon.active_color, + ribbon_linetype: self.ribbon.active_linetype.clone(), + ribbon_lineweight: self.ribbon.active_lineweight, + }; + self.add_selected_restore = Some(restore); + // Adopt the template's general properties as the current defaults. The // entity-creation path stamps new objects from the tab's active layer // and the ribbon's active colour / linetype / lineweight, mirrored into @@ -860,6 +890,21 @@ impl OpenCADStudio { header.current_line_weight = lw.value(); header.current_entity_linetype_scale = lt_scale; } + // A dimension template also sets the current DIMSTYLE so the cloned + // dimension inherits the template's dimension style (#239). + if let Some(ds) = &template_dimstyle { + let ds_handle = self.tabs[i] + .scene + .document + .dim_styles + .iter() + .find(|s| s.name.eq_ignore_ascii_case(ds)) + .map(|s| s.handle) + .unwrap_or(acadrust::types::Handle::NULL); + let header = &mut self.tabs[i].scene.document.header; + header.current_dimstyle_name = ds.clone(); + header.current_dimstyle_handle = ds_handle; + } self.tabs[i].active_layer = layer.clone(); self.tabs[i].layers.current_layer = layer.clone(); self.tabs[i].dirty = true; @@ -875,6 +920,35 @@ impl OpenCADStudio { // Launch the matching draw command (installs its interactive step). self.dispatch_command(verb) } + + /// Restore the drawing defaults ADDSELECTED overrode, once the draw command + /// it launched ends (commit / cancel / interrupt). No-op unless an + /// ADDSELECTED override is pending. Issue #239. + pub(crate) fn restore_add_selected_defaults(&mut self) { + let Some(r) = self.add_selected_restore.take() else { + return; + }; + let i = self.active_tab; + { + let h = &mut self.tabs[i].scene.document.header; + h.current_layer_name = r.layer_name; + h.current_layer_handle = r.layer_handle; + h.current_entity_color = r.color; + h.current_linetype_name = r.linetype_name; + h.current_linetype_handle = r.linetype_handle; + h.current_line_weight = r.line_weight; + h.current_entity_linetype_scale = r.lt_scale; + h.current_dimstyle_name = r.dimstyle_name; + h.current_dimstyle_handle = r.dimstyle_handle; + } + self.tabs[i].active_layer = r.tab_active_layer; + self.tabs[i].layers.current_layer = r.tab_layers_current; + self.ribbon.active_layer = r.ribbon_layer; + self.ribbon.active_color = r.ribbon_color; + self.ribbon.active_linetype = r.ribbon_linetype; + self.ribbon.active_lineweight = r.ribbon_lineweight; + self.refresh_properties(); + } } /// Map a template entity to the draw-command verb that creates the same kind of @@ -899,6 +973,19 @@ fn add_selected_verb(entity: &acadrust::EntityType) -> Option<&'static str> { EntityType::Solid(_) => "SOLID", EntityType::Ray(_) => "RAY", EntityType::XLine(_) => "XLINE", + // Dimensions launch the matching dimension command by their stored type + // (issue #239). Angular 2-line and 3-point both use DIMANGULAR. + EntityType::Dimension(d) => { + use acadrust::entities::DimensionType; + match d.base().dimension_type { + DimensionType::Linear => "DIMLINEAR", + DimensionType::Aligned => "DIMALIGNED", + DimensionType::Angular | DimensionType::Angular3Point => "DIMANGULAR", + DimensionType::Diameter => "DIMDIAMETER", + DimensionType::Radius => "DIMRADIUS", + DimensionType::Ordinate => "DIMORDINATE", + } + } _ => return None, }) } diff --git a/src/app/commands/mod.rs b/src/app/commands/mod.rs index 63411c42f..cfac945c7 100644 --- a/src/app/commands/mod.rs +++ b/src/app/commands/mod.rs @@ -66,6 +66,9 @@ impl OpenCADStudio { if self.tabs[i].active_cmd.is_some() { self.tabs[i].scene.clear_preview_wire(); self.tabs[i].active_cmd = None; + // Interrupting an ADDSELECTED draw with another command reverts its + // template-property override too (#239). + self.restore_add_selected_defaults(); } // Starting any command leaves interactive PAN mode (the PAN arm below // re-enables it). diff --git a/src/app/mod.rs b/src/app/mod.rs index 3ee2e9324..0669e5654 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -187,6 +187,27 @@ pub struct OpenProgress { // ── Application state ────────────────────────────────────────────────────── +/// Drawing defaults ADDSELECTED overrides while it draws a new object matching a +/// template, then restores so the current layer/colour/linetype/lineweight are +/// left unchanged (issue #239). +struct AddSelectedRestore { + layer_name: String, + layer_handle: acadrust::types::Handle, + color: AcadColor, + linetype_name: String, + linetype_handle: acadrust::types::Handle, + line_weight: i16, + lt_scale: f64, + dimstyle_name: String, + dimstyle_handle: acadrust::types::Handle, + tab_active_layer: String, + tab_layers_current: String, + ribbon_layer: String, + ribbon_color: AcadColor, + ribbon_linetype: String, + ribbon_lineweight: LineWeight, +} + pub(super) struct OpenCADStudio { start: Instant, tabs: Vec, @@ -242,6 +263,10 @@ pub(super) struct OpenCADStudio { otrack_active: Option<(glam::Vec3, glam::Vec3)>, /// Whether Tangent snap was enabled before a tangent-pick command started. pre_cmd_tangent: Option, + /// Drawing defaults captured by ADDSELECTED before it adopts the template + /// object's properties, restored when the launched draw command ends so + /// ADDSELECTED doesn't permanently change CLAYER / CECOLOR / … (issue #239). + add_selected_restore: Option, /// Whether Ortho mode was temporarily suppressed by a command (e.g. RECTANG). rect_suppressed_ortho: bool, /// Orthogonal drawing constraint (F8): constrains picks to 0°/90°/180°/270°. @@ -1949,6 +1974,7 @@ impl OpenCADStudio { perf_hud: false, cycle_candidates: None, pre_cmd_tangent: None, + add_selected_restore: None, rect_suppressed_ortho: false, ortho_mode: false, polar_mode: false, diff --git a/src/app/properties.rs b/src/app/properties.rs index a5e230a89..3171f7335 100644 --- a/src/app/properties.rs +++ b/src/app/properties.rs @@ -667,6 +667,22 @@ impl OpenCADStudio { entity.as_entity_mut().set_layer(layer.clone()); } + // A new dimension adopts the current dimension style (DIMSTYLE), like + // AutoCAD — the DIM commands leave the default "Standard" on the entity, + // so stamp the header's current style here. ADDSELECTED sets DIMSTYLE to + // the template's first, so a cloned dimension keeps its style (#239). + if let acadrust::EntityType::Dimension(ref mut d) = entity { + let cur = self.tabs[i] + .scene + .document + .header + .current_dimstyle_name + .clone(); + if !cur.trim().is_empty() { + d.base_mut().style_name = cur; + } + } + // INSUNITS: when inserting a block whose BlockRecord.units differ // from the host's header.insertion_units, scale the new INSERT so // 1 source-unit equals the matching host length. When either side