use crate::sdf;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, derive_more::From)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct TimeCode(f64);
impl TimeCode {
pub const EARLIEST: TimeCode = TimeCode(f64::MIN);
pub const fn new(t: f64) -> Self {
Self(t)
}
#[inline]
pub const fn value(self) -> f64 {
self.0
}
pub fn is_earliest_time(self) -> bool {
self.0 == f64::MIN
}
pub const fn safe_step(max_value: f64, max_compression: f64) -> f64 {
f64::EPSILON * max_value * max_compression
}
pub const fn safe_step_default() -> f64 {
Self::safe_step(1e6, 10.0)
}
}
impl From<f32> for TimeCode {
fn from(t: f32) -> Self {
Self::new(t as f64)
}
}
impl From<i32> for TimeCode {
fn from(t: i32) -> Self {
Self::new(t as f64)
}
}
impl From<u32> for TimeCode {
fn from(t: u32) -> Self {
Self::new(t as f64)
}
}
impl From<sdf::TimeCode> for TimeCode {
fn from(t: sdf::TimeCode) -> Self {
Self::new(t.value())
}
}
impl From<TimeCode> for f64 {
fn from(t: TimeCode) -> Self {
t.value()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn earliest_is_min() {
assert!(TimeCode::EARLIEST.is_earliest_time());
assert!(!TimeCode::new(0.0).is_earliest_time());
}
#[test]
fn numeric_round_trip() {
assert_eq!(TimeCode::new(12.5).value(), 12.5);
}
#[test]
fn safe_step_perturbs() {
let s = TimeCode::safe_step_default();
assert!(s > 0.0);
let t = 1.0e6;
assert_ne!(t + s, t);
assert_eq!(TimeCode::safe_step(1e6, 10.0), s);
}
#[test]
fn from_conversions() {
assert_eq!(TimeCode::from(5.0_f64).value(), 5.0);
assert_eq!(TimeCode::from(5.0_f32).value(), 5.0);
assert_eq!(TimeCode::from(5_i32).value(), 5.0);
assert_eq!(TimeCode::from(5_u32).value(), 5.0);
assert_eq!(TimeCode::from(sdf::TimeCode(7.0)).value(), 7.0);
assert_eq!(f64::from(TimeCode::new(9.0)), 9.0);
}
#[test]
fn into_option_coerces() {
fn take(t: impl Into<Option<TimeCode>>) -> Option<TimeCode> {
t.into()
}
assert_eq!(take(None), None);
assert_eq!(take(TimeCode::new(3.0)), Some(TimeCode::new(3.0)));
assert_eq!(take(Some(TimeCode::new(3.0))), Some(TimeCode::new(3.0)));
}
}