use rand::prelude::*; use serde::{Deserialize, Serialize}; use crate::system::{Position, Storage}; use crate::system::spawner::Spawner; use crate::system::sticker::Sticker; use crate::system::walker::Walker; #[derive(Serialize, Deserialize)] pub struct HistoryLine { pub frame: usize, pub cluster_radius: f32, pub fd: f32, pub position: P, } pub struct DLASystem, W: Walker

, Sp: Spawner

, St: Sticker> { rng: R, /* * These object encapsulate the behaviour of our particular model. */ /* * The space, along with the chosen position choose the embedding space that the cluster grows * in. */ space: S, /* * Walkers allow us to choose between different particle movement behaviours, eg random or * biased walker */ walker: W, spawner: Sp, sticker: St, pub frame: usize, pub running: bool, pub history: Vec>, max_particles: usize, particles: Vec

, active_particle: Option

, add_ratio: f32, add_circle: f32, kill_ratio: f32, kill_circle: f32, cluster_radius: f32, } impl, W: Walker

, Sp: Spawner

, St: Sticker> DLASystem { pub fn new(rng: R, space: S, walker: W, spawner: Sp, sticker: St, max_particles: usize) -> Self { let mut sys = DLASystem { rng, max_particles, running: true, spawner, sticker, space, walker, frame: 0, particles: vec![], history: vec![], active_particle: None, add_ratio: 1.2, kill_ratio: 1.7, add_circle: 10.0, kill_circle: 20.0, cluster_radius: 0.0, }; sys.deposit(&P::zero()); sys } pub fn update(&mut self) { self.frame += 1; if self.active_particle.is_some() { self.move_particle(); } else if self.particles.len() < self.max_particles { self.spawn_particle(); } else { self.running = false; } } fn move_particle(&mut self) { let current_position = &self .active_particle .clone() .expect("No active particle"); let next_position = self.walker.walk(&mut self.rng, current_position); let distance = next_position.abs(); if distance > self.kill_circle { self.active_particle = None; } else if !self.space.is_occupied(&next_position) { if self.sticker.should_stick(&mut self.rng, &self.space, &next_position) { self.deposit(&next_position); self.active_particle = None; return; } else { self.active_particle.replace(next_position); } } } fn spawn_particle(&mut self) { let position = self.spawner.spawn(&mut self.rng, self.add_circle); if !self.space.is_occupied(&position) { self.active_particle = Some(position); } } fn deposit(&mut self, p0: &P) { self.particles.push(p0.clone()); self.space.deposit(p0); let distance = p0.abs(); if distance > self.cluster_radius { self.cluster_radius = distance; let new_add_circle = (self.cluster_radius * self.add_ratio).max(self.cluster_radius + 5.0); if self.add_circle < new_add_circle { self.add_circle = new_add_circle; self.kill_circle = self.kill_ratio * self.add_circle; } } self.history.push(HistoryLine { frame: self.frame, position: p0.clone(), cluster_radius: self.cluster_radius, fd: (self.particles.len() as f32).ln() / self.cluster_radius.ln() }); } }