///|
/// Reusable sample caches for repeated timeline evaluation. Cache keys are
/// explicit so callers can invalidate only the track or resolution that
/// changed during editing.
pub struct SampleCacheKey2 {
track_id : Int
revision : Int
samples : Int
start : Double
end : Double
} derive(Debug)
///|
pub fn sample_cache_key2(
track_id : Int,
revision : Int,
samples : Int,
start : Double,
end : Double,
) -> SampleCacheKey2 {
{ track_id, revision, samples: samples.max(1), start, end }
}
///|
pub fn SampleCacheKey2::track_id(self : SampleCacheKey2) -> Int {
self.track_id
}
///|
pub fn SampleCacheKey2::revision(self : SampleCacheKey2) -> Int {
self.revision
}
///|
pub fn SampleCacheKey2::samples(self : SampleCacheKey2) -> Int {
self.samples
}
///|
pub fn SampleCacheKey2::matches(
self : SampleCacheKey2,
other : SampleCacheKey2,
) -> Bool {
self.track_id == other.track_id &&
self.revision == other.revision &&
self.samples == other.samples &&
self.start == other.start &&
self.end == other.end
}
///|
pub struct ScalarSampleCache2 {
key : SampleCacheKey2
times : Array[Double]
values : Array[Double]
} derive(Debug)
///|
pub fn ScalarSampleCache2::new(
key : SampleCacheKey2,
values : Array[Double],
) -> ScalarSampleCache2 {
let times : Array[Double] = []
for index in 0.. SampleCacheKey2 {
self.key
}
///|
pub fn ScalarSampleCache2::times(self : ScalarSampleCache2) -> Array[Double] {
self.times.copy()
}
///|
pub fn ScalarSampleCache2::values(self : ScalarSampleCache2) -> Array[Double] {
self.values.copy()
}
///|
pub fn ScalarSampleCache2::length(self : ScalarSampleCache2) -> Int {
self.values.length()
}
///|
pub fn ScalarSampleCache2::sample(
self : ScalarSampleCache2,
time : Double,
) -> Double {
if self.values.length() == 0 {
return 0.0
}
if time <= self.times[0] {
return self.values[0]
}
let last = self.values.length() - 1
if time >= self.times[last] {
return self.values[last]
}
for index in 0.. Double {
if self.values.length() < 2 {
return 0.0
}
let epsilon = (self.key.end - self.key.start) /
(self.values.length() - 1).to_double()
(self.sample(time + epsilon) - self.sample(time - epsilon)) / (2.0 * epsilon)
}
///|
pub struct PointSampleCache2 {
key : SampleCacheKey2
values : Array[Point2]
} derive(Debug)
///|
pub fn PointSampleCache2::new(
key : SampleCacheKey2,
values : Array[Point2],
) -> PointSampleCache2 {
{ key, values: values.copy() }
}
///|
pub fn PointSampleCache2::key(self : PointSampleCache2) -> SampleCacheKey2 {
self.key
}
///|
pub fn PointSampleCache2::values(self : PointSampleCache2) -> Array[Point2] {
self.values.copy()
}
///|
pub fn PointSampleCache2::sample(
self : PointSampleCache2,
time : Double,
) -> Point2 {
if self.values.length() == 0 {
return point2(x=0.0, y=0.0)
}
let ratio = if self.key.end == self.key.start {
0.0
} else {
clamp01((time - self.key.start) / (self.key.end - self.key.start))
}
let position = ratio * (self.values.length() - 1).to_double()
let lower = @math.floor(position)
.to_int()
.min(self.values.length() - 2)
.max(0)
self.values[lower].lerp(self.values[lower + 1], position - lower.to_double())
}
///|
pub struct ColorSampleCache2 {
key : SampleCacheKey2
values : Array[Rgba]
} derive(Debug)
///|
pub fn ColorSampleCache2::new(
key : SampleCacheKey2,
values : Array[Rgba],
) -> ColorSampleCache2 {
{ key, values: values.copy() }
}
///|
pub fn ColorSampleCache2::key(self : ColorSampleCache2) -> SampleCacheKey2 {
self.key
}
///|
pub fn ColorSampleCache2::values(self : ColorSampleCache2) -> Array[Rgba] {
self.values.copy()
}
///|
pub fn ColorSampleCache2::sample(
self : ColorSampleCache2,
time : Double,
) -> Rgba {
if self.values.length() == 0 {
return rgba(r=0.0, g=0.0, b=0.0, a=0.0)
}
let ratio = if self.key.end == self.key.start {
0.0
} else {
clamp01((time - self.key.start) / (self.key.end - self.key.start))
}
let position = ratio * (self.values.length() - 1).to_double()
let lower = @math.floor(position)
.to_int()
.min(self.values.length() - 2)
.max(0)
self.values[lower].lerp_linear(
self.values[lower + 1],
position - lower.to_double(),
)
}
///|
pub struct CachePool2 {
scalar : Array[ScalarSampleCache2]
point : Array[PointSampleCache2]
color : Array[ColorSampleCache2]
capacity : Int
} derive(Debug)
///|
pub fn cache_pool2(capacity? : Int = 16) -> CachePool2 {
{ scalar: [], point: [], color: [], capacity: capacity.max(1) }
}
///|
pub fn CachePool2::capacity(self : CachePool2) -> Int {
self.capacity
}
///|
pub fn CachePool2::scalar_count(self : CachePool2) -> Int {
self.scalar.length()
}
///|
pub fn CachePool2::point_count(self : CachePool2) -> Int {
self.point.length()
}
///|
pub fn CachePool2::color_count(self : CachePool2) -> Int {
self.color.length()
}
///|
pub fn CachePool2::put_scalar(
self : CachePool2,
cache : ScalarSampleCache2,
) -> Unit {
self.scalar.push(cache)
while self.scalar.length() > self.capacity {
ignore(self.scalar.remove(0))
}
}
///|
pub fn CachePool2::put_point(
self : CachePool2,
cache : PointSampleCache2,
) -> Unit {
self.point.push(cache)
while self.point.length() > self.capacity {
ignore(self.point.remove(0))
}
}
///|
pub fn CachePool2::put_color(
self : CachePool2,
cache : ColorSampleCache2,
) -> Unit {
self.color.push(cache)
while self.color.length() > self.capacity {
ignore(self.color.remove(0))
}
}
///|
pub fn CachePool2::find_scalar(
self : CachePool2,
key : SampleCacheKey2,
) -> ScalarSampleCache2? {
for cache in self.scalar {
if cache.key.matches(key) {
return Some(cache)
}
}
None
}
///|
pub fn CachePool2::find_point(
self : CachePool2,
key : SampleCacheKey2,
) -> PointSampleCache2? {
for cache in self.point {
if cache.key.matches(key) {
return Some(cache)
}
}
None
}
///|
pub fn CachePool2::find_color(
self : CachePool2,
key : SampleCacheKey2,
) -> ColorSampleCache2? {
for cache in self.color {
if cache.key.matches(key) {
return Some(cache)
}
}
None
}
///|
pub fn CachePool2::clear(self : CachePool2) -> Unit {
self.scalar.clear()
self.point.clear()
self.color.clear()
}