engine: geometry.stitch, imported surfaces stitched for meshing (#507)

geometry.stitch {name, surface, tolerance} records a stitch; the
surfaces keep what was read (TaggedSurface::original) and are stitched
again from it whenever a surface is imported, the model opened, or a
stitch added or deleted (geometry.stitch.delete). Opening a model also
warns about merges that no longer apply.

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 12:07:16 -04:00
1 parent 12b3128f53
commit 4e6fd59409
15 files changed
+259 -8

No files matched your search

+12 -2
View File
@@ -520,7 +520,17 @@ fn import_mesh(s: &mut Session, p: &Value) -> Result<Value> {
.collect();
s.surfaces.extend(surfaces.into_iter().map(Arc::new));
s.sources.meshes.push(source);
Ok(json!({"path": path, "surfaces": added}))
// The model's stitches of surfaces by these names apply to them too.
let (_, failed) = super::stitch::restitch(s);
let mut reply = json!({"path": path, "surfaces": added});
let warnings: Vec<String> =
failed.into_iter().filter(|(_, why)| !why.starts_with("no surface")).map(|(n, why)| format!("stitch `{n}`: {why}")).collect();
if !warnings.is_empty()
&& let Some(o) = reply.as_object_mut()
{
o.insert("warnings".into(), json!(warnings));
}
Ok(reply)
}
#[cfg(test)]
@@ -660,7 +670,7 @@ pub(crate) mod tests {
}
}
fn package(model: &str) -> Vec<u8> {
pub(crate) fn package(model: &str) -> Vec<u8> {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
zip.start_file("3D/3dmodel.model", zip::write::SimpleFileOptions::default()).unwrap();
zip.write_all(model.as_bytes()).unwrap();