Face-tagged 3MF exchange: format spec, reader, geometry.import.mesh

docs/formats/tagged-mesh.md defines the exchange: 3MF Core with an optional
ft:face attribute per triangle (and ft:faces per object) in a ModelCraft
namespace. modelcraft_geometry::tagged_mesh::read_3mf reads it from bytes
(wasm), capping every size and count and range-checking every index and
coordinate. geometry.import.mesh adds the surfaces to the session, which keeps
them beside the open design and restores them when a command fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-13 14:48:22 -04:00
1 parent f36b1d9a69
commit f181c98b75
13 files changed
+791 -6

No files matched your search

+15
View File
@@ -13,6 +13,7 @@ pub static ALL: &[CommandSpec] = &[
geometry::OPEN,
geometry::BODIES,
geometry::TESSELLATE,
geometry::IMPORT_MESH,
mesh::NODE_ADD,
mesh::NODE_REMOVE,
mesh::ELEMENT_ADD,
@@ -37,3 +38,17 @@ pub(crate) fn xyz(p: &Value, key: &str) -> Result<[f64; 3]> {
}
Ok(out)
}
/// A file's bytes, refusing anything over `cap`.
pub(crate) fn read_capped(path: &str, cap: usize) -> Result<Vec<u8>> {
use std::io::Read;
let err = |e: std::io::Error| EngineError::File(format!("{path}: {e}"));
let f = std::fs::File::open(path).map_err(err)?;
let len = f.metadata().map(|m| m.len()).unwrap_or(0);
let mut bytes = Vec::with_capacity(usize::try_from(len).unwrap_or(0).min(cap));
f.take(cap as u64 + 1).read_to_end(&mut bytes).map_err(err)?;
if bytes.len() > cap {
return Err(EngineError::File(format!("{path}: larger than {} MB", cap >> 20)));
}
Ok(bytes)
}