ferroui_base/animation/
spring.rs1use crate::utilities::{FormatError, SpanStringTokenizer, spring_solver::SpringSolver};
2use std::cell::Cell;
3use std::str::FromStr;
4
5pub struct Spring {
7 spring_solver: Cell<SpringSolver>,
8 mass: Cell<f64>,
9 stiffness: Cell<f64>,
10 damping: Cell<f64>,
11 initial_velocity: Cell<f64>,
12 is_dirty: Cell<bool>,
13}
14
15
16impl PartialEq for Spring {
19 #[inline]
20 fn eq(&self, other: &Self) -> bool {
21 std::ptr::eq(self, other)
22 }
23}
24
25impl Default for Spring {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl Spring {
32 pub fn new() -> Self {
34 Self::with_values(0.0, 0.0, 0.0, 0.0)
35 }
36
37 pub fn with_values(mass: f64, stiffness: f64, damping: f64, initial_velocity: f64) -> Self {
39 Self {
40 spring_solver: Cell::new(SpringSolver::default()),
41 mass: Cell::new(mass),
42 stiffness: Cell::new(stiffness),
43 damping: Cell::new(damping),
44 initial_velocity: Cell::new(initial_velocity),
45 is_dirty: Cell::new(true),
46 }
47 }
48
49 pub fn parse(value: &str) -> Result<Spring, FormatError> {
52 let error = |_| FormatError::from_string(format!("Invalid Spring string: \"{value}\"."));
53 let mut tokenizer = SpanStringTokenizer::new(value);
54 let result = Spring::with_values(
55 tokenizer.read_double().map_err(error)?,
56 tokenizer.read_double().map_err(error)?,
57 tokenizer.read_double().map_err(error)?,
58 tokenizer.read_double().map_err(error)?,
59 );
60 tokenizer.finish().map_err(error)?;
61 Ok(result)
62 }
63
64 pub fn mass(&self) -> f64 {
66 self.mass.get()
67 }
68
69 pub fn set_mass(&self, value: f64) {
70 self.mass.set(value);
71 self.is_dirty.set(true);
72 }
73
74 pub fn stiffness(&self) -> f64 {
76 self.stiffness.get()
77 }
78
79 pub fn set_stiffness(&self, value: f64) {
80 self.stiffness.set(value);
81 self.is_dirty.set(true);
82 }
83
84 pub fn damping(&self) -> f64 {
86 self.damping.get()
87 }
88
89 pub fn set_damping(&self, value: f64) {
90 self.damping.set(value);
91 self.is_dirty.set(true);
92 }
93
94 pub fn initial_velocity(&self) -> f64 {
96 self.initial_velocity.get()
97 }
98
99 pub fn set_initial_velocity(&self, value: f64) {
100 self.initial_velocity.set(value);
101 self.is_dirty.set(true);
102 }
103
104 pub fn get_spring_progress(&self, linear_progress: f64) -> f64 {
106 if self.is_dirty.get() {
107 self.build();
108 }
109
110 self.spring_solver.get().solve(linear_progress)
111 }
112
113 fn build(&self) {
115 self.spring_solver.set(SpringSolver::new(
116 self.mass.get(),
117 self.stiffness.get(),
118 self.damping.get(),
119 self.initial_velocity.get(),
120 ));
121 self.is_dirty.set(false);
122 }
123}
124
125impl FromStr for Spring {
126 type Err = FormatError;
127 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 Spring::parse(s)
129 }
130}