forked from 1b-agent/fieldcraft-upstream
geometry: tests open a closed hollow box, a post on its void floor and an open tray (#364)
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
358ed2f54f
commit
8ba59701b4
8 files changed
+488
No files matched your search
@@ -0,0 +1,3 @@
|
||||
Designs saved by SolidCraft, opened by `tests/void_shells.rs` as a person's file would be. Each
|
||||
`<name>.solidcraft` is `solidcraft-cli run <name>.json --out <name>.solidcraft`; the volumes the
|
||||
tests expect are analytic.
|
||||
@@ -0,0 +1,64 @@
|
||||
[
|
||||
{
|
||||
"command": "solid.box",
|
||||
"params": {
|
||||
"length": 40,
|
||||
"width": 30,
|
||||
"height": 20,
|
||||
"corner": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"body_name": "Case"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.shell",
|
||||
"params": {
|
||||
"faces": [
|
||||
[
|
||||
20,
|
||||
15,
|
||||
20
|
||||
]
|
||||
],
|
||||
"thickness": 2,
|
||||
"direction": "inside",
|
||||
"body": "Case"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.box",
|
||||
"params": {
|
||||
"length": 40,
|
||||
"width": 30,
|
||||
"height": 2,
|
||||
"corner": [
|
||||
0,
|
||||
0,
|
||||
20
|
||||
],
|
||||
"operation": "join",
|
||||
"targets": [
|
||||
"Case"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.cylinder",
|
||||
"params": {
|
||||
"diameter": 6,
|
||||
"height": 5,
|
||||
"base": [
|
||||
8,
|
||||
8,
|
||||
2
|
||||
],
|
||||
"operation": "join",
|
||||
"targets": [
|
||||
"Case"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"format": "solidcraft/21",
|
||||
"name": "Untitled",
|
||||
"units": "mm",
|
||||
"params": [],
|
||||
"features": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Box1",
|
||||
"suppressed": false,
|
||||
"body_names": [
|
||||
"Case"
|
||||
],
|
||||
"param_names": [
|
||||
"d1",
|
||||
"d2",
|
||||
"d3"
|
||||
],
|
||||
"type": "box",
|
||||
"corner": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"length": "40",
|
||||
"width": "30",
|
||||
"height": "20",
|
||||
"operation": "new_body"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Shell1",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d4"
|
||||
],
|
||||
"face_names": [
|
||||
"F1:+z"
|
||||
],
|
||||
"type": "shell",
|
||||
"faces": [
|
||||
[
|
||||
20.0,
|
||||
15.0,
|
||||
20.0
|
||||
]
|
||||
],
|
||||
"thickness": "2",
|
||||
"body": "Case"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Box2",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d5",
|
||||
"d6",
|
||||
"d7"
|
||||
],
|
||||
"participants": [
|
||||
"Case"
|
||||
],
|
||||
"type": "box",
|
||||
"corner": [
|
||||
0.0,
|
||||
0.0,
|
||||
20.0
|
||||
],
|
||||
"length": "40",
|
||||
"width": "30",
|
||||
"height": "2",
|
||||
"operation": "join"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Cylinder1",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d8",
|
||||
"d9"
|
||||
],
|
||||
"participants": [
|
||||
"Case"
|
||||
],
|
||||
"type": "cylinder",
|
||||
"base": [
|
||||
8.0,
|
||||
8.0,
|
||||
2.0
|
||||
],
|
||||
"axis": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"radius": "(6 mm) / 2",
|
||||
"height": "5",
|
||||
"operation": "join"
|
||||
}
|
||||
],
|
||||
"next_id": 5
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
[
|
||||
{
|
||||
"command": "solid.box",
|
||||
"params": {
|
||||
"length": 40,
|
||||
"width": 30,
|
||||
"height": 20,
|
||||
"corner": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"body_name": "Case"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.shell",
|
||||
"params": {
|
||||
"faces": [
|
||||
[
|
||||
20,
|
||||
15,
|
||||
20
|
||||
]
|
||||
],
|
||||
"thickness": 2,
|
||||
"direction": "inside",
|
||||
"body": "Case"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.box",
|
||||
"params": {
|
||||
"length": 40,
|
||||
"width": 30,
|
||||
"height": 2,
|
||||
"corner": [
|
||||
0,
|
||||
0,
|
||||
20
|
||||
],
|
||||
"operation": "join",
|
||||
"targets": [
|
||||
"Case"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"format": "solidcraft/21",
|
||||
"name": "Untitled",
|
||||
"units": "mm",
|
||||
"params": [],
|
||||
"features": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Box1",
|
||||
"suppressed": false,
|
||||
"body_names": [
|
||||
"Case"
|
||||
],
|
||||
"param_names": [
|
||||
"d1",
|
||||
"d2",
|
||||
"d3"
|
||||
],
|
||||
"type": "box",
|
||||
"corner": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"length": "40",
|
||||
"width": "30",
|
||||
"height": "20",
|
||||
"operation": "new_body"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Shell1",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d4"
|
||||
],
|
||||
"face_names": [
|
||||
"F1:+z"
|
||||
],
|
||||
"type": "shell",
|
||||
"faces": [
|
||||
[
|
||||
20.0,
|
||||
15.0,
|
||||
20.0
|
||||
]
|
||||
],
|
||||
"thickness": "2",
|
||||
"body": "Case"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Box2",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d5",
|
||||
"d6",
|
||||
"d7"
|
||||
],
|
||||
"participants": [
|
||||
"Case"
|
||||
],
|
||||
"type": "box",
|
||||
"corner": [
|
||||
0.0,
|
||||
0.0,
|
||||
20.0
|
||||
],
|
||||
"length": "40",
|
||||
"width": "30",
|
||||
"height": "2",
|
||||
"operation": "join"
|
||||
}
|
||||
],
|
||||
"next_id": 4
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
[
|
||||
{
|
||||
"command": "solid.box",
|
||||
"params": {
|
||||
"length": 40,
|
||||
"width": 30,
|
||||
"height": 20,
|
||||
"corner": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"body_name": "Case"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.shell",
|
||||
"params": {
|
||||
"faces": [
|
||||
[
|
||||
20,
|
||||
15,
|
||||
20
|
||||
]
|
||||
],
|
||||
"thickness": 2,
|
||||
"direction": "inside",
|
||||
"body": "Case"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.cylinder",
|
||||
"params": {
|
||||
"diameter": 6,
|
||||
"height": 5,
|
||||
"base": [
|
||||
8,
|
||||
8,
|
||||
2
|
||||
],
|
||||
"operation": "join",
|
||||
"targets": [
|
||||
"Case"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"format": "solidcraft/21",
|
||||
"name": "Untitled",
|
||||
"units": "mm",
|
||||
"params": [],
|
||||
"features": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Box1",
|
||||
"suppressed": false,
|
||||
"body_names": [
|
||||
"Case"
|
||||
],
|
||||
"param_names": [
|
||||
"d1",
|
||||
"d2",
|
||||
"d3"
|
||||
],
|
||||
"type": "box",
|
||||
"corner": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"length": "40",
|
||||
"width": "30",
|
||||
"height": "20",
|
||||
"operation": "new_body"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Shell1",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d4"
|
||||
],
|
||||
"face_names": [
|
||||
"F1:+z"
|
||||
],
|
||||
"type": "shell",
|
||||
"faces": [
|
||||
[
|
||||
20.0,
|
||||
15.0,
|
||||
20.0
|
||||
]
|
||||
],
|
||||
"thickness": "2",
|
||||
"body": "Case"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Cylinder1",
|
||||
"suppressed": false,
|
||||
"param_names": [
|
||||
"d5",
|
||||
"d6"
|
||||
],
|
||||
"participants": [
|
||||
"Case"
|
||||
],
|
||||
"type": "cylinder",
|
||||
"base": [
|
||||
8.0,
|
||||
8.0,
|
||||
2.0
|
||||
],
|
||||
"axis": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"radius": "(6 mm) / 2",
|
||||
"height": "5",
|
||||
"operation": "join"
|
||||
}
|
||||
],
|
||||
"next_id": 4
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//! A solid with a void shell (a box shelled, then capped) opens with every shell and everything
|
||||
//! joined to them (#364); a feature that fails to rebuild is reported naming the body it changes.
|
||||
|
||||
use fieldcraft_geometry::{Design, open_design};
|
||||
|
||||
const PI: f64 = std::f64::consts::PI;
|
||||
/// The outer box, 40 × 30 × 20 mm, and its cavity with 2 mm walls and floor.
|
||||
const BOX: f64 = 40.0 * 30.0 * 20.0;
|
||||
const CAVITY: f64 = 36.0 * 26.0 * 18.0;
|
||||
const LID: f64 = 40.0 * 30.0 * 2.0;
|
||||
/// A post ⌀6 × 5 mm standing on the floor.
|
||||
const POST: f64 = PI * 9.0 * 5.0;
|
||||
|
||||
fn open(bytes: &[u8]) -> Design {
|
||||
let design = open_design(bytes).unwrap();
|
||||
assert!(design.errors.is_empty(), "{:?}", design.errors);
|
||||
assert_eq!(design.bodies.len(), 1);
|
||||
design
|
||||
}
|
||||
|
||||
fn check(design: &Design, volume: f64, faces: usize) {
|
||||
let body = design.body("Case").unwrap();
|
||||
let m = body.measure().unwrap();
|
||||
assert!((m.volume - volume).abs() < 1e-5 * volume, "{} vs {volume}", m.volume);
|
||||
assert_eq!(m.faces, faces);
|
||||
let mut names: Vec<&str> = body.faces().iter().map(|f| f.name.as_str()).collect();
|
||||
assert_eq!(names.len(), faces);
|
||||
names.sort_unstable();
|
||||
names.dedup();
|
||||
assert_eq!(names.len(), faces, "every face has its own name");
|
||||
// The mesh sees the same solid: every face tessellated, the same enclosed volume.
|
||||
let mesh = body.tessellate(0.01).unwrap();
|
||||
let mut tags = mesh.tri_face.clone();
|
||||
tags.sort_unstable();
|
||||
tags.dedup();
|
||||
assert_eq!(tags.len(), faces);
|
||||
assert!((mesh.enclosed_volume() - volume).abs() < 1e-3 * volume, "{} vs {volume}", mesh.enclosed_volume());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_closed_hollow_box_keeps_its_void_shell() {
|
||||
// Six outside faces and six cavity faces.
|
||||
check(&open(include_bytes!("designs/hollow-box.solidcraft")), BOX - CAVITY + LID, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_post_joined_to_the_void_floor_comes_through() {
|
||||
// The hollow box's twelve faces, the post's top and its side (two halves).
|
||||
check(&open(include_bytes!("designs/hollow-box-post.solidcraft")), BOX - CAVITY + LID + POST, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_post_in_an_open_tray_comes_through() {
|
||||
// Five outside faces, the rim, five cavity faces and the post's three.
|
||||
check(&open(include_bytes!("designs/open-tray-post.solidcraft")), BOX - CAVITY + POST, 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_feature_that_fails_names_the_body_it_changes() {
|
||||
let text = std::str::from_utf8(include_bytes!("designs/hollow-box-post.solidcraft")).unwrap();
|
||||
assert!(text.contains(r#""height": "5""#));
|
||||
let broken = text.replace(r#""height": "5""#, r#""height": "0""#);
|
||||
let design = open_design(broken.as_bytes()).unwrap();
|
||||
let [error] = design.errors.as_slice() else { panic!("{:?}", design.errors) };
|
||||
assert_eq!(error.feature, "Cylinder1");
|
||||
assert_eq!(error.bodies, ["Case"]);
|
||||
let warning = error.warning();
|
||||
assert!(warning.starts_with(r#"Cylinder1 failed to build, so body "Case" may differ from the design: "#), "{warning}");
|
||||
}
|
||||
Reference in new issue
Block a user