solid.mirror: mirror bodies of placed components where they are shown (#1896)
Thanks @NOWN-DEV.
This commit is contained in:
1 parent
9ce940b0b1
commit
2831426c51
9 files changed
+198
-8
No files matched your search
@@ -0,0 +1,105 @@
|
||||
//! `solid.mirror` of bodies in a placed component (a moved occurrence, a further instance, an
|
||||
//! inserted design) mirrors each body where it is shown. Before, it mirrored the component's own
|
||||
//! geometry and put the copy in the active component, so the copy lost the placement.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
use super::run;
|
||||
use crate::*;
|
||||
|
||||
fn world_box(s: &Session, body: &str) -> (Vec3, Vec3) {
|
||||
let b = s.world_state().body(body).unwrap_or_else(|| panic!("no body `{body}`")).mesh().bounds();
|
||||
(b.min, b.max)
|
||||
}
|
||||
|
||||
/// The box of `body` mirrored in the world YZ plane.
|
||||
fn mirrored_yz(s: &Session, body: &str) -> (Vec3, Vec3) {
|
||||
let (lo, hi) = world_box(s, body);
|
||||
(Vec3::new(-hi.x, lo.y, lo.z), Vec3::new(-lo.x, hi.y, hi.z))
|
||||
}
|
||||
|
||||
fn assert_box(got: (Vec3, Vec3), want: (Vec3, Vec3)) {
|
||||
assert!(got.0.dist(want.0) < 1e-6 && got.1.dist(want.1) < 1e-6, "got {got:?}, want {want:?}");
|
||||
}
|
||||
|
||||
/// A component "Arm" holding "ArmBody", its occurrence turned and moved; the root is active.
|
||||
fn moved_arm(s: &mut Session) -> (u64, u64) {
|
||||
let c = run(s, "component.create", json!({"name": "Arm"}));
|
||||
let (component, occurrence) = (c["component"].as_u64().unwrap(), c["occurrence"].as_u64().unwrap());
|
||||
run(s, "solid.box", json!({"corner": [10, 0, 0], "length": 20, "width": 4, "height": 6, "body_name": "ArmBody"}));
|
||||
run(s, "component.activate", json!({"component": 0}));
|
||||
run(s, "occurrence.move", json!({"occurrence": occurrence, "axis": [1, 0, 0], "angle": "90 deg", "translate": [30, 50, 0]}));
|
||||
(component, occurrence)
|
||||
}
|
||||
|
||||
fn created(r: &Value) -> String {
|
||||
r["created_bodies"][0].as_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_moved_components_body_is_mirrored_where_it_is_shown() {
|
||||
let mut s = Session::default();
|
||||
let (_, occurrence) = moved_arm(&mut s);
|
||||
let copy = created(&run(&mut s, "solid.mirror", json!({"bodies": ["ArmBody"], "plane": "YZ"})));
|
||||
assert_box(world_box(&s, ©), mirrored_yz(&s, "ArmBody"));
|
||||
// The copy follows its source when the occurrence moves again.
|
||||
run(&mut s, "occurrence.move", json!({"occurrence": occurrence, "space": "world", "translate": [0, 200, 0]}));
|
||||
assert_box(world_box(&s, ©), mirrored_yz(&s, "ArmBody"));
|
||||
// It survives a save and reopen.
|
||||
let t = Session::new(solidcraft_doc::Document::from_json(&s.doc.to_json()).unwrap());
|
||||
assert_box(world_box(&t, ©), mirrored_yz(&t, "ArmBody"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_further_instance_is_mirrored_at_its_own_placement() {
|
||||
let mut s = Session::default();
|
||||
let (component, _) = moved_arm(&mut s);
|
||||
run(&mut s, "occurrence.copy", json!({"component": component, "translate": [0, 0, 40]}));
|
||||
let names: Vec<String> = s.world_state().bodies.iter().map(|b| b.name.clone()).collect();
|
||||
let instance = names.iter().find(|n| n.starts_with("ArmBody (")).cloned().unwrap();
|
||||
let r = run(&mut s, "solid.mirror", json!({"bodies": ["ArmBody", instance], "plane": "YZ"}));
|
||||
let copies: Vec<String> = r["created_bodies"].as_array().unwrap().iter().map(|n| n.as_str().unwrap().to_string()).collect();
|
||||
assert_eq!(copies.len(), 2, "{r}");
|
||||
assert_box(world_box(&s, &copies[0]), mirrored_yz(&s, "ArmBody"));
|
||||
assert_box(world_box(&s, &copies[1]), mirrored_yz(&s, &instance));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_plane_picked_on_a_moved_body_is_a_plane_of_the_active_component() {
|
||||
let mut s = Session::default();
|
||||
moved_arm(&mut s);
|
||||
// A world plane x = 30 through the moved body: the copy is mirrored in it, as shown.
|
||||
let copy = created(&run(&mut s, "solid.mirror", json!({"bodies": ["ArmBody"], "plane": {"origin": [30, 50, 0], "normal": [1, 0, 0]}})));
|
||||
let (lo, hi) = world_box(&s, "ArmBody");
|
||||
assert_box(world_box(&s, ©), (Vec3::new(60.0 - hi.x, lo.y, lo.z), Vec3::new(60.0 - lo.x, hi.y, hi.z)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_joined_mirror_of_a_moved_body_stays_in_its_component() {
|
||||
let mut s = Session::default();
|
||||
moved_arm(&mut s);
|
||||
// A world plane through the body's far end: the joined body doubles along x where it is.
|
||||
let (lo, hi) = world_box(&s, "ArmBody");
|
||||
run(&mut s, "solid.mirror", json!({"bodies": ["ArmBody"], "plane": {"origin": [hi.x, 0, 0], "normal": [1, 0, 0]}, "combine": true}));
|
||||
let len = hi.x - lo.x;
|
||||
assert_box(world_box(&s, "ArmBody"), (lo, Vec3::new(hi.x + len, hi.y, hi.z)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bodies_in_place_keep_no_placement() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "body_name": "Base"}));
|
||||
run(&mut s, "solid.mirror", json!({"bodies": ["Base"], "plane": "YZ"}));
|
||||
let f = s.doc.features.last().unwrap();
|
||||
assert!(matches!(&f.kind, solidcraft_doc::FeatureKind::Mirror { placements, .. } if placements.is_empty()), "{:?}", f.kind);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_position_not_captured_yet_is_refused() {
|
||||
let mut s = Session::default();
|
||||
let (_, occurrence) = moved_arm(&mut s);
|
||||
run(&mut s, "occurrence.move", json!({"occurrence": occurrence, "translate": [5, 0, 0], "capture": false}));
|
||||
let e = s.execute("solid.mirror", &json!({"bodies": ["ArmBody"], "plane": "YZ"})).unwrap_err().to_string();
|
||||
assert!(e.contains("Capture Position"), "{e}");
|
||||
}
|
||||
Reference in new issue
Block a user