par_particle_life/simulation/
mod.rs1mod boundary;
4mod game_of_life;
5mod particle;
6mod physics;
7mod spatial_hash;
8
9pub use boundary::BoundaryMode;
10pub use game_of_life::GameOfLife;
11pub use particle::{
12 InteractionMatrix, Particle, ParticlePosType, ParticlePosTypeHalf, ParticleVel,
13 ParticleVelHalf, RadiusMatrix,
14};
15pub use physics::{PhysicsEngine, advance_particles, compute_forces_cpu};
16pub use spatial_hash::SpatialHash;
17
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct SimulationConfig {
23 pub num_particles: u32,
25
26 pub num_types: u32,
28
29 pub force_factor: f32,
31
32 pub friction: f32,
34
35 pub repel_strength: f32,
37
38 pub max_velocity: f32,
40
41 pub boundary_mode: BoundaryMode,
43
44 pub wall_repel_strength: f32,
46
47 pub mirror_wrap_count: u32,
49
50 pub world_size: glam::Vec2,
52
53 pub enable_3d: bool,
55
56 pub depth_limit: f32,
58
59 pub particle_size: f32,
61
62 pub enable_glow: bool,
64
65 pub glow_intensity: f32,
67
68 pub glow_size: f32,
70
71 pub glow_steepness: f32,
73
74 pub use_spatial_hash: bool,
76
77 pub spatial_hash_cell_size: f32,
79
80 #[serde(default = "default_max_bin_density")]
82 pub max_bin_density: f32,
83
84 #[serde(default)]
87 pub neighbor_budget: u32,
88
89 pub background_color: [f32; 3],
91}
92
93fn default_max_bin_density() -> f32 {
95 5000.0
96}
97
98impl Default for SimulationConfig {
99 fn default() -> Self {
100 Self {
101 num_particles: 64_000,
102 num_types: 7,
103 force_factor: 1.0,
104 friction: 0.3,
105 repel_strength: 3.0, max_velocity: 500.0,
107 boundary_mode: BoundaryMode::Wrap,
108 wall_repel_strength: 100.0,
109 mirror_wrap_count: 5,
110 world_size: glam::Vec2::new(1920.0, 1080.0),
111 enable_3d: false,
112 depth_limit: 420.0,
113 particle_size: 0.5,
114 enable_glow: true,
115 glow_intensity: 0.35,
116 glow_size: 4.0,
117 glow_steepness: 2.0,
118 use_spatial_hash: true,
120 spatial_hash_cell_size: 64.0,
121 background_color: [0.0, 0.0, 0.0], max_bin_density: 5000.0,
123 neighbor_budget: 0, }
125 }
126}
127
128impl SimulationConfig {
129 pub fn gpu_defaults() -> Self {
131 Self::default()
132 }
133
134 pub fn validate(&self) -> Result<(), String> {
136 if self.num_particles == 0 {
137 return Err("num_particles must be greater than 0".to_string());
138 }
139 if self.num_types == 0 || self.num_types > 16 {
140 return Err("num_types must be between 1 and 16".to_string());
141 }
142 if self.force_factor <= 0.0 {
143 return Err("force_factor must be positive".to_string());
144 }
145 if !(0.0..=1.0).contains(&self.friction) {
146 return Err("friction must be between 0.0 and 1.0".to_string());
147 }
148 if self.repel_strength < 0.0 {
149 return Err("repel_strength must be non-negative".to_string());
150 }
151 if self.world_size.x <= 0.0 || self.world_size.y <= 0.0 {
152 return Err("world_size must have positive dimensions".to_string());
153 }
154 Ok(())
155 }
156}