1use std::cell::RefCell;
5use std::rc::Rc;
6
7#[derive(Debug, Clone)]
13pub struct Particle {
14 pub x: f64,
15 pub y: f64,
16 pub vx: f64,
17 pub vy: f64,
18 pub life: f64,
19 pub size: f64,
20 pub color: String,
21}
22
23impl Particle {
24 pub fn new(x: f64, y: f64, vx: f64, vy: f64, life: f64, size: f64, color: &str) -> Self {
25 Self {
26 x,
27 y,
28 vx,
29 vy,
30 life,
31 size,
32 color: color.to_string(),
33 }
34 }
35
36 pub fn update(&mut self, gravity: f64, friction: f64) {
37 self.vy += gravity;
38 self.vx *= friction;
39 self.vy *= friction;
40 self.x += self.vx;
41 self.y += self.vy;
42 self.life -= 0.02;
43 }
44
45 pub fn is_alive(&self) -> bool {
46 self.life > 0.0
47 }
48}
49
50pub struct ParticleSystem {
52 pub particles: Vec<Particle>,
53 pub gravity: f64,
54 pub friction: f64,
55}
56
57impl ParticleSystem {
58 pub fn new() -> Self {
59 Self {
60 particles: Vec::new(),
61 gravity: 0.1,
62 friction: 0.98,
63 }
64 }
65
66 pub fn emit(&mut self, x: f64, y: f64, effect: &str, count: usize) {
67 match effect {
68 "fire" => self.emit_fire(x, y, count),
69 "smoke" => self.emit_smoke(x, y, count),
70 "spark" => self.emit_spark(x, y, count),
71 "explosion" => self.emit_explosion(x, y, count),
72 "rain" => self.emit_rain(x, y, count),
73 _ => self.emit_default(x, y, count),
74 }
75 }
76
77 pub fn update(&mut self) {
78 for particle in &mut self.particles {
79 particle.update(self.gravity, self.friction);
80 }
81 self.particles.retain(|p| p.is_alive());
82 }
83
84 pub fn count(&self) -> usize {
85 self.particles.len()
86 }
87
88 pub fn clear(&mut self) {
89 self.particles.clear();
90 }
91
92 fn emit_fire(&mut self, x: f64, y: f64, count: usize) {
93 for _ in 0..count {
94 let vx = (rand_f64() - 0.5) * 2.0;
95 let vy = -(rand_f64() * 3.0 + 2.0);
96 let life = rand_f64() * 0.5 + 0.5;
97 let size = rand_f64() * 4.0 + 2.0;
98 let color = if rand_f64() > 0.5 {
99 "naranja"
100 } else {
101 "amarillo"
102 };
103 self.particles
104 .push(Particle::new(x, y, vx, vy, life, size, color));
105 }
106 }
107
108 fn emit_smoke(&mut self, x: f64, y: f64, count: usize) {
109 for _ in 0..count {
110 let vx = (rand_f64() - 0.5) * 1.0;
111 let vy = -(rand_f64() * 1.5 + 0.5);
112 let life = rand_f64() * 0.8 + 0.2;
113 let size = rand_f64() * 6.0 + 4.0;
114 self.particles
115 .push(Particle::new(x, y, vx, vy, life, size, "gris"));
116 }
117 }
118
119 fn emit_spark(&mut self, x: f64, y: f64, count: usize) {
120 for _ in 0..count {
121 let angle = rand_f64() * std::f64::consts::PI * 2.0;
122 let speed = rand_f64() * 5.0 + 3.0;
123 let vx = angle.cos() * speed;
124 let vy = angle.sin() * speed;
125 let life = rand_f64() * 0.3 + 0.2;
126 let size = rand_f64() * 2.0 + 1.0;
127 self.particles
128 .push(Particle::new(x, y, vx, vy, life, size, "amarillo"));
129 }
130 }
131
132 fn emit_explosion(&mut self, x: f64, y: f64, count: usize) {
133 for _ in 0..count {
134 let angle = rand_f64() * std::f64::consts::PI * 2.0;
135 let speed = rand_f64() * 6.0 + 4.0;
136 let vx = angle.cos() * speed;
137 let vy = angle.sin() * speed;
138 let life = rand_f64() * 0.4 + 0.2;
139 let size = rand_f64() * 3.0 + 2.0;
140 let color = if rand_f64() > 0.5 { "naranja" } else { "rojo" };
141 self.particles
142 .push(Particle::new(x, y, vx, vy, life, size, color));
143 }
144 }
145
146 fn emit_rain(&mut self, x: f64, y: f64, count: usize) {
147 for _ in 0..count {
148 let vx = (rand_f64() - 0.5) * 0.5;
149 let vy = rand_f64() * 3.0 + 5.0;
150 let life = 1.0;
151 let size = rand_f64() * 8.0 + 4.0;
152 self.particles
153 .push(Particle::new(x, y, vx, vy, life, size, "azul"));
154 }
155 }
156
157 fn emit_default(&mut self, x: f64, y: f64, count: usize) {
158 for _ in 0..count {
159 let vx = (rand_f64() - 0.5) * 2.0;
160 let vy = (rand_f64() - 0.5) * 2.0;
161 let life = rand_f64() * 0.5 + 0.5;
162 let size = rand_f64() * 4.0 + 2.0;
163 self.particles
164 .push(Particle::new(x, y, vx, vy, life, size, "blanco"));
165 }
166 }
167}
168
169impl Default for ParticleSystem {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175fn rand_f64() -> f64 {
176 static mut SEED: u32 = 12345;
177 unsafe {
178 SEED = SEED.wrapping_mul(1664525).wrapping_add(1013904223);
179 (SEED as f64) / (u32::MAX as f64)
180 }
181}
182
183thread_local! {
188 static PARTICLE_SYSTEM: Rc<RefCell<ParticleSystem>> = Rc::new(RefCell::new(ParticleSystem::new()));
189}
190
191pub fn get_particle_system() -> Rc<RefCell<ParticleSystem>> {
192 PARTICLE_SYSTEM.with(|ps| ps.clone())
193}
194
195#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_particle_new() {
205 let p = Particle::new(100.0, 200.0, 1.0, -2.0, 1.0, 5.0, "rojo");
206 assert_eq!(p.x, 100.0);
207 assert_eq!(p.y, 200.0);
208 }
209
210 #[test]
211 fn test_particle_system() {
212 let mut ps = ParticleSystem::new();
213 ps.emit(400.0, 300.0, "fire", 20);
214 assert!(ps.count() > 0);
215 ps.update();
216 ps.clear();
217 assert_eq!(ps.count(), 0);
218 }
219}