use std::fmt::Write as FmtWrite;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct StepExportConfig {
pub author: String,
pub organisation: String,
pub originating_system: String,
pub description: String,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct StepEntity {
pub id: u32,
pub entity_type: String,
pub params: Vec<String>,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct StepDocument {
pub config: StepExportConfig,
pub entities: Vec<StepEntity>,
next_id: u32,
}
#[allow(dead_code)]
pub fn default_step_config() -> StepExportConfig {
StepExportConfig {
author: "OxiHuman".to_string(),
organisation: "COOLJAPAN OU".to_string(),
originating_system: "OxiHuman STEP Exporter 1.0".to_string(),
description: "STEP export generated by OxiHuman".to_string(),
}
}
#[allow(dead_code)]
pub fn new_step_document(cfg: &StepExportConfig) -> StepDocument {
StepDocument {
config: cfg.clone(),
entities: Vec::new(),
next_id: 1,
}
}
#[allow(dead_code)]
pub fn step_add_entity(
doc: &mut StepDocument,
entity_type: &str,
params: Vec<String>,
) -> u32 {
let id = doc.next_id;
doc.next_id += 1;
doc.entities.push(StepEntity {
id,
entity_type: entity_type.to_string(),
params,
});
id
}
#[allow(dead_code)]
pub fn step_to_string(doc: &StepDocument) -> String {
let mut out = String::new();
out.push_str(&step_header_string(doc));
out.push_str("DATA;\n");
for ent in &doc.entities {
let params = ent.params.join(",");
let _ = writeln!(out, "#{} = {}({});", ent.id, ent.entity_type, params);
}
out.push_str("ENDSEC;\nEND-ISO-10303-21;\n");
out
}
#[allow(dead_code)]
pub fn step_write_to_file(doc: &StepDocument, path: &str) -> Result<(), String> {
let content = step_to_string(doc);
std::fs::write(path, content).map_err(|e| e.to_string())
}
#[allow(dead_code)]
pub fn step_entity_count(doc: &StepDocument) -> usize {
doc.entities.len()
}
#[allow(dead_code)]
pub fn step_add_cartesian_point(doc: &mut StepDocument, x: f64, y: f64, z: f64) -> u32 {
step_add_entity(
doc,
"CARTESIAN_POINT",
vec![
"''".to_string(),
format!("({x:.6},{y:.6},{z:.6})"),
],
)
}
#[allow(dead_code)]
pub fn step_add_face(doc: &mut StepDocument, point_refs: &[u32]) -> u32 {
let refs: Vec<String> = point_refs.iter().map(|r| format!("#{r}")).collect();
let list = format!("({})", refs.join(","));
step_add_entity(doc, "ADVANCED_FACE", vec!["''".to_string(), list, ".T.".to_string()])
}
#[allow(dead_code)]
pub fn step_header_string(doc: &StepDocument) -> String {
format!(
"ISO-10303-21;\nHEADER;\nFILE_DESCRIPTION(('{}'),'2;1');\n\
FILE_NAME('','',('{}'),('{}'),'{}','OxiHuman','');\n\
FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));\nENDSEC;\n",
doc.config.description,
doc.config.author,
doc.config.organisation,
doc.config.originating_system,
)
}
#[allow(dead_code)]
pub fn step_document_clear(doc: &mut StepDocument) {
doc.entities.clear();
doc.next_id = 1;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config_fields() {
let cfg = default_step_config();
assert!(!cfg.author.is_empty());
assert!(!cfg.organisation.is_empty());
assert!(!cfg.originating_system.is_empty());
}
#[test]
fn test_new_document_empty() {
let cfg = default_step_config();
let doc = new_step_document(&cfg);
assert_eq!(step_entity_count(&doc), 0);
}
#[test]
fn test_add_entity_increments_count() {
let cfg = default_step_config();
let mut doc = new_step_document(&cfg);
step_add_entity(&mut doc, "CARTESIAN_POINT", vec!["''".to_string()]);
assert_eq!(step_entity_count(&doc), 1);
}
#[test]
fn test_add_entity_ids_sequential() {
let cfg = default_step_config();
let mut doc = new_step_document(&cfg);
let id1 = step_add_entity(&mut doc, "TYPE_A", vec![]);
let id2 = step_add_entity(&mut doc, "TYPE_B", vec![]);
assert_eq!(id1, 1);
assert_eq!(id2, 2);
}
#[test]
fn test_step_to_string_contains_header() {
let cfg = default_step_config();
let doc = new_step_document(&cfg);
let s = step_to_string(&doc);
assert!(s.contains("ISO-10303-21;"));
assert!(s.contains("HEADER;"));
assert!(s.contains("END-ISO-10303-21;"));
}
#[test]
fn test_step_add_cartesian_point() {
let cfg = default_step_config();
let mut doc = new_step_document(&cfg);
let id = step_add_cartesian_point(&mut doc, 1.0, 2.0, 3.0);
assert_eq!(id, 1);
let s = step_to_string(&doc);
assert!(s.contains("CARTESIAN_POINT"));
}
#[test]
fn test_step_add_face() {
let cfg = default_step_config();
let mut doc = new_step_document(&cfg);
let p1 = step_add_cartesian_point(&mut doc, 0.0, 0.0, 0.0);
let p2 = step_add_cartesian_point(&mut doc, 1.0, 0.0, 0.0);
let p3 = step_add_cartesian_point(&mut doc, 0.0, 1.0, 0.0);
let face_id = step_add_face(&mut doc, &[p1, p2, p3]);
assert_eq!(face_id, 4);
assert_eq!(step_entity_count(&doc), 4);
}
#[test]
fn test_document_clear() {
let cfg = default_step_config();
let mut doc = new_step_document(&cfg);
step_add_cartesian_point(&mut doc, 0.0, 0.0, 0.0);
step_document_clear(&mut doc);
assert_eq!(step_entity_count(&doc), 0);
let id = step_add_entity(&mut doc, "RESET_CHECK", vec![]);
assert_eq!(id, 1);
}
#[test]
fn test_step_header_string_contains_author() {
let mut cfg = default_step_config();
cfg.author = "TestAuthor".to_string();
let doc = new_step_document(&cfg);
let hdr = step_header_string(&doc);
assert!(hdr.contains("TestAuthor"));
}
}