pub mod container;
pub mod decode;
pub mod deltas;
pub mod geometry;
pub mod intersection;
mod jt;
mod jt_topology;
pub(crate) mod native;
pub mod nurbs;
pub mod om;
pub mod om_tokens;
pub mod parasolid;
pub mod topology;
use std::collections::BTreeMap;
use cadmpeg_ir::codec::{
Codec, CodecError, Confidence, ContainerEntry, ContainerSummary, DecodeResult,
};
use cadmpeg_ir::decode::{DecodeContext, View};
#[derive(Debug, Default, Clone, Copy)]
pub struct NxCodec;
impl Codec for NxCodec {
fn id(&self) -> &'static str {
"nx"
}
fn detect(&self, prefix: &[u8]) -> Confidence {
if container::looks_like_nx(prefix) {
Confidence::High
} else {
Confidence::No
}
}
fn inspect_impl(
&self,
ctx: &DecodeContext<'_>,
root: View<'_>,
) -> Result<ContainerSummary, CodecError> {
let scan = decode::scan(ctx, root)?;
Ok(summarize(&scan))
}
fn decode_impl(
&self,
ctx: &DecodeContext<'_>,
root: View<'_>,
) -> Result<DecodeResult, CodecError> {
decode::decode(ctx, root)
}
}
fn summarize(scan: &decode::Scan) -> ContainerSummary {
let mut entries = Vec::new();
let semantic_streams = native::topology_streams(scan);
for entry in &scan.container.entries {
let mut attributes = BTreeMap::new();
attributes.insert("region".to_string(), entry.region.label().to_string());
let (compressed, uncompressed) = match entry.file_span {
Some((off, size)) => {
attributes.insert("file_offset".to_string(), off.to_string());
(size, size)
}
None => {
attributes.insert("kind".to_string(), "directory".to_string());
(0, 0)
}
};
entries.push(ContainerEntry {
name: entry.name.clone(),
role: "stream".to_string(),
compression: "none".to_string(),
compressed_size: compressed,
uncompressed_size: uncompressed,
attributes,
});
}
for (si, stream) in scan.streams.iter().enumerate() {
let mut attributes = BTreeMap::new();
attributes.insert("file_offset".to_string(), stream.file_offset.to_string());
attributes.insert("kind".to_string(), stream.kind.label().to_string());
if let Some(schema) = &stream.schema {
attributes.insert("schema".to_string(), schema.clone());
}
if stream.kind.is_parasolid() {
let graph = topology::Graph::parse(&stream.inflated);
for (kind, name) in [
(12, "body"),
(13, "shell"),
(14, "face"),
(15, "loop"),
(16, "edge"),
(17, "fin"),
(18, "vertex"),
(19, "region"),
] {
attributes.insert(
format!("records.{name}"),
graph.of_kind(kind).count().to_string(),
);
}
if stream.kind == parasolid::StreamKind::Partition {
let graph = topology::Graph::parse(&semantic_streams[si]);
for (kind, name) in [
(12, "body"),
(13, "shell"),
(14, "face"),
(15, "loop"),
(16, "edge"),
(17, "fin"),
(18, "vertex"),
(19, "region"),
] {
attributes.insert(
format!("records.live.{name}"),
graph.of_kind(kind).count().to_string(),
);
}
} else if stream.kind == parasolid::StreamKind::Deltas {
let census = deltas::walk(&stream.inflated);
for (family, count) in census.full_counts {
attributes.insert(
format!("records.delta.full.{}", family.to_ascii_lowercase()),
count.to_string(),
);
}
for (family, count) in census.tombstone_counts {
attributes.insert(
format!("records.delta.tombstone.{}", family.to_ascii_lowercase()),
count.to_string(),
);
}
}
}
entries.push(ContainerEntry {
name: format!("parasolid#{si}"),
role: if stream.kind.is_parasolid() {
"parasolid-stream".to_string()
} else {
"preview".to_string()
},
compression: "zlib".to_string(),
compressed_size: 0,
uncompressed_size: stream.inflated.len() as u64,
attributes,
});
}
ContainerSummary {
format: "nx".to_string(),
container_kind: "splmsstr".to_string(),
entries,
notes: decode::summary_notes(scan),
}
}
#[cfg(test)]
pub(crate) mod test_support;
#[cfg(test)]
mod tests;