geometry: virtual faces, adjacent faces merged for meshing only (#505)

A Merge makes adjacent faces of a body one virtual face in what meshers
take from it (tessellation tags, faces, surfaces, edge and vertex names):
the edges inside it are dropped and the pieces of an edge it leaves are
joined; the B-rep is unchanged. Design::merged applies merges after the
partitions and lists the ones that fail as feature errors.

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 11:09:11 -04:00
1 parent f38a3be917
commit b126c2293b
5 files changed
+463 -20

No files matched your search

+12 -4
View File
@@ -27,6 +27,9 @@ pub struct Design {
pub cuts: Vec<crate::partition::CutReport>,
/// The bodies as opened, before any partition: what [`Design::partitioned`] starts from.
pub(crate) source: std::sync::Arc<Vec<Shape>>,
/// The bodies as opened with the face merges on them ([`Design::merged`]): what
/// [`Design::source_body`] and [`Design::features`] describe.
pub(crate) opened: std::sync::Arc<Vec<Shape>>,
/// The timeline the design was rebuilt from ([`Design::features`]).
pub(crate) history: Option<std::sync::Arc<crate::features::History>>,
}
@@ -79,9 +82,9 @@ impl Design {
self.bodies.iter().find(|b| b.name == name)
}
/// The body `name` of the design as opened (before any partition).
/// The body `name` of the design as opened (before any partition), with its merged faces.
pub fn source_body(&self, name: &str) -> Option<&Shape> {
self.source.iter().find(|b| b.name == name)
self.opened.iter().find(|b| b.name == name)
}
/// The body of the design as opened that `name` is, or that partitions cut it from
@@ -120,9 +123,13 @@ pub(crate) fn rebuild(doc: &Document) -> Result<Design> {
for b in &state.bodies {
let faces = guard("name faces", || {
let names = naming::face_names(b);
Ok(names.iter().enumerate().map(|(i, name)| Face { name: name.clone(), exact: naming::face_name_exact(b, i) }).collect())
Ok(names
.iter()
.enumerate()
.map(|(i, name)| Face { name: name.clone(), exact: naming::face_name_exact(b, i), members: Vec::new() })
.collect())
})?;
bodies.push(Shape { name: b.name.clone(), body: b.body.clone(), faces });
bodies.push(Shape { name: b.name.clone(), body: b.body.clone(), faces, face_map: Vec::new() });
}
// A feature that failed leaves a different solid than SolidCraft's: each one names the bodies
// it would have changed, so opening never hands back a silently different solid.
@@ -152,6 +159,7 @@ pub(crate) fn rebuild(doc: &Document) -> Result<Design> {
Ok(Design {
name: doc.name.clone(),
source: std::sync::Arc::new(bodies.clone()),
opened: std::sync::Arc::new(bodies.clone()),
bodies,
errors,
cuts: Vec::new(),