fix(blocks): preserve UCS transformations
Bake BEDIT frames into block-local geometry while keeping model and viewport UCS isolated. Normalize legacy nonzero block origins during load.
This commit is contained in:
parent
02d15f5d23
commit
2c64888048
22 changed files with 693 additions and 41 deletions
|
|
@ -406,8 +406,8 @@ impl OpenCADStudio {
|
|||
)));
|
||||
}
|
||||
|
||||
// Snapshot the block's block-local entities so Discard can restore
|
||||
// them (skip structural Block/BlockEnd/AttDef, mirroring REFEDIT).
|
||||
// Snapshot the complete block definition so Discard can restore
|
||||
// structural markers, ATTDEFs and geometry with exact handles.
|
||||
let snapshot: Vec<_> = {
|
||||
let br = self.tabs[i]
|
||||
.scene
|
||||
|
|
@ -418,26 +418,45 @@ impl OpenCADStudio {
|
|||
br.entity_handles
|
||||
.iter()
|
||||
.filter_map(|h| self.tabs[i].scene.document.get_entity(*h).cloned())
|
||||
.filter(|e| {
|
||||
!matches!(
|
||||
e,
|
||||
acadrust::EntityType::Block(_)
|
||||
| acadrust::EntityType::BlockEnd(_)
|
||||
| acadrust::EntityType::AttributeDefinition(_)
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let dependent_snapshot: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.block_definition_dependent_handles(br_handle)
|
||||
.into_iter()
|
||||
.filter_map(|handle| {
|
||||
self.tabs[i].scene.document.get_entity_arc(handle)
|
||||
})
|
||||
.collect();
|
||||
let reference_attributes: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|entity| match entity {
|
||||
acadrust::EntityType::Insert(reference)
|
||||
if reference.block_name.eq_ignore_ascii_case(&insert.block_name)
|
||||
&& !reference.attributes.is_empty() =>
|
||||
{
|
||||
Some((
|
||||
reference.common.handle,
|
||||
reference.attributes.clone(),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.push_undo_snapshot(i, "BEDIT");
|
||||
|
||||
// Capture the camera before the editor reframes it, so leaving
|
||||
// the block editor returns the view exactly where it was (#425).
|
||||
let current_camera = self.tabs[i].scene.camera.borrow().clone();
|
||||
let current_ucs = self.tabs[i].active_ucs.clone();
|
||||
let (return_layout, return_block, return_camera) =
|
||||
if let Some(parent_index) = self.tabs[i].active_block_edit {
|
||||
let parent = &mut self.tabs[i].block_edits[parent_index];
|
||||
parent.editor_camera = current_camera.clone();
|
||||
parent.editor_ucs = current_ucs;
|
||||
(
|
||||
parent.return_layout.clone(),
|
||||
Some(parent.block_name.clone()),
|
||||
|
|
@ -463,10 +482,16 @@ impl OpenCADStudio {
|
|||
return_layout,
|
||||
return_block,
|
||||
snapshot,
|
||||
dependent_snapshot,
|
||||
reference_attributes,
|
||||
return_camera,
|
||||
editor_camera: current_camera,
|
||||
editor_ucs: None,
|
||||
});
|
||||
self.tabs[i].active_block_edit = Some(self.tabs[i].block_edits.len() - 1);
|
||||
// Every BEDIT tab owns an isolated local UCS. Never inherit or
|
||||
// overwrite the drawing's model-space UCS.
|
||||
self.tabs[i].refresh_active_ucs();
|
||||
|
||||
self.tabs[i].scene.deselect_all();
|
||||
// The first click of a double-click selects the model-space
|
||||
|
|
@ -574,10 +599,25 @@ impl OpenCADStudio {
|
|||
let return_layout = session.return_layout.clone();
|
||||
let return_camera = session.return_camera.clone();
|
||||
for mut entity in session.snapshot {
|
||||
entity.common_mut().handle = acadrust::Handle::NULL;
|
||||
// Preserve the original handle so ATTDEF references and
|
||||
// other handle-based relationships survive Discard.
|
||||
entity.common_mut().owner_handle = session.br_handle;
|
||||
let _ = self.tabs[i].scene.document.add_entity(entity);
|
||||
}
|
||||
for (handle, attributes) in session.reference_attributes {
|
||||
if let Some(acadrust::EntityType::Insert(reference)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
reference.attributes = attributes;
|
||||
}
|
||||
}
|
||||
for entity in session.dependent_snapshot {
|
||||
let handle = entity.common().handle;
|
||||
let _ = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.replace_entity_arc(handle, entity);
|
||||
}
|
||||
self.restore_after_block_edit_close(
|
||||
i,
|
||||
return_block.as_deref(),
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ impl OpenCADStudio {
|
|||
use acadrust::types::Vector3;
|
||||
let parts: Vec<&str> = cmd.splitn(4, ' ').collect();
|
||||
let sub = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
||||
let mut active_changed = false;
|
||||
match sub.as_str() {
|
||||
"" | "LIST" | "?" => {
|
||||
let active_name = self.tabs[i]
|
||||
|
|
@ -248,6 +249,7 @@ impl OpenCADStudio {
|
|||
}
|
||||
"W" | "WORLD" => {
|
||||
self.tabs[i].active_ucs = None;
|
||||
active_changed = true;
|
||||
self.command_line
|
||||
.push_output("UCS reset to World Coordinate System.");
|
||||
}
|
||||
|
|
@ -271,6 +273,7 @@ impl OpenCADStudio {
|
|||
wcs_origin.y as f64,
|
||||
wcs_origin.z as f64,
|
||||
);
|
||||
active_changed = true;
|
||||
self.command_line.push_output(&format!(
|
||||
"UCS origin set to ({:.4}, {:.4}, {:.4}).",
|
||||
wcs_origin.x, wcs_origin.y, wcs_origin.z
|
||||
|
|
@ -313,6 +316,7 @@ impl OpenCADStudio {
|
|||
Vector3::new(ny.x as f64, ny.y as f64, ny.z as f64);
|
||||
}
|
||||
self.tabs[i].active_ucs = Some(new_ucs);
|
||||
active_changed = true;
|
||||
self.command_line
|
||||
.push_output(&format!("UCS rotated {:.2}° around Z.", angle_deg));
|
||||
} else {
|
||||
|
|
@ -340,6 +344,7 @@ impl OpenCADStudio {
|
|||
let rot = glam::Quat::from_axis_angle(x_ax, rad);
|
||||
let ny = rot * old_y;
|
||||
ucs.y_axis = Vector3::new(ny.x as f64, ny.y as f64, ny.z as f64);
|
||||
active_changed = true;
|
||||
self.command_line
|
||||
.push_output(&format!("UCS rotated {:.2}° around X.", angle_deg));
|
||||
} else {
|
||||
|
|
@ -367,6 +372,7 @@ impl OpenCADStudio {
|
|||
let rot = glam::Quat::from_axis_angle(y_ax, rad);
|
||||
let nx = rot * old_x;
|
||||
ucs.x_axis = Vector3::new(nx.x as f64, nx.y as f64, nx.z as f64);
|
||||
active_changed = true;
|
||||
self.command_line
|
||||
.push_output(&format!("UCS rotated {:.2}° around Y.", angle_deg));
|
||||
} else {
|
||||
|
|
@ -378,6 +384,7 @@ impl OpenCADStudio {
|
|||
let name = sub.clone();
|
||||
if let Some(named) = self.tabs[i].scene.document.ucss.get(&name).cloned() {
|
||||
self.tabs[i].active_ucs = Some(named);
|
||||
active_changed = true;
|
||||
self.command_line
|
||||
.push_output(&format!("UCS '{}' activated.", name));
|
||||
} else {
|
||||
|
|
@ -388,11 +395,9 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Keep the scene's ViewCube UCS in lock-step with active_ucs and
|
||||
// persist it to the active pane (per-viewport UCS inside a
|
||||
// viewport, header model UCS in the Model tab) so it round-trips.
|
||||
self.tabs[i].sync_ucs_to_scene();
|
||||
self.tabs[i].persist_active_ucs();
|
||||
if active_changed {
|
||||
self.commit_active_ucs_change(i, "UCS");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Named Views (VIEW command) ────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in a new issue