fountain/
fountain.rs

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