Rib/Web: grow along the sketch normal (#2056) (#2057)

Thanks @pablopeza.
This commit is contained in:
Pablo López authored and GitHub committed 2026-10-17 14:15:03 -04:00
1 parent e347f820e6
commit cb857f1fa7
19 files changed
+173 -10

No files matched your search

+8 -3
View File
@@ -25,11 +25,11 @@ pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("solid.rib", "Rib", rib)
.at("SOLID", "CREATE")
.icon("rib")
.params("sketch, curves: [open curve ids, in order]; thickness (across the sketch plane); depth? (default: to the next face); flip?: bool (fill to the other side)"),
.params("sketch, curves: [open curve ids, in order]; thickness (across the sketch plane, or across the curves when normal); depth? (default: to the next face); flip?: bool (fill to the other side); direction?: in_plane|normal (grow across the sketch plane, the default, or along its normal)"),
CommandSpec::new("solid.web", "Web", web)
.at("SOLID", "CREATE")
.icon("web")
.params("sketch, curves: [open curve ids] (one wall each); thickness; depth? (default: to the next face); flip?: bool"),
.params("sketch, curves: [open curve ids] (one wall each); thickness; depth? (default: to the next face); flip?: bool; direction?: in_plane|normal"),
CommandSpec::new("solid.emboss", "Emboss", emboss)
.at("SOLID", "CREATE")
.icon("emboss")
@@ -133,7 +133,12 @@ fn rib_like(s: &mut Session, p: &Value, cmd: &str, web: bool) -> Result<Value> {
check_expr(s, d, Kind::Length, cmd, "depth")?;
}
let flip = bool_(p, "flip").unwrap_or(false);
add_feature(s, p, FeatureKind::Rib { sketch: SketchId(sketch), curves, thickness, depth, flip, web })
let normal = match str_(p, "direction").map(str::to_ascii_lowercase).as_deref() {
None | Some("in_plane" | "plane") => false,
Some("normal") => true,
Some(o) => return Err(bad(cmd, format!("unknown direction `{o}` (in_plane or normal)"))),
};
add_feature(s, p, FeatureKind::Rib { sketch: SketchId(sketch), curves, thickness, depth, flip, web, normal })
}
fn rib(s: &mut Session, p: &Value) -> Result<Value> {
@@ -77,6 +77,28 @@ fn rib_with_a_depth_and_web() {
assert!(e.is_err_and(|e| e.to_string().contains("do not face a body")));
}
#[test]
fn rib_along_the_sketch_normal_reaches_the_body() {
// A 20 x 20 x 10 box; a line along X on a sketch 10 in front of it (y = -10), whose plane
// the box does not cross, so only a rib along the sketch normal can reach it.
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length": 20, "width": 20, "height": 10, "corner": [0, 0, 0]}));
run(&mut s, "sketch.create", json!({"plane": {"origin": [0, -10, 0], "x_dir": [1, 0, 0], "y_dir": [0, 0, 1]}, "name": "Front"}));
run(&mut s, "sketch.line", json!({"points": [[2, 5], [18, 5]], "ids": ["l1"]}));
run(&mut s, "sketch.finish", json!({}));
let e = s.execute("solid.rib", &json!({"sketch": "Front", "curves": ["l1"], "thickness": 2}));
assert!(e.is_err_and(|e| e.to_string().contains("do not face a body")));
run(&mut s, "solid.rib", json!({"sketch": "Front", "curves": ["l1"], "thickness": 2, "direction": "normal"}));
// 16 long, 2 thick, 10 from the sketch to the box.
assert!(rel(volume(&mut s), 4000.0 + 320.0) < 1e-6, "{}", volume(&mut s));
assert_eq!(measure(&mut s)["body_count"], 1);
run(&mut s, "edit.undo", json!({}));
run(&mut s, "solid.rib", json!({"sketch": "Front", "curves": ["l1"], "thickness": 2, "direction": "normal", "depth": 4}));
assert!(rel(volume(&mut s), 4000.0 + 128.0) < 1e-6, "{}", volume(&mut s));
let e = s.execute("solid.rib", &json!({"sketch": "Front", "curves": ["l1"], "thickness": 2, "direction": "sideways"}));
assert!(e.is_err_and(|e| e.to_string().contains("unknown direction")));
}
#[test]
fn emboss_and_deboss_on_a_face() {
let mut s = Session::default();