firework_rs/
utils.rs

1//! `utils` module provides some useful helper functions of random generation and gradient scale
2
3use std::f32::consts::PI;
4
5use glam::Vec2;
6use rand::Rng;
7use rand_distr::Distribution;
8
9/// Round a `Vec2` from `(f32, f32)` to `(isize, isize)`
10pub fn round(input: Vec2) -> (isize, isize) {
11    (input.x.round() as isize, input.y.round() as isize)
12}
13
14/// Generate random `Vec2` within a circle range
15pub fn gen_points_circle(radius: isize, n: usize) -> Vec<Vec2> {
16    let mut res = Vec::new();
17    while res.len() < n {
18        let x = rand::thread_rng().gen_range(-radius..=radius);
19        let y = rand::thread_rng().gen_range(-radius..=radius);
20        if x.pow(2) + y.pow(2) <= radius.pow(2) {
21            res.push(Vec2::new(x as f32, y as f32));
22        }
23    }
24    res
25}
26
27/// Generate random `Vec2` within a circle range with normal distribution
28///
29/// Points closer to the center will be denser
30pub fn gen_points_circle_normal(radius: f32, n: usize) -> Vec<Vec2> {
31    let mut rng = rand::thread_rng();
32    let normal =
33        rand_distr::Normal::new(0., radius / 9.).expect("Unable to generate normal distribution.");
34    let mut res = Vec::new();
35    while res.len() < n {
36        let x = normal.sample(&mut rng);
37        if x < -radius || x > radius {
38            continue;
39        }
40        let y = normal.sample(&mut rng);
41        if x < -radius || y > radius {
42            continue;
43        }
44        if x.powi(2) + y.powi(2) <= radius.powi(2) {
45            res.push(Vec2::new(x, y));
46        }
47    }
48    res
49}
50
51/// Generate random `Vec2` within a circle range with normal distribution
52///
53/// Points closer to the center will be denser
54/// You can specify standard deviation yourself
55pub fn gen_points_circle_normal_dev(radius: f32, n: usize, std_dev: f32) -> Vec<Vec2> {
56    let mut rng = rand::thread_rng();
57    let normal =
58        rand_distr::Normal::new(0., std_dev).expect("Unable to generate normal distribution.");
59    let mut res = Vec::new();
60    while res.len() < n {
61        let x = normal.sample(&mut rng);
62        if x < -radius || x > radius {
63            continue;
64        }
65        let y = normal.sample(&mut rng);
66        if x < -radius || y > radius {
67            continue;
68        }
69        if x.powi(2) + y.powi(2) <= radius.powi(2) {
70            res.push(Vec2::new(x, y));
71        }
72    }
73    res
74}
75
76/// Generate random `Vec2` within a fan-shape range
77pub fn gen_points_fan(radius: f32, n: usize, st_angle: f32, ed_angle: f32) -> Vec<Vec2> {
78    let mut res = Vec::new();
79    while res.len() < n {
80        let x = rand::thread_rng().gen_range(-radius..=radius);
81        let y = rand::thread_rng().gen_range(-radius..=radius);
82        let t = y.atan2(x);
83        if t <= ed_angle && t >= st_angle && x.powi(2) + y.powi(2) <= radius.powi(2) {
84            res.push(Vec2::new(x, -y));
85        }
86    }
87    res
88}
89
90/// Generate random `Vec2` on an arc
91pub fn gen_points_arc(radius: f32, n: usize, st_angle: f32, ed_angle: f32) -> Vec<Vec2> {
92    let mut res = Vec::new();
93    while res.len() < n {
94        let a = rand::thread_rng().gen_range(st_angle..=ed_angle);
95        res.push(Vec2::new(radius * a.cos(), -radius * a.sin()));
96    }
97    res
98}
99
100/// Generate random `Vec2` on a circle
101pub fn gen_points_on_circle(radius: f32, n: usize) -> Vec<Vec2> {
102    let mut res = Vec::new();
103    while res.len() < n {
104        let a = rand::thread_rng().gen_range(0.0..PI);
105        res.push(Vec2::new(radius * a.cos(), -radius * a.sin()));
106    }
107    res
108}
109
110/// Return squared distance between to points
111pub fn distance_squared(a: Vec2, b: Vec2) -> f32 {
112    (b.x - a.x).powi(2) + (b.y - a.y).powi(2)
113}
114
115/// A sample function defining the gradient of the `Particle`
116///
117/// The visual effect is similar to an explosion
118pub fn explosion_gradient_1(x: f32) -> f32 {
119    if x < 0.087 {
120        150. * x.powi(2)
121    } else {
122        -0.8 * x + 1.2
123    }
124}
125
126/// A sample function defining the gradient of the `Particle`
127///
128/// The visual effect is similar to an explosion
129pub fn explosion_gradient_2(x: f32) -> f32 {
130    if x < 0.067 {
131        5. * x + 0.1
132    } else if x < 0.2 {
133        2. * x + 0.3
134    } else if x < 0.5 {
135        x + 0.5
136    } else if x < 0.684 {
137        0.5 * x + 0.75
138    } else {
139        -7. * (x - 0.65).powi(2) + 1.1
140    }
141}
142
143/// A sample function defining the gradient of the `Particle`
144///
145/// The visual effect is similar to an explosion, darkar than `explosion_gradient_1`
146pub fn explosion_gradient_3(x: f32) -> f32 {
147    if x < 0.087 {
148        150. * x.powi(2) * 0.6
149    } else {
150        (-0.8 * x + 1.2) * 0.6
151    }
152}
153
154/// A sample function defining the gradient of the `Particle`
155///
156/// Linear gradient
157pub fn linear_gradient_1(x: f32) -> f32 {
158    -0.7 * x + 1.
159}