forked from 1b-agent/fieldcraft-upstream
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
55 lines
1.7 KiB
Rust
55 lines
1.7 KiB
Rust
//! The command registry. Each area has its own module; [`ALL`] lists every command.
|
|
|
|
mod geometry;
|
|
mod mesh;
|
|
mod model;
|
|
|
|
use serde_json::Value;
|
|
|
|
use crate::{CommandSpec, EngineError, Result};
|
|
|
|
pub static ALL: &[CommandSpec] = &[
|
|
model::INFO,
|
|
geometry::OPEN,
|
|
geometry::BODIES,
|
|
geometry::TESSELLATE,
|
|
geometry::IMPORT_MESH,
|
|
mesh::NODE_ADD,
|
|
mesh::NODE_REMOVE,
|
|
mesh::ELEMENT_ADD,
|
|
mesh::ELEMENT_REMOVE,
|
|
];
|
|
|
|
/// A required label parameter (1..=u32::MAX; the model checks its own range).
|
|
pub(crate) fn label(p: &Value, key: &str) -> Result<u32> {
|
|
p.get(key)
|
|
.and_then(Value::as_u64)
|
|
.and_then(|v| u32::try_from(v).ok())
|
|
.ok_or_else(|| EngineError::Params(format!("`{key}` must be a positive whole number")))
|
|
}
|
|
|
|
/// A required `[x, y, z]` parameter.
|
|
pub(crate) fn xyz(p: &Value, key: &str) -> Result<[f64; 3]> {
|
|
let bad = || EngineError::Params(format!("`{key}` must be [x, y, z]"));
|
|
let a = p.get(key).and_then(Value::as_array).filter(|a| a.len() == 3).ok_or_else(bad)?;
|
|
let mut out = [0.0; 3];
|
|
for (o, v) in out.iter_mut().zip(a) {
|
|
*o = v.as_f64().ok_or_else(bad)?;
|
|
}
|
|
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)
|
|
}
|