use std::ops::Range;
use crate::geometry::Vec2;
use crate::raster::{RasterImage, Resolution};
use crate::render_context::RenderContext;
use crate::time::{LocalTime, Time};
use super::*;
#[derive(Debug, Clone, Copy, crate::Keyable)]
pub struct Placement {
start: f32,
end: Option<f32>,
fill: bool,
}
impl Placement {
pub fn start(&self) -> f32 {
self.start
}
pub fn end(&self) -> Option<f32> {
self.end
}
pub fn is_fill(&self) -> bool {
self.fill
}
fn fill() -> Self {
Self {
start: 0.0,
end: None,
fill: true,
}
}
}
impl From<f32> for Placement {
fn from(start: f32) -> Self {
Self {
start,
end: None,
fill: false,
}
}
}
impl From<Range<f32>> for Placement {
fn from(window: Range<f32>) -> Self {
Self {
start: window.start,
end: Some(window.end),
fill: false,
}
}
}
#[derive(crate::Keyable)]
pub struct Placed {
placement: Placement,
child: Box<dyn TimelineComponent + Send>,
}
impl Placed {
pub fn new(placement: Placement, child: Box<dyn TimelineComponent + Send>) -> Self {
Self { placement, child }
}
pub fn placement(&self) -> Placement {
self.placement
}
pub fn is_fill(&self) -> bool {
self.placement.fill
}
pub fn child(&self) -> &(dyn TimelineComponent + Send) {
&*self.child
}
pub fn speed(&self) -> f32 {
match (self.placement.end, self.child.duration()) {
(Some(end), Some(content)) => {
let window = end - self.placement.start;
if window > 0.0 {
content / window
} else {
1.0
}
}
_ => 1.0,
}
}
}
impl TimelineComponent for Placed {
fn duration(&self) -> Option<f32> {
match self.placement.end {
Some(end) => Some(end - self.placement.start),
None => self.child.duration(),
}
}
fn measure(&self) -> Option<f32> {
match self.placement.end {
Some(end) => Some(end),
None => self
.child
.measure()
.map(|inner| self.placement.start + inner),
}
}
fn resolve(&self, abs_start: f32, out: &mut ResolveCtx) -> f32 {
if let Some(end) = self.placement.end {
if end - self.placement.start <= 0.0 {
out.error(format!(
"a placement window must have positive length, got .at({}..{})",
self.placement.start, end
));
}
}
let scale = out.local_scale;
let child_abs = abs_start + self.placement.start * scale;
let saved = out.local_scale;
out.local_scale = scale / self.speed();
let child_len = self.child.resolve(child_abs, out);
out.local_scale = saved;
self.duration().unwrap_or(self.placement.start + child_len)
}
fn frame(
&self,
clock: Clock<'_>,
canvas: Vec2,
target: Resolution,
ctx: &mut dyn RenderContext,
) -> Option<RasterImage> {
let t = clock.local().seconds();
if !self.is_fill() {
let active = match self.placement.end {
Some(end) => t >= self.placement.start && t < end,
None => match self.child.duration() {
Some(d) => t >= self.placement.start && t < self.placement.start + d,
None => true,
},
};
if !active {
return None;
}
}
let rebased = (t - self.placement.start) * self.speed();
let window = if self.is_fill() {
None
} else {
match self.placement.end {
Some(end) => Some((end - self.placement.start) * self.speed()),
None => self.child.duration(),
}
};
let child_clock = clock.with_local_window(LocalTime::new(rebased), window);
self.child.frame(child_clock, canvas, target, ctx)
}
fn samples(&self, clock: Clock<'_>, window: f32) -> Option<AudioBuffer> {
self.child.samples(clock, window)
}
fn mix_into(&self, mix: &mut crate::audio::AudioMix, start_secs: f32, speed: f32) {
self.child
.mix_into(mix, start_secs + self.placement.start, speed * self.speed());
}
fn cues(&self, offset: f32) -> Vec<Cue> {
let child_offset = offset + self.placement.start;
let mut cues = self.child.cues(child_offset);
if self.child.duration().is_none() {
if let Some(end) = self.placement.end {
let abs_end = offset + end;
for cue in &mut cues {
cue.end = abs_end;
}
}
}
cues
}
fn arrangement(&self, offset: f32) -> Arrangement {
let child_offset = offset + self.placement.start;
let mut node = self.child.arrangement(child_offset);
if self.child.duration().is_none() {
if let Some(end) = self.placement.end {
node.end = offset + end;
}
}
node
}
}
impl From<Placed> for Box<dyn TimelineComponent + Send> {
fn from(placed: Placed) -> Self {
Box::new(placed)
}
}
pub trait Timed: TimelineComponent + Sized + Send + 'static {
fn at(self, placement: impl Into<Placement>) -> Placed {
Placed::new(placement.into(), Box::new(self))
}
fn fill(self) -> Placed {
Placed::new(Placement::fill(), Box::new(self))
}
fn trim(self, _r: Range<f32>) -> Self {
self
}
}
impl<T: TimelineComponent + Send + 'static> Timed for T {}
pub trait TimedBuilder: TimelineBuilder
where
Self::Output: Send,
{
fn at(self, placement: impl Into<Placement>) -> Placed {
self.build_component().at(placement)
}
fn fill(self) -> Placed {
self.build_component().fill()
}
fn trim(self, r: Range<f32>) -> Self::Output {
self.build_component().trim(r)
}
}
impl<B> TimedBuilder for B
where
B: TimelineBuilder,
B::Output: Send,
{
}