use rand::rngs::StdRng;
use rand::SeedableRng;
use rustsim_core::interaction::{PositionedAgent, SpaceInteraction};
use rustsim_core::prelude::*;
use rustsim_spaces::continuous3d::{ContinuousPos3D, ContinuousSpace3D};
#[derive(Debug, Clone)]
struct Bot3D {
id: AgentId,
pos: ContinuousPos3D,
}
impl Agent for Bot3D {
fn id(&self) -> AgentId {
self.id
}
}
impl PositionedAgent for Bot3D {
type Position = ContinuousPos3D;
fn position(&self) -> &ContinuousPos3D {
&self.pos
}
fn set_position(&mut self, p: ContinuousPos3D) {
self.pos = p;
}
}
#[test]
fn add_and_nearby_3d() {
let mut space = ContinuousSpace3D::new(100.0, 100.0, 100.0, false, 10.0).unwrap();
let a = Bot3D {
id: 1,
pos: ContinuousPos3D::new(50.0, 50.0, 50.0),
};
let b = Bot3D {
id: 2,
pos: ContinuousPos3D::new(52.0, 50.0, 50.0),
};
let c = Bot3D {
id: 3,
pos: ContinuousPos3D::new(90.0, 90.0, 90.0),
};
SpaceInteraction::<Bot3D>::add_agent(&mut space, &a).unwrap();
SpaceInteraction::<Bot3D>::add_agent(&mut space, &b).unwrap();
SpaceInteraction::<Bot3D>::add_agent(&mut space, &c).unwrap();
let near = space.nearby_ids_euclidean(&ContinuousPos3D::new(50.0, 50.0, 50.0), 5.0);
assert!(near.contains(&1));
assert!(near.contains(&2));
assert!(!near.contains(&3));
}
#[test]
fn remove_3d() {
let mut space = ContinuousSpace3D::new(100.0, 100.0, 100.0, false, 10.0).unwrap();
let a = Bot3D {
id: 1,
pos: ContinuousPos3D::new(10.0, 10.0, 10.0),
};
SpaceInteraction::<Bot3D>::add_agent(&mut space, &a).unwrap();
let near = space.nearby_ids_euclidean(&ContinuousPos3D::new(10.0, 10.0, 10.0), 5.0);
assert_eq!(near.len(), 1);
SpaceInteraction::<Bot3D>::remove_agent(&mut space, &a).unwrap();
let near = space.nearby_ids_euclidean(&ContinuousPos3D::new(10.0, 10.0, 10.0), 5.0);
assert!(near.is_empty());
}
#[test]
fn move_updates_grid_3d() {
let mut space = ContinuousSpace3D::new(100.0, 100.0, 100.0, false, 10.0).unwrap();
let a = Bot3D {
id: 1,
pos: ContinuousPos3D::new(5.0, 5.0, 5.0),
};
SpaceInteraction::<Bot3D>::add_agent(&mut space, &a).unwrap();
space
.move_agent_pos(1, ContinuousPos3D::new(95.0, 95.0, 95.0))
.unwrap();
let near_origin = space.nearby_ids_euclidean(&ContinuousPos3D::new(5.0, 5.0, 5.0), 5.0);
assert!(!near_origin.contains(&1));
let near_new = space.nearby_ids_euclidean(&ContinuousPos3D::new(95.0, 95.0, 95.0), 5.0);
assert!(near_new.contains(&1));
}
#[test]
fn periodic_wrapping_3d() {
let mut space = ContinuousSpace3D::new(100.0, 100.0, 100.0, true, 10.0).unwrap();
let a = Bot3D {
id: 1,
pos: ContinuousPos3D::new(1.0, 1.0, 1.0),
};
SpaceInteraction::<Bot3D>::add_agent(&mut space, &a).unwrap();
let near = space.nearby_ids_euclidean(&ContinuousPos3D::new(99.0, 99.0, 99.0), 5.0);
assert!(
near.contains(&1),
"periodic wrapping: agent at (1,1,1) should be near (99,99,99)"
);
}
#[test]
fn random_position_3d() {
let space = ContinuousSpace3D::new(50.0, 60.0, 70.0, false, 5.0).unwrap();
let mut rng = StdRng::seed_from_u64(42);
for _ in 0..100 {
let pos = SpaceInteraction::<Bot3D>::random_position(&space, &mut rng);
assert!(pos.x >= 0.0 && pos.x < 50.0);
assert!(pos.y >= 0.0 && pos.y < 60.0);
assert!(pos.z >= 0.0 && pos.z < 70.0);
}
}
#[test]
fn distance_and_ops() {
let a = ContinuousPos3D::new(1.0, 2.0, 3.0);
let b = ContinuousPos3D::new(4.0, 6.0, 3.0);
assert!((a.distance_to(&b) - 5.0).abs() < 1e-10);
let sum = a + b;
assert!((sum.x - 5.0).abs() < 1e-10);
assert!((sum.y - 8.0).abs() < 1e-10);
assert!((sum.z - 6.0).abs() < 1e-10);
let diff = b - a;
assert!((diff.x - 3.0).abs() < 1e-10);
assert!((diff.y - 4.0).abs() < 1e-10);
assert!((diff.z - 0.0).abs() < 1e-10);
let scaled = a * 2.0;
assert!((scaled.x - 2.0).abs() < 1e-10);
assert!((scaled.y - 4.0).abs() < 1e-10);
assert!((scaled.z - 6.0).abs() < 1e-10);
let norm = ContinuousPos3D::new(0.0, 3.0, 4.0).normalized();
assert!((norm.length() - 1.0).abs() < 1e-10);
}