feat: preserve adaptive sketch profiles and curved modeling workflows

This commit is contained in:
bherbruck committed 2026-10-11 09:06:25 -04:00
1 parent 4c4a0449a4
commit 4e595deff2
62 files changed
+7114 -362

No files matched your search

@@ -465,3 +465,57 @@ fn transferred_frozen_face_paint_ignores_consumed_legacy_offset() {
let paint = s.doc.appearances.faces.iter().find(|face| face.body == "Foreign (copy)").unwrap();
assert!(paint.point.dist(expected) < 1e-6, "{:?} versus {expected:?}", paint.point);
}
#[test]
fn boundary_material_proof_requires_resolvable_stock_and_void() {
use solvecraft_geom::Vec3;
let a = solvecraft_kernel::box_solid(Vec3::ZERO, Vec3::new(10., 10., 10.)).unwrap().tessellate(1e-3).unwrap();
let b = solvecraft_kernel::box_solid(Vec3::new(1e-5, 0., 0.), Vec3::new(10.00001, 10., 10.)).unwrap().tessellate(1e-3).unwrap();
let p = Vec3::new(5e-6, 5., 5.);
assert_ne!(a.contains(p), b.contains(p));
assert!((super::mesh_distance(&a, p) - 5e-6).abs() < 1e-12);
assert!((super::mesh_distance(&b, p) - 5e-6).abs() < 1e-12);
assert!(super::material_probe_matches(&a, &b, p, 1e-3, 1e-3));
let shifted = solvecraft_kernel::box_solid(Vec3::new(1., 0., 0.), Vec3::new(11., 10., 10.)).unwrap().tessellate(1e-3).unwrap();
assert!(!super::material_probe_matches(&a, &shifted, p, 1e-3, 1e-3));
let thin = solvecraft_kernel::box_solid(Vec3::ZERO, Vec3::new(10., 10., 1e-4)).unwrap().tessellate(1e-6).unwrap();
let thin_shifted = solvecraft_kernel::box_solid(Vec3::new(0., 0., 1e-4), Vec3::new(10., 10., 2e-4)).unwrap().tessellate(1e-6).unwrap();
// Every resolvable replacement is void: thin ambiguous material is refused.
assert!(!super::material_probe_matches(&thin, &thin_shifted, Vec3::new(5., 5., 5e-5), 1e-3, 1e-3));
assert!(!super::material_probe_matches(&a, &b, p, f64::INFINITY, 1e-3));
}
#[test]
fn frozen_bore_boundary_copy_preserves_stock_void_and_reopen() {
use solvecraft_geom::Vec3;
let bore = |center: f64| {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length":30,"width":20,"height":10,"body_name":"Drilled"}));
run(&mut s, "solid.cylinder", json!({"base":[center,10,0],"radius":4,"height":10,"operation":"cut"}));
s
};
let mut s = bore(10.);
let source = s.model.state().body("Drilled").unwrap().body.tessellate(1e-3).unwrap();
// The exact R4 wall passes through x6; a chord may end either side of that
// cardinal angle, so its distance is bounded by the requested sag tolerance.
assert!(super::mesh_distance(&source, Vec3::new(6., 10., 5.)) <= 1e-3);
let shifted = bore(11.).model.state().body("Drilled").unwrap().body.tessellate(1e-3).unwrap();
let lost = solvecraft_kernel::box_solid(Vec3::ZERO, Vec3::new(30., 20., 10.)).unwrap().tessellate(1e-3).unwrap();
assert!(!super::material_probe_matches(&source, &shifted, Vec3::new(6.5, 10., 5.), 1e-3, 1e-3));
assert!(!super::material_probe_matches(&source, &lost, Vec3::new(10., 10., 5.), 1e-3, 1e-3));
let created = run(&mut s, "component.from_bodies", json!({"bodies":["Drilled"]}));
let part = created["components"][0]["component"].as_u64().unwrap();
let copied = copy(&mut s, part)["component"].as_u64().unwrap();
let check = |session: &Session| {
let state = session.model.state();
let body = state.bodies.iter().find(|body| session.doc.model_body_component(body) == copied).unwrap();
let mesh = body.body.tessellate(1e-3).unwrap();
for (point, stock) in
[(Vec3::new(5.5, 10., 5.), true), (Vec3::new(6.5, 10., 5.), false), (Vec3::new(10., 10., 5.), false), (Vec3::new(18., 10., 5.), true)]
{
assert_eq!(mesh.contains(point), stock, "{point:?}");
}
};
check(&s);
check(&Session::new(solvecraft_doc::Document::from_json(&s.doc.to_json()).unwrap()));
}