Thin Extrude builds walls of a set thickness along sketch curves, open chains and closed loops, with the wall centred, inside, outside or on both sides at two thicknesses. Saved under format 20 (thin on an Extrude); an extrude without thin saves as before. Closes #721.
85 lines
3.6 KiB
Rust
85 lines
3.6 KiB
Rust
//! The Extrude dialog's Thin Extrude (#721): a picked profile's outline becomes the wall's
|
||
//! curves, and editing a thin extrude keeps it thin.
|
||
use crate::scenario::Harness;
|
||
use serde_json::json;
|
||
use solidcraft_engine::Sel;
|
||
use solidcraft_engine::doc::{FeatureKind, ThinSide};
|
||
|
||
fn rectangle(h: &mut Harness) -> u64 {
|
||
h.app.run("sketch.create", json!({"plane":"XY","name":"Base"})).unwrap();
|
||
h.app.run("sketch.rectangle.two_point", json!({"p0":[0,0],"p1":[40,20]})).unwrap();
|
||
h.app.run("sketch.finish", json!({})).unwrap();
|
||
h.app.session.doc.find_feature("Base").unwrap().id
|
||
}
|
||
|
||
fn volume(h: &Harness) -> f64 {
|
||
h.app.session.world_state().bodies.iter().map(|b| solidcraft_engine::doc::kernel::measure(&b.body).unwrap().volume).sum()
|
||
}
|
||
|
||
#[test]
|
||
fn a_thin_extrude_of_a_picked_profile_follows_its_outline() {
|
||
let mut h = Harness::new();
|
||
let sketch = rectangle(&mut h);
|
||
h.step(&json!({"call":"ui.start","params":{"command":"solid.extrude"}})).unwrap();
|
||
h.frames(3);
|
||
h.app.dialog.as_mut().unwrap().inputs[0].items = vec![Sel::Profile { sketch, index: 0 }];
|
||
h.step(&json!({"widget":"Thin Extrude"})).unwrap();
|
||
h.frames(3);
|
||
assert!(h.widget("Wall Thickness").is_some(), "the wall rows show once Thin is on");
|
||
h.step(&json!({"widget":"OK"})).unwrap();
|
||
assert!(h.app.dialog.is_none(), "{:?}", h.app.session.log);
|
||
let f = h.app.session.doc.features.last().unwrap();
|
||
let FeatureKind::Extrude { thin: Some(thin), .. } = &f.kind else { panic!("a thin extrude: {:?}", f.kind) };
|
||
assert_eq!(thin.curves.len(), 4);
|
||
assert_eq!(thin.side, ThinSide::Center);
|
||
// A 2 mm wall centred on the 40 × 20 outline, 10 mm high.
|
||
assert!((volume(&h) - (42.0 * 22.0 - 38.0 * 18.0) * 10.0).abs() < 1e-6, "{}", volume(&h));
|
||
}
|
||
|
||
#[test]
|
||
fn editing_a_thin_extrude_keeps_its_wall() {
|
||
let mut h = Harness::new();
|
||
rectangle(&mut h);
|
||
h.app
|
||
.run(
|
||
"solid.extrude",
|
||
json!({"sketch":"Base","thin":true,"thickness":"3 mm","side":"two_sides","thickness2":"1 mm","distance":"5 mm","name":"Wall"}),
|
||
)
|
||
.unwrap();
|
||
h.frames(4);
|
||
let before = h.app.session.doc.find_feature("Wall").unwrap().clone();
|
||
let v = volume(&h);
|
||
h.step(&json!({"call":"ui.editFeature","params":{"feature":"Wall"}})).unwrap();
|
||
h.frames(8);
|
||
assert!(h.widget("Thickness Outside").is_some());
|
||
h.step(&json!({"widget":"OK"})).unwrap();
|
||
assert!(h.app.dialog.is_none(), "{:?}", h.app.session.log);
|
||
let after = h.app.session.doc.find_feature("Wall").unwrap();
|
||
assert_eq!(after.kind, before.kind);
|
||
assert_eq!(after.param_names, before.param_names);
|
||
assert!((volume(&h) - v).abs() < 1e-9);
|
||
}
|
||
|
||
#[test]
|
||
fn the_thin_rows_are_translated() {
|
||
for (lang, thin, location, outside) in
|
||
[("ja", "薄肉押し出し", "壁の位置", "外側の厚さ"), ("de", "Dünnwandig extrudieren", "Wandlage", "Dicke außen")]
|
||
{
|
||
let mut h = Harness::new();
|
||
h.app.preferences.language = lang.into();
|
||
rectangle(&mut h);
|
||
h.app
|
||
.run(
|
||
"solid.extrude",
|
||
json!({"sketch":"Base","thin":true,"thickness":"3 mm","side":"two_sides","thickness2":"1 mm","distance":"5 mm","name":"Wall"}),
|
||
)
|
||
.unwrap();
|
||
h.step(&json!({"call":"ui.editFeature","params":{"feature":"Wall"}})).unwrap();
|
||
h.frames(8);
|
||
for word in [thin, location, outside] {
|
||
assert!(h.widget(word).is_some(), "{lang}: {word:?} must be on screen");
|
||
}
|
||
assert!(h.widget("Wall Location").is_none(), "{lang}: must not fall back to English");
|
||
}
|
||
}
|