extern crate byteorder;
use std::io::{Result, Write, ErrorKind, Error};
use byteorder::{ReadBytesExt, LittleEndian, WriteBytesExt};
pub struct Triangle {
normal: [f32; 3],
v1: [f32; 3],
v2: [f32; 3],
v3: [f32; 3],
attr_byte_count: u16
}
fn point_eq(lhs: [f32; 3], rhs: [f32; 3]) -> bool {
lhs[0] == rhs[0] && lhs[1] == rhs[1] && lhs[2] == rhs[2]
}
impl PartialEq for Triangle {
fn eq(&self, rhs: &Triangle) -> bool {
point_eq(self.normal, rhs.normal)
&& point_eq(self.v1, rhs.v1)
&& point_eq(self.v2, rhs.v2)
&& point_eq(self.v3, rhs.v3)
&& self.attr_byte_count == rhs.attr_byte_count
}
}
impl Eq for Triangle {}
pub struct BinaryStlHeader {
pub header: [u8; 80],
pub num_triangles: u32
}
pub struct BinaryStlFile {
pub header: BinaryStlHeader,
pub triangles: Vec<Triangle>
}
fn read_point<T: ReadBytesExt>(input: &mut T) -> Result<[f32; 3]> {
let x1 = try!(input.read_f32::<LittleEndian>());
let x2 = try!(input.read_f32::<LittleEndian>());
let x3 = try!(input.read_f32::<LittleEndian>());
Ok([x1, x2, x3])
}
fn read_triangle<T: ReadBytesExt>(input: &mut T) -> Result<Triangle> {
let normal = try!(read_point(input));
let v1 = try!(read_point(input));
let v2 = try!(read_point(input));
let v3 = try!(read_point(input));
let attr_count = try!(input.read_u16::<LittleEndian>());
Ok(Triangle { normal: normal,
v1: v1, v2: v2, v3: v3,
attr_byte_count: attr_count })
}
fn read_header<T: ReadBytesExt>(input: &mut T) -> Result<BinaryStlHeader> {
let mut header = [0u8; 80];
match input.read(&mut header) {
Ok(n) => if n == header.len() {
()
}
else {
return Err(Error::new(ErrorKind::Other,
"Couldn't read STL header"));
},
Err(e) => return Err(e)
};
let num_triangles = try!(input.read_u32::<LittleEndian>());
Ok(BinaryStlHeader{ header: header, num_triangles: num_triangles })
}
pub fn read_stl<T: ReadBytesExt>(input: &mut T) -> Result<BinaryStlFile> {
let header = try!(read_header(input));
let mut triangles = Vec::new();
for _ in 0 .. header.num_triangles {
triangles.push(try!(read_triangle(input)));
}
Ok(BinaryStlFile {
header: header,
triangles: triangles
})
}
fn write_point<T: WriteBytesExt>(out: &mut T, p: [f32; 3]) -> Result<()> {
for x in p.iter() {
try!(out.write_f32::<LittleEndian>(*x));
}
Ok(())
}
pub fn write_stl<T: WriteBytesExt>(out: &mut T,
stl: &BinaryStlFile) -> Result<()> {
assert!(stl.header.num_triangles as usize == stl.triangles.len());
try!(out.write(&stl.header.header));
try!(out.write_u32::<LittleEndian>(stl.header.num_triangles));
for t in stl.triangles.iter() {
try!(write_point(out, t.normal));
try!(write_point(out, t.v1));
try!(write_point(out, t.v2));
try!(write_point(out, t.v3));
try!(out.write_u16::<LittleEndian>(t.attr_byte_count));
}
Ok(())
}
#[cfg(test)]
mod test {
use super::{BinaryStlFile, BinaryStlHeader, write_stl, read_stl, Triangle};
use std::io::Cursor;
#[test]
fn write_read() {
let file = BinaryStlFile {
header: BinaryStlHeader { header: [0u8; 80],
num_triangles: 1 },
triangles: vec![Triangle { normal: [0f32, 1f32, 0f32],
v1: [0f32, 0f32, 0f32],
v2: [0f32, 0f32, 1f32],
v3: [1f32, 0f32, 1f32],
attr_byte_count: 0 }]
};
let mut buffer = Vec::new();
match write_stl(&mut buffer, &file) {
Ok(_) => (),
Err(_) => panic!()
}
match read_stl(&mut Cursor::new(buffer)) {
Ok(stl) => {
assert!(stl.header.num_triangles == file.header.num_triangles);
assert!(stl.triangles.len() == 1);
assert!(stl.triangles[0] == file.triangles[0])
},
Err(_) => panic!()
}
}
}