From c8c2238d4a9c61e7cba7dc52255faf5fe005d58a Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Mon, 28 Sep 2026 18:56:08 +0300 Subject: [PATCH] fix(wblock): exported drawings carry their block definitions and styles WBLOCK copied only the entities and their layers, so INSERTs pointed at missing blocks and the file opened almost empty or needed recovery. It now goes through the cross-drawing paste kernels, which bring layers, linetypes, styles, block definitions and baked dimension blocks along. Fixes #1111 Co-Authored-By: Claude Opus 5.5 --- src/app/command_driver/mod.rs | 4 +- src/app/mod.rs | 1 + src/modules/insert/wblock.rs | 86 ++++++++++++++--------------------- 3 files changed, 37 insertions(+), 54 deletions(-) diff --git a/src/app/command_driver/mod.rs b/src/app/command_driver/mod.rs index 4890e8533..3712e7b08 100644 --- a/src/app/command_driver/mod.rs +++ b/src/app/command_driver/mod.rs @@ -94,7 +94,9 @@ mod text_edit; mod constraint; mod dim_edit; -pub(crate) use clipboard::{copy_to_clipboard_kernel, remap_ext_subtree_reference}; +pub(crate) use clipboard::{ + copy_to_clipboard_kernel, paste_entities_kernel, remap_ext_subtree_reference, +}; impl OpenCADStudio { /// Applies one command result, then โ€” when that result ended the active diff --git a/src/app/mod.rs b/src/app/mod.rs index 5256c968b..1602c6392 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -9,6 +9,7 @@ pub(crate) mod config; pub use automation::{export_headless, serve}; mod annotation_data; mod command_driver; +pub(crate) use command_driver::{copy_to_clipboard_kernel, paste_entities_kernel}; pub(crate) mod commands; pub(crate) mod dim_viewport; #[cfg(test)] diff --git a/src/modules/insert/wblock.rs b/src/modules/insert/wblock.rs index 854d37cff..74aa8207d 100644 --- a/src/modules/insert/wblock.rs +++ b/src/modules/insert/wblock.rs @@ -50,29 +50,7 @@ pub fn extract_block_into( .into_owned()); } - // Copy layers referenced by the block entities. - for h in &handles { - if let Some(e) = src.get_entity(*h) { - let layer = e.common().layer.clone(); - if !layer.is_empty() && !layer.eq("0") && out.layers.get(&layer).is_none() { - if let Some(src_layer) = src.layers.get(&layer) { - let _ = out.layers.add(src_layer.clone()); - } - } - } - } - - for h in handles { - if let Some(entity) = src.get_entity(h) { - if matches!(entity, EntityType::Block(_) | EntityType::BlockEnd(_)) { - continue; - } - let mut clone = entity.clone(); - clone.common_mut().handle = codec::types::Handle::NULL; - clone.common_mut().owner_handle = codec::types::Handle::NULL; - let _ = out.add_entity(clone); - } - } + copy_with_dependencies(src, &handles, out, None); if out.entities().count() == 0 { return Err(t!( @@ -106,24 +84,7 @@ pub fn extract_entities_into( return Err(t!("No entities selected for WBLOCK.").into_owned()); } - for &h in handles { - if let Some(entity) = src.get_entity(h) { - if matches!(entity, EntityType::Block(_) | EntityType::BlockEnd(_)) { - continue; - } - // Copy layer definition. - let layer = entity.common().layer.clone(); - if !layer.is_empty() && !layer.eq("0") && out.layers.get(&layer).is_none() { - if let Some(src_layer) = src.layers.get(&layer) { - let _ = out.layers.add(src_layer.clone()); - } - } - let mut clone = entity.clone(); - clone.common_mut().handle = codec::types::Handle::NULL; - clone.common_mut().owner_handle = codec::types::Handle::NULL; - let _ = out.add_entity(clone); - } - } + copy_with_dependencies(src, handles, out, None); if out.entities().count() == 0 { return Err(t!("None of the selected entities could be exported.").into_owned()); @@ -132,6 +93,30 @@ pub fn extract_entities_into( Ok(()) } +/// Copy entities into `out` the way a cross-drawing paste does: fresh handles, +/// plus the layers, linetypes, styles, block definitions and baked dimension +/// blocks they use โ€” without those an exported INSERT points at nothing and +/// the file opens empty or needs recovery. (#1111) +fn copy_with_dependencies( + src: &CadDocument, + handles: &[codec::Handle], + out: &mut CadDocument, + translate: Option<&crate::command::EntityTransform>, +) { + let handles: Vec = handles + .iter() + .copied() + .filter(|&handle| { + !matches!( + src.get_entity(handle), + Some(EntityType::Block(_) | EntityType::BlockEnd(_)) + ) + }) + .collect(); + let (entities, deps) = crate::app::copy_to_clipboard_kernel(src, &handles); + crate::app::paste_entities_kernel(out, &entities, &deps, translate); +} + /// Translate every entity of `out` so the overall bounds minimum lands on /// the origin โ€” SPM.ACAD's clone flow normalizes the new drawing to 0,0,0 /// after the copy (Catalog ยง2.1/CF-01.2). Opt-in per request; the @@ -193,18 +178,13 @@ pub fn extract_entities_to_doc_with_base( ) -> Result { let mut out = CadDocument::new(); out.header.insertion_units = unit; - extract_entities_into(src, handles, &mut out)?; - if base_point != glam::DVec3::ZERO { - let shift = crate::command::EntityTransform::Affine( - codec::types::Transform::from_translation(codec::types::Vector3::new( - -base_point.x, - -base_point.y, - -base_point.z, - )), - ); - for entity in out.entities_mut() { - crate::scene::view::dispatch::apply_transform(entity, &shift); - } + if handles.is_empty() { + return Err(t!("No entities selected for WBLOCK.").into_owned()); + } + let shift = crate::command::EntityTransform::Translate(-base_point); + copy_with_dependencies(src, handles, &mut out, Some(&shift)); + if out.entities().count() == 0 { + return Err(t!("None of the selected entities could be exported.").into_owned()); } Ok(out) }