1use std::{
2 io::{stdout, Result},
3 thread::sleep,
4 time::{Duration, SystemTime},
5};
6
7use crossterm::{
8 cursor,
9 event::{self, KeyCode},
10 execute, terminal,
11};
12use firework_rs::{
13 config::Config,
14 fireworks::{ExplosionForm, Firework, FireworkConfig, FireworkManager},
15 particle::ParticleConfig,
16 term::Terminal,
17 utils::gen_points_circle,
18};
19use glam::Vec2;
20use rand::{seq::IteratorRandom, thread_rng, Rng};
21
22fn main() -> Result<()> {
23 let mut stdout = stdout();
24 let (_width, _height) = terminal::size()?;
25 let mut is_running = true;
26 let cfg = Config::default();
27
28 terminal::enable_raw_mode()?;
29 execute!(stdout, terminal::EnterAlternateScreen, cursor::Hide)?;
30
31 let mut time = SystemTime::now();
32 let mut term = Terminal::default();
33 let mut fm = FireworkManager::default().with_firework(gen_vortex_firework(Vec2::new(
34 _width as f32 / 4.,
35 _height as f32 / 2.,
36 )));
37
38 while is_running {
39 if event::poll(Duration::ZERO)? {
40 match event::read()? {
41 event::Event::Key(e) => {
42 if e.code == KeyCode::Esc {
43 is_running = false;
44 }
45 }
46 event::Event::Resize(_, _) => {
47 fm.reset();
48 term.reinit(&cfg);
49 }
50 _ => {}
51 };
52 }
53
54 let delta_time = SystemTime::now().duration_since(time).unwrap();
55 fm.update(time, delta_time);
56 time = SystemTime::now();
57
58 term.render(&fm, &cfg);
59 term.print(&mut stdout, &cfg);
60
61 if delta_time < Duration::from_secs_f32(0.05) {
62 let rem = Duration::from_secs_f32(0.05) - delta_time;
63 sleep(rem);
64 }
65 }
66
67 execute!(stdout, cursor::Show, terminal::LeaveAlternateScreen)?;
68 terminal::disable_raw_mode()?;
69
70 Ok(())
71}
72
73fn gen_vortex_firework(center: Vec2) -> Firework {
74 let colors = vec![
75 (6, 55, 63),
76 (24, 90, 96),
77 (47, 123, 119),
78 (92, 174, 166),
79 (200, 255, 255),
80 ];
81 let mut particles = Vec::new();
82 for p in gen_points_circle(30, 45).iter() {
83 particles.push(ParticleConfig::new(
84 center + *p,
85 Vec2::new((*p).y, -(*p).x).normalize() * 15.,
86 thread_rng().gen_range(28..40),
87 Duration::from_secs_f32(thread_rng().gen_range(4.5..7.0)),
88 *colors.iter().choose(&mut thread_rng()).unwrap(),
89 ));
90 }
91 let mut config = FireworkConfig::default()
92 .with_ar_scale(0.05)
93 .with_gravity_scale(0.)
94 .with_gradient_scale(gradient)
95 .with_additional_force(move |particle| {
96 (center - particle.pos).normalize() * (1. / center.distance(particle.pos)) * 150.
97 });
98 config.set_enable_gradient(true);
99 Firework {
100 init_time: SystemTime::now(),
101 spawn_after: Duration::ZERO,
102 center,
103 particles,
104 config,
105 form: ExplosionForm::Sustained {
106 lasts: Duration::from_secs(10),
107 time_interval: Duration::from_secs_f32(0.01),
108 timer: Duration::ZERO,
109 },
110 ..Default::default()
111 }
112}
113
114fn gradient(x: f32) -> f32 {
115 if x < 0.8125 {
116 -0.4 * x + 1.1
117 } else {
118 -2. * x + 2.2
119 }
120}