#[derive(Clone, Debug)]
pub struct InteractionMatrix {
interactions: Vec<(f32, f32)>,
num_types: usize,
max_radius: f32,
}
impl InteractionMatrix {
pub fn new(num_types: usize) -> Self {
Self {
interactions: vec![(0.0, 0.0); num_types * num_types],
num_types,
max_radius: 0.0,
}
}
pub fn set<T: Into<u32>, U: Into<u32>>(
&mut self,
self_type: T,
other_type: U,
strength: f32,
radius: f32,
) {
let s = self_type.into() as usize;
let o = other_type.into() as usize;
if s < self.num_types && o < self.num_types {
self.interactions[s * self.num_types + o] = (strength, radius);
if radius > self.max_radius {
self.max_radius = radius;
}
}
}
pub fn attract<T: Into<u32>, U: Into<u32>>(
&mut self,
self_type: T,
other_type: U,
strength: f32,
radius: f32,
) {
self.set(self_type, other_type, strength.abs(), radius);
}
pub fn repel<T: Into<u32>, U: Into<u32>>(
&mut self,
self_type: T,
other_type: U,
strength: f32,
radius: f32,
) {
self.set(self_type, other_type, -strength.abs(), radius);
}
pub fn set_symmetric<T: Into<u32> + Copy, U: Into<u32> + Copy>(
&mut self,
type_a: T,
type_b: U,
strength: f32,
radius: f32,
) {
self.set(type_a, type_b, strength, radius);
self.set(type_b, type_a, strength, radius);
}
pub fn num_types(&self) -> usize {
self.num_types
}
pub fn max_radius(&self) -> f32 {
self.max_radius
}
pub fn data(&self) -> &[(f32, f32)] {
&self.interactions
}
pub fn to_wgsl_init(&self) -> String {
let mut table_entries = Vec::new();
for s in 0..self.num_types {
for o in 0..self.num_types {
let (strength, radius) = self.interactions[s * self.num_types + o];
table_entries.push(format!("vec2<f32>({strength}, {radius})"));
}
}
let table_str = table_entries.join(", ");
let num_types = self.num_types;
let total = self.num_types * self.num_types;
format!(
r#" // Interaction matrix lookup table
let interaction_table = array<vec2<f32>, {total}>(
{table_str}
);
let my_type = p.particle_type;
var interaction_force = vec3<f32>(0.0);
let interaction_num_types = {num_types}u;"#
)
}
pub fn to_wgsl_neighbor(&self) -> String {
r#" // Interaction matrix force
let other_type = other.particle_type;
let lookup_idx = my_type * interaction_num_types + other_type;
let interaction = interaction_table[lookup_idx];
let int_strength = interaction.x;
let int_radius = interaction.y;
if int_radius > 0.0 && neighbor_dist < int_radius && neighbor_dist > 0.001 {
let falloff = 1.0 - (neighbor_dist / int_radius);
let force_mag = int_strength * falloff * falloff;
interaction_force += neighbor_dir * force_mag;
}"#
.to_string()
}
pub fn to_wgsl_post(&self) -> String {
" // Apply interaction matrix forces\n p.velocity += interaction_force * uniforms.delta_time;".to_string()
}
}