ranim_anims/camera.rs
1//! Camera frame animations.
2
3use ranim_core::{
4 animation::eval::{Eval, EvalExt},
5 core_item::camera_frame::CameraFrame,
6 glam::DVec3,
7};
8
9// MARK: Anim Trait
10/// The methods to create animations for [`CameraFrame`].
11pub trait CameraFrameAnim {
12 /// Create an orbit animation that rotates the camera around `target`
13 /// by `total_angle` radians in the XY plane (Z-up).
14 ///
15 /// The camera's current position is used to derive the spherical
16 /// coordinates (distance, elevation) which are kept constant during the orbit.
17 ///
18 /// # Example
19 /// ```ignore
20 /// use std::f64::consts::TAU;
21 ///
22 /// let mut cam = CameraFrame::from_spherical(phi, theta, distance);
23 /// r.play(
24 /// cam.orbit(DVec3::ZERO, TAU)
25 /// .with_duration(8.0)
26 /// .with_rate_func(linear),
27 /// );
28 /// ```
29 fn orbit(&mut self, target: DVec3, total_angle: f64) -> Orbit;
30}
31
32impl CameraFrameAnim for CameraFrame {
33 fn orbit(&mut self, target: DVec3, total_angle: f64) -> Orbit {
34 let offset = self.pos - target;
35 let distance = offset.length();
36 let phi = if distance > 0.0 {
37 (offset.z / distance).acos()
38 } else {
39 0.0
40 };
41 let theta0 = offset.y.atan2(offset.x);
42
43 Orbit {
44 src: self.clone(),
45 target,
46 distance,
47 phi,
48 theta0,
49 total_angle,
50 }
51 .apply_to(self)
52 }
53}
54
55// MARK: Impl
56/// An orbit animation rotating the camera around a target.
57pub struct Orbit {
58 /// The camera state at the start of the orbit.
59 pub src: CameraFrame,
60 /// The orbit target.
61 pub target: DVec3,
62 /// Distance from the target.
63 pub distance: f64,
64 /// Elevation angle kept constant during the orbit.
65 pub phi: f64,
66 /// Initial azimuth angle.
67 pub theta0: f64,
68 /// Total angle to rotate, in radians.
69 pub total_angle: f64,
70}
71
72impl Eval for Orbit {
73 type Output = CameraFrame;
74
75 fn eval_alpha(&self, alpha: f64) -> Self::Output {
76 let theta = self.theta0 + self.total_angle * alpha;
77 let mut result = self.src.clone();
78 result.set_spherical(self.phi, theta, self.distance, self.target);
79 result
80 }
81}