STEP export: AP242 file of our own, names, colours, components as assemblies
Our writer assembles the file: AP242 header and contexts, a product per part (or per component, with NEXT_ASSEMBLY_USAGE_OCCURRENCE and item-defined placements per occurrence), named solids, surface colours as styled items, ADVANCED_FACE for advanced breps. truck now writes only each body's B-rep, which we copy in; identical geometric entities are written once and shared. Designs with components export as assemblies (bodies in component frames). step_validate also rejects unreferenced non-root entities. Tests: the sample plate and a component assembly export, validate and read back with no warnings and the same volume, area and face counts; the oracle replay checks area and faces too (39 rebuildable cases pass). An independent Part 21 parser (step-p21, run outside the repo) accepts our exports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
dd7485503a
commit
9330ee5698
10 files changed
+682
-14
No files matched your search
@@ -577,7 +577,8 @@ impl<'a> Reader<'a> {
|
||||
}
|
||||
|
||||
/// Check a STEP file's structure: it parses, no entity id is defined twice, every reference
|
||||
/// names a defined entity, and ids are dense (`#1`…`#n`). Returns the entity count.
|
||||
/// names a defined entity, nothing but root entities (relationships, definitions,
|
||||
/// presentation) is left unreferenced, and ids are dense (`#1`…`#n`). Returns the entity count.
|
||||
pub fn step_validate(text: &str) -> std::result::Result<usize, String> {
|
||||
let ex = p21::parse(text)?;
|
||||
if let Some(d) = ex.duplicates.first() {
|
||||
@@ -601,6 +602,36 @@ pub fn step_validate(text: &str) -> std::result::Result<usize, String> {
|
||||
return Err(format!("#{id} refers to undefined #{r}"));
|
||||
}
|
||||
}
|
||||
// Unreferenced entities must be roots of the exchange (relationships, definitions,
|
||||
// presentation); anything else is dead data.
|
||||
let mut used = std::collections::HashSet::new();
|
||||
for e in ex.entities.values() {
|
||||
let mut out = Vec::new();
|
||||
e.records.iter().flat_map(|r| &r.params).for_each(|p| refs(p, &mut out));
|
||||
used.extend(out);
|
||||
}
|
||||
const ROOTS: &[&str] = &[
|
||||
"SHAPE_DEFINITION_REPRESENTATION",
|
||||
"APPLICATION_PROTOCOL_DEFINITION",
|
||||
"PRODUCT_RELATED_PRODUCT_CATEGORY",
|
||||
"PRODUCT_CATEGORY_RELATIONSHIP",
|
||||
"SHAPE_REPRESENTATION_RELATIONSHIP",
|
||||
"REPRESENTATION_RELATIONSHIP",
|
||||
"CONTEXT_DEPENDENT_SHAPE_REPRESENTATION",
|
||||
"MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION",
|
||||
"DRAUGHTING_MODEL",
|
||||
"PROPERTY_DEFINITION_REPRESENTATION",
|
||||
"PRESENTATION_LAYER_ASSIGNMENT",
|
||||
];
|
||||
for id in &ids {
|
||||
if used.contains(*id) {
|
||||
continue;
|
||||
}
|
||||
let Some(e) = ex.get(**id) else { continue };
|
||||
if !e.records.iter().any(|r| ROOTS.contains(&r.name.as_str()) || r.name.starts_with("APPLIED_")) {
|
||||
return Err(format!("#{id} ({}) is not referenced by anything", e.name()));
|
||||
}
|
||||
}
|
||||
let n = ex.entities.len();
|
||||
if ids.last().is_some_and(|m| **m != n as u64) || ids.first().is_some_and(|m| **m != 1) {
|
||||
return Err(format!("entity ids are not dense: {n} entities, #{}…#{}", ids.first().map_or(0, |x| **x), ids.last().map_or(0, |x| **x)));
|
||||
|
||||
@@ -309,10 +309,13 @@ fn exponential_assemblies_are_bounded() {
|
||||
|
||||
#[test]
|
||||
fn validator_catches_broken_files() {
|
||||
assert!(step_validate(&format!("{HEAD}#1=A(#2);\n#2=B();\n{TAIL}")).is_ok());
|
||||
assert!(step_validate(&format!("{HEAD}#1=A(#2);\n#2=B();\n#2=C();\n{TAIL}")).unwrap_err().contains("duplicate"));
|
||||
assert!(step_validate(&format!("{HEAD}#1=SHAPE_DEFINITION_REPRESENTATION(#2);\n#2=B();\n{TAIL}")).is_ok());
|
||||
assert!(
|
||||
step_validate(&format!("{HEAD}#1=SHAPE_DEFINITION_REPRESENTATION(#2);\n#2=B();\n#3=C();\n{TAIL}")).unwrap_err().contains("not referenced")
|
||||
);
|
||||
assert!(step_validate(&format!("{HEAD}#1=SHAPE_DEFINITION_REPRESENTATION(#2);\n#2=B();\n#2=C();\n{TAIL}")).unwrap_err().contains("duplicate"));
|
||||
assert!(step_validate(&format!("{HEAD}#1=A((#7));\n{TAIL}")).unwrap_err().contains("undefined"));
|
||||
assert!(step_validate(&format!("{HEAD}#1=A();\n#5=B();\n{TAIL}")).unwrap_err().contains("dense"));
|
||||
assert!(step_validate(&format!("{HEAD}#1=SHAPE_DEFINITION_REPRESENTATION(#5);\n#5=B();\n{TAIL}")).unwrap_err().contains("dense"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user