use std::fs;
use std::io::{BufRead, BufReader, BufWriter, Write};
use crate::{Error, Job, Result};
pub fn stl_to_obj(job: &Job) -> Result<()> {
let mut file = fs::File::open(job.input)?;
let stl = stl_io::read_stl(&mut file)
.map_err(|e| Error::other(format!("stl parse: {e}")))?;
let out = fs::File::create(job.output)?;
let mut w = BufWriter::new(out);
writeln!(w, "o Solid Object")?;
writeln!(w)?;
for v in &stl.vertices {
writeln!(w, "v {} {} {}", v[0], v[1], v[2])?;
}
writeln!(w)?;
for t in &stl.faces {
writeln!(
w,
"f {} {} {}",
t.vertices[0] + 1,
t.vertices[1] + 1,
t.vertices[2] + 1,
)?;
}
w.flush()?;
Ok(())
}
pub fn stl_to_png(job: &Job) -> Result<()> {
crate::converters::scad::stl_to_png(job)
}
pub fn obj_to_stl(job: &Job) -> Result<()> {
let file = fs::File::open(job.input)?;
let reader = BufReader::new(file);
let mut vertices: Vec<[f32; 3]> = Vec::new();
let mut faces: Vec<Vec<usize>> = Vec::new();
for line in reader.lines() {
let line = line?;
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let mut parts = trimmed.split_whitespace();
match parts.next() {
Some("v") => {
let nums: Vec<f32> = parts
.take(3)
.map(|s| s.parse::<f32>())
.collect::<std::result::Result<_, _>>()
.map_err(|e| Error::other(format!("obj: bad vertex: {e}")))?;
if nums.len() != 3 {
return Err(Error::other("obj: vertex needs 3 coordinates"));
}
vertices.push([nums[0], nums[1], nums[2]]);
}
Some("f") => {
let indices: Vec<usize> = parts
.map(|tok| {
let idx_str = tok.split('/').next().unwrap_or(tok);
idx_str.parse::<i64>().map(|i| {
if i > 0 {
(i - 1) as usize
} else if i < 0 {
((vertices.len() as i64) + i) as usize
} else {
0
}
})
})
.collect::<std::result::Result<_, _>>()
.map_err(|e| Error::other(format!("obj: bad face: {e}")))?;
if indices.len() >= 3 {
faces.push(indices);
}
}
_ => {}
}
}
let mut triangles: Vec<stl_io::Triangle> = Vec::new();
for face in &faces {
for i in 1..face.len() - 1 {
let v0 = vertices[face[0]];
let v1 = vertices[face[i]];
let v2 = vertices[face[i + 1]];
triangles.push(stl_io::Triangle {
normal: face_normal(v0, v1, v2),
vertices: [
stl_io::Vertex::new(v0),
stl_io::Vertex::new(v1),
stl_io::Vertex::new(v2),
],
});
}
}
let out = fs::File::create(job.output)?;
let mut w = BufWriter::new(out);
stl_io::write_stl(&mut w, triangles.iter())
.map_err(|e| Error::other(format!("stl write: {e}")))?;
w.flush()?;
Ok(())
}
fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> stl_io::Normal {
let ux = b[0] - a[0];
let uy = b[1] - a[1];
let uz = b[2] - a[2];
let vx = c[0] - a[0];
let vy = c[1] - a[1];
let vz = c[2] - a[2];
let nx = uy * vz - uz * vy;
let ny = uz * vx - ux * vz;
let nz = ux * vy - uy * vx;
let len = (nx * nx + ny * ny + nz * nz).sqrt();
if len > 0.0 {
stl_io::Normal::new([nx / len, ny / len, nz / len])
} else {
stl_io::Normal::new([0.0, 0.0, 0.0])
}
}