STEP export: an inserted assembly's nested parts are placed once (#1895)
Thanks @NOWN-DEV.
This commit is contained in:
1 parent
2831426c51
commit
713111d9df
2 files changed
+52
-4
No files matched your search
@@ -322,3 +322,41 @@ fn a_design_with_a_loose_import_rebuilds_unchanged() {
|
|||||||
assert_eq!(shown_boxes(&s), file_boxes(&path));
|
assert_eq!(shown_boxes(&s), file_boxes(&path));
|
||||||
let _ = std::fs::remove_dir_all(&dir);
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A design whose `Outer` component holds a plate and an `Inner` component placed twice (the
|
||||||
|
/// first placement turned and moved), with `Outer` itself turned about X and lifted.
|
||||||
|
fn nested_file(dir: &std::path::Path) -> String {
|
||||||
|
let mut s = Session::default();
|
||||||
|
let outer = run(&mut s, "component.create", json!({"name": "Outer"}))["occurrence"].clone();
|
||||||
|
run(&mut s, "solid.box", json!({"length": 30, "width": 30, "height": 5, "body_name": "Plate"}));
|
||||||
|
let inner = run(&mut s, "component.create", json!({"name": "Inner"}))["occurrence"].clone();
|
||||||
|
run(&mut s, "solid.box", json!({"length": 40, "width": 20, "height": 10, "body_name": "Block"}));
|
||||||
|
run(&mut s, "component.activate", json!({"component": "Outer"}));
|
||||||
|
run(&mut s, "occurrence.move", json!({"occurrence": inner, "axis": [0, 0, 1], "angle": "90 deg", "translate": [100, 0, 0]}));
|
||||||
|
run(&mut s, "occurrence.copy", json!({"component": "Inner", "translate": [0, 0, 30]}));
|
||||||
|
run(&mut s, "component.activate", json!({"component": "root"}));
|
||||||
|
run(&mut s, "occurrence.move", json!({"occurrence": outer, "space": "world", "axis": [1, 0, 0], "angle": "90 deg", "translate": [0, 0, 50]}));
|
||||||
|
let path = dir.join("nested.step").to_string_lossy().to_string();
|
||||||
|
run(&mut s, "file.export", json!({"path": path}));
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn an_inserted_assembly_exports_its_nested_parts_where_they_are() {
|
||||||
|
let dir = temp_dir("reexport");
|
||||||
|
let path = nested_file(&dir);
|
||||||
|
let placed = file_boxes(&path);
|
||||||
|
assert_eq!(placed.len(), 3, "a plate and two blocks: {placed:?}");
|
||||||
|
let mut s = Session::default();
|
||||||
|
run(&mut s, "file.insert_step", json!({"path": path}));
|
||||||
|
assert_eq!(shown_boxes(&s), placed);
|
||||||
|
// The whole design as an assembly: every part is placed once, by its occurrences.
|
||||||
|
let again = dir.join("again.step").to_string_lossy().to_string();
|
||||||
|
run(&mut s, "file.export", json!({"path": again}));
|
||||||
|
assert_eq!(file_boxes(&again), placed);
|
||||||
|
// And inserted again, it shows as the first file did.
|
||||||
|
let mut back = Session::default();
|
||||||
|
run(&mut back, "file.insert_step", json!({"path": again}));
|
||||||
|
assert_eq!(shown_boxes(&back), placed);
|
||||||
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
|
}
|
||||||
+14
-4
@@ -488,7 +488,9 @@ fn mesh_object(b: &solidcraft_doc::ModelBody, tagged: bool) -> Result<MeshObject
|
|||||||
|
|
||||||
/// STEP assembly of a design with components: a product per component holding its bodies in
|
/// STEP assembly of a design with components: a product per component holding its bodies in
|
||||||
/// the component's own frame (`local` is the model before placing occurrences), and an
|
/// the component's own frame (`local` is the model before placing occurrences), and an
|
||||||
/// assembly occurrence per component occurrence.
|
/// assembly occurrence per component occurrence. A body with an offset (an imported assembly's
|
||||||
|
/// part, built where the file placed it) is taken into its component's frame by that offset,
|
||||||
|
/// as the world placement does, so the occurrences above it place it only once.
|
||||||
pub fn step_assembly(doc: &Document, local: &ModelState, name: &str) -> Result<Vec<u8>> {
|
pub fn step_assembly(doc: &Document, local: &ModelState, name: &str) -> Result<Vec<u8>> {
|
||||||
use solidcraft_kernel::{ExportBody, ExportProduct};
|
use solidcraft_kernel::{ExportBody, ExportProduct};
|
||||||
let mut index: std::collections::HashMap<u64, usize> = std::collections::HashMap::new();
|
let mut index: std::collections::HashMap<u64, usize> = std::collections::HashMap::new();
|
||||||
@@ -498,10 +500,18 @@ pub fn step_assembly(doc: &Document, local: &ModelState, name: &str) -> Result<V
|
|||||||
index.insert(c.id, products.len());
|
index.insert(c.id, products.len());
|
||||||
products.push(ExportProduct { name: c.name.clone(), bodies: Vec::new(), children: Vec::new() });
|
products.push(ExportProduct { name: c.name.clone(), bodies: Vec::new(), children: Vec::new() });
|
||||||
}
|
}
|
||||||
for b in &local.bodies {
|
let framed: Vec<Option<solidcraft_kernel::Body>> = local
|
||||||
let comp = doc.body_component(&b.name, b.feature);
|
.bodies
|
||||||
|
.iter()
|
||||||
|
.map(|b| match doc.model_body_offset(b) {
|
||||||
|
Some(offset) if !solidcraft_doc::is_identity(offset) => solidcraft_kernel::transform_matrix(&b.body, *offset).map(Some),
|
||||||
|
_ => Ok(None),
|
||||||
|
})
|
||||||
|
.collect::<std::result::Result<_, _>>()?;
|
||||||
|
for (b, framed) in local.bodies.iter().zip(&framed) {
|
||||||
|
let comp = doc.model_body_component(b);
|
||||||
let Some(p) = index.get(&comp).and_then(|i| products.get_mut(*i)) else { continue };
|
let Some(p) = index.get(&comp).and_then(|i| products.get_mut(*i)) else { continue };
|
||||||
p.bodies.push(ExportBody { name: b.name.clone(), body: &b.body, color: b.body.color() });
|
p.bodies.push(ExportBody { name: b.name.clone(), body: framed.as_ref().unwrap_or(&b.body), color: b.body.color() });
|
||||||
}
|
}
|
||||||
for o in &doc.occurrences {
|
for o in &doc.occurrences {
|
||||||
let (Some(&parent), Some(&child)) = (index.get(&o.parent), index.get(&o.component)) else { continue };
|
let (Some(&parent), Some(&child)) = (index.get(&o.parent), index.get(&o.component)) else { continue };
|
||||||
|
|||||||
Reference in new issue
Block a user