use crate::utilities::{FormatError, SpanStringTokenizer, spring_solver::SpringSolver};
use std::cell::Cell;
use std::str::FromStr;
pub struct Spring {
spring_solver: Cell<SpringSolver>,
mass: Cell<f64>,
stiffness: Cell<f64>,
damping: Cell<f64>,
initial_velocity: Cell<f64>,
is_dirty: Cell<bool>,
}
impl PartialEq for Spring {
#[inline]
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Default for Spring {
fn default() -> Self {
Self::new()
}
}
impl Spring {
pub fn new() -> Self {
Self::with_values(0.0, 0.0, 0.0, 0.0)
}
pub fn with_values(mass: f64, stiffness: f64, damping: f64, initial_velocity: f64) -> Self {
Self {
spring_solver: Cell::new(SpringSolver::default()),
mass: Cell::new(mass),
stiffness: Cell::new(stiffness),
damping: Cell::new(damping),
initial_velocity: Cell::new(initial_velocity),
is_dirty: Cell::new(true),
}
}
pub fn parse(value: &str) -> Result<Spring, FormatError> {
let error = |_| FormatError::from_string(format!("Invalid Spring string: \"{value}\"."));
let mut tokenizer = SpanStringTokenizer::new(value);
let result = Spring::with_values(
tokenizer.read_double().map_err(error)?,
tokenizer.read_double().map_err(error)?,
tokenizer.read_double().map_err(error)?,
tokenizer.read_double().map_err(error)?,
);
tokenizer.finish().map_err(error)?;
Ok(result)
}
pub fn mass(&self) -> f64 {
self.mass.get()
}
pub fn set_mass(&self, value: f64) {
self.mass.set(value);
self.is_dirty.set(true);
}
pub fn stiffness(&self) -> f64 {
self.stiffness.get()
}
pub fn set_stiffness(&self, value: f64) {
self.stiffness.set(value);
self.is_dirty.set(true);
}
pub fn damping(&self) -> f64 {
self.damping.get()
}
pub fn set_damping(&self, value: f64) {
self.damping.set(value);
self.is_dirty.set(true);
}
pub fn initial_velocity(&self) -> f64 {
self.initial_velocity.get()
}
pub fn set_initial_velocity(&self, value: f64) {
self.initial_velocity.set(value);
self.is_dirty.set(true);
}
pub fn get_spring_progress(&self, linear_progress: f64) -> f64 {
if self.is_dirty.get() {
self.build();
}
self.spring_solver.get().solve(linear_progress)
}
fn build(&self) {
self.spring_solver.set(SpringSolver::new(
self.mass.get(),
self.stiffness.get(),
self.damping.get(),
self.initial_velocity.get(),
));
self.is_dirty.set(false);
}
}
impl FromStr for Spring {
type Err = FormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Spring::parse(s)
}
}