use core::ops::{Add, Div, Mul, Sub};
use rinia::numeric::{Half, IsFinite, MinMax, Zero};
use crate::{Duration, HasDuration, HasTimeRange, Time, TimeRange, common};
impl<T> TimeRange<T> {
#[inline]
pub fn new(start: Time<T>, end: Time<T>) -> Self {
Self { start, end }
}
#[inline]
pub fn try_new(start: Time<T>, end: Time<T>) -> Result<Self, crate::Error>
where
T: Copy + IsFinite + PartialOrd,
{
if !start.is_finite() || !end.is_finite() {
return Err(crate::Error::InvalidValue(
"TimeRange cannot be created from NaN or infinite values",
));
}
if start > end {
return Err(crate::Error::InvalidValue(
"TimeRange cannot be created with start greater than end",
));
}
Ok(Self { start, end })
}
#[inline]
pub fn from_times(start: Time<T>, end: Time<T>) -> Self
where
T: Copy + IsFinite + PartialOrd,
{
Self::try_new(start, end).unwrap_or_else(|err| panic!("{err}"))
}
#[inline]
pub fn start(&self) -> Time<T>
where
T: Copy,
{
self.start
}
#[inline]
pub fn end(&self) -> Time<T>
where
T: Copy,
{
self.end
}
#[inline]
pub fn into_parts(self) -> (Time<T>, Time<T>) {
(self.start, self.end)
}
#[inline]
pub fn duration(&self) -> Duration<T>
where
Time<T>: Copy + Sub<Output = Duration<T>>,
{
self.end - self.start
}
#[inline]
pub fn clamp_time(&self, t: Time<T>) -> Time<T>
where
Time<T>: Copy + MinMax,
{
t.maximum(self.start).minimum(self.end)
}
#[inline]
pub fn normalize_time(&self, t: Time<T>) -> T
where
Time<T>: Copy + Sub<Output = Duration<T>>,
Duration<T>: Div<Output = T>,
{
(t - self.start) / (self.end - self.start)
}
#[inline]
pub fn try_normalize_time(&self, t: Time<T>) -> Option<T>
where
T: Zero,
Time<T>: Copy + Sub<Output = Duration<T>>,
Duration<T>: PartialEq + Zero + Div<Output = T>,
{
let duration = self.end - self.start;
if duration == Duration::ZERO { None } else { Some((t - self.start) / duration) }
}
#[inline]
pub fn center(&self) -> Time<T>
where
T: Half,
Time<T>: Copy + Sub<Output = Duration<T>> + Add<Duration<T>, Output = Time<T>>,
Duration<T>: Mul<T, Output = Duration<T>>,
{
self.start + ((self.end - self.start) * T::HALF)
}
#[inline]
pub fn shift(self, delta: Duration<T>) -> Self
where
Time<T>: Copy + Add<Duration<T>, Output = Time<T>>,
Duration<T>: Copy,
{
Self { start: self.start + delta, end: self.end + delta }
}
#[inline]
pub fn scale(self, factor: T) -> Self
where
T: Copy + Zero + Half + MinMax + Mul<Output = T>,
Time<T>: Sub<Output = Duration<T>>
+ Add<Duration<T>, Output = Time<T>>
+ Sub<Duration<T>, Output = Time<T>>,
Duration<T>: Mul<T, Output = Duration<T>>,
{
let factor = factor.maximum(T::ZERO);
let center = self.center();
let half = (self.end - self.start) * (factor * T::HALF);
Self { start: center - half, end: center + half }
}
#[inline]
pub fn scale_from_start(self, factor: T) -> Self
where
T: Copy + Zero + MinMax,
Time<T>: Sub<Output = Duration<T>>
+ Add<Duration<T>, Output = Time<T>>
+ Sub<Duration<T>, Output = Time<T>>,
Duration<T>: Mul<T, Output = Duration<T>>,
{
let factor = factor.maximum(T::ZERO);
let duration = self.duration() * factor;
Self { start: self.start, end: self.start + duration }
}
#[inline]
pub fn expand(self, amount: Duration<T>) -> Self
where
Time<T>: Sub<Duration<T>, Output = Time<T>> + Add<Duration<T>, Output = Time<T>>,
Duration<T>: Copy,
{
Self { start: self.start - amount, end: self.end + amount }
}
#[inline]
pub fn pad_start(self, amount: Duration<T>) -> Self
where
Time<T>: Sub<Duration<T>, Output = Time<T>>,
{
Self { start: self.start - amount, end: self.end }
}
#[inline]
pub fn pad_end(self, amount: Duration<T>) -> Self
where
Time<T>: Add<Duration<T>, Output = Time<T>>,
{
Self { start: self.start, end: self.end + amount }
}
#[inline]
pub fn remap_time(&self, t: Time<T>, target: &Self) -> Time<T>
where
T: Copy,
Time<T>: Copy + Sub<Output = Duration<T>> + Add<Duration<T>, Output = Time<T>>,
Duration<T>: Zero + PartialEq + Mul<T, Output = Duration<T>> + Div<Output = T>,
{
let u = self.normalize_time(t);
target.start + (target.end - target.start) * u
}
#[inline]
pub fn try_remap_time(&self, t: Time<T>, target: &Self) -> Option<Time<T>>
where
T: Copy + Zero,
Time<T>: Sub<Output = Duration<T>> + Add<Duration<T>, Output = Time<T>>,
Duration<T>: Zero + PartialEq + Mul<T, Output = Duration<T>> + Div<Output = T>,
{
let u = self.try_normalize_time(t)?;
Some(target.start + (target.end - target.start) * u)
}
#[inline]
pub fn to_duration_range(&self) -> TimeRange<T>
where
T: Copy + Zero,
Time<T>: Copy + Sub<Output = Duration<T>>,
{
let seconds = (self.end - self.start).seconds();
let time = Time::new(seconds);
Self { start: Time::ZERO, end: time }
}
#[inline]
pub fn split_at(&self, t: Time<T>) -> (Option<Self>, Option<Self>)
where
Self: Copy,
Time<T>: Copy + PartialOrd,
{
if t <= self.start {
(None, Some(*self))
} else if t >= self.end {
(Some(*self), None)
} else {
let left = Self { start: self.start, end: t };
let right = Self { start: t, end: self.end };
(Some(left), Some(right))
}
}
#[inline]
pub fn include_time(&self, time: Time<T>) -> Self
where
Self: Copy,
Time<T>: Copy + MinMax,
{
let mut merged = *self;
merged.include_time_in_place(time);
merged
}
#[inline]
pub fn include_time_in_place(&mut self, time: Time<T>)
where
Time<T>: Copy + MinMax,
{
self.start = self.start.minimum(time);
self.end = self.end.maximum(time);
}
#[inline]
pub fn contains_time(&self, time: Time<T>) -> bool
where
Time<T>: PartialOrd,
{
self.start <= time && time <= self.end
}
#[inline]
pub fn contains_range(&self, other: &Self) -> bool
where
Time<T>: PartialOrd,
{
self.start <= other.start && other.end <= self.end
}
#[inline]
pub fn is_within(&self, other: &Self) -> bool
where
Time<T>: PartialOrd,
{
other.contains_range(self)
}
#[inline]
pub fn intersects(&self, other: &Self) -> bool
where
Time<T>: PartialOrd,
{
self.start <= other.end && other.start <= self.end
}
#[inline]
pub fn intersection(&self, other: &Self) -> Option<Self>
where
Time<T>: Copy + PartialOrd + MinMax,
{
if !self.intersects(other) {
return None;
}
Some(Self { start: self.start.maximum(other.start), end: self.end.minimum(other.end) })
}
#[inline]
pub fn union(&self, other: &Self) -> Self
where
Self: Copy,
Time<T>: Copy + MinMax,
{
let mut merged = *self;
merged.union_in_place(other);
merged
}
#[inline]
pub fn union_in_place(&mut self, other: &Self)
where
Time<T>: Copy + MinMax,
{
self.start = self.start.minimum(other.start);
self.end = self.end.maximum(other.end);
}
#[inline]
pub fn union_all(ranges: &[Self]) -> Option<Self>
where
Self: Copy,
Time<T>: Copy + MinMax,
{
Self::union_iter(ranges.iter().copied())
}
#[inline]
pub fn union_iter<I>(ranges: I) -> Option<Self>
where
I: IntoIterator<Item = Self>,
Time<T>: Copy + MinMax,
{
let mut iter = ranges.into_iter();
let first = iter.next()?;
let mut merged = first;
for range in iter {
merged.union_in_place(&range);
}
Some(merged)
}
#[inline]
pub fn from_time_slice(times: &[Time<T>]) -> Option<Self>
where
Time<T>: Copy + MinMax,
{
Self::from_time_iter(times.iter().copied())
}
#[inline]
pub fn from_time_iter<I>(times: I) -> Option<Self>
where
I: IntoIterator<Item = Time<T>>,
Time<T>: Copy + MinMax,
{
let mut iter = times.into_iter();
let first = iter.next()?;
let mut range = Self::new(first, first);
for time in iter {
range.include_time_in_place(time);
}
Some(range)
}
}
impl<T> Default for TimeRange<T>
where
Time<T>: Zero,
{
fn default() -> Self {
Self { start: Time::<T>::ZERO, end: Time::<T>::ZERO }
}
}
impl<T> HasTimeRange<T> for TimeRange<T>
where
T: Copy,
Time<T>: Copy + Sub<Output = Duration<T>>,
{
#[inline]
fn time_range(&self) -> Option<TimeRange<T>> {
Some(*self)
}
#[inline]
fn duration(&self) -> Option<Duration<T>> {
Some(self.duration())
}
}
impl<T> HasDuration<T> for TimeRange<T>
where
Time<T>: Copy + Sub<Output = Duration<T>>,
{
#[inline]
fn duration(&self) -> Duration<T> {
self.duration()
}
}
rinia::impl_approx_eq_wrapper!([T], impl: TimeRange<T>, item: T, fields: [start, end]);
common::impl_bytemuck_basic!(
[T],
TimeRange<T>,
item: T,
);
impl<T> From<core::range::Range<Time<T>>> for TimeRange<T> {
#[inline]
fn from(range: core::range::Range<Time<T>>) -> Self {
Self::new(range.start, range.end)
}
}
impl<T> From<core::ops::Range<Time<T>>> for TimeRange<T> {
#[inline]
fn from(range: core::ops::Range<Time<T>>) -> Self {
Self::new(range.start, range.end)
}
}
impl<T> From<core::range::Range<T>> for TimeRange<T> {
#[inline]
fn from(range: core::range::Range<T>) -> Self {
Self::new(Time::new(range.start), Time::new(range.end))
}
}
impl<T> From<core::ops::Range<T>> for TimeRange<T> {
#[inline]
fn from(range: core::ops::Range<T>) -> Self {
Self::new(Time::new(range.start), Time::new(range.end))
}
}