use std::f32::consts::PI; use std::fs::File; use std::io::Write; use std::io; use std::path::Path; use rand::prelude::*; use crate::system::{GriddedPosition, Position}; use crate::system::storage::{Storage, VectorStorage}; use crate::system::walker::{LocalRandomWalker, Walker}; use serde::{Serialize, Deserialize}; pub struct DLASystem> { rng: R, storage: S, walker: W, stick_probability: f32, max_particles: usize, pub running: bool, particles: Vec, active_particle: Option, add_ratio: f32, add_circle: f32, kill_ratio: f32, kill_circle: f32, cluster_radius: f32, } impl> DLASystem { pub fn new(rng: R, max_particles: usize, stick_probability: f32) -> DLASystem { let mut sys: DLASystem = DLASystem { rng, stick_probability, max_particles, running: true, storage: VectorStorage::new(1600, 2), walker: LocalRandomWalker::new(2), particles: 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(&Position::zero()); sys } pub fn update(&mut self) { 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.storage.at(&next_position) { if self.check_stick(&next_position) { self.deposit(&next_position); self.active_particle = None; return; } else { self.active_particle.replace(next_position); } } } fn check_stick(&mut self, position: &Position) -> bool { position.neighbours() .iter() .any(|neighbour| self.storage.at(&neighbour) && self.rng.gen_range(0.0f32..=1.0) < self.stick_probability ) } fn spawn_particle(&mut self) { let theta = self.rng.gen_range(0f32..1.0) * 2.0 * PI; let (x, y) = (self.add_circle * theta.cos(), self.add_circle * theta.sin()); let position = Position { x: x as i32, y: y as i32 }; if !self.storage.at(&position) { self.active_particle = Some(position); } } fn deposit(&mut self, p0: &Position) { self.particles.push(p0.clone()); self.storage.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; } } } pub fn export_data(&self, path: &Path) -> io::Result<()> { let mut wtr = csv::Writer::from_path(path)?; for particle in &self.particles { wtr.serialize(particle)?; } wtr.flush()?; Ok(()) } }