use std::io::Write;
use crate::io::PointWriter;
use crate::ply::PlyEncoding;
use crate::point::PointRecord;
use crate::Result;
pub struct PlyWriter<W: Write> {
inner: W,
encoding: PlyEncoding,
has_color: bool,
has_normals: bool,
points_written: u64,
}
impl<W: Write> PlyWriter<W> {
pub fn new(
mut inner: W,
point_count: u64,
encoding: PlyEncoding,
has_color: bool,
has_normals: bool,
) -> Result<Self> {
write_ply_header(&mut inner, point_count, encoding, has_color, has_normals)?;
Ok(PlyWriter { inner, encoding, has_color, has_normals, points_written: 0 })
}
}
impl<W: Write> PointWriter for PlyWriter<W> {
fn write_point(&mut self, p: &PointRecord) -> Result<()> {
match self.encoding {
PlyEncoding::Ascii => write_ascii_point(&mut self.inner, p, self.has_color, self.has_normals)?,
PlyEncoding::BinaryLittleEndian => {
write_binary_point(&mut self.inner, p, self.has_color, self.has_normals, false)?;
}
PlyEncoding::BinaryBigEndian => {
write_binary_point(&mut self.inner, p, self.has_color, self.has_normals, true)?;
}
}
self.points_written += 1;
Ok(())
}
fn finish(&mut self) -> Result<()> {
Ok(self.inner.flush()?)
}
}
fn write_ply_header<W: Write>(
w: &mut W, count: u64, enc: PlyEncoding, color: bool, normals: bool,
) -> Result<()> {
let enc_str = match enc {
PlyEncoding::Ascii => "ascii",
PlyEncoding::BinaryLittleEndian => "binary_little_endian",
PlyEncoding::BinaryBigEndian => "binary_big_endian",
};
write!(w, "ply\nformat {enc_str} 1.0\ncomment generated by wblidar\n")?;
write!(w, "element vertex {count}\n")?;
write!(w, "property double x\nproperty double y\nproperty double z\n")?;
write!(w, "property uint16 intensity\n")?;
if color {
write!(w, "property uchar red\nproperty uchar green\nproperty uchar blue\n")?;
}
if normals {
write!(w, "property float nx\nproperty float ny\nproperty float nz\n")?;
}
write!(w, "property uint8 classification\n")?;
write!(w, "end_header\n")?;
Ok(())
}
fn write_ascii_point<W: Write>(
w: &mut W, p: &PointRecord, color: bool, normals: bool,
) -> Result<()> {
write!(w, "{} {} {} {}", p.x, p.y, p.z, p.intensity)?;
if color {
let c = p.color.map(|c| (c.red >> 8) as u8).unwrap_or(0);
let g = p.color.map(|c| (c.green >> 8) as u8).unwrap_or(0);
let b = p.color.map(|c| (c.blue >> 8) as u8).unwrap_or(0);
write!(w, " {c} {g} {b}")?;
}
if normals {
write!(w, " {} {} {}", p.normal_x.unwrap_or(0.0), p.normal_y.unwrap_or(0.0), p.normal_z.unwrap_or(0.0))?;
}
writeln!(w, " {}", p.classification)?;
Ok(())
}
fn write_binary_point<W: Write>(
w: &mut W, p: &PointRecord, color: bool, normals: bool, big: bool,
) -> Result<()> {
write_f64(w, p.x, big)?;
write_f64(w, p.y, big)?;
write_f64(w, p.z, big)?;
write_u16(w, p.intensity, big)?;
if color {
let r = p.color.map(|c| (c.red >> 8) as u8).unwrap_or(0);
let g = p.color.map(|c| (c.green >> 8) as u8).unwrap_or(0);
let b = p.color.map(|c| (c.blue >> 8) as u8).unwrap_or(0);
w.write_all(&[r, g, b])?;
}
if normals {
write_f32(w, p.normal_x.unwrap_or(0.0), big)?;
write_f32(w, p.normal_y.unwrap_or(0.0), big)?;
write_f32(w, p.normal_z.unwrap_or(0.0), big)?;
}
w.write_all(&[p.classification])?;
Ok(())
}
fn write_f64<W: Write>(w: &mut W, v: f64, big: bool) -> std::io::Result<()> {
if big { w.write_all(&v.to_be_bytes()) } else { w.write_all(&v.to_le_bytes()) }
}
fn write_f32<W: Write>(w: &mut W, v: f32, big: bool) -> std::io::Result<()> {
if big { w.write_all(&v.to_be_bytes()) } else { w.write_all(&v.to_le_bytes()) }
}
fn write_u16<W: Write>(w: &mut W, v: u16, big: bool) -> std::io::Result<()> {
if big { w.write_all(&v.to_be_bytes()) } else { w.write_all(&v.to_le_bytes()) }
}