scenes.rs(共享场景与项目装配)
//! Iterative animation example: a damped spring (state stepped by a closure,
//! no ECS required).
//!
//! The spring's state (`x`, `v`) is owned by `Iterative` and advanced by a
//! closure via semi-implicit Euler; the `Extract` impl projects the state into
//! a rectangle once per frame, with the displacement carried by a
//! `Translation` wrapper. Reset is structural — the adapter restores the
//! stored initial state, so `SceneEvaluator::sample_at` matches forward
//! advancement for free.
//!
//! The spring's logical duration is the local `sim_secs` value captured by the
//! closure; the same value is passed to `with_duration`, so physical time and
//! timeline time stay in sync.
use ranim::{
color::palettes::manim, core::animation::eval::iterative::Iterative, glam::dvec3,
items::vitem::geometry::Rectangle, prelude::*,
};
/// The spring's state: displacement and velocity.
#[derive(Clone)]
struct SpringState {
x: f64,
v: f64,
}
#[cfg(feature = "render")]
impl ranim::render::extract::Extract for SpringState {
fn extract(&self, ctx: &mut ranim::render::extract::ExtractContext<'_>) {
let mut ball = Rectangle::new(0.6, 0.6).transformed(Translation(dvec3(self.x, 0.0, 0.0)));
ball.set_fill_color(manim::BLUE_C);
ball.set_stroke_opacity(0.0);
ball.extract(ctx);
}
}
const K: f64 = 25.0;
const C: f64 = 1.0;
#[scene]
#[wasm_demo_doc]
#[output(dir = "./output/iterative_spring")]
fn iterative_spring(r: &mut RanimScene) {
let sim_secs = 4.0;
r.play(CameraFrame::default().show().with_duration(sim_secs));
r.play(
Iterative::from_fn(
SpringState { x: 1.0, v: 0.0 },
move |state: &mut SpringState, _alpha: f64, delta_alpha: f64| {
let dt = sim_secs * delta_alpha;
let acc = -K * state.x - C * state.v;
state.v += acc * dt;
state.x += state.v * dt;
},
)
.with_duration(sim_secs),
);
r.insert_time_mark(sim_secs / 2.0, TimeMark::Capture("preview.png".to_string()));
}
#[cfg(feature = "render")]
pub(crate) fn configure_render(app: &mut bevy_app::App) {
use ranim::render::extract::ExtractPlugin;
app.add_plugins(ExtractPlugin::<SpringState>::default());
}
/// Register the scenes provided by this example.
pub struct ScenesPlugin;
impl ranim::Plugin for ScenesPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_scenes([iterative_spring::scene()]);
#[cfg(feature = "render")]
{
use ranim::{items::RanimItemsPlugin, render::RenderSetup};
app.insert_resource(RenderSetup::new(|render_app| {
render_app.add_plugins(RanimItemsPlugin);
configure_render(render_app);
}));
}
}
}
main.rs(平台入口)
mod scenes;
#[cfg(not(target_arch = "wasm32"))]
fn main() {
#[cfg(feature = "cmd-render")]
{
use ranim::prelude::*;
let mut app = App::new();
app.add_plugins(scenes::ScenesPlugin);
run_cli(app.build());
}
#[cfg(not(feature = "cmd-render"))]
compile_error!("native examples require the `cmd-render` feature (enabled by default)");
}
// The WASM module is driven by JavaScript through app().
#[cfg(target_arch = "wasm32")]
fn main() {}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn app() -> ranim::wasm::WasmApp {
use ranim::prelude::*;
let mut app = App::new();
app.add_plugins(scenes::ScenesPlugin);
ranim::wasm::WasmApp::new(app.build())
}