#![cfg_attr(
feature = "derive",
doc = r##"
```rust
use anyhow::Result;
use pcd_rs::{DataKind, PcdSerialize, Writer, WriterInit};
use std::path::Path;
#[derive(PcdSerialize)]
pub struct Point {
x: f32,
y: f32,
z: f32,
}
fn main() -> Result<()> {
let mut writer: Writer<Point, _> = WriterInit {
height: 300,
width: 1,
viewpoint: Default::default(),
data_kind: DataKind::Ascii,
schema: None,
}
.create("test_files/dump.pcd")?;
let point = Point {
x: 3.14159,
y: 2.71828,
z: -5.0,
};
writer.push(&point)?;
writer.finish()?;
# std::fs::remove_file("test_files/dump.pcd").unwrap();
Ok(())
}
```
"##
)]
use crate::{
metas::{DataKind, FieldDef, Schema, ValueKind, ViewPoint},
record::{DynRecord, PcdSerialize},
};
use anyhow::{bail, ensure, Result};
use std::{
collections::HashSet,
fs::File,
io::{prelude::*, BufWriter, SeekFrom},
marker::PhantomData,
path::Path,
};
pub type DynWriter<W> = Writer<DynRecord, W>;
pub struct WriterInit {
pub width: u64,
pub height: u64,
pub viewpoint: ViewPoint,
pub data_kind: DataKind,
pub schema: Option<Schema>,
}
impl WriterInit {
pub fn build_from_writer<Record: PcdSerialize, W: Write + Seek>(
self,
writer: W,
) -> Result<Writer<Record, W>> {
let record_spec = match (Record::is_dynamic(), self.schema) {
(true, Some(schema)) => {
let names: Result<HashSet<_>> = schema
.iter()
.cloned()
.map(|FieldDef { name, count, .. }| {
ensure!(!name.is_empty(), "field name must not be empty");
ensure!(count > 0, "count must not be zero");
Ok(name)
})
.collect();
ensure!(names?.len() == schema.len(), "schema names must be unique");
schema
}
(true, None) => bail!("schema is not set "),
(false, Some(_schema)) => bail!("schema must not be set for static record type"),
(false, None) => Record::write_spec(),
};
let seq_writer = Writer::new(
self.width,
self.height,
self.data_kind,
self.viewpoint,
record_spec,
writer,
)?;
Ok(seq_writer)
}
pub fn create<Record, P>(self, path: P) -> Result<Writer<Record, BufWriter<File>>>
where
Record: PcdSerialize,
P: AsRef<Path>,
{
let writer = BufWriter::new(File::create(path.as_ref())?);
let seq_writer = self.build_from_writer(writer)?;
Ok(seq_writer)
}
}
pub struct Writer<T, W>
where
W: Write + Seek,
{
data_kind: DataKind,
record_spec: Schema,
writer: W,
num_records: usize,
points_arg_begin: u64,
points_arg_width: usize,
finished: bool,
_phantom: PhantomData<T>,
}
impl<W, Record> Writer<Record, W>
where
Record: PcdSerialize,
W: Write + Seek,
{
fn new(
width: u64,
height: u64,
data_kind: DataKind,
viewpoint: ViewPoint,
record_spec: Schema,
mut writer: W,
) -> Result<Self> {
let (points_arg_begin, points_arg_width) = {
let fields_args: Vec<_> = record_spec
.iter()
.map(|field| field.name.to_owned())
.collect();
let size_args: Vec<_> = record_spec
.iter()
.map(|field| {
use ValueKind::*;
let size = match field.kind {
U8 | I8 => 1,
U16 | I16 => 2,
U32 | I32 | F32 => 4,
F64 => 8,
};
size.to_string()
})
.collect();
let type_args: Vec<_> = record_spec
.iter()
.map(|field| {
use ValueKind::*;
match field.kind {
U8 | U16 | U32 => "U",
I8 | I16 | I32 => "I",
F32 | F64 => "F",
}
})
.collect();
let count_args: Vec<_> = record_spec
.iter()
.map(|field| field.count.to_string())
.collect();
let viewpoint_args: Vec<_> = {
[
viewpoint.tx,
viewpoint.ty,
viewpoint.tz,
viewpoint.qw,
viewpoint.qx,
viewpoint.qy,
viewpoint.qz,
]
.iter()
.map(|value| value.to_string())
.collect()
};
let points_arg_width = (usize::max_value() as f64).log10().floor() as usize + 1;
writeln!(writer, "# .PCD v.7 - Point Cloud Data file format")?;
writeln!(writer, "VERSION .7")?;
writeln!(writer, "FIELDS {}", fields_args.join(" "))?;
writeln!(writer, "SIZE {}", size_args.join(" "))?;
writeln!(writer, "TYPE {}", type_args.join(" "))?;
writeln!(writer, "COUNT {}", count_args.join(" "))?;
writeln!(writer, "WIDTH {}", width)?;
writeln!(writer, "HEIGHT {}", height)?;
writeln!(writer, "VIEWPOINT {}", viewpoint_args.join(" "))?;
write!(writer, "POINTS ")?;
let points_arg_begin = writer.seek(SeekFrom::Current(0))?;
writeln!(writer, "{:width$}", " ", width = points_arg_width)?;
match data_kind {
DataKind::Binary => writeln!(writer, "DATA binary")?,
DataKind::Ascii => writeln!(writer, "DATA ascii")?,
}
(points_arg_begin, points_arg_width)
};
let seq_writer = Self {
data_kind,
record_spec,
writer,
num_records: 0,
points_arg_begin,
points_arg_width,
finished: false,
_phantom: PhantomData,
};
Ok(seq_writer)
}
pub fn finish(mut self) -> Result<()> {
self.writer.seek(SeekFrom::Start(self.points_arg_begin))?;
write!(
self.writer,
"{:<width$}",
self.num_records,
width = self.points_arg_width
)?;
self.finished = true;
Ok(())
}
pub fn push(&mut self, record: &Record) -> Result<()> {
match self.data_kind {
DataKind::Binary => record.write_chunk(&mut self.writer, &self.record_spec)?,
DataKind::Ascii => record.write_line(&mut self.writer, &self.record_spec)?,
}
self.num_records += 1;
Ok(())
}
}
impl<W, Record> Drop for Writer<Record, W>
where
W: Write + Seek,
{
fn drop(&mut self) {
if !self.finished {
panic!("call finish() before Writer drops");
}
}
}