commit 6f1c60fc70eaf7cecd30c36918d4af1096675359 Author: Spencer Killen Date: Sun Nov 26 16:16:45 2023 -0700 proof of concept MDD diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4185094 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "pointcache" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9105f5a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "pointcache" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..07f7d56 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Parser / Encoder for pointcache file formats + +## References +- https://github.com/brainexcerpts/MDD_file_exporter \ No newline at end of file diff --git a/output.mdd b/output.mdd new file mode 100644 index 0000000..9997e02 Binary files /dev/null and b/output.mdd differ diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c450c6d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,213 @@ +use std::fmt::Display; +use std::io::Write; +use std::mem::size_of; +use std::ops::Range; +use std::{ + error::Error, + fs::File, + io::{BufReader, Read, Seek, SeekFrom}, + iter::repeat, +}; + +pub trait PointCache { + fn map Result<(), Box>>( + infile: File, + op: F, + ) -> Result<(), Box>; + fn map_to Result>>( + infile: File, + outfile: File, + op: F, + ) -> Result<(), Box>; +} + +pub struct MDDSeekableFile; +struct MDDSeekableFileReader { + total_frames: i32, + total_points: i32, + data: Box>>>, +} + +#[derive(Debug)] +pub struct Point { + x: f32, + y: f32, + z: f32, +} +#[derive(Debug)] +pub struct MDDFrame { + frame_idx: usize, + point_idx: usize, + time: f32, + point: Point, +} + +impl MDDSeekableFile { + fn read_header(infile: &mut File) -> Result<(i32, i32), Box> { + infile.seek(SeekFrom::Start(0)); + let mut buff = [0u8; size_of::()]; + infile.read_exact(&mut buff)?; + let total_frames = i32::from_be_bytes(buff); + infile.read_exact(&mut buff)?; + let total_points = i32::from_be_bytes(buff); + + return Ok((total_frames, total_points)); + } + fn write_header( + outfile: &mut File, + total_frames: i32, + total_points: i32, + ) -> Result<(), Box> { + outfile.seek(SeekFrom::Start(0)); + outfile.write_all(&total_frames.to_be_bytes())?; + outfile.write_all(&total_points.to_be_bytes())?; + Ok(()) + } + fn read(mut infile: File) -> Result> { + let (total_frames, total_points) = MDDSeekableFile::read_header(&mut infile)?; + let time_byte_offset = (total_frames as usize) * size_of::(); + + let mut buff = [0u8; size_of::()]; + let times = Box::new( + repeat(()) + .map::>, _>(move |()| { + //times.read_exact(&mut buff)?; + //Ok(f32::from_be_bytes(buff)) + Ok(0.0) + }) + .take(total_frames.try_into()?), + ); + + let header_size: u64 = (size_of::() * 2).try_into()?; + let f32_size: u64 = size_of::().try_into()?; + let mut buff = [0u8; size_of::()]; + let data = Box::new( + (0..((total_frames * total_points) as u64)).map::>, _>( + move |count| { + let frame_idx: usize = (count / (total_points as u64)).try_into()?; + let time_offset: u64 = f32_size * (frame_idx as u64); + let time_size: u64 = f32_size * (total_frames as u64); + let data_offset: u64 = f32_size * count * 3; + + infile.seek(SeekFrom::Start(header_size + time_offset))?; + infile.read_exact(&mut buff)?; + let time = f32::from_be_bytes(buff); + + infile.seek(SeekFrom::Start(header_size + time_size + data_offset))?; + infile.read_exact(&mut buff)?; + let x = f32::from_be_bytes(buff); + infile.read_exact(&mut buff)?; + let y = f32::from_be_bytes(buff); + infile.read_exact(&mut buff)?; + let z = f32::from_be_bytes(buff); + let point = Point { x, y, z }; + + let point_idx: usize = (count % (total_points as u64)).try_into()?; + Ok(MDDFrame { + frame_idx, + point_idx, + time, + point, + }) + }, + ), + ); + + Ok(MDDSeekableFileReader { + total_frames, + total_points, + data, + }) + } +} + +#[derive(Debug)] +struct ExaustedPointsUnexpectedly; +impl Display for ExaustedPointsUnexpectedly { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ExaustedPointsUnexpectedly") + } +} +impl Error for ExaustedPointsUnexpectedly {} + +impl PointCache for MDDSeekableFile { + fn map(infile: File, mut op: F) -> Result<(), Box> + where + F: FnMut(MDDFrame) -> Result<(), Box>, + { + let MDDSeekableFileReader { + total_frames: _, + total_points: _, + data, + } = MDDSeekableFile::read(infile)?; + + for frame in data { + op(frame?)?; + } + + Ok(()) + } + + fn map_to(mut infile: File, mut outfile: File, mut op: F) -> Result<(), Box> + where + F: FnMut(MDDFrame) -> Result>, + { + let (total_frames, total_points) = MDDSeekableFile::read_header(&mut infile)?; + MDDSeekableFile::write_header(&mut outfile, total_frames, total_points)?; + + let header_size: u64 = (size_of::() * 2).try_into()?; + let f32_size: u64 = size_of::().try_into()?; + let mut count = 0; + + Self::map(infile, |frame| { + let frame = op(frame)?; + let frame_idx: u64 = count / (total_points as u64); + let time_offset: u64 = f32_size * frame_idx; + let time_size: u64 = f32_size * (total_frames as u64); + let data_offset: u64 = f32_size * count * 3; + + outfile.seek(SeekFrom::Start(header_size + time_offset))?; + outfile.write_all(&frame.time.to_be_bytes())?; + + outfile.seek(SeekFrom::Start(header_size + time_size + data_offset))?; + outfile.write_all(&frame.point.x.to_be_bytes())?; + outfile.write_all(&frame.point.y.to_be_bytes())?; + outfile.write_all(&frame.point.z.to_be_bytes())?; + + count += 1; + Ok(()) + })?; + Ok(()) + } +} + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() -> Result<(), Box> { + let infile = File::open("test_cube.mdd")?; + let outfile = File::create("output.mdd")?; + println!("MAPPING test_cube.mdd to output.mdd"); + MDDSeekableFile::map_to(infile, outfile, |mut frame| { + frame.point.x = frame.frame_idx as f32; + frame.point.y = (frame.frame_idx * 2) as f32; + frame.point.z = 0.0; + + println!("{:?}", frame); + Ok(frame) + })?; + println!("\nReading back output.mdd"); + let infile = File::open("output.mdd")?; + MDDSeekableFile::map(infile, |frame| { + println!("{:?}", frame); + Ok(()) + })?; + Ok(()) + } +} diff --git a/test_cube.mdd b/test_cube.mdd new file mode 100644 index 0000000..148908f Binary files /dev/null and b/test_cube.mdd differ