matter-js-rs 0.1.1

A Rust port of Matter.js, the 2D rigid body physics engine
Documentation
use crate::body::BodyHandle;
use crate::constraint::ConstraintHandle;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CompositeHandle(pub usize);

#[derive(Debug, Clone)]
pub struct Composite {
    pub handle: CompositeHandle,
    pub bodies: Vec<BodyHandle>,
    pub constraints: Vec<ConstraintHandle>,
    pub composites: Vec<CompositeHandle>,
    pub parent: Option<CompositeHandle>,
}

impl Composite {
    pub fn new(handle: CompositeHandle) -> Composite {
        Composite {
            handle,
            bodies: Vec::new(),
            constraints: Vec::new(),
            composites: Vec::new(),
            parent: None,
        }
    }

    pub fn add_body(&mut self, body: BodyHandle) {
        self.bodies.push(body);
    }

    pub fn remove_body(&mut self, body: BodyHandle) {
        self.bodies.retain(|b| *b != body);
    }

    pub fn add_constraint(&mut self, constraint: ConstraintHandle) {
        self.constraints.push(constraint);
    }

    pub fn remove_constraint(&mut self, constraint: ConstraintHandle) {
        self.constraints.retain(|c| *c != constraint);
    }

    pub fn add_composite(&mut self, composite: CompositeHandle) {
        self.composites.push(composite);
    }

    pub fn remove_composite(&mut self, composite: CompositeHandle) {
        self.composites.retain(|c| *c != composite);
    }

    pub fn all_bodies(&self, composites: &[Composite]) -> Vec<BodyHandle> {
        let mut bodies = self.bodies.clone();
        for &child_handle in &self.composites {
            if let Some(child) = composites.iter().find(|c| c.handle == child_handle) {
                bodies.extend(child.all_bodies(composites));
            }
        }
        bodies
    }

    pub fn all_constraints(&self, composites: &[Composite]) -> Vec<ConstraintHandle> {
        let mut constraints = self.constraints.clone();
        for &child_handle in &self.composites {
            if let Some(child) = composites.iter().find(|c| c.handle == child_handle) {
                constraints.extend(child.all_constraints(composites));
            }
        }
        constraints
    }

    pub fn clear(&mut self) {
        self.bodies.clear();
        self.constraints.clear();
        self.composites.clear();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn composite_new_is_empty() {
        let composite = Composite::new(CompositeHandle(0));
        assert!(composite.bodies.is_empty());
        assert!(composite.constraints.is_empty());
        assert!(composite.composites.is_empty());
        assert_eq!(composite.parent, None);
    }

    #[test]
    fn composite_add_remove_body() {
        let mut composite = Composite::new(CompositeHandle(0));
        composite.add_body(BodyHandle(1));
        composite.add_body(BodyHandle(2));

        assert_eq!(composite.bodies.len(), 2);
        assert_eq!(composite.bodies[0], BodyHandle(1));

        composite.remove_body(BodyHandle(1));
        assert_eq!(composite.bodies.len(), 1);
        assert_eq!(composite.bodies[0], BodyHandle(2));
    }

    #[test]
    fn composite_add_remove_constraint() {
        let mut composite = Composite::new(CompositeHandle(0));
        composite.add_constraint(ConstraintHandle(1));
        composite.add_constraint(ConstraintHandle(2));

        assert_eq!(composite.constraints.len(), 2);

        composite.remove_constraint(ConstraintHandle(1));
        assert_eq!(composite.constraints.len(), 1);
        assert_eq!(composite.constraints[0], ConstraintHandle(2));
    }

    #[test]
    fn composite_add_remove_sub_composite() {
        let mut composite = Composite::new(CompositeHandle(0));
        composite.add_composite(CompositeHandle(1));

        assert_eq!(composite.composites.len(), 1);

        composite.remove_composite(CompositeHandle(1));
        assert!(composite.composites.is_empty());
    }

    #[test]
    fn composite_all_bodies_flat() {
        let mut composite = Composite::new(CompositeHandle(0));
        composite.add_body(BodyHandle(1));
        composite.add_body(BodyHandle(2));

        let all = composite.all_bodies(&[]);
        assert_eq!(all, vec![BodyHandle(1), BodyHandle(2)]);
    }

    #[test]
    fn composite_all_bodies_recursive() {
        let mut parent = Composite::new(CompositeHandle(0));
        parent.add_body(BodyHandle(1));
        parent.add_composite(CompositeHandle(1));

        let mut child = Composite::new(CompositeHandle(1));
        child.add_body(BodyHandle(2));
        child.add_body(BodyHandle(3));

        let composites = vec![parent.clone(), child];
        let all = composites[0].all_bodies(&composites);
        assert_eq!(all, vec![BodyHandle(1), BodyHandle(2), BodyHandle(3)]);
    }

    #[test]
    fn composite_all_constraints_recursive() {
        let mut parent = Composite::new(CompositeHandle(0));
        parent.add_constraint(ConstraintHandle(1));
        parent.add_composite(CompositeHandle(1));

        let mut child = Composite::new(CompositeHandle(1));
        child.add_constraint(ConstraintHandle(2));

        let composites = vec![parent.clone(), child];
        let all = composites[0].all_constraints(&composites);
        assert_eq!(all, vec![ConstraintHandle(1), ConstraintHandle(2)]);
    }

    #[test]
    fn composite_clear() {
        let mut composite = Composite::new(CompositeHandle(0));
        composite.add_body(BodyHandle(1));
        composite.add_constraint(ConstraintHandle(1));
        composite.add_composite(CompositeHandle(1));

        composite.clear();
        assert!(composite.bodies.is_empty());
        assert!(composite.constraints.is_empty());
        assert!(composite.composites.is_empty());
    }
}