Skip to main content

ranim_core/animation/
eval.rs

1//! Evaluation protocols and the standard author-facing adapters.
2//!
3//! [`Eval`](crate::animation::eval::Eval) is the single visual leaf protocol;
4//! [`EvalExt`](crate::animation::eval::EvalExt) adds build-time conveniences;
5//! [`pure::Pure`](crate::animation::eval::pure::Pure) adapts a closure
6//! and [`iterative::Iterative`](crate::animation::eval::iterative::Iterative)
7//! adapts a stepping function into that protocol. `EvalDyn` is the
8//! runtime-erased leaf dispatch held by `NodeContent::Leaf`.
9
10use crate::core_item::{AnyExtractCoreItem, DynItem};
11
12use super::build::{Paramed, PlaybackExt};
13
14/// Iterative (stateful, stepped) evaluation.
15pub mod iterative;
16/// Pure (closed-form) evaluation adapters.
17pub mod pure;
18
19/// The general animation segment protocol: what the runtime can do with a segment.
20///
21/// An animation's content is immutable once defined: it is a pure function of
22/// its own normalized progress `alpha ∈ [0, 1]`. The protocol exposes a single
23/// entry — `eval_alpha` — and it is a **pure query** on `&self` (evaluating
24/// the same `alpha` always yields the same `Output`, regardless of call order
25/// or repetition). No evaluator sees seconds or the scene clock; the owning
26/// cell remaps time to progress before calling in.
27///
28/// Stateful (iterative) segments memoize their integration behind a snapshot so
29/// repeated queries are cheap; pure segments are a closed form. The standard
30/// author-facing adapters live here too:
31/// [`Iterative`](crate::animation::eval::iterative::Iterative) turns an
32/// [`IterativeEval`](crate::animation::eval::iterative::IterativeEval) step
33/// function into an `Eval`, and
34/// [`Pure`](crate::animation::eval::pure::Pure) wraps a raw closure
35/// `Fn(f64) -> T`. Implementing `Eval` directly remains the path for
36/// exotic segments.
37pub trait Eval {
38    /// Value produced by this evaluator.
39    type Output;
40
41    /// Evaluate the segment's content at normalized progress `alpha`.
42    fn eval_alpha(&self, alpha: f64) -> Self::Output;
43
44    /// The content resolution declared by iterative segments: `1/N` progress
45    /// per integration step (`N` declared via
46    /// [`Iterative::with_steps`](crate::animation::eval::iterative::Iterative::with_steps)).
47    ///
48    /// `None` for non-iterative segments. This is an introspection query for
49    /// tooling (e.g. `ranim inspect tree`); it does not affect evaluation.
50    fn sim_step(&self) -> Option<f64> {
51        None
52    }
53}
54
55/// Build-time conveniences over [`Eval`], split out so `Eval` stays a single
56/// primitive (`eval_alpha`).
57///
58/// The only consumers today are the built-in pure-animation families, which use
59/// `apply_to` to write an item's end state (or `apply_alpha_to` to write an
60/// arbitrary progress state) while constructing the animation.
61pub trait EvalExt: Eval + Sized {
62    /// Write the state at progress `alpha` into `item` and return this
63    /// evaluator (defined through `eval_alpha`).
64    fn apply_alpha_to(self, item: &mut Self::Output, alpha: f64) -> Self {
65        *item = self.eval_alpha(alpha);
66        self
67    }
68
69    /// Write the end state (`alpha == 1.0`) into `item` and return this
70    /// evaluator (defined through [`EvalExt::apply_alpha_to`]).
71    fn apply_to(self, item: &mut Self::Output) -> Self {
72        self.apply_alpha_to(item, 1.0)
73    }
74}
75
76impl<E: Eval + Sized> EvalExt for E {}
77
78/// The erased visual-leaf protocol: [`Eval`] without its type.
79///
80/// This is the only type-erased box in the runtime tree
81/// (`NodeContent::Leaf`) — the open world of user evaluators. Containers are
82/// closed runtime variants and never implement this.
83pub(super) trait EvalDyn {
84    /// Evaluate this leaf's content at its normalized progress `alpha`,
85    /// pushing the resulting erased items into `output`.
86    fn eval_into(&self, alpha: f64, output: &mut Vec<DynItem>);
87
88    /// The iterative content step, if this leaf is an iterative segment.
89    fn sim_step(&self) -> Option<f64> {
90        None
91    }
92}
93
94impl<E> EvalDyn for E
95where
96    E: Eval,
97    E::Output: AnyExtractCoreItem,
98{
99    fn eval_into(&self, alpha: f64, output: &mut Vec<DynItem>) {
100        output.push(DynItem(Box::new(self.eval_alpha(alpha))));
101    }
102
103    fn sim_step(&self) -> Option<f64> {
104        Eval::sim_step(self)
105    }
106}
107
108/// A constant evaluator.
109pub struct Static<T: Clone>(pub T);
110
111impl<T: Clone> Eval for Static<T> {
112    type Output = T;
113
114    fn eval_alpha(&self, _alpha: f64) -> Self::Output {
115        self.0.clone()
116    }
117}
118
119/// Requirement for [`StaticAnim`].
120pub trait StaticAnimRequirement: Clone + AnyExtractCoreItem {}
121
122impl<T: Clone + AnyExtractCoreItem> StaticAnimRequirement for T {}
123
124/// Convenience methods for zero-duration static animations.
125pub trait StaticAnim: StaticAnimRequirement + Sized {
126    /// Show this value.
127    fn show(&self) -> Paramed<Static<Self>>;
128    /// Hide this value.
129    fn hide(&self) -> Paramed<Static<Self>>;
130}
131
132impl<T: StaticAnimRequirement + 'static> StaticAnim for T {
133    fn show(&self) -> Paramed<Static<Self>> {
134        Static(self.clone()).with_duration(0.0)
135    }
136
137    fn hide(&self) -> Paramed<Static<Self>> {
138        Static(self.clone()).with_enabled(false).with_duration(0.0)
139    }
140}