rustsim-spaces 0.0.1

Space implementations (grid, continuous, graph, hybrid) for rustsim
Documentation
use rustsim_core::{interaction::PositionedAgent, prelude::Agent, types::AgentId};
use rustsim_spaces::continuous::{ContinuousPos, ContinuousSpace2D};

#[derive(Debug, Clone)]
struct Particle {
    id: AgentId,
    pos: ContinuousPos,
}

impl Agent for Particle {
    fn id(&self) -> AgentId {
        self.id
    }
}

impl PositionedAgent for Particle {
    type Position = ContinuousPos;

    fn position(&self) -> &ContinuousPos {
        &self.pos
    }

    fn set_position(&mut self, pos: ContinuousPos) {
        self.pos = pos;
    }
}

#[test]
fn continuous_space_add_and_nearby() {
    let mut space = ContinuousSpace2D::new(10.0, 10.0, false, 1.0).unwrap();

    let p1 = Particle {
        id: 1,
        pos: ContinuousPos::new(2.5, 2.5),
    };
    let p2 = Particle {
        id: 2,
        pos: ContinuousPos::new(3.0, 2.5),
    };
    let p3 = Particle {
        id: 3,
        pos: ContinuousPos::new(8.0, 8.0),
    };

    use rustsim_core::interaction::SpaceInteraction;
    space.add_agent(&p1).unwrap();
    space.add_agent(&p2).unwrap();
    space.add_agent(&p3).unwrap();

    let near = space.nearby_ids_euclidean(&ContinuousPos::new(2.5, 2.5), 1.0);
    assert!(near.contains(&1));
    assert!(near.contains(&2));
    assert!(!near.contains(&3));
}

#[test]
fn continuous_space_periodic_wrapping() {
    let mut space = ContinuousSpace2D::new(10.0, 10.0, true, 2.0).unwrap();

    let p1 = Particle {
        id: 1,
        pos: ContinuousPos::new(0.5, 0.5),
    };
    let p2 = Particle {
        id: 2,
        pos: ContinuousPos::new(9.5, 9.5),
    };

    use rustsim_core::interaction::SpaceInteraction;
    space.add_agent(&p1).unwrap();
    space.add_agent(&p2).unwrap();

    let near = space.nearby_ids_euclidean(&ContinuousPos::new(0.5, 0.5), 2.0);
    assert!(near.contains(&1));
    assert!(near.contains(&2), "periodic wrapping should find agent 2");
}

#[test]
fn continuous_space_move_updates_grid() {
    let mut space = ContinuousSpace2D::new(10.0, 10.0, false, 1.0).unwrap();

    let p1 = Particle {
        id: 1,
        pos: ContinuousPos::new(1.0, 1.0),
    };

    use rustsim_core::interaction::SpaceInteraction;
    space.add_agent(&p1).unwrap();

    let near_old = space.nearby_ids_euclidean(&ContinuousPos::new(1.0, 1.0), 0.5);
    assert!(near_old.contains(&1));

    space
        .move_agent_pos(1, ContinuousPos::new(8.0, 8.0))
        .unwrap();

    let near_old = space.nearby_ids_euclidean(&ContinuousPos::new(1.0, 1.0), 0.5);
    assert!(!near_old.contains(&1));

    let near_new = space.nearby_ids_euclidean(&ContinuousPos::new(8.0, 8.0), 0.5);
    assert!(near_new.contains(&1));
}

#[test]
fn continuous_space_remove() {
    let mut space = ContinuousSpace2D::new(10.0, 10.0, false, 1.0).unwrap();

    let p1 = Particle {
        id: 1,
        pos: ContinuousPos::new(5.0, 5.0),
    };

    use rustsim_core::interaction::SpaceInteraction;
    space.add_agent(&p1).unwrap();
    assert!(!space
        .nearby_ids_euclidean(&ContinuousPos::new(5.0, 5.0), 1.0)
        .is_empty());

    space.remove_agent(&p1).unwrap();
    assert!(space
        .nearby_ids_euclidean(&ContinuousPos::new(5.0, 5.0), 1.0)
        .is_empty());
}

#[test]
fn continuous_pos_2d_arithmetic() {
    let a = ContinuousPos::new(1.0, 2.0);
    let b = ContinuousPos::new(3.0, 5.0);

    let sum = a + b;
    assert!((sum.x - 4.0).abs() < 1e-10);
    assert!((sum.y - 7.0).abs() < 1e-10);

    let diff = b - a;
    assert!((diff.x - 2.0).abs() < 1e-10);
    assert!((diff.y - 3.0).abs() < 1e-10);

    let scaled = a * 3.0;
    assert!((scaled.x - 3.0).abs() < 1e-10);
    assert!((scaled.y - 6.0).abs() < 1e-10);
}

#[test]
fn continuous_pos_2d_length_and_normalized() {
    let v = ContinuousPos::new(3.0, 4.0);
    assert!((v.length() - 5.0).abs() < 1e-10);

    let n = v.normalized();
    assert!((n.length() - 1.0).abs() < 1e-10);
    assert!((n.x - 0.6).abs() < 1e-10);
    assert!((n.y - 0.8).abs() < 1e-10);

    let zero = ContinuousPos::zero();
    let nz = zero.normalized();
    assert!((nz.length()).abs() < 1e-10);
}