///|
/// A named scalar track placed on a shared media timeline.
pub struct TrackSlot {
name : String
track : ScalarTrack
offset : Double
enabled : Bool
} derive(Debug)
///|
pub fn track_slot(
name : String,
track : ScalarTrack,
offset? : Double = 0.0,
enabled? : Bool = true,
) -> TrackSlot {
{ name, track, offset, enabled }
}
///|
pub fn TrackSlot::name(self : TrackSlot) -> String {
self.name
}
///|
pub fn TrackSlot::track(self : TrackSlot) -> ScalarTrack {
self.track
}
///|
pub fn TrackSlot::offset(self : TrackSlot) -> Double {
self.offset
}
///|
pub fn TrackSlot::enabled(self : TrackSlot) -> Bool {
self.enabled
}
///|
pub(all) struct TimelineValue {
name : String
value : Double
} derive(Debug)
///|
pub fn TimelineValue::name(self : TimelineValue) -> String {
self.name
}
///|
pub fn TimelineValue::value(self : TimelineValue) -> Double {
self.value
}
///|
/// A deterministic collection of named scalar tracks.
pub struct Timeline {
slots : Array[TrackSlot]
} derive(Debug)
///|
pub fn Timeline::new() -> Timeline {
{ slots: [] }
}
///|
pub fn Timeline::add(self : Timeline, slot : TrackSlot) -> Unit {
self.slots.push(slot)
}
///|
pub fn Timeline::remove(self : Timeline, name : String) -> Bool {
match self.slots.search_by(fn(slot) { slot.name == name }) {
None => false
Some(index) => {
ignore(self.slots.remove(index))
true
}
}
}
///|
pub fn Timeline::set_enabled(
self : Timeline,
name : String,
enabled : Bool,
) -> Bool {
match self.slots.search_by(fn(slot) { slot.name == name }) {
None => false
Some(index) => {
let old = self.slots[index]
self.slots[index] = { ..old, enabled, }
true
}
}
}
///|
pub fn Timeline::track_count(self : Timeline) -> Int {
self.slots.length()
}
///|
pub fn Timeline::tracks(self : Timeline) -> Array[TrackSlot] {
self.slots.copy()
}
///|
pub fn Timeline::duration(self : Timeline) -> Double {
let mut result = 0.0
for slot in self.slots {
let end = slot.offset + slot.track.duration()
if slot.enabled && end > result {
result = end
}
}
result
}
///|
/// Sample every enabled track at a timeline coordinate.
pub fn Timeline::sample(
self : Timeline,
time : Double,
mode? : Extrapolation = Clamp,
) -> Array[TimelineValue] {
let result : Array[TimelineValue] = []
for slot in self.slots {
if slot.enabled {
result.push({
name: slot.name,
value: slot.track.sample(time - slot.offset, mode~),
})
}
}
result
}
///|
/// Sample a timeline on a deterministic export grid.
pub fn Timeline::sample_window(
self : Timeline,
config : SamplingConfig,
mode? : Extrapolation = Clamp,
) -> Array[Array[TimelineValue]] {
let count = config.frame_count()
let result : Array[Array[TimelineValue]] = []
let step = 1.0 / config.frame_rate
for frame in 0..