1use bevy::prelude::*;
2use bevy::time::common_conditions::on_timer;
3use std::time::Duration;
4
5mod components;
6mod math;
7mod resources;
8mod systems;
9
10pub use components::*;
11pub use math::*;
12pub use resources::*;
13pub use systems::*;
14
15use std::f32::consts::PI;
16
17#[derive(Resource, Clone, Debug, Copy)]
19pub struct TimeConfig {
20 pub physics_rate: f32,
22 pub fixed_timestep: f32,
24}
25
26impl Default for TimeConfig {
27 fn default() -> Self {
28 Self {
29 physics_rate: 240.0,
30 fixed_timestep: 1.0 / 240.0,
31 }
32 }
33}
34
35#[derive(Resource, Clone, Debug, Copy)]
37pub struct DomainConfig {
38 pub box_size: Vec2,
40 pub freestream_velocity: Vec2,
42 pub gravity: Vec2,
44}
45
46impl Default for DomainConfig {
47 fn default() -> Self {
48 Self {
49 box_size: Vec2::new(200.0, 200.0),
50 freestream_velocity: Vec2::new(50.0, 0.0),
51 gravity: Vec2::new(0.0, 0.0),
52 }
53 }
54}
55
56#[derive(Resource, Clone, Debug, Copy)]
58pub struct ParticleConfig {
59 pub radius: f32,
61 pub initial_count: usize,
63 pub spawn_rate: usize,
65 pub grid_cell_size: f32,
67 pub mass: f32,
69}
70
71impl ParticleConfig {
72 pub fn new(radius: f32, initial_count: usize, spawn_rate: usize, rest_density: f32) -> Self {
73 Self {
74 radius,
75 initial_count,
76 spawn_rate,
77 grid_cell_size: radius * 4.0,
78 mass: rest_density * (4.0 / 3.0) * PI * radius.powi(3),
79 }
80 }
81}
82
83impl Default for ParticleConfig {
84 fn default() -> Self {
85 Self::new(0.5, 600, 50, 1.225)
86 }
87}
88
89#[derive(Resource, Clone, Debug, Copy)]
91pub struct FluidConfig {
92 pub rest_density: f32,
94 pub gas_constant: f32,
96 pub smoothing_length: f32,
98 pub viscosity: f32,
100}
101
102impl FluidConfig {
103 pub fn new(particle_radius: f32) -> Self {
104 Self {
105 rest_density: 1.225,
106 gas_constant: 287.05,
107 smoothing_length: particle_radius * 16.0,
108 viscosity: 1.81e-5,
109 }
110 }
111}
112
113impl Default for FluidConfig {
114 fn default() -> Self {
115 Self::new(0.5)
116 }
117}
118
119#[derive(Resource, Clone, Debug, Copy)]
121pub struct RenderConfig {
122 pub render_scale: f32,
124 pub min_zoom: f32,
126 pub max_zoom: f32,
127 pub pan_speed: f32,
129}
130
131impl Default for RenderConfig {
132 fn default() -> Self {
133 Self {
134 render_scale: 4.0,
135 min_zoom: 0.1,
136 max_zoom: 10.0,
137 pan_speed: 150.0,
138 }
139 }
140}
141
142#[derive(Resource, Clone, Debug, Copy)]
144pub struct AirfoilConfig {
145 pub aoa: f32,
147 pub chord_length: f32,
149 pub thickness_ratio: f32,
151 pub position: Vec2,
153}
154
155impl Default for AirfoilConfig {
156 fn default() -> Self {
157 Self {
158 aoa: 5.0,
159 chord_length: 50.0,
160 thickness_ratio: 0.12,
161 position: Vec2::ZERO,
162 }
163 }
164}
165
166#[derive(Resource, Clone, Debug, Copy)]
168pub struct SPHConfig {
169 pub time: TimeConfig,
170 pub domain: DomainConfig,
171 pub particle: ParticleConfig,
172 pub fluid: FluidConfig,
173 pub render: RenderConfig,
174 pub airfoil: AirfoilConfig,
175}
176
177impl Default for SPHConfig {
178 fn default() -> Self {
179 Self {
180 time: TimeConfig::default(),
181 domain: DomainConfig::default(),
182 particle: ParticleConfig::default(),
183 fluid: FluidConfig::default(),
184 render: RenderConfig::default(),
185 airfoil: AirfoilConfig::default(),
186 }
187 }
188}
189
190pub struct SPHPlugin {
191 pub config: SPHConfig,
192}
193
194impl Default for SPHPlugin {
195 fn default() -> Self {
196 Self {
197 config: SPHConfig::default(),
198 }
199 }
200}
201
202impl Plugin for SPHPlugin {
203 fn build(&self, app: &mut App) {
204 app.insert_resource(self.config)
206 .insert_resource(ParticlePool::new(self.config.particle.initial_count))
207 .insert_resource(SpatialGrid::new(
208 self.config.particle.grid_cell_size,
209 self.config.particle.radius,
210 ))
211 .init_resource::<SimulationStats>()
212 .add_systems(Startup, setup)
214 .add_systems(
216 FixedUpdate,
217 (
218 update_spatial_grid,
219 compute_density_pressure,
220 apply_sph_forces,
221 )
222 .chain()
223 .run_if(on_timer(Duration::from_secs_f32(
224 self.config.time.fixed_timestep,
225 ))),
226 )
227 .add_systems(Update, (spawn_particles, print_fps, pan_camera))
228 .init_resource::<VisualizationMode>()
229 .add_systems(
230 Update,
231 (handle_visualization_toggle, update_particle_colors),
232 );
233 }
234}
235
236pub mod prelude {
238 pub use crate::{components::*, math::*, resources::*, SPHConfig, SPHPlugin};
239}