Skip to main content

ranim_core/animation/
eval.rs

1//! Evaluation protocols and the standard author-facing adapters.
2//!
3//! [`Eval`] is the single visual leaf protocol;
4//! [`EvalExt`] adds build-time conveniences;
5//! [`pure::Pure`] adapts a closure
6//! and [`iterative::Iterative`]
7//! adapts a stepping function into that protocol. `EvalDyn` is the
8//! runtime-erased leaf dispatch held by `LeafContent::Anim`.
9
10use dyn_clone::DynClone;
11
12use crate::frame::EvaluatedFrame;
13
14use super::build::{Paramed, PlaybackExt};
15
16/// Iterative (stateful, stepped) evaluation.
17pub mod iterative;
18/// Pure (closed-form) evaluation adapters.
19pub mod pure;
20
21/// The general animation segment protocol: what the runtime can do with a segment.
22///
23/// An animation's content is immutable once defined: it is a pure function of
24/// its own normalized progress `alpha ∈ [0, 1]`. The protocol exposes a single
25/// entry — `eval_alpha` — and it is a **pure query** on `&self` (evaluating
26/// the same `alpha` always yields the same `Output`, regardless of call order
27/// or repetition). No evaluator sees seconds or the scene clock; the owning
28/// cell remaps time to progress before calling in.
29///
30/// Stateful (iterative) segments memoize their integration behind a snapshot so
31/// repeated queries are cheap; pure segments are a closed form. The standard
32/// author-facing adapters live here too:
33/// [`Iterative`](crate::animation::eval::iterative::Iterative) turns an
34/// [`IterativeEval`](crate::animation::eval::iterative::IterativeEval) step
35/// function into an `Eval`, and
36/// [`Pure`](crate::animation::eval::pure::Pure) wraps a raw closure
37/// `Fn(f64) -> T`. Implementing `Eval` directly remains the path for
38/// exotic segments.
39pub trait Eval {
40    /// Value produced by this evaluator.
41    ///
42    /// Playback requires `Clone + Send + Sync + 'static`. Each sample retains
43    /// the complete value, including any containers or placement wrappers.
44    type Output;
45
46    /// Evaluate the segment's content at normalized progress `alpha`.
47    fn eval_alpha(&self, alpha: f64) -> Self::Output;
48
49    /// The content resolution declared by iterative segments: `1/N` progress
50    /// per integration step (`N` declared via
51    /// [`Iterative::with_steps`](crate::animation::eval::iterative::Iterative::with_steps)).
52    ///
53    /// `None` for non-iterative segments. This is an introspection query for
54    /// tooling (e.g. `ranim inspect tree`); it does not affect evaluation.
55    fn sim_step(&self) -> Option<f64> {
56        None
57    }
58}
59
60/// Build-time conveniences over [`Eval`], split out so `Eval` stays a single
61/// primitive (`eval_alpha`).
62///
63/// The only consumers today are the built-in pure-animation families, which use
64/// `apply_to` to write an item's end state (or `apply_alpha_to` to write an
65/// arbitrary progress state) while constructing the animation.
66pub trait EvalExt: Eval + Sized {
67    /// Write the state at progress `alpha` into `item` and return this
68    /// evaluator (defined through `eval_alpha`).
69    fn apply_alpha_to(self, item: &mut Self::Output, alpha: f64) -> Self {
70        *item = self.eval_alpha(alpha);
71        self
72    }
73
74    /// Write the end state (`alpha == 1.0`) into `item` and return this
75    /// evaluator (defined through [`EvalExt::apply_alpha_to`]).
76    fn apply_to(self, item: &mut Self::Output) -> Self {
77        self.apply_alpha_to(item, 1.0)
78    }
79}
80
81impl<E: Eval + Sized> EvalExt for E {}
82
83/// Frame insertion state shared by the leaves of one top-level animation.
84pub(crate) struct OutputContext<'a> {
85    frame: &'a mut EvaluatedFrame,
86    source_id: usize,
87    start_order: usize,
88}
89
90impl<'a> OutputContext<'a> {
91    pub(crate) fn new(frame: &'a mut EvaluatedFrame, source_id: usize) -> Self {
92        let start_order = frame.len();
93        Self {
94            frame,
95            source_id,
96            start_order,
97        }
98    }
99
100    fn push<T: Send + Sync + 'static>(&mut self, value: T) {
101        let part = self.frame.len() - self.start_order;
102        self.frame.push((self.source_id, part), value);
103    }
104}
105
106/// A cloneable complete output captured by a hold.
107#[derive(Clone)]
108pub(crate) struct DynOutput(Box<dyn CapturedOutput>);
109
110trait CapturedOutput: DynClone + Send + Sync {
111    fn write_box(self: Box<Self>, output: &mut OutputContext<'_>);
112}
113
114impl<T: Clone + Send + Sync + 'static> CapturedOutput for T {
115    fn write_box(self: Box<Self>, output: &mut OutputContext<'_>) {
116        output.push(*self);
117    }
118}
119
120dyn_clone::clone_trait_object!(CapturedOutput);
121
122impl DynOutput {
123    pub(super) fn write(self, output: &mut OutputContext<'_>) {
124        self.0.write_box(output);
125    }
126}
127
128/// The erased visual-leaf protocol held by the runtime tree.
129///
130/// Ordinary sampling inserts each complete typed output directly. Holds capture
131/// the same raw values and replay them without introducing a container payload.
132pub(super) trait EvalDyn {
133    fn eval_frame(&self, alpha: f64, output: &mut OutputContext<'_>);
134    fn eval_capture(&self, alpha: f64, output: &mut Vec<DynOutput>);
135
136    /// The iterative content step, if this leaf is an iterative segment.
137    fn sim_step(&self) -> Option<f64> {
138        None
139    }
140}
141
142impl<E> EvalDyn for E
143where
144    E: Eval,
145    E::Output: Clone + Send + Sync + 'static,
146{
147    fn eval_frame(&self, alpha: f64, output: &mut OutputContext<'_>) {
148        output.push(self.eval_alpha(alpha));
149    }
150
151    fn eval_capture(&self, alpha: f64, output: &mut Vec<DynOutput>) {
152        output.push(DynOutput(Box::new(self.eval_alpha(alpha))));
153    }
154
155    fn sim_step(&self) -> Option<f64> {
156        Eval::sim_step(self)
157    }
158}
159
160/// A held batch replays each captured value with its original concrete type.
161pub(super) struct HeldOutput(Vec<DynOutput>);
162
163impl HeldOutput {
164    pub(super) fn new(values: Vec<DynOutput>) -> Self {
165        Self(values)
166    }
167}
168
169impl EvalDyn for HeldOutput {
170    fn eval_frame(&self, _alpha: f64, output: &mut OutputContext<'_>) {
171        for value in &self.0 {
172            value.clone().write(output);
173        }
174    }
175
176    fn eval_capture(&self, _alpha: f64, output: &mut Vec<DynOutput>) {
177        output.extend(self.0.iter().cloned());
178    }
179}
180
181/// A constant evaluator.
182#[derive(Clone)]
183pub struct Static<T: Clone>(pub T);
184
185impl<T: Clone> Eval for Static<T> {
186    type Output = T;
187
188    fn eval_alpha(&self, _alpha: f64) -> Self::Output {
189        self.0.clone()
190    }
191}
192
193/// Requirement for [`StaticAnim`].
194pub trait StaticAnimRequirement: Clone + Send + Sync + 'static {}
195
196impl<T: Clone + Send + Sync + 'static> StaticAnimRequirement for T {}
197
198/// Convenience methods for zero-duration static animations.
199pub trait StaticAnim: StaticAnimRequirement + Sized {
200    /// Show this value.
201    fn show(&self) -> Paramed<Static<Self>>;
202    /// Hide this value.
203    fn hide(&self) -> Paramed<Static<Self>>;
204}
205
206impl<T: StaticAnimRequirement + 'static> StaticAnim for T {
207    fn show(&self) -> Paramed<Static<Self>> {
208        Static(self.clone()).with_duration(0.0)
209    }
210
211    fn hide(&self) -> Paramed<Static<Self>> {
212        Static(self.clone()).with_enabled(false).with_duration(0.0)
213    }
214}