Files
solidcraft-upstream/crates/engine/src/split_face_tests.rs
T
bherbruckandClaude Opus 5.5 296683229d Rename SolveCraft to SolidCraft everywhere, with compatibility shims (#389)
* WIP deep rename (not for push)

* WIP2

* WIP3

* WIP4 wxs fix

* rustfmt

* Rename the remaining identifiers in merged crash-report code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:17:01 -04:00

100 lines
4.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Split Face: the top of a box divided by a plane, by another body's face and by sketch curves.
use serde_json::{Value, json};
use crate::Session;
fn run(s: &mut Session, id: &str, p: Value) -> Value {
match s.execute(id, &p) {
Ok(v) => v,
Err(e) => panic!("{id} {p}: {e}"),
}
}
/// (kernel faces, volume) of a body: the merged count joins pieces of one surface back up.
fn faces_volume(s: &mut Session, b: &str) -> (u64, f64) {
let m = run(s, "inspect.measure", json!({ "bodies": [b] }))["bodies"][0].clone();
(m["kernel"]["faces"].as_u64().unwrap_or(0), m["volume_mm3"].as_f64().unwrap_or(0.0))
}
fn boxed() -> Session {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length": 40, "width": 30, "height": 20, "body_name": "B"}));
s
}
#[test]
fn split_a_face_by_a_plane() {
let mut s = boxed();
run(&mut s, "solid.split_face", json!({"faces": [[10, 15, 20]], "plane": {"origin": [20, 0, 0], "normal": [1, 0, 0]}}));
let (f, v) = faces_volume(&mut s, "B");
assert_eq!(f, 7);
assert!((v - 24000.0).abs() < 1e-6 * 24000.0, "{v}");
assert_eq!(s.doc.features.last().unwrap().kind.type_name(), "SplitFaceFeature");
}
#[test]
fn split_a_face_by_another_bodys_face() {
let mut s = boxed();
run(&mut s, "solid.box", json!({"corner": [-5, 10, 25], "length": 50, "width": 5, "height": 5, "body_name": "Tool"}));
// The tool's face at y = 10, extended down through the box's top.
run(&mut s, "solid.split_face", json!({"faces": [[20, 5, 20]], "body": "B", "tool": {"body": "Tool", "point": [20, 10, 27]}}));
assert_eq!(faces_volume(&mut s, "B").0, 7);
}
#[test]
fn split_a_face_by_sketch_curves() {
let mut s = boxed();
run(&mut s, "sketch.create", json!({"plane": "XY", "name": "Cuts"}));
run(&mut s, "sketch.line", json!({"points": [[10, -5], [10, 35]]}));
run(&mut s, "sketch.line", json!({"points": [[30, -5], [30, 35]]}));
run(&mut s, "sketch.finish", json!({}));
run(&mut s, "solid.split_face", json!({"faces": [[20, 15, 20]], "body": "B", "sketch": "Cuts"}));
let (f, v) = faces_volume(&mut s, "B");
assert_eq!(f, 8, "two lines make three pieces of the top");
assert!((v - 24000.0).abs() < 1e-6 * 24000.0, "{v}");
}
#[test]
fn a_tool_that_misses_is_refused() {
let mut s = boxed();
let n = s.doc.features.len();
assert!(s.execute("solid.split_face", &json!({"faces": [[10, 15, 20]], "plane": {"origin": [100, 0, 0], "normal": [1, 0, 0]}})).is_err());
assert_eq!(s.doc.features.len(), n);
}
#[test]
fn export_keeps_split_faces_through_later_features() {
let mut s = boxed();
run(&mut s, "solid.split_face", json!({"faces": [[10, 15, 20]], "plane": {"origin": [20, 0, 0], "normal": [1, 0, 0]}}));
// A later feature rebuilds the body; the split stays a split in the file.
run(&mut s, "solid.fillet", json!({"edges": [[20, 0, 0]], "radius": 2}));
let (f, _) = faces_volume(&mut s, "B");
assert_eq!(f, 8);
let dir = std::env::temp_dir().join(format!("solidcraft-split-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let step = dir.join("split.step");
run(&mut s, "file.export", json!({"path": step.to_string_lossy()}));
let text = std::fs::read_to_string(&step).unwrap();
let _ = std::fs::remove_dir_all(&dir);
// Six box faces, the top's second piece and the fillet.
assert_eq!(text.matches("ADVANCED_FACE(").count(), 8, "{text}");
}
#[test]
fn split_pieces_count_and_move_apart() {
let mut s = boxed();
run(&mut s, "solid.split_face", json!({"faces": [[10, 15, 20]], "plane": {"origin": [20, 0, 0], "normal": [1, 0, 0]}}));
// Measure counts the two halves of the top as two faces.
let m = run(&mut s, "inspect.measure", json!({ "bodies": ["B"] }))["bodies"][0].clone();
assert_eq!(m["faces"].as_u64(), Some(7), "{m}");
// Offset Face on one half moves that half only (Fusion): a 20 × 30 step 5 high.
run(&mut s, "solid.offset_face", json!({"faces": [[10, 15, 20]], "distance": 5}));
let (_, v) = faces_volume(&mut s, "B");
assert!((v - 27000.0).abs() < 1e-6 * 27000.0, "{v}");
// And the other half down 3: a 20 × 30 pocket.
run(&mut s, "solid.offset_face", json!({"faces": [[30, 15, 20]], "distance": -3}));
let (_, v) = faces_volume(&mut s, "B");
assert!((v - 25200.0).abs() < 1e-6 * 25200.0, "{v}");
}