pub(crate) struct IgesEntity {
pub entity_type: i64,
pub params: Vec<String>,
pub form: i64,
pub label: String,
}
impl IgesEntity {
pub fn new(entity_type: i64, params: Vec<String>) -> Self {
Self {
entity_type,
params,
form: 0,
label: String::new(),
}
}
}
pub(crate) struct GlobalParams {
pub product_id: String,
pub file_name: String,
pub units_flag: i64,
pub units_name: String,
pub timestamp: String,
pub min_resolution: f64,
}
pub(crate) struct IgesWriter {
start_lines: Vec<String>,
entities: Vec<IgesEntity>,
}
impl IgesWriter {
pub fn new() -> Self {
Self {
start_lines: Vec::new(),
entities: Vec::new(),
}
}
pub fn add_start_line(&mut self, text: &str) {
self.start_lines.push(text.to_string());
}
pub fn add_entity(&mut self, entity: IgesEntity) -> i64 {
let index = self.entities.len();
self.entities.push(entity);
(2 * index + 1) as i64
}
pub fn finish(self, global: GlobalParams) -> String {
let mut pd_lines: Vec<String> = Vec::new();
let mut pd_extent: Vec<(i64, i64)> = Vec::with_capacity(self.entities.len());
for (index, entity) in self.entities.iter().enumerate() {
let de_pointer = (2 * index + 1) as i64;
let mut fields = Vec::with_capacity(entity.params.len() + 1);
fields.push(entity.entity_type.to_string());
fields.extend(entity.params.iter().cloned());
let data_lines = wrap_free_format(&fields, 64);
let start_seq = pd_lines.len() as i64 + 1;
for data in &data_lines {
let seq = pd_lines.len() as i64 + 1;
pd_lines.push(format!(
"{:<64}{:>8}P{:>7}",
data, de_pointer, seq
));
}
pd_extent.push((start_seq, data_lines.len() as i64));
}
let mut de_lines: Vec<String> = Vec::with_capacity(self.entities.len() * 2);
for (index, entity) in self.entities.iter().enumerate() {
let seq1 = (2 * index + 1) as i64;
let seq2 = seq1 + 1;
let (pd_start, pd_count) = pd_extent[index];
let line1 = format!(
"{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}{}D{:>7}",
entity.entity_type, pd_start, 0, 0, 0, 0, 0, 0, "00000000", seq1,
);
let label = truncate_right(&entity.label, 8);
let line2 = format!(
"{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}{:>8}D{:>7}",
entity.entity_type, 0, 0, pd_count, entity.form, "", "", label, 0, seq2,
);
de_lines.push(line1);
de_lines.push(line2);
}
let global_fields = build_global_fields(&global);
let global_data_lines = wrap_free_format(&global_fields, 72);
let mut global_lines: Vec<String> = Vec::with_capacity(global_data_lines.len());
for (i, data) in global_data_lines.iter().enumerate() {
global_lines.push(format!("{:<72}G{:>7}", data, i as i64 + 1));
}
let start_source: Vec<String> = if self.start_lines.is_empty() {
vec!["Generated by the BREP kernel IGES exporter.".to_string()]
} else {
self.start_lines.clone()
};
let mut start_lines: Vec<String> = Vec::with_capacity(start_source.len());
for (i, text) in start_source.iter().enumerate() {
start_lines.push(format!("{:<72}S{:>7}", truncate_right_pad(text, 72), i as i64 + 1));
}
let terminate = format!(
"{:<72}T{:>7}",
format!(
"S{:>7}G{:>7}D{:>7}P{:>7}",
start_lines.len(),
global_lines.len(),
de_lines.len(),
pd_lines.len()
),
1
);
let mut out = String::new();
for line in start_lines
.iter()
.chain(global_lines.iter())
.chain(de_lines.iter())
.chain(pd_lines.iter())
{
out.push_str(line);
out.push('\n');
}
out.push_str(&terminate);
out.push('\n');
out
}
}
fn build_global_fields(g: &GlobalParams) -> Vec<String> {
vec![
hollerith(","), hollerith(";"), hollerith(&g.product_id), hollerith(&g.file_name), hollerith("BREP-kernel-rs"), hollerith("BREP-IGES-1.0"), "32".to_string(), "38".to_string(), "6".to_string(), "308".to_string(), "15".to_string(), hollerith(&g.product_id), real_field(1.0), g.units_flag.to_string(), hollerith(&g.units_name), "1".to_string(), real_field(0.0), hollerith(&g.timestamp), real_field(g.min_resolution), real_field(0.0), hollerith("BREP kernel"), hollerith("Autodrop3d"), "11".to_string(), "0".to_string(), ]
}
pub(crate) fn hollerith(text: &str) -> String {
format!("{}H{}", text.chars().count(), text)
}
pub(crate) fn real_field(value: f64) -> String {
let mut s = format!("{}", value);
if !s.contains('.') && !s.contains('e') && !s.contains('E') {
s.push('.');
}
s
}
fn truncate_right(text: &str, width: usize) -> String {
let chars: Vec<char> = text.chars().collect();
if chars.len() > width {
chars[chars.len() - width..].iter().collect()
} else {
text.to_string()
}
}
fn truncate_right_pad(text: &str, width: usize) -> String {
let chars: Vec<char> = text.chars().collect();
if chars.len() > width {
chars[..width].iter().collect()
} else {
text.to_string()
}
}
fn wrap_free_format(fields: &[String], width: usize) -> Vec<String> {
let mut tokens: Vec<String> = Vec::with_capacity(fields.len());
let count = fields.len();
for (i, field) in fields.iter().enumerate() {
let sep = if i + 1 == count { ';' } else { ',' };
tokens.push(format!("{}{}", field, sep));
}
let mut lines: Vec<String> = Vec::new();
let mut current = String::new();
for token in tokens {
if !current.is_empty() && current.len() + token.len() > width {
lines.push(std::mem::take(&mut current));
}
current.push_str(&token);
}
if !current.is_empty() {
lines.push(current);
}
if lines.is_empty() {
lines.push(String::new());
}
lines
}