use cadrum::DVec3;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use crate::Result;
pub struct Filaments {
pub nfp: u32,
pub coils: Vec<Vec<DVec3>>,
}
pub fn parse(path: &Path) -> Result<Filaments> {
let file = File::open(path).map_err(|e| format!("open {}: {}", path.display(), e))?;
let reader = BufReader::new(file);
let mut nfp: Option<u32> = None;
let mut coils: Vec<Vec<DVec3>> = Vec::new();
let mut current: Vec<DVec3> = Vec::new();
let mut header_seen = 0;
for (line_num, line_res) in reader.lines().enumerate() {
let line = line_res.map_err(|e| format!("read line {}: {}", line_num + 1, e))?;
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
if trimmed.starts_with("end") {
break;
}
if header_seen < 3 {
if let Some(rest) = trimmed.strip_prefix("periods") {
nfp = Some(
rest.trim()
.parse::<u32>()
.map_err(|e| format!("parse nfp at line {}: {}", line_num + 1, e))?,
);
header_seen += 1;
continue;
}
if trimmed.starts_with("begin filament") || trimmed.starts_with("mirror") {
header_seen += 1;
continue;
}
}
let fields: Vec<&str> = trimmed.split_whitespace().collect();
if fields.len() < 4 {
return Err(format!(
"line {}: expected at least 4 fields, got {}",
line_num + 1,
fields.len()
)
.into());
}
let x: f64 = fields[0]
.parse()
.map_err(|e| format!("parse x at line {}: {}", line_num + 1, e))?;
let y: f64 = fields[1]
.parse()
.map_err(|e| format!("parse y at line {}: {}", line_num + 1, e))?;
let z: f64 = fields[2]
.parse()
.map_err(|e| format!("parse z at line {}: {}", line_num + 1, e))?;
let i: f64 = fields[3]
.parse()
.map_err(|e| format!("parse current at line {}: {}", line_num + 1, e))?;
current.push(DVec3::new(x, y, z));
if i == 0.0 {
coils.push(std::mem::take(&mut current));
}
}
if !current.is_empty() {
coils.push(current);
}
let nfp = nfp.ok_or("no 'periods' header found")?;
Ok(Filaments { nfp, coils })
}