use constraint::Constraint;
use world::Body;
use math::{Vec2, PI, Cross};
use joint::Joint;
const SPRING_DISPLACEMENT_SLOP: f32 = 0.05;
#[derive(Clone)]
pub struct SpringJoint {
pub local_anchor_a: Vec2,
pub local_anchor_b: Vec2,
pub distance: f32,
pub frequency: f32,
pub damping: f32,
impulse: f32,
mass: f32,
r_a: Vec2,
r_b: Vec2,
x: f32,
normal: Vec2,
softness: f32,
k: f32,
beta: f32,
damp_coeff: f32,
omega: f32,
}
impl SpringJoint {
pub fn new(local_anchor_a: Vec2, local_anchor_b: Vec2,
distance: f32, frequency: f32, damping: f32) -> SpringJoint {
SpringJoint {
local_anchor_a,
local_anchor_b,
distance,
frequency,
damping,
impulse: 0.0,
mass: 0.0,
r_a: Vec2::ZERO,
r_b: Vec2::ZERO,
x: 0.0,
normal: Vec2::ZERO,
softness: 0.0,
k: 0.0,
beta: 0.0,
damp_coeff: 0.0,
omega: 0.0
}
}
pub fn into_joint(self) -> Joint {
Joint::Spring(self)
}
}
impl Constraint for SpringJoint {
fn initialize_velocity(&mut self, a: &Body, b: &Body, dt: f32) {
self.r_a = a.transform.world_dir(&self.local_anchor_a);
self.r_b = b.transform.world_dir(&self.local_anchor_b);
self.omega = 2.0 * PI * self.frequency;
let displacement = b.transform.position + self.r_b - a.transform.position - self.r_a;
let length = displacement.len();
self.x = length - self.distance;
self.normal = if length != 0.0 {
displacement / length
} else {
Vec2::UP
};
let inv_mass_sum = a.inv_mass + b.inv_mass;
let r_a_normal_perp = self.r_a.cross(self.normal);
let r_b_normal_perp = self.r_b.cross(self.normal);
let inv_reduced_mass = inv_mass_sum + r_a_normal_perp * r_a_normal_perp * a.inv_inertia +
r_b_normal_perp * r_b_normal_perp * b.inv_inertia;
self.mass = if inv_reduced_mass != 0.0 { 1.0 / inv_reduced_mass } else { 0.0 };
self.k = self.mass * self.omega * self.omega;
self.damp_coeff = 2.0 * self.mass * self.omega * self.damping;
let inv_softness = dt * (self.damp_coeff + dt * self.k);
self.softness = if inv_softness != 0.0 { 1.0 / inv_softness } else { 0.0 };
self.beta = dt * dt * self.k * self.softness;
let inv_reduced_mass = inv_reduced_mass + self.softness;
self.mass = if inv_reduced_mass != 0.0 { 1.0 / inv_reduced_mass } else { 0.0 };
}
fn warm_start_velocity(&mut self, a: &mut Body, b: &mut Body, dt: f32) {
let impulse = self.impulse * self.normal;
a.add_impulse_at_pos(-impulse, self.r_a);
b.add_impulse_at_pos(impulse, self.r_b);
}
fn warm_start_position(&mut self, a: &mut Body, b: &mut Body, dt: f32) {}
fn solve_velocity(&mut self, a: &mut Body, b: & mut Body, dt: f32) {
let rel_vel = b.velocity - a.velocity +
b.angular_vel.cross(&self.r_b) - a.angular_vel.cross(&self.r_a);
let rel_vel_normal = self.normal.dot(&rel_vel);
let bias = self.beta * f32::max(self.x.abs() - SPRING_DISPLACEMENT_SLOP, 0.0) * self.x.signum() / dt;
let impulse = -(rel_vel_normal + bias + self.softness * self.impulse) * self.mass;
self.impulse += impulse;
a.add_impulse_at_pos(-impulse * self.normal, self.r_a);
b.add_impulse_at_pos(impulse * self.normal, self.r_b);
}
fn solve_position(&mut self, a: &mut Body, b: &mut Body, dt: f32) {}
}