frames parse correctly

This commit is contained in:
Lee-Orr
2024-02-08 17:20:29 -05:00
parent 5ed4352fb4
commit 733b3cf9f3
+50 -34
View File
@@ -3,19 +3,6 @@ use std::{num::ParseFloatError, path::Display, str::FromStr, sync::Arc};
use bevy::{time::{self, Time}, utils::{default, HashMap}};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct MorphID {
pub id: usize,
}
impl MorphID {
pub fn new(id: usize) -> Self {
Self {
id
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Timestamp {
hour: u8,
@@ -28,7 +15,7 @@ pub struct Timestamp {
pub enum TimestampParseError {
EmptyTimestamp,
TooManySegments,
NumberParseError(ParseFloatError),
NumberParseError(ParseFloatError, String),
}
impl std::fmt::Display for TimestampParseError {
@@ -43,6 +30,7 @@ impl FromStr for Timestamp {
type Err = TimestampParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.is_empty() {
return Err(TimestampParseError::EmptyTimestamp);
}
@@ -58,10 +46,10 @@ impl FromStr for Timestamp {
return Err(TimestampParseError::EmptyTimestamp);
};
let frame = frame.parse::<f32>().map(|v| v.floor() as u8).map_err(TimestampParseError::NumberParseError)?;
let second = segments.get(1).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(TimestampParseError::NumberParseError)?;
let minute = segments.get(2).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(TimestampParseError::NumberParseError)?;
let hour = segments.get(3).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(TimestampParseError::NumberParseError)?;
let frame = frame.parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("frame failed for {s}")))?;
let second = segments.get(1).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("second failed for {s}")))?;
let minute = segments.get(2).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("minute failed for {s}")))?;
let hour = segments.get(3).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("hour failed for {s}")))?;
Ok(Self { hour, minute, second, frame })
}
@@ -70,7 +58,7 @@ impl FromStr for Timestamp {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct CSVFrame {
pub timestamp: Timestamp,
pub morphs: HashMap<MorphID, f32>,
pub morphs: Vec<f32>
}
@@ -137,7 +125,7 @@ impl CSVAnimation {
let _ = iter.next().ok_or(CSVParseError::MisssingKnownHeaders)?; // similarly, we already validate that there are a single morph in the CSV, so the skipped columns shouldn't be valid CSVs.
}
let morphs = iter.map(|v| v.parse::<f32>().map_err(CSVParseError::MorphParseError)).enumerate().map(|(i, v)| v.map(|v| (MorphID::new(i), v))).collect::<Result<HashMap<_, _>,_>>()?;
let morphs = iter.map(|v| v.trim().parse::<f32>().map_err(CSVParseError::MorphParseError)).collect::<Result<Vec<_>,_>>()?;
if fps < timestamp.frame {
fps = timestamp.frame;
@@ -146,10 +134,14 @@ impl CSVAnimation {
frames.push(CSVFrame { timestamp, morphs });
}
if frames.is_empty() {
return Err(CSVParseError::NoFrames);
}
Ok(CSVAnimation {
frames,
morph_names,
fps
fps: fps + 1
})
}
}
@@ -159,6 +151,29 @@ mod tests {
use bevy::time::{self, Time};
use crate::{CSVAnimation, CSVParseError, Timestamp, TimestampParseError};
#[test]
fn given_an_empty_timestamp_it_returns_an_empty_timestamp_error() {
let timestamp = "".parse::<Timestamp>().expect_err("succeeded against expectations");
assert!(matches!(timestamp, TimestampParseError::EmptyTimestamp))
}
#[test]
fn given_a_timestamp_with_more_than_4_segments_returns_a_too_many_segments_error() {
let timestamp = "00:00:00:00:00.1234".parse::<Timestamp>().expect_err("succeeded against expectations");
assert!(matches!(timestamp, TimestampParseError::TooManySegments))
}
#[test]
fn given_a_zero_timestamp_it_is_parsed_correctly() {
let timestamp = "00:00:00:00".parse::<Timestamp>().expect("Timestamp failed to parse");
assert_eq!(timestamp, Timestamp { hour: 0, minute: 0, second: 0, frame: 0 })
}
#[test]
fn given_a_valid_timestamp_it_is_parsed_correctly() {
let timestamp = "01:02:03:04.123".parse::<Timestamp>().expect("Timestamp failed to parse");
assert_eq!(timestamp, Timestamp { hour: 1, minute: 2, second: 3, frame: 4 })
}
#[test]
fn given_no_data_it_emits_a_no_data_error() {
@@ -178,7 +193,7 @@ mod tests {
#[test]
fn given_a_header_row_it_sets_correct_headers() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2
0, 0, 0, 0").unwrap();
00:00:00:00, 0, 0, 0").unwrap();
assert_eq!(result.morph_names.first().unwrap(), "shape_1");
assert_eq!(result.morph_names.get(1).unwrap(), "shape_2");
}
@@ -204,20 +219,21 @@ mod tests {
}
#[test]
fn given_an_empty_timestamp_it_returns_an_empty_timestamp_error() {
let timestamp = "".parse::<Timestamp>().expect_err("succeeded against expectations");
assert!(matches!(timestamp, TimestampParseError::EmptyTimestamp))
fn given_multiple_frames_can_deduce_fps() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2
00:00:00:00.00, 0, 0, 0
00:00:00:01.00, 0, 0, 0
00:00:00:02.00, 0, 0, 0
00:00:01:00.00, 0, 0, 0
00:00:01:01.00, 0, 0, 0").unwrap();
assert_eq!(result.fps, 3);
}
#[test]
fn given_a_timestamp_with_more_than_4_segments_returns_a_too_many_segments_error() {
let timestamp = "00:00:00:00:00.1234".parse::<Timestamp>().expect_err("succeeded against expectations");
assert!(matches!(timestamp, TimestampParseError::TooManySegments))
}
#[test]
fn given_a_valid_timestamp_it_is_parsed_correctly() {
let timestamp = "01:02:03:04.123".parse::<Timestamp>().expect("Timestamp failed to parse");
assert_eq!(timestamp, Timestamp { hour: 1, minute: 2, second: 3, frame: 4 })
fn given_a_single_frame_it_parses_morphs_correctly() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2
00:00:00:00.00, 0, 0.1, 0.5").unwrap();
assert!(&result.frames.first().unwrap().morphs[0] - 0.1 < 0.0001);
assert!(&result.frames.first().unwrap().morphs[1] - 0.5 < 0.0001);
}
}