// Generic struct `Animated($T: Lerpable)` monomorphized with a struct type — the // `#inline` protocol constraint participates in method dispatch via `self.from.lerp(...)`. #import "modules/std.sx"; #import "modules/math"; Lerpable :: protocol #inline { lerp :: (b: Self, t: f32) -> Self; } Size :: struct { width, height: f32; zero :: () -> Size => .{ width = 0.0, height = 0.0 }; } impl Lerpable for Size { lerp :: (self: Size, b: Size, t: f32) -> Size { Size.{ width = self.width + (b.width - self.width) * t, height = self.height + (b.height - self.height) * t } } } Animated :: struct ($T: Lerpable) { current: T; from: T; to: T; elapsed: f32; duration: f32; active: bool; make :: (value: T) -> Animated(T) { Animated(T).{ current = value, from = value, to = value, elapsed = 0.0, duration = 0.0, active = false } } set_immediate :: (self: *Animated(T), value: T) { self.current = value; self.from = value; self.to = value; self.active = false; } animate_to :: (self: *Animated(T), target: T, dur: f32) { self.from = self.current; self.to = target; self.elapsed = 0.0; self.duration = dur; self.active = true; } tick :: (self: *Animated(T), dt: f32) { if !self.active { return; } self.elapsed += dt; t := clamp(self.elapsed / self.duration, 0.0, 1.0); self.current = self.from.lerp(self.to, t); if t >= 1.0 { self.current = self.to; self.active = false; } } } main :: () -> void { anim := Animated(Size).make(Size.zero()); anim.set_immediate(Size.{ width = 100.0, height = 50.0 }); print("after set: {}x{}\n", anim.current.width, anim.current.height); anim.animate_to(Size.{ width = 200.0, height = 100.0 }, 1.0); anim.tick(0.5); print("mid anim: {}x{}\n", anim.current.width, anim.current.height); anim.tick(0.5); print("end anim: {}x{}\n", anim.current.width, anim.current.height); }