mesh: projection and relaxation keep to a merged face's B-rep pieces

A merged face on several surfaces (a sliver absorbed into its neighbour)
had no exact surface, so the projection target fell back to its
triangles: flat curvature probes gave fewer divisions round the boss and
the enclosure base's symmetric mesh dropped from 0.70 to 0.51 once its
absorbs reached the cells. The target now projects a point of such a
face onto the exact surface of the B-rep piece it is on, trimmed to that
piece (Tessellation::with_pieces, Shape::tessellate_pieces); node
relaxation slides on it the same way.

Test from commands: the enclosure base's four slivers absorbed, meshed
whole and by symmetry, the absorbs kept in every cell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-16 15:25:37 -04:00
1 parent 09385f069f
commit a2e747b9ba
7 files changed
+186 -61

No files matched your search

+14 -2
View File
@@ -42,6 +42,9 @@ pub(super) struct Input<'a> {
/// The design's edge seeds ([`super::seeds::edge_seeds`]); the mesher applies those on its
/// edges.
pub(super) seeds: Vec<fieldcraft_mesh::seeds::EdgeSeed>,
/// Per triangle of `mesh`, the B-rep face it lies on: its piece of a merged face (empty for
/// a surface that is not a body's).
pub(super) pieces: Vec<u32>,
}
fn analytic(s: FaceSurface) -> Surface {
@@ -73,7 +76,8 @@ pub(super) fn shape_input(shape: &Shape, tolerance: Option<f64>) -> Result<Input
}
let surfaces = shape.face_surfaces()?.into_iter().map(|f| f.map(analytic)).collect();
let face_names = shape.faces().iter().map(|f| f.name.clone()).collect();
Ok(Input { name: shape.name.clone(), mesh: Cow::Owned(shape.tessellate(tol)?), surfaces, face_names, diagonal: diag, seeds: Vec::new() })
let (mesh, pieces) = shape.tessellate_pieces(tol)?;
Ok(Input { name: shape.name.clone(), mesh: Cow::Owned(mesh), surfaces, face_names, diagonal: diag, seeds: Vec::new(), pieces })
}
/// The imported surface `surface` names, with the body name its mesh is tagged with.
@@ -109,7 +113,15 @@ pub(super) fn inputs<'a>(s: &'a Session, p: &Value) -> Result<Vec<Input<'a>>> {
}
// Faces named by index, as mesh.tet.generate names them.
let face_names = (0..t.faces).map(|f| f.to_string()).collect();
Ok(vec![Input { name, mesh: Cow::Borrowed(&t.mesh), surfaces: Vec::new(), face_names, diagonal: diagonal(lo, hi), seeds: Vec::new() }])
Ok(vec![Input {
name,
mesh: Cow::Borrowed(&t.mesh),
surfaces: Vec::new(),
face_names,
diagonal: diagonal(lo, hi),
seeds: Vec::new(),
pieces: Vec::new(),
}])
}
(None, None) => {
let design = s.design.as_deref().ok_or_else(|| EngineError::Params("open a design first, or give `surface`".into()))?;