cyclone2d 0.1.2

A small 2D physics engine from 'Game Physics Engine Development'
Documentation
use crate::particle::Particle;
use crate::vek2d::Vec2d;
use crate::FP;
use std::ptr;

pub trait ForceGenerator {
    fn update_force(&mut self);
    fn contains_ptr(&self, ptr: usize) -> bool;
}

/////////////////////////////////////////////////////////////////////////////////
pub struct Gravity {
    particle: ptr::NonNull<Particle>,
    gravity:  FP,
}
impl Gravity {
    pub fn new(particle: ptr::NonNull<Particle>, gravity: FP) -> Gravity {
        Gravity { particle, gravity }
    }
}
impl ForceGenerator for Gravity {
    fn update_force(&mut self) {
        let particle = unsafe { self.particle.as_mut() };
        let force = Vec2d::new(0.0, self.gravity * particle.mass());
        particle.add_force(force);
    }
    fn contains_ptr(&self, ptr: usize) -> bool {
        self.particle.as_ptr() as usize == ptr
    }
}

/////////////////////////////////////////////////////////////////////////////////
pub struct Drag {
    particle: ptr::NonNull<Particle>,
    k1:       FP,
    k2:       FP,
}
impl Drag {
    pub fn new(particle: ptr::NonNull<Particle>, k1: FP, k2: FP) -> Drag {
        Drag { particle, k1, k2 }
    }
}
impl ForceGenerator for Drag {
    fn update_force(&mut self) {
        let particle = unsafe { self.particle.as_mut() };
        let mut force = particle.velocity();
        let mut drag_coeff = force.magnitude();
        drag_coeff = self.k1 * drag_coeff + self.k2 * drag_coeff * drag_coeff;
        force = force.normalise();
        force *= -drag_coeff;
        particle.add_force(force);
    }
    fn contains_ptr(&self, ptr: usize) -> bool {
        self.particle.as_ptr() as usize == ptr
    }
}

/////////////////////////////////////////////////////////////////////////////////
pub struct AnchoredSpring {
    particle:     ptr::NonNull<Particle>,
    anchor:       Vec2d<FP>,
    spring_const: FP,
    rest_len:     FP,
}
impl AnchoredSpring {
    pub fn new(
        particle: ptr::NonNull<Particle>,
        anchor: Vec2d<FP>,
        spring_const: FP,
        rest_len: FP,
    ) -> AnchoredSpring {
        AnchoredSpring {
            particle,
            anchor,
            spring_const,
            rest_len,
        }
    }
}
impl ForceGenerator for AnchoredSpring {
    fn update_force(&mut self) {
        let particle = unsafe { self.particle.as_mut() };
        // Calculate the vector of the spring
        let mut force = particle.position();
        force -= self.anchor;
        // Calculate the magnitude of the force
        let mut magnitude = force.magnitude();
        magnitude = (self.rest_len - magnitude) * self.spring_const;
        // Calculate the final force and apply it
        force = force.normalise();
        force *= magnitude;
        particle.add_force(force);
    }
    fn contains_ptr(&self, ptr: usize) -> bool {
        self.particle.as_ptr() as usize == ptr
    }
}

/////////////////////////////////////////////////////////////////////////////////
pub struct AnchoredBungee {
    particle:     ptr::NonNull<Particle>,
    anchor:       ptr::NonNull<Particle>,
    spring_const: FP,
    rest_len:     FP,
}
impl AnchoredBungee {
    pub fn new(
        particle: ptr::NonNull<Particle>,
        anchor: ptr::NonNull<Particle>,
        spring_const: FP,
        rest_len: FP,
    ) -> AnchoredBungee {
        AnchoredBungee {
            particle,
            anchor,
            spring_const,
            rest_len,
        }
    }
}
impl ForceGenerator for AnchoredBungee {
    fn update_force(&mut self) {
        let pos = unsafe { self.anchor.as_ref().position() };
        let particle = unsafe { self.particle.as_mut() };
        // Calculate the vector of the spring
        let mut force = particle.position();
        force -= pos;

        // Calculate the magnitude of the force
        let mut magnitude = force.magnitude();
        if magnitude <= self.rest_len {
            return;
        }
        magnitude -= self.rest_len;
        magnitude *= self.spring_const;

        // Calculate the final force and apply it
        force = force.normalise();
        force *= -magnitude;
        particle.add_force(force);
    }
    fn contains_ptr(&self, ptr: usize) -> bool {
        self.particle.as_ptr() as usize == ptr
            || self.anchor.as_ptr() as usize == ptr
    }
}

/////////////////////////////////////////////////////////////////////////////////
pub struct Buoyancy {
    particle:       ptr::NonNull<Particle>,
    max_depth:      FP,
    volume:         FP,
    water_height:   FP,
    liquid_density: FP,
}
impl Buoyancy {
    pub fn new(
        particle: ptr::NonNull<Particle>,
        max_depth: FP,
        volume: FP,
        water_height: FP,
        liquid_density: FP,
    ) -> Buoyancy {
        Buoyancy {
            particle,
            max_depth,
            volume,
            water_height,
            liquid_density,
        }
    }
}
impl ForceGenerator for Buoyancy {
    fn update_force(&mut self) {
        let particle = unsafe { self.particle.as_mut() };
        let depth = particle.position().y();

        // check if out of water
        if depth >= self.water_height + self.max_depth {
            return;
        }

        let mut force = Vec2d::default();
        // check if at max depth
        if depth < self.water_height - self.max_depth {
            force.set_y(self.liquid_density * self.volume);
            particle.add_force(force);
            return;
        }

        // otherwise partly submerged
        force.set_y(
            self.liquid_density
                * self.volume
                * (depth - self.max_depth - self.water_height)
                / 2.0
                * self.max_depth,
        );
        particle.add_force(force);
    }
    fn contains_ptr(&self, ptr: usize) -> bool {
        self.particle.as_ptr() as usize == ptr
    }
}