Reconstruct constant-section meshes as parametric solids
This commit is contained in:
1 parent
417511367c
commit
df4c8a2994
19 files changed
+717
-12
No files matched your search
@@ -0,0 +1,93 @@
|
||||
use super::*;
|
||||
use solvecraft_doc::{Document, FeatureKind, MeshData};
|
||||
use solvecraft_geom::Vec3;
|
||||
|
||||
fn imported_box() -> Session {
|
||||
let body = solvecraft_kernel::box_solid(Vec3::ZERO, Vec3::new(10., 20., 30.)).unwrap();
|
||||
let mesh = body.tessellate(0.01).unwrap();
|
||||
let mut doc = Document::default();
|
||||
doc.add_feature(
|
||||
FeatureKind::MeshImport {
|
||||
file: "public-safe-box.stl".into(),
|
||||
meshes: vec![MeshData {
|
||||
name: "Mesh".into(),
|
||||
positions: mesh.positions.iter().flat_map(|p| [p.x, p.y, p.z]).collect(),
|
||||
triangles: mesh.triangles.iter().flatten().copied().collect(),
|
||||
color: Some([0.2, 0.5, 0.8]),
|
||||
}],
|
||||
},
|
||||
Some("Imported mesh"),
|
||||
)
|
||||
.unwrap();
|
||||
Session::new(doc)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mesh_conversion_is_parametric_undoable_and_editable_after_cold_reopen() {
|
||||
let mut s = imported_box();
|
||||
run(&mut s, "parameters.add", json!({"name":"size","expression":"1","unit":""}));
|
||||
run(&mut s, "solid.scale", json!({"bodies":["Mesh"],"factor":"size","name":"Mesh scale"}));
|
||||
run(&mut s, "mesh.convert", json!({"body":"Mesh","tolerance":"0.01 mm","name":"Reconstructed prism"}));
|
||||
let check = |s: &Session| {
|
||||
let state = s.model.state();
|
||||
let body = &state.body("Mesh").unwrap().body;
|
||||
assert!(!body.is_mesh());
|
||||
assert_eq!(body.faces(0.01).unwrap().len(), 6);
|
||||
assert_eq!(body.color(), Some([0.2, 0.5, 0.8]));
|
||||
assert!(s.model.results.iter().all(|r| r.error.is_none()), "{:?}", s.model.results);
|
||||
};
|
||||
check(&s);
|
||||
assert!((volume(&mut s) - 6000.).abs() < 1e-6);
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert!(s.model.state().body("Mesh").unwrap().body.is_mesh());
|
||||
run(&mut s, "edit.redo", json!({}));
|
||||
check(&s);
|
||||
run(&mut s, "parameters.change", json!({"name":"size","expression":"2"}));
|
||||
check(&s);
|
||||
assert!((volume(&mut s) - 48000.).abs() < 1e-5);
|
||||
let mut cold = Session::new(Document::from_json(&s.doc.to_json()).unwrap());
|
||||
check(&cold);
|
||||
assert!((volume(&mut cold) - 48000.).abs() < 1e-5);
|
||||
run(&mut cold, "solid.fillet", json!({"body":"Mesh","edges":[[0,20,60]],"radius":2}));
|
||||
assert!(volume(&mut cold) < 48000.);
|
||||
assert!(cold.model.results.iter().all(|r| r.error.is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_mesh_conversions_leave_the_design_unchanged() {
|
||||
let mut s = imported_box();
|
||||
let before = s.doc.to_json();
|
||||
for params in [
|
||||
json!({}),
|
||||
json!({"body":"Missing"}),
|
||||
json!({"body":"Mesh","tolerance":0}),
|
||||
json!({"body":"Mesh","tolerance":"unknown"}),
|
||||
json!({"body":"Mesh","tolerance":"2 mm"}),
|
||||
] {
|
||||
assert!(s.execute("mesh.convert", ¶ms).is_err());
|
||||
assert_eq!(s.doc.to_json(), before);
|
||||
assert!(s.model.state().body("Mesh").unwrap().body.is_mesh());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exported_binary_stl_with_a_bore_reconstructs_analytic_faces() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "sketch.create", json!({"plane":"XY","name":"Section"}));
|
||||
run(&mut s, "sketch.line", json!({"points":[[0,0],[40,0],[40,20],[20,20],[20,30],[0,30]],"closed":true}));
|
||||
run(&mut s, "sketch.circle.center", json!({"center":[10,10],"radius":4}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"sketch":"Section","profiles":[{"point":[30,10]}],"distance":15,"body_name":"Bracket"}));
|
||||
let path = std::env::temp_dir().join(format!("solvecraft-mesh-convert-{}.stl", std::process::id()));
|
||||
run(&mut s, "file.save_mesh", json!({"path":path}));
|
||||
run(&mut s, "doc.open", json!({"path":path}));
|
||||
let name = s.model.state().bodies[0].name.clone();
|
||||
run(&mut s, "mesh.convert", json!({"body":name,"tolerance":"0.05 mm"}));
|
||||
let state = s.model.state();
|
||||
let body = &state.bodies[0].body;
|
||||
assert!(!body.is_mesh());
|
||||
assert!(body.validity().is_empty());
|
||||
assert_eq!(solvecraft_kernel::measure(body).unwrap().merged.face_types.get("cylinder"), Some(&1));
|
||||
assert_eq!(body.faces(0.01).unwrap().len(), 10);
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user