1use bevy::prelude::*;
2use cores::{EvenCore, EvenCoreError, UnevenCore, UnevenCoreError};
3use serde::{Deserialize, Serialize};
4
5#[derive(Serialize, Deserialize, Clone, Reflect, Debug)]
8pub enum FireworkCurve<T> {
9 SampleAuto(SampleAutoCurve<T>),
10 UnevenSampleAuto(UnevenSampleAutoCurve<T>),
11 Constant(ConstantCurve<T>),
12}
13
14impl<T> Curve<T> for FireworkCurve<T>
15where
16 T: StableInterpolate,
17{
18 fn domain(&self) -> Interval {
19 match self {
20 FireworkCurve::SampleAuto(c) => c.domain(),
21 FireworkCurve::UnevenSampleAuto(c) => c.domain(),
22 FireworkCurve::Constant(c) => c.domain(),
23 }
24 }
25
26 fn sample_unchecked(&self, t: f32) -> T {
27 match self {
28 FireworkCurve::SampleAuto(c) => c.sample_unchecked(t),
29 FireworkCurve::UnevenSampleAuto(c) => c.sample_unchecked(t),
30 FireworkCurve::Constant(c) => c.sample_unchecked(t),
31 }
32 }
33}
34
35impl<T: Clone> FireworkCurve<T> {
36 pub fn uneven_samples(samples: impl IntoIterator<Item = (f32, T)>) -> Self {
41 let samples = samples.into_iter().collect::<Vec<_>>();
44 match samples.len() {
45 0 => panic!("Cannot create curve from 0 samples"),
46 1 => FireworkCurve::Constant(ConstantCurve::new(
47 interval(0., 1.).unwrap(),
48 samples[0].1.clone(),
49 )),
50 _ => FireworkCurve::UnevenSampleAuto(UnevenSampleAutoCurve::new(samples).unwrap()),
51 }
52 }
53
54 pub fn even_samples(samples: impl IntoIterator<Item = T>) -> Self {
57 let samples = samples.into_iter().collect::<Vec<_>>();
60 match samples.len() {
61 0 => panic!("Cannot create curve from 0 samples"),
62 1 => FireworkCurve::Constant(ConstantCurve::new(
63 interval(0., 1.).unwrap(),
64 samples[0].clone(),
65 )),
66 _ => FireworkCurve::SampleAuto(
67 SampleAutoCurve::new(interval(0., 1.).unwrap(), samples).unwrap(),
68 ),
69 }
70 }
71
72 pub fn constant(sample: T) -> Self {
73 FireworkCurve::Constant(ConstantCurve::new(interval(0., 1.).unwrap(), sample))
74 }
75}
76
77#[derive(Clone, Debug, Reflect, Serialize, Deserialize)]
79pub struct ColorSampleAutoCurve<T> {
80 core: EvenCore<T>,
81}
82
83impl<T> ColorSampleAutoCurve<T>
84where
85 T: Mix + Clone,
86{
87 pub fn new(colors: impl IntoIterator<Item = T>) -> Result<Self, EvenCoreError> {
88 let colors = colors.into_iter().collect::<Vec<_>>();
89 if colors.len() < 2 {
90 Err(EvenCoreError::NotEnoughSamples {
91 samples: colors.len(),
92 })
93 } else {
94 Ok(Self {
95 core: EvenCore::new(Interval::new(0., 1.).unwrap(), colors)?,
96 })
97 }
98 }
99}
100
101impl<T> Curve<T> for ColorSampleAutoCurve<T>
102where
103 T: Mix + Clone,
104{
105 #[inline]
106 fn domain(&self) -> Interval {
107 interval(0., 1.).unwrap()
108 }
109
110 #[inline]
111 fn sample_clamped(&self, t: f32) -> T {
112 self.core.sample_with(t, T::mix)
114 }
115
116 #[inline]
117 fn sample_unchecked(&self, t: f32) -> T {
118 self.sample_clamped(t)
119 }
120}
121
122#[derive(Clone, Debug, Reflect, Serialize, Deserialize)]
124pub struct ColorSampleUnevenAutoCurve<T> {
125 core: UnevenCore<T>,
126}
127
128impl<T> ColorSampleUnevenAutoCurve<T>
129where
130 T: Mix + Clone,
131{
132 pub fn new(colors: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
133 let colors = colors.into_iter().collect::<Vec<_>>();
134 if colors.len() < 2 {
135 Err(UnevenCoreError::NotEnoughSamples {
136 samples: colors.len(),
137 })
138 } else {
139 Ok(Self {
140 core: UnevenCore::new(colors)?,
141 })
142 }
143 }
144}
145
146impl<T> Curve<T> for ColorSampleUnevenAutoCurve<T>
147where
148 T: Mix + Clone,
149{
150 #[inline]
151 fn domain(&self) -> Interval {
152 interval(0., 1.).unwrap()
153 }
154
155 #[inline]
156 fn sample_clamped(&self, t: f32) -> T {
157 self.core.sample_with(t, T::mix)
158 }
159
160 #[inline]
161 fn sample_unchecked(&self, t: f32) -> T {
162 self.sample_clamped(t)
163 }
164}
165
166#[derive(Serialize, Deserialize, Clone, Reflect, Debug)]
171pub enum FireworkGradient<T> {
172 ColorSampleAuto(ColorSampleAutoCurve<T>),
173 ColorSampleUnevenAuto(ColorSampleUnevenAutoCurve<T>),
174 Constant(ConstantCurve<T>),
175}
176
177impl<T> Curve<T> for FireworkGradient<T>
178where
179 T: Mix + Clone,
180{
181 fn domain(&self) -> Interval {
182 match self {
183 FireworkGradient::ColorSampleAuto(c) => c.domain(),
184 FireworkGradient::ColorSampleUnevenAuto(c) => c.domain(),
185 FireworkGradient::Constant(c) => c.domain(),
186 }
187 }
188
189 fn sample_unchecked(&self, t: f32) -> T {
190 match self {
191 FireworkGradient::ColorSampleAuto(c) => c.sample_unchecked(t),
192 FireworkGradient::ColorSampleUnevenAuto(c) => c.sample_unchecked(t),
193 FireworkGradient::Constant(c) => c.sample_unchecked(t),
194 }
195 }
196}
197
198impl<T> FireworkGradient<T>
199where
200 T: Mix + Clone,
201{
202 pub fn uneven_samples(samples: impl IntoIterator<Item = (f32, T)>) -> Self {
207 let samples = samples.into_iter().collect::<Vec<_>>();
210 match samples.len() {
211 0 => panic!("Cannot create curve from 0 samples"),
212 1 => FireworkGradient::Constant(ConstantCurve::new(
213 interval(0., 1.).unwrap(),
214 samples[0].1.clone(),
215 )),
216 _ => FireworkGradient::ColorSampleUnevenAuto(
217 ColorSampleUnevenAutoCurve::new(samples).unwrap(),
218 ),
219 }
220 }
221
222 pub fn even_samples(samples: impl IntoIterator<Item = T>) -> Self {
225 let samples = samples.into_iter().collect::<Vec<_>>();
226 match samples.len() {
227 0 => panic!("Cannot create curve from 0 samples"),
228 1 => FireworkGradient::Constant(ConstantCurve::new(
229 interval(0., 1.).unwrap(),
230 samples[0].clone(),
231 )),
232 _ => FireworkGradient::ColorSampleAuto(ColorSampleAutoCurve::new(samples).unwrap()),
233 }
234 }
235
236 pub fn constant(sample: T) -> Self {
237 FireworkGradient::Constant(ConstantCurve::new(interval(0., 1.).unwrap(), sample))
238 }
239}
240
241#[cfg(test)]
242mod test {
243 use super::*;
244
245 #[test]
246 fn test_curve_linear_rgba() {
247 let curve = FireworkGradient::ColorSampleAuto(
248 ColorSampleAutoCurve::new(vec![
249 Srgba::new(1.0, 0.0, 0.0, 1.0),
250 Srgba::new(0.0, 1.0, 0.0, 1.0),
251 Srgba::new(0.0, 0.0, 1.0, 1.0),
252 ])
253 .unwrap(),
254 );
255 assert_eq!(curve.sample_unchecked(0.0), Srgba::new(1.0, 0.0, 0.0, 1.0));
256 assert_eq!(curve.sample_unchecked(0.5), Srgba::new(0.0, 1.0, 0.0, 1.0));
257 assert_eq!(curve.sample_unchecked(1.0), Srgba::new(0.0, 0.0, 1.0, 1.0));
258 }
259}