feat(model): persist solid creation history
This commit is contained in:
parent
dc6dc2256d
commit
e37b77e426
10 changed files with 331 additions and 21 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -84,7 +84,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "acadrust"
|
name = "acadrust"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=7e2fa8c#7e2fa8c7c7c774edc4fa54b328b727dc76a3ad95"
|
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=1ece83a#1ece83af08a93bf84cac16f919a4b274298c8c59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.12",
|
"ahash 0.8.12",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -890,7 +890,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cadkernel"
|
name = "cadkernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=2c73f76#2c73f76526c2e3c8032aa49dedddd5ac11876cd5"
|
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=36c5b85#36c5b85919cd216c718c5b463fbea04da641bd17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acadrust",
|
"acadrust",
|
||||||
"cavalier_contours",
|
"cavalier_contours",
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ glam = { version = "0.33", features = ["bytemuck"] }
|
||||||
rfd = "0.17"
|
rfd = "0.17"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7e2fa8c", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "1ece83a", features = ["serde"] }
|
||||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "2c73f76", features = ["acis", "offset"] }
|
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "36c5b85", features = ["acis", "offset"] }
|
||||||
acadifc = { git = "https://github.com/OpenAEC-Foundation/acadifc.git", rev = "47b5603", optional = true }
|
acadifc = { git = "https://github.com/OpenAEC-Foundation/acadifc.git", rev = "47b5603", optional = true }
|
||||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ license = "GPL-3.0-only"
|
||||||
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
||||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7e2fa8c", optional = true, features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "1ece83a", optional = true, features = ["serde"] }
|
||||||
|
|
||||||
# Runtime IPC and serialization (host feature only).
|
# Runtime IPC and serialization (host feature only).
|
||||||
interprocess = { version = "2", optional = true }
|
interprocess = { version = "2", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ publish = false
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "7e2fa8c", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "1ece83a", features = ["serde"] }
|
||||||
bincode = "1.3"
|
bincode = "1.3"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
|
|
|
||||||
|
|
@ -1046,10 +1046,14 @@ impl OpenCADStudio {
|
||||||
self.commit_undo_delta(i, pd);
|
self.commit_undo_delta(i, pd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CmdResult::CommitSolid { entity, solid } => {
|
CmdResult::CommitSolid {
|
||||||
|
entity,
|
||||||
|
solid,
|
||||||
|
history,
|
||||||
|
} => {
|
||||||
let label = self.history_label_from_active_cmd(i, "SOLID");
|
let label = self.history_label_from_active_cmd(i, "SOLID");
|
||||||
let pending = self.begin_undo(i, label, 1, true);
|
let pending = self.begin_undo(i, label, 1, true);
|
||||||
self.add_solid_model(entity, *solid);
|
self.add_solid_model(entity, *solid, history);
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
self.tabs[i].scene.clear_preview_wire();
|
self.tabs[i].scene.clear_preview_wire();
|
||||||
self.tabs[i].active_cmd = None;
|
self.tabs[i].active_cmd = None;
|
||||||
|
|
@ -2673,12 +2677,20 @@ impl OpenCADStudio {
|
||||||
let result = sweep_model::extruded(&entity, height as f64)
|
let result = sweep_model::extruded(&entity, height as f64)
|
||||||
.and_then(|body| Some((solid_model::mesh_from_solid(&body, color)?, body)));
|
.and_then(|body| Some((solid_model::mesh_from_solid(&body, color)?, body)));
|
||||||
if let Some((mesh, solid)) = result {
|
if let Some((mesh, solid)) = result {
|
||||||
|
let history = crate::scene::model::solid_history::extrusion_op(
|
||||||
|
&entity,
|
||||||
|
height as f64,
|
||||||
|
);
|
||||||
let pending = self.begin_undo(i, "EXTRUDE", 1, true);
|
let pending = self.begin_undo(i, "EXTRUDE", 1, true);
|
||||||
let mut s3d = empty_solid3d();
|
let mut s3d = empty_solid3d();
|
||||||
if let acadrust::EntityType::Solid3D(inner) = &mut s3d {
|
if let acadrust::EntityType::Solid3D(inner) = &mut s3d {
|
||||||
inner.wires = solid_model::edge_wires(&solid);
|
inner.wires = solid_model::edge_wires(&solid);
|
||||||
}
|
}
|
||||||
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.create_solid_history(new_handle, history);
|
||||||
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
||||||
let _ = mesh;
|
let _ = mesh;
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
|
|
@ -2726,12 +2738,22 @@ impl OpenCADStudio {
|
||||||
)
|
)
|
||||||
.and_then(|body| Some((solid_model::mesh_from_solid(&body, color)?, body)));
|
.and_then(|body| Some((solid_model::mesh_from_solid(&body, color)?, body)));
|
||||||
if let Some((mesh, solid)) = result {
|
if let Some((mesh, solid)) = result {
|
||||||
|
let history = crate::scene::model::solid_history::revolve_op(
|
||||||
|
&entity,
|
||||||
|
axis_start.to_array(),
|
||||||
|
axis_end.to_array(),
|
||||||
|
(angle_deg as f64).to_radians(),
|
||||||
|
);
|
||||||
let pending = self.begin_undo(i, "REVOLVE", 1, true);
|
let pending = self.begin_undo(i, "REVOLVE", 1, true);
|
||||||
let mut s3d = empty_solid3d();
|
let mut s3d = empty_solid3d();
|
||||||
if let acadrust::EntityType::Solid3D(inner) = &mut s3d {
|
if let acadrust::EntityType::Solid3D(inner) = &mut s3d {
|
||||||
inner.wires = solid_model::edge_wires(&solid);
|
inner.wires = solid_model::edge_wires(&solid);
|
||||||
}
|
}
|
||||||
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.create_solid_history(new_handle, history);
|
||||||
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
||||||
let _ = mesh;
|
let _ = mesh;
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
|
|
@ -2768,6 +2790,12 @@ impl OpenCADStudio {
|
||||||
.get_entity(profile_handle)
|
.get_entity(profile_handle)
|
||||||
.cloned();
|
.cloned();
|
||||||
let path_ent = self.tabs[i].scene.document.get_entity(path_handle).cloned();
|
let path_ent = self.tabs[i].scene.document.get_entity(path_handle).cloned();
|
||||||
|
let history = profile_ent
|
||||||
|
.as_ref()
|
||||||
|
.zip(path_ent.as_ref())
|
||||||
|
.map(|(profile, path)| {
|
||||||
|
crate::scene::model::solid_history::sweep_op(profile, path)
|
||||||
|
});
|
||||||
let result = profile_ent
|
let result = profile_ent
|
||||||
.zip(path_ent)
|
.zip(path_ent)
|
||||||
.and_then(|(profile, path)| sweep_model::swept(&profile, &path, color));
|
.and_then(|(profile, path)| sweep_model::swept(&profile, &path, color));
|
||||||
|
|
@ -2782,6 +2810,12 @@ impl OpenCADStudio {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let new_handle = self.tabs[i].scene.add_entity(entity);
|
let new_handle = self.tabs[i].scene.add_entity(entity);
|
||||||
|
if let Some(history) = history {
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.create_solid_history(new_handle, history);
|
||||||
|
}
|
||||||
for mesh in &mut set.lods {
|
for mesh in &mut set.lods {
|
||||||
mesh.name = format!("{}", new_handle.value());
|
mesh.name = format!("{}", new_handle.value());
|
||||||
}
|
}
|
||||||
|
|
@ -2810,6 +2844,7 @@ impl OpenCADStudio {
|
||||||
.filter_map(|handle| self.tabs[i].scene.document.get_entity(*handle).cloned())
|
.filter_map(|handle| self.tabs[i].scene.document.get_entity(*handle).cloned())
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(mut set) = sweep_model::lofted(&profiles, color) {
|
if let Some(mut set) = sweep_model::lofted(&profiles, color) {
|
||||||
|
let history = crate::scene::model::solid_history::loft_op(&profiles);
|
||||||
let pending = self.begin_undo(i, "LOFT", 1, true);
|
let pending = self.begin_undo(i, "LOFT", 1, true);
|
||||||
let mut entity = empty_solid3d();
|
let mut entity = empty_solid3d();
|
||||||
if let acadrust::EntityType::Solid3D(solid) = &mut entity {
|
if let acadrust::EntityType::Solid3D(solid) = &mut entity {
|
||||||
|
|
@ -2819,6 +2854,10 @@ impl OpenCADStudio {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let new_handle = self.tabs[i].scene.add_entity(entity);
|
let new_handle = self.tabs[i].scene.add_entity(entity);
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.create_solid_history(new_handle, history);
|
||||||
for mesh in &mut set.lods {
|
for mesh in &mut set.lods {
|
||||||
mesh.name = format!("{}", new_handle.value());
|
mesh.name = format!("{}", new_handle.value());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,34 @@
|
||||||
// cylinder afterwards, and saves as one.
|
// cylinder afterwards, and saves as one.
|
||||||
|
|
||||||
use acadrust::entities::Solid3D;
|
use acadrust::entities::Solid3D;
|
||||||
|
use acadrust::objects::SolidHistoryOperation;
|
||||||
use cadkernel::brep::Body;
|
use cadkernel::brep::Body;
|
||||||
use acadrust::{EntityType, Handle};
|
use acadrust::{EntityType, Handle};
|
||||||
use iced::Task;
|
use iced::Task;
|
||||||
|
|
||||||
use super::Message;
|
use super::Message;
|
||||||
use crate::modules::model::boolean_cmd::BoolOp;
|
use crate::modules::model::boolean_cmd::BoolOp;
|
||||||
|
use crate::scene::model::solid_history;
|
||||||
use crate::scene::model::solid_model::{self, Bool};
|
use crate::scene::model::solid_model::{self, Bool};
|
||||||
|
|
||||||
impl super::OpenCADStudio {
|
impl super::OpenCADStudio {
|
||||||
/// Commit a Model-tab solid: add its acadrust entity to the document, then
|
/// Commit a Model-tab solid: add its acadrust entity to the document, then
|
||||||
/// register the B-rep (caches it for booleans + tessellates it into the
|
/// register the B-rep (caches it for booleans + tessellates it into the
|
||||||
/// shaded mesh pipeline). Returns the new entity handle.
|
/// shaded mesh pipeline). Returns the new entity handle.
|
||||||
pub(super) fn add_solid_model(&mut self, entity: EntityType, solid: Body) -> Handle {
|
pub(super) fn add_solid_model(
|
||||||
|
&mut self,
|
||||||
|
entity: EntityType,
|
||||||
|
solid: Body,
|
||||||
|
history: SolidHistoryOperation,
|
||||||
|
) -> Handle {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let Some(handle) = self.commit_entity_handle(entity) else {
|
let Some(handle) = self.commit_entity_handle(entity) else {
|
||||||
return Handle::NULL;
|
return Handle::NULL;
|
||||||
};
|
};
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.create_solid_history(handle, history);
|
||||||
self.tabs[i].scene.register_solid_model(handle, solid);
|
self.tabs[i].scene.register_solid_model(handle, solid);
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +77,8 @@ impl super::OpenCADStudio {
|
||||||
// mesh + cached B-rep; its edge wires make it pickable.
|
// mesh + cached B-rep; its edge wires make it pickable.
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&result);
|
s3d.wires = solid_model::edge_wires(&result);
|
||||||
let handle = self.add_solid_model(EntityType::Solid3D(s3d), result);
|
let history = solid_history::brep_op(&result);
|
||||||
|
let handle = self.add_solid_model(EntityType::Solid3D(s3d), result, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
self.tabs[i].scene.select_entity(handle, false);
|
self.tabs[i].scene.select_entity(handle, false);
|
||||||
|
|
@ -137,7 +149,8 @@ impl super::OpenCADStudio {
|
||||||
self.tabs[i].scene.erase_entities(&handles);
|
self.tabs[i].scene.erase_entities(&handles);
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&result);
|
s3d.wires = solid_model::edge_wires(&result);
|
||||||
let handle = self.add_solid_model(EntityType::Solid3D(s3d), result);
|
let history = solid_history::brep_op(&result);
|
||||||
|
let handle = self.add_solid_model(EntityType::Solid3D(s3d), result, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
self.tabs[i].scene.select_entity(handle, false);
|
self.tabs[i].scene.select_entity(handle, false);
|
||||||
|
|
@ -176,7 +189,8 @@ impl super::OpenCADStudio {
|
||||||
// Keep both originals; add the interference solid.
|
// Keep both originals; add the interference solid.
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&result);
|
s3d.wires = solid_model::edge_wires(&result);
|
||||||
self.add_solid_model(EntityType::Solid3D(s3d), result);
|
let history = solid_history::brep_op(&result);
|
||||||
|
self.add_solid_model(EntityType::Solid3D(s3d), result, history);
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
self.refresh_properties();
|
self.refresh_properties();
|
||||||
self.command_line
|
self.command_line
|
||||||
|
|
@ -223,7 +237,8 @@ impl super::OpenCADStudio {
|
||||||
self.tabs[i].scene.erase_entities(&handles);
|
self.tabs[i].scene.erase_entities(&handles);
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&rotated);
|
s3d.wires = solid_model::edge_wires(&rotated);
|
||||||
let handle = self.add_solid_model(EntityType::Solid3D(s3d), rotated);
|
let history = solid_history::brep_op(&rotated);
|
||||||
|
let handle = self.add_solid_model(EntityType::Solid3D(s3d), rotated, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
self.tabs[i].scene.select_entity(handle, false);
|
self.tabs[i].scene.select_entity(handle, false);
|
||||||
|
|
@ -306,7 +321,8 @@ impl super::OpenCADStudio {
|
||||||
self.tabs[i].scene.erase_entities(&[handle]);
|
self.tabs[i].scene.erase_entities(&[handle]);
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&result);
|
s3d.wires = solid_model::edge_wires(&result);
|
||||||
let h = self.add_solid_model(EntityType::Solid3D(s3d), result);
|
let history = solid_history::brep_op(&result);
|
||||||
|
let h = self.add_solid_model(EntityType::Solid3D(s3d), result, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !h.is_null() {
|
if !h.is_null() {
|
||||||
self.tabs[i].scene.select_entity(h, false);
|
self.tabs[i].scene.select_entity(h, false);
|
||||||
|
|
@ -351,7 +367,8 @@ impl super::OpenCADStudio {
|
||||||
self.push_undo_snapshot(i, "3DMIRROR");
|
self.push_undo_snapshot(i, "3DMIRROR");
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&reflected);
|
s3d.wires = solid_model::edge_wires(&reflected);
|
||||||
let h = self.add_solid_model(EntityType::Solid3D(s3d), reflected);
|
let history = solid_history::brep_op(&reflected);
|
||||||
|
let h = self.add_solid_model(EntityType::Solid3D(s3d), reflected, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !h.is_null() {
|
if !h.is_null() {
|
||||||
self.tabs[i].scene.select_entity(h, false);
|
self.tabs[i].scene.select_entity(h, false);
|
||||||
|
|
@ -421,7 +438,8 @@ impl super::OpenCADStudio {
|
||||||
self.tabs[i].scene.erase_entities(&handles);
|
self.tabs[i].scene.erase_entities(&handles);
|
||||||
let mut s3d = Solid3D::new();
|
let mut s3d = Solid3D::new();
|
||||||
s3d.wires = solid_model::edge_wires(&aligned);
|
s3d.wires = solid_model::edge_wires(&aligned);
|
||||||
let h = self.add_solid_model(EntityType::Solid3D(s3d), aligned);
|
let history = solid_history::brep_op(&aligned);
|
||||||
|
let h = self.add_solid_model(EntityType::Solid3D(s3d), aligned, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !h.is_null() {
|
if !h.is_null() {
|
||||||
self.tabs[i].scene.select_entity(h, false);
|
self.tabs[i].scene.select_entity(h, false);
|
||||||
|
|
@ -514,7 +532,13 @@ impl super::OpenCADStudio {
|
||||||
if let EntityType::Solid3D(inner) = &mut entity {
|
if let EntityType::Solid3D(inner) = &mut entity {
|
||||||
inner.wires = solid_model::edge_wires(&solid);
|
inner.wires = solid_model::edge_wires(&solid);
|
||||||
}
|
}
|
||||||
let handle = self.add_solid_model(entity, solid);
|
let history = solid_history::pyramid_op(
|
||||||
|
glam::DMat4::IDENTITY.to_cols_array(),
|
||||||
|
radius,
|
||||||
|
height,
|
||||||
|
n,
|
||||||
|
);
|
||||||
|
let handle = self.add_solid_model(entity, solid, history);
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
self.tabs[i].scene.select_entity(handle, false);
|
self.tabs[i].scene.select_entity(handle, false);
|
||||||
|
|
|
||||||
|
|
@ -1159,6 +1159,7 @@ pub enum CmdResult {
|
||||||
CommitSolid {
|
CommitSolid {
|
||||||
entity: EntityType,
|
entity: EntityType,
|
||||||
solid: Box<cadkernel::brep::Body>,
|
solid: Box<cadkernel::brep::Body>,
|
||||||
|
history: acadrust::objects::SolidHistoryOperation,
|
||||||
},
|
},
|
||||||
/// Commit an acadrust entity, end the command, and open the in-place text
|
/// Commit an acadrust entity, end the command, and open the in-place text
|
||||||
/// editor on it (used by MLEADER to type the annotation after placement).
|
/// editor on it (used by MLEADER to type the annotation after placement).
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
// entity is committed, so the Design-group boolean tools can combine it.
|
// entity is committed, so the Design-group boolean tools can combine it.
|
||||||
|
|
||||||
use acadrust::entities::Solid3D;
|
use acadrust::entities::Solid3D;
|
||||||
|
use acadrust::objects::SolidHistoryOperation;
|
||||||
use acadrust::{primitives, EntityType};
|
use acadrust::{primitives, EntityType};
|
||||||
use glam::DVec3;
|
use glam::DVec3;
|
||||||
use crate::t;
|
use crate::t;
|
||||||
|
|
@ -119,10 +120,22 @@ impl PrimitiveCommand {
|
||||||
preview
|
preview
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn history_transform(&self, origin: DVec3) -> [f64; 16] {
|
||||||
|
glam::DMat4::from_cols(
|
||||||
|
self.plane.x.extend(0.0),
|
||||||
|
self.plane.y.extend(0.0),
|
||||||
|
self.plane.z.extend(0.0),
|
||||||
|
self.plane.to_world(origin).extend(1.0),
|
||||||
|
)
|
||||||
|
.to_cols_array()
|
||||||
|
}
|
||||||
|
|
||||||
/// Build both the acadrust `Solid3D` (ACIS, for persistence) and the
|
/// Build both the acadrust `Solid3D` (ACIS, for persistence) and the
|
||||||
/// kernel `Body` (rendering + booleans) from the footprint + `height`.
|
/// kernel `Body` (rendering + booleans) from the footprint + `height`.
|
||||||
fn build(&self, height: f64) -> Option<(EntityType, Body)> {
|
fn build(&self, height: f64) -> Option<(EntityType, Body, SolidHistoryOperation)> {
|
||||||
let (doc, solid) = match self.shape {
|
use crate::scene::model::solid_history;
|
||||||
|
|
||||||
|
let (doc, solid, history) = match self.shape {
|
||||||
Shape::Box | Shape::Wedge => {
|
Shape::Box | Shape::Wedge => {
|
||||||
let (a, b) = (self.pts[0], self.pts[1]);
|
let (a, b) = (self.pts[0], self.pts[1]);
|
||||||
let length = (b.x - a.x).abs();
|
let length = (b.x - a.x).abs();
|
||||||
|
|
@ -131,6 +144,7 @@ impl PrimitiveCommand {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if self.shape == Shape::Box {
|
if self.shape == Shape::Box {
|
||||||
|
let origin = DVec3::new(a.x.min(b.x), a.y.min(b.y), a.z);
|
||||||
let center = [
|
let center = [
|
||||||
(a.x + b.x) / 2.0,
|
(a.x + b.x) / 2.0,
|
||||||
(a.y + b.y) / 2.0,
|
(a.y + b.y) / 2.0,
|
||||||
|
|
@ -139,12 +153,24 @@ impl PrimitiveCommand {
|
||||||
(
|
(
|
||||||
primitives::build_box(center, length, width, height),
|
primitives::build_box(center, length, width, height),
|
||||||
solid_model::box_solid(center, length, width, height),
|
solid_model::box_solid(center, length, width, height),
|
||||||
|
solid_history::box_op(
|
||||||
|
self.history_transform(origin),
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let origin = [a.x.min(b.x), a.y.min(b.y), a.z];
|
let origin = [a.x.min(b.x), a.y.min(b.y), a.z];
|
||||||
(
|
(
|
||||||
primitives::build_wedge(origin, length, width, height),
|
primitives::build_wedge(origin, length, width, height),
|
||||||
solid_model::wedge_solid(origin, length, width, height),
|
solid_model::wedge_solid(origin, length, width, height),
|
||||||
|
solid_history::wedge_op(
|
||||||
|
self.history_transform(DVec3::from_array(origin)),
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,11 +185,21 @@ impl PrimitiveCommand {
|
||||||
(
|
(
|
||||||
primitives::build_cylinder(center, r, height),
|
primitives::build_cylinder(center, r, height),
|
||||||
solid_model::cylinder_solid(center, r, height),
|
solid_model::cylinder_solid(center, r, height),
|
||||||
|
solid_history::cylinder_op(
|
||||||
|
self.history_transform(c),
|
||||||
|
r,
|
||||||
|
height,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
primitives::build_cone(center, r, height),
|
primitives::build_cone(center, r, height),
|
||||||
solid_model::cone_solid(center, r, height),
|
solid_model::cone_solid(center, r, height),
|
||||||
|
solid_history::cone_op(
|
||||||
|
self.history_transform(c),
|
||||||
|
r,
|
||||||
|
height,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -177,6 +213,7 @@ impl PrimitiveCommand {
|
||||||
(
|
(
|
||||||
primitives::build_sphere(center, r),
|
primitives::build_sphere(center, r),
|
||||||
solid_model::sphere_solid(center, r),
|
solid_model::sphere_solid(center, r),
|
||||||
|
solid_history::sphere_op(self.history_transform(c), r),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Shape::Torus => {
|
Shape::Torus => {
|
||||||
|
|
@ -190,6 +227,11 @@ impl PrimitiveCommand {
|
||||||
(
|
(
|
||||||
primitives::build_torus(center, major, minor),
|
primitives::build_torus(center, major, minor),
|
||||||
solid_model::torus_solid(center, major, minor),
|
solid_model::torus_solid(center, major, minor),
|
||||||
|
solid_history::torus_op(
|
||||||
|
self.history_transform(c),
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -199,12 +241,12 @@ impl PrimitiveCommand {
|
||||||
// Edge wires make the solid click-pickable and draw a wireframe over
|
// Edge wires make the solid click-pickable and draw a wireframe over
|
||||||
// the shaded mesh.
|
// the shaded mesh.
|
||||||
s3d.wires = solid_model::edge_wires(&solid);
|
s3d.wires = solid_model::edge_wires(&solid);
|
||||||
Some((EntityType::Solid3D(s3d), solid))
|
Some((EntityType::Solid3D(s3d), solid, history))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit(&self, height: f64) -> CmdResult {
|
fn commit(&self, height: f64) -> CmdResult {
|
||||||
match self.build(height) {
|
match self.build(height) {
|
||||||
Some((entity, solid)) => {
|
Some((entity, solid, history)) => {
|
||||||
// Built upright in its own frame, then put on the working
|
// Built upright in its own frame, then put on the working
|
||||||
// plane — the same move `place_entity` makes for the ACIS
|
// plane — the same move `place_entity` makes for the ACIS
|
||||||
// copy, so the two stay on top of each other.
|
// copy, so the two stay on top of each other.
|
||||||
|
|
@ -223,6 +265,7 @@ impl PrimitiveCommand {
|
||||||
Some(placed) => CmdResult::CommitSolid {
|
Some(placed) => CmdResult::CommitSolid {
|
||||||
entity: self.plane.place_entity(entity),
|
entity: self.plane.place_entity(entity),
|
||||||
solid: Box::new(placed),
|
solid: Box::new(placed),
|
||||||
|
history,
|
||||||
},
|
},
|
||||||
None => CmdResult::Cancel,
|
None => CmdResult::Cancel,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ pub mod ole_pres;
|
||||||
pub mod pdf_raster;
|
pub mod pdf_raster;
|
||||||
pub mod mesh_model;
|
pub mod mesh_model;
|
||||||
pub mod solid_model;
|
pub mod solid_model;
|
||||||
|
pub mod solid_history;
|
||||||
pub mod sweep_model;
|
pub mod sweep_model;
|
||||||
pub mod visual_style_model;
|
pub mod visual_style_model;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
|
|
|
||||||
202
src/scene/model/solid_history.rs
Normal file
202
src/scene/model/solid_history.rs
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
use acadrust::entities::{EmbeddedEntity, Solid3D};
|
||||||
|
use acadrust::objects::{
|
||||||
|
SolidHistoryBox, SolidHistoryBrep, SolidHistoryCylinder, SolidHistoryLoft,
|
||||||
|
SolidHistoryNodeBase, SolidHistoryOperation, SolidHistoryPyramid,
|
||||||
|
SolidHistoryRevolve, SolidHistorySphere, SolidHistorySweep, SolidHistoryTorus,
|
||||||
|
};
|
||||||
|
use acadrust::types::{Vector2, Vector3};
|
||||||
|
use acadrust::EntityType;
|
||||||
|
use cadkernel::brep::Body;
|
||||||
|
|
||||||
|
fn base(transform: [f64; 16]) -> SolidHistoryNodeBase {
|
||||||
|
let mut base = SolidHistoryNodeBase::new(1);
|
||||||
|
base.transform = transform;
|
||||||
|
base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn embedded(entity: &EntityType) -> Option<EmbeddedEntity> {
|
||||||
|
Some(match entity {
|
||||||
|
EntityType::Point(value) => EmbeddedEntity::Point(value.clone()),
|
||||||
|
EntityType::Line(value) => EmbeddedEntity::Line(value.clone()),
|
||||||
|
EntityType::Arc(value) => EmbeddedEntity::Arc(value.clone()),
|
||||||
|
EntityType::Circle(value) => EmbeddedEntity::Circle(value.clone()),
|
||||||
|
EntityType::Ellipse(value) => EmbeddedEntity::Ellipse(value.clone()),
|
||||||
|
EntityType::Spline(value) => EmbeddedEntity::Spline(value.clone()),
|
||||||
|
EntityType::LwPolyline(value) => EmbeddedEntity::LwPolyline(value.clone()),
|
||||||
|
EntityType::Ray(value) => EmbeddedEntity::Ray(value.clone()),
|
||||||
|
EntityType::XLine(value) => EmbeddedEntity::XLine(value.clone()),
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn box_op(
|
||||||
|
transform: [f64; 16],
|
||||||
|
length: f64,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Box(SolidHistoryBox {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
..SolidHistoryBox::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wedge_op(
|
||||||
|
transform: [f64; 16],
|
||||||
|
length: f64,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Wedge(SolidHistoryBox {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
..SolidHistoryBox::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cylinder_op(
|
||||||
|
transform: [f64; 16],
|
||||||
|
radius: f64,
|
||||||
|
height: f64,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Cylinder(SolidHistoryCylinder {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
height,
|
||||||
|
major_radius: radius,
|
||||||
|
minor_radius: radius,
|
||||||
|
x_radius: radius,
|
||||||
|
..SolidHistoryCylinder::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cone_op(
|
||||||
|
transform: [f64; 16],
|
||||||
|
radius: f64,
|
||||||
|
height: f64,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Cone(SolidHistoryCylinder {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
height,
|
||||||
|
major_radius: radius,
|
||||||
|
minor_radius: radius,
|
||||||
|
x_radius: radius,
|
||||||
|
..SolidHistoryCylinder::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sphere_op(transform: [f64; 16], radius: f64) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Sphere(SolidHistorySphere {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
radius,
|
||||||
|
..SolidHistorySphere::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn torus_op(
|
||||||
|
transform: [f64; 16],
|
||||||
|
major_radius: f64,
|
||||||
|
minor_radius: f64,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Torus(SolidHistoryTorus {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
major_radius,
|
||||||
|
minor_radius,
|
||||||
|
..SolidHistoryTorus::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pyramid_op(
|
||||||
|
transform: [f64; 16],
|
||||||
|
radius: f64,
|
||||||
|
height: f64,
|
||||||
|
sides: usize,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Pyramid(SolidHistoryPyramid {
|
||||||
|
base: base(transform),
|
||||||
|
operation_major: 1,
|
||||||
|
height,
|
||||||
|
sides: sides as i32,
|
||||||
|
radius,
|
||||||
|
..SolidHistoryPyramid::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn brep_op(body: &Body) -> SolidHistoryOperation {
|
||||||
|
let acis_data = crate::scene::convert::acis_export::planar_solid_to_sat(body)
|
||||||
|
.map(|document| {
|
||||||
|
let mut solid = Solid3D::new();
|
||||||
|
solid.set_sat_document(&document);
|
||||||
|
solid.acis_data
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
SolidHistoryOperation::Brep(SolidHistoryBrep {
|
||||||
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
||||||
|
operation_major: 1,
|
||||||
|
acis_data,
|
||||||
|
..SolidHistoryBrep::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extrusion_op(profile: &EntityType, height: f64) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Extrusion(SolidHistorySweep {
|
||||||
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
||||||
|
operation_major: 1,
|
||||||
|
direction: Vector3::new(0.0, 0.0, height),
|
||||||
|
sweep_entity: embedded(profile),
|
||||||
|
scale_factor: 1.0,
|
||||||
|
sweep_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||||
|
path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||||
|
..SolidHistorySweep::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_op(profile: &EntityType, path: &EntityType) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Sweep(SolidHistorySweep {
|
||||||
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
||||||
|
operation_major: 1,
|
||||||
|
sweep_entity: embedded(profile),
|
||||||
|
path_entity: embedded(path),
|
||||||
|
scale_factor: 1.0,
|
||||||
|
sweep_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||||
|
path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||||
|
..SolidHistorySweep::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loft_op(profiles: &[EntityType]) -> SolidHistoryOperation {
|
||||||
|
SolidHistoryOperation::Loft(SolidHistoryLoft {
|
||||||
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
||||||
|
operation_major: 1,
|
||||||
|
cross_sections: profiles.iter().filter_map(embedded).collect(),
|
||||||
|
..SolidHistoryLoft::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn revolve_op(
|
||||||
|
profile: &EntityType,
|
||||||
|
axis_start: [f64; 3],
|
||||||
|
axis_end: [f64; 3],
|
||||||
|
angle: f64,
|
||||||
|
) -> SolidHistoryOperation {
|
||||||
|
let direction = glam::DVec3::from_array(axis_end) - glam::DVec3::from_array(axis_start);
|
||||||
|
SolidHistoryOperation::Revolve(SolidHistoryRevolve {
|
||||||
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
||||||
|
operation_major: 1,
|
||||||
|
axis_point: Vector3::new(axis_start[0], axis_start[1], axis_start[2]),
|
||||||
|
direction: Vector2::new(direction.x, direction.y),
|
||||||
|
revolve_angle: angle,
|
||||||
|
sweep_entity: embedded(profile),
|
||||||
|
..SolidHistoryRevolve::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue