M1: robust revolves touching the axis (cone shells), partial revolves toward the sketch normal, coplanar profile growth for joins/cuts, membership-checked booleans, per-face measurement fallback

Oracle: 18/29 parts match Fusion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-07 18:41:02 +00:00
1 parent 1467ad1d89
commit ac43365416
5 files changed
+279 -16

No files matched your search

+35 -10
View File
@@ -1,5 +1,5 @@
use serde::Serialize;
use solvecraft_geom::{Aabb3, Vec3};
use solvecraft_geom::{Aabb3, Mesh, Vec3};
use crate::Result;
use crate::body::Body;
@@ -22,21 +22,46 @@ pub struct BodyMeasure {
pub merged: TopoCounts,
}
/// Measure a body from a fine tessellation (chord tolerance 5e-5 of the body size). Curved
/// faces are slightly under-estimated (relative error typically below 2e-4).
/// Per-face sums: area, 6 × signed volume and 24 × first moments (divergence theorem, origin 0).
fn face_sums(m: &Mesh, nf: usize) -> Vec<(f64, f64, Vec3)> {
let mut out = vec![(0.0, 0.0, Vec3::ZERO); nf];
for (t, f) in m.triangles.iter().zip(&m.tri_face) {
let (Some([a, b, c]), Some(slot)) = (m.tri(t), out.get_mut(*f as usize)) else { continue };
let d = a.dot(b.cross(c));
slot.0 += (b - a).cross(c - a).len() * 0.5;
slot.1 += d;
slot.2 += (a + b + c) * d;
}
out
}
/// Measure a body from a fine tessellation (chord tolerance 5e-5 of the body size). Faces whose
/// fine tessellation disagrees with a coarse one (a meshing failure) use the coarse result.
pub fn measure(b: &Body) -> Result<BodyMeasure> {
let size = b.size();
let m2 = b.tessellate((size * 5e-5).max(1e-4))?;
let a2 = m2.measure();
let fine = b.tessellate((size * 5e-5).max(1e-4))?;
let coarse = b.tessellate((size * 1e-3).max(1e-3))?;
let nf = b.face_count();
let (sf, sc) = (face_sums(&fine, nf), face_sums(&coarse, nf));
let (mut area, mut v6, mut mom) = (0.0, 0.0, Vec3::ZERO);
for (f, c) in sf.iter().zip(&sc) {
let bad = (f.0 - c.0).abs() > 0.01 * c.0.max(f.0) + 1e-9;
let pick = if bad { c } else { f };
area += pick.0;
v6 += pick.1;
mom += pick.2;
}
let volume = v6 / 6.0;
let centroid = if v6.abs() > 1e-12 { mom / (4.0 * v6) } else { fine.bounds().center() };
Ok(BodyMeasure {
volume: a2.volume,
area: a2.area,
centroid: a2.centroid,
bbox: m2.bounds(),
volume,
area,
centroid,
bbox: coarse.bounds().union(&fine.bounds()),
faces: b.face_count(),
edges: b.edge_count(),
vertices: b.vertex_count(),
shells: b.shell_count(),
merged: merged_topology(b, &m2)?,
merged: merged_topology(b, &coarse)?,
})
}