Coil (PrimitiveCoil: diameter, two of revolutions/height/pitch, circular or square section inside/on/outside, start angle, handedness) on a new kernel helical sweep; loft to a point (sketch point sections, pyramids); recipes: a sweep along a fitted 3D helix replays as a coil (oracle 49, compared within 0.5% since Fusion's path is a spline fit) and loft point sections (oracle 37)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
4455402eab
commit
9df954cd94
12 files changed
+490
-12
No files matched your search
@@ -8,6 +8,13 @@ use crate::{read_json, recipe};
|
||||
/// Relative tolerance for volume and area (our curved-face tessellation is within ~2e-4).
|
||||
pub const REL_TOL: f64 = 1e-3;
|
||||
|
||||
/// Cases compared loosely (relative tolerance on volume and area, topology not compared), and why.
|
||||
const APPROXIMATE: [(&str, f64, &str); 1] = [(
|
||||
"49-helical-sweep",
|
||||
5e-3,
|
||||
"Fusion sweeps along a spline fitted through helix points; SolveCraft builds the true helix (a coil), so the size is compared within 0.5% and the topology not at all",
|
||||
)];
|
||||
|
||||
fn check_case(dir: &str) -> Value {
|
||||
let name = std::path::Path::new(dir).file_name().map(|x| x.to_string_lossy().to_string()).unwrap_or_default();
|
||||
let rp = format!("{dir}/recipe.json");
|
||||
@@ -38,18 +45,27 @@ fn check_case(dir: &str) -> Value {
|
||||
pass &= ok;
|
||||
checks.push(json!({"check": what, "got": g, "want": w, "ok": ok}));
|
||||
};
|
||||
// Cases where Fusion's part is itself an approximation: compare size loosely, not topology.
|
||||
let approx = APPROXIMATE.iter().find(|(c, _, _)| *c == name);
|
||||
let tol = approx.map(|(_, t, _)| *t).unwrap_or(REL_TOL);
|
||||
num("body_count", got["body_count"].as_f64(), want["body_count"].as_f64(), 0.0);
|
||||
num("volume_mm3", got["total"]["volume_mm3"].as_f64(), want["total"]["volume_mm3"].as_f64(), REL_TOL);
|
||||
num("area_mm2", got["total"]["area_mm2"].as_f64(), want["total"]["area_mm2"].as_f64(), REL_TOL);
|
||||
num("faces", got["total"]["faces"].as_f64(), want["total"]["faces"].as_f64(), 0.0);
|
||||
num("edges", got["total"]["edges"].as_f64(), want["total"]["edges"].as_f64(), 0.0);
|
||||
num("vertices", got["total"]["vertices"].as_f64(), want["total"]["vertices"].as_f64(), 0.0);
|
||||
num("volume_mm3", got["total"]["volume_mm3"].as_f64(), want["total"]["volume_mm3"].as_f64(), tol);
|
||||
num("area_mm2", got["total"]["area_mm2"].as_f64(), want["total"]["area_mm2"].as_f64(), tol);
|
||||
if approx.is_none() {
|
||||
num("faces", got["total"]["faces"].as_f64(), want["total"]["faces"].as_f64(), 0.0);
|
||||
num("edges", got["total"]["edges"].as_f64(), want["total"]["edges"].as_f64(), 0.0);
|
||||
num("vertices", got["total"]["vertices"].as_f64(), want["total"]["vertices"].as_f64(), 0.0);
|
||||
}
|
||||
// Our STEP export of the rebuilt part: well formed, and read back with no warnings and the
|
||||
// same volume.
|
||||
let (ok, note) = step_export_check(&s, got["total"]["volume_mm3"].as_f64().unwrap_or(0.0));
|
||||
pass &= ok;
|
||||
checks.push(json!({"check": "step_export", "got": note, "want": "valid, re-imports cleanly", "ok": ok}));
|
||||
json!({"case": name, "pass": pass, "checks": checks})
|
||||
let mut out = json!({"case": name, "pass": pass, "checks": checks});
|
||||
if let Some((_, _, why)) = approx {
|
||||
out["note"] = json!(why);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Export the session's bodies as STEP, validate the file and import it again.
|
||||
|
||||
Reference in new issue
Block a user