brep_kernel/io/iges/
export.rs1use crate::step::edge_subcurve;
14use crate::topology::{BrepSolid, EdgeRecord, FaceRecord, LoopRecord};
15
16use super::entities::{
17 composite_102, curve_on_surface_142, curve_to_126, surface_to_128, trimmed_surface_144,
18};
19use super::writer::{GlobalParams, IgesWriter};
20
21pub fn export_iges(
27 solids: &[BrepSolid],
28 name: &str,
29 unit: &str,
30 timestamp: &str,
31) -> Result<String, String> {
32 if solids.is_empty() {
33 return Err("iges_export: no solids to export".into());
34 }
35 let (units_flag, units_name) = units_for(unit);
36
37 let mut writer = IgesWriter::new();
38 writer.add_start_line(&format!("IGES export of '{name}' from the BREP kernel."));
39
40 let mut face_count = 0usize;
41 for solid in solids {
42 for shell in &solid.shells {
43 for face in &shell.faces {
44 export_face(&mut writer, solid, face)?;
45 face_count += 1;
46 }
47 }
48 }
49 if face_count == 0 {
50 return Err("iges_export: solids contain no faces".into());
51 }
52
53 let global = GlobalParams {
54 product_id: if name.is_empty() { "PART".into() } else { name.to_string() },
55 file_name: format!("{}.igs", if name.is_empty() { "part" } else { name }),
56 units_flag,
57 units_name,
58 timestamp: normalize_timestamp(timestamp),
59 min_resolution: 1e-7,
60 };
61 Ok(writer.finish(global))
62}
63
64fn export_face(writer: &mut IgesWriter, solid: &BrepSolid, face: &FaceRecord) -> Result<(), String> {
65 if face.loops.is_empty() {
66 return Err(format!("iges_export: face {} has no loops", face.id));
67 }
68 let sptr = writer.add_entity(surface_to_128(&face.surface)?);
69
70 let mut loop_ptrs: Vec<i64> = Vec::with_capacity(face.loops.len());
71 for loop_record in &face.loops {
72 loop_ptrs.push(export_loop(writer, solid, sptr, loop_record)?);
73 }
74 let (outer, holes) = loop_ptrs
75 .split_first()
76 .ok_or_else(|| format!("iges_export: face {} produced no boundaries", face.id))?;
77 writer.add_entity(trimmed_surface_144(sptr, *outer, holes));
78 Ok(())
79}
80
81fn export_loop(
84 writer: &mut IgesWriter,
85 solid: &BrepSolid,
86 sptr: i64,
87 loop_record: &LoopRecord,
88) -> Result<i64, String> {
89 if loop_record.coedges.is_empty() {
90 return Err(format!("iges_export: loop {} has no coedges", loop_record.id));
91 }
92 let mut model_ptrs: Vec<i64> = Vec::with_capacity(loop_record.coedges.len());
93 let mut param_ptrs: Vec<i64> = Vec::with_capacity(loop_record.coedges.len());
94 for coedge in &loop_record.coedges {
95 let edge = edge_for(solid, coedge.edge_id)?;
96 let mut model_curve = edge_subcurve(edge)?;
99 if !coedge.forward {
100 model_curve = model_curve.reversed()?;
101 }
102 model_ptrs.push(writer.add_entity(curve_to_126(&model_curve)?));
103 param_ptrs.push(writer.add_entity(curve_to_126(&coedge.pcurve)?));
105 }
106 let model_composite = writer.add_entity(composite_102(&model_ptrs));
107 let param_composite = writer.add_entity(composite_102(¶m_ptrs));
108 Ok(writer.add_entity(curve_on_surface_142(sptr, param_composite, model_composite)))
109}
110
111fn edge_for<'a>(solid: &'a BrepSolid, id: u64) -> Result<&'a EdgeRecord, String> {
112 solid
113 .edges
114 .iter()
115 .find(|e| e.id == id)
116 .ok_or_else(|| format!("iges_export: dangling edge id {id}"))
117}
118
119fn units_for(unit: &str) -> (i64, String) {
120 match unit.trim().to_ascii_lowercase().as_str() {
121 "inch" | "inches" | "in" => (1, "IN".into()),
122 "foot" | "feet" | "ft" => (4, "FT".into()),
123 "meter" | "metre" | "m" => (6, "M".into()),
124 "centimeter" | "centimetre" | "cm" => (10, "CM".into()),
125 _ => (2, "MM".into()),
126 }
127}
128
129fn normalize_timestamp(timestamp: &str) -> String {
130 if timestamp.trim().is_empty() {
131 "00000000.000000".into()
132 } else {
133 timestamp.to_string()
134 }
135}