proof of concept MDD

This commit is contained in:
Spencer Killen 2023-11-26 16:16:45 -07:00
commit 6f1c60fc70
Signed by: sjkillen
GPG Key ID: 3AF3117BA6FBB75B
7 changed files with 231 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -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"

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "pointcache"
version = "0.1.0"
edition = "2021"
[dependencies]

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# Parser / Encoder for pointcache file formats
## References
- https://github.com/brainexcerpts/MDD_file_exporter

BIN
output.mdd Normal file

Binary file not shown.

213
src/lib.rs Normal file
View File

@ -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<F: FnMut(MDDFrame) -> Result<(), Box<dyn Error>>>(
infile: File,
op: F,
) -> Result<(), Box<dyn Error>>;
fn map_to<F: FnMut(MDDFrame) -> Result<MDDFrame, Box<dyn Error>>>(
infile: File,
outfile: File,
op: F,
) -> Result<(), Box<dyn Error>>;
}
pub struct MDDSeekableFile;
struct MDDSeekableFileReader {
total_frames: i32,
total_points: i32,
data: Box<dyn Iterator<Item = Result<MDDFrame, Box<dyn Error>>>>,
}
#[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<dyn Error>> {
infile.seek(SeekFrom::Start(0));
let mut buff = [0u8; size_of::<i32>()];
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<dyn Error>> {
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<MDDSeekableFileReader, Box<dyn Error>> {
let (total_frames, total_points) = MDDSeekableFile::read_header(&mut infile)?;
let time_byte_offset = (total_frames as usize) * size_of::<f32>();
let mut buff = [0u8; size_of::<f32>()];
let times = Box::new(
repeat(())
.map::<Result<_, Box<dyn Error>>, _>(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::<i32>() * 2).try_into()?;
let f32_size: u64 = size_of::<f32>().try_into()?;
let mut buff = [0u8; size_of::<f32>()];
let data = Box::new(
(0..((total_frames * total_points) as u64)).map::<Result<_, Box<dyn Error>>, _>(
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<F>(infile: File, mut op: F) -> Result<(), Box<dyn Error>>
where
F: FnMut(MDDFrame) -> Result<(), Box<dyn Error>>,
{
let MDDSeekableFileReader {
total_frames: _,
total_points: _,
data,
} = MDDSeekableFile::read(infile)?;
for frame in data {
op(frame?)?;
}
Ok(())
}
fn map_to<F>(mut infile: File, mut outfile: File, mut op: F) -> Result<(), Box<dyn Error>>
where
F: FnMut(MDDFrame) -> Result<MDDFrame, Box<dyn Error>>,
{
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::<i32>() * 2).try_into()?;
let f32_size: u64 = size_of::<f32>().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<dyn Error>> {
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(())
}
}

BIN
test_cube.mdd Normal file

Binary file not shown.