use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AgentField {
pub agents: Vec<AgentViz>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AgentViz {
pub position: [f32; 3],
pub heading: [f32; 3],
pub state: String,
pub speed: f32,
pub alertness: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TerritoryMap {
pub territories: Vec<TerritoryRegion>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TerritoryRegion {
pub center: [f32; 2],
pub radius: f32,
pub owner_id: u32,
pub strength: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MigrationPath {
pub waypoints: Vec<[f32; 3]>,
pub timestamps: Vec<f32>,
pub label: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SocialGraph {
pub nodes: Vec<SocialNode>,
pub edges: Vec<SocialEdge>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SocialNode {
pub id: u32,
pub position: [f32; 2],
pub rank: f32,
pub role: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SocialEdge {
pub from: u32,
pub to: u32,
pub relation: String,
pub strength: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SwarmField {
pub density: Vec<f32>,
pub velocity: Vec<[f32; 2]>,
pub dimensions: [usize; 2],
pub cell_size: f32,
pub max_density: f32,
}
impl SwarmField {
#[must_use]
pub fn from_agents(
positions: &[[f32; 2]],
velocities: &[[f32; 2]],
grid_min: [f32; 2],
grid_max: [f32; 2],
cell_size: f32,
) -> Self {
if cell_size <= 0.0 {
return Self {
density: Vec::new(),
velocity: Vec::new(),
dimensions: [0, 0],
cell_size,
max_density: 0.0,
};
}
let nx = ((grid_max[0] - grid_min[0]) / cell_size).ceil() as usize;
let nz = ((grid_max[1] - grid_min[1]) / cell_size).ceil() as usize;
let count = nx.max(1) * nz.max(1);
let mut density = vec![0.0_f32; count];
let mut vel_sum = vec![[0.0_f32; 2]; count];
let n = positions.len().min(velocities.len());
for i in 0..n {
let ix = ((positions[i][0] - grid_min[0]) / cell_size) as usize;
let iz = ((positions[i][1] - grid_min[1]) / cell_size) as usize;
if ix < nx && iz < nz {
let idx = iz * nx + ix;
density[idx] += 1.0;
vel_sum[idx][0] += velocities[i][0];
vel_sum[idx][1] += velocities[i][1];
}
}
let mut max_density = 0.0_f32;
let velocity: Vec<[f32; 2]> = density
.iter()
.zip(vel_sum.iter())
.map(|(&d, &vs)| {
if d > max_density {
max_density = d;
}
if d > 0.0 {
[vs[0] / d, vs[1] / d]
} else {
[0.0, 0.0]
}
})
.collect();
Self {
density,
velocity,
dimensions: [nx, nz],
cell_size,
max_density,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn agent_field_manual() {
let field = AgentField {
agents: vec![AgentViz {
position: [1.0, 0.0, 2.0],
heading: [1.0, 0.0, 0.0],
state: String::from("Foraging"),
speed: 1.5,
alertness: 0.3,
}],
};
assert_eq!(field.agents.len(), 1);
}
#[test]
fn territory_map_manual() {
let map = TerritoryMap {
territories: vec![TerritoryRegion {
center: [50.0, 50.0],
radius: 20.0,
owner_id: 0,
strength: 0.8,
}],
};
assert_eq!(map.territories.len(), 1);
}
#[test]
fn migration_path_manual() {
let path = MigrationPath {
waypoints: vec![[0.0, 0.0, 0.0], [100.0, 0.0, 50.0]],
timestamps: vec![0.0, 0.5],
label: String::from("caribou"),
};
assert_eq!(path.waypoints.len(), 2);
}
#[test]
fn social_graph_manual() {
let graph = SocialGraph {
nodes: vec![
SocialNode {
id: 0,
position: [0.0, 0.0],
rank: 1.0,
role: String::from("Alpha"),
},
SocialNode {
id: 1,
position: [1.0, 0.0],
rank: 0.5,
role: String::from("Beta"),
},
],
edges: vec![SocialEdge {
from: 0,
to: 1,
relation: String::from("Dominance"),
strength: 0.9,
}],
};
assert_eq!(graph.nodes.len(), 2);
assert_eq!(graph.edges.len(), 1);
}
#[test]
fn swarm_field_from_agents() {
let positions = [[1.0, 1.0], [1.5, 1.5], [5.0, 5.0]];
let velocities = [[1.0, 0.0], [0.0, 1.0], [-1.0, 0.0]];
let field = SwarmField::from_agents(&positions, &velocities, [0.0, 0.0], [10.0, 10.0], 2.0);
assert_eq!(field.dimensions, [5, 5]);
assert!(field.max_density >= 1.0);
}
#[test]
fn swarm_field_empty() {
let field = SwarmField::from_agents(&[], &[], [0.0, 0.0], [10.0, 10.0], 2.0);
assert_eq!(field.max_density, 0.0);
}
#[test]
fn swarm_field_zero_cell_size() {
let field = SwarmField::from_agents(&[], &[], [0.0, 0.0], [10.0, 10.0], 0.0);
assert!(field.density.is_empty());
}
#[test]
fn agent_field_serializes() {
let field = AgentField { agents: Vec::new() };
let json = serde_json::to_string(&field);
assert!(json.is_ok());
}
}