use crate::rational::{self, Rational};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct TimeRange {
in_: Rational,
out: Rational,
}
impl TimeRange {
pub fn new(in_: Rational, out: Rational) -> TimeRange {
let mut r = TimeRange { in_, out };
r.normalize();
r
}
pub fn in_(&self) -> Rational {
self.in_
}
pub fn out(&self) -> Rational {
self.out
}
pub fn length(&self) -> Rational {
self.out - self.in_
}
pub fn contains(&self, t: Rational) -> bool {
t >= self.in_ && t < self.out
}
fn contains_range(&self, compare: &TimeRange, in_inclusive: bool, out_inclusive: bool) -> bool {
let contains_in = if in_inclusive {
compare.in_ >= self.in_
} else {
compare.in_ > self.in_
};
let contains_out = if out_inclusive {
compare.out <= self.out
} else {
compare.out < self.out
};
contains_in && contains_out
}
fn overlaps_with(&self, a: &TimeRange, in_inclusive: bool, out_inclusive: bool) -> bool {
let does_not_overlap_in = if in_inclusive {
a.out < self.in_
} else {
a.out <= self.in_
};
let does_not_overlap_out = if out_inclusive {
a.in_ > self.out
} else {
a.in_ >= self.out
};
!does_not_overlap_in && !does_not_overlap_out
}
pub fn intersected(&self, other: &TimeRange) -> TimeRange {
TimeRange::new(
std::cmp::max(self.in_, other.in_),
std::cmp::min(self.out, other.out),
)
}
pub fn combined(&self, other: &TimeRange) -> TimeRange {
TimeRange::new(
std::cmp::min(self.in_, other.in_),
std::cmp::max(self.out, other.out),
)
}
fn set_in(&mut self, in_: Rational) {
self.in_ = in_;
self.normalize();
}
fn set_out(&mut self, out: Rational) {
self.out = out;
self.normalize();
}
fn normalize(&mut self) {
if self.out < self.in_ {
std::mem::swap(&mut self.out, &mut self.in_);
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TimeRangeList(Vec<TimeRange>);
impl TimeRangeList {
pub fn new() -> Self {
TimeRangeList(Vec::new())
}
fn contains_range(&self, range: &TimeRange) -> bool {
self.0
.iter()
.any(|r| r.contains_range(range, true, true))
}
pub fn insert(&mut self, range: TimeRange) {
if self.contains_range(&range) {
return;
}
let mut range = range;
let mut i = 0;
while i < self.0.len() {
let compare = self.0[i];
if compare.overlaps_with(&range, true, true) {
range = compare.combined(&range);
self.0.remove(i);
} else {
i += 1;
}
}
self.0.push(range);
}
pub fn remove(&mut self, range: TimeRange) {
let mut additions: Vec<TimeRange> = Vec::new();
let mut i = 0;
while i < self.0.len() {
let compare = self.0[i];
if range.contains_range(&compare, true, true) {
self.0.remove(i);
} else if compare.contains_range(&range, false, false) {
let mut new_range = compare;
new_range.set_in(range.out);
let mut trimmed = compare;
trimmed.set_out(range.in_);
self.0[i] = trimmed;
additions.push(new_range);
break;
} else {
if compare.in_ < range.in_ && compare.out > range.in_ {
self.0[i].set_out(range.in_);
} else if compare.in_ < range.out && compare.out > range.out {
self.0[i].set_in(range.out);
}
i += 1;
}
}
self.0.extend(additions);
}
pub fn ranges(&self) -> &[TimeRange] {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn total_length(&self) -> Rational {
let mut total = Rational::new(0, 1);
for r in &self.0 {
total = total + r.length();
}
total
}
pub fn first(&self) -> Rational {
match self.0.first() {
Some(r) => r.in_(),
None => Rational::NULL,
}
}
pub fn snap(time: Rational, timebase: Rational) -> Rational {
rational::snap_time_to_timebase(time, timebase)
}
}