1use crate::led::led_color::LedColor;
2use rand::Rng;
3use std::f64::consts::PI;
4
5pub enum MeanderStyle {
6 Sphere,
7 Cylinder,
8 Surface,
9}
10
11pub struct ColorMeander {
12 step_length: f64,
13 noise_level: f64,
14 xyz: (f64, f64, f64),
15 dir: (f64, f64, f64),
16 style: MeanderStyle,
17}
18
19impl ColorMeander {
20 pub fn new(style: MeanderStyle, speed: f64, noise: f64, start: (f64, f64, f64)) -> Self {
21 let mut rng = rand::thread_rng();
22 let dir = (
23 rng.gen_range(-0.5..0.5),
24 rng.gen_range(-0.5..0.5),
25 rng.gen_range(-0.5..0.5) - start.2,
26 );
27 ColorMeander {
28 step_length: speed,
29 noise_level: noise,
30 xyz: start,
31 dir,
32 style,
33 }
34 }
35
36 fn normalize(&self, vec: (f64, f64, f64)) -> (f64, f64, f64) {
37 let nrm = (vec.0 * vec.0 + vec.1 * vec.1 + vec.2 * vec.2).sqrt();
38 if nrm == 0.0 {
39 (0.0, 0.0, 0.0)
40 } else {
41 (vec.0 / nrm, vec.1 / nrm, vec.2 / nrm)
42 }
43 }
44
45 fn xyz_to_hsl(&self, x: f64, y: f64, z: f64) -> (f64, f64, f64) {
46 match self.style {
47 MeanderStyle::Cylinder => {
48 let h = y.atan2(x) / (2.0 * PI) + 0.5;
49 let s = (x * x + y * y).sqrt().min(1.0);
50 let l = z;
51 (h, s, l)
52 }
53 _ => {
54 let h = y.atan2(x) / (2.0 * PI) + 0.5;
55 let l = z.asin() * 2.0 / PI;
56 let r = (x * x + y * y).sqrt();
57 let r0 = (1.0 - z * z).sqrt();
58 let s = if r0 > 0.0 { r / r0 } else { 0.0 }.min(1.0);
59 (h, s, l)
60 }
61 }
62 }
63 fn xyz_color(&self, x: f64, y: f64, z: f64, led_color: &LedColor) -> (u8, u8, u8) {
64 let (h, s, l) = self.xyz_to_hsl(x, y, z);
65 led_color.hsl_color(h, s, l)
66 }
67
68 pub fn get(&self, led_color: &LedColor) -> (u8, u8, u8) {
69 self.xyz_color(self.xyz.0, self.xyz.1, self.xyz.2, led_color)
70 }
71
72 pub fn get_compl(&self, led_color: &LedColor) -> (u8, u8, u8) {
73 self.xyz_color(-self.xyz.0, -self.xyz.1, self.xyz.2, led_color)
74 }
75
76 pub fn get_xyz(&self) -> (f64, f64, f64) {
77 self.xyz
78 }
79
80 pub fn get_hsl(&self) -> (f64, f64, f64) {
81 self.xyz_to_hsl(self.xyz.0, self.xyz.1, self.xyz.2)
82 }
83
84 pub fn step(&mut self) {
85 let mut rng = rand::thread_rng();
86 let (mut nx, mut ny, mut nz) = (
87 self.xyz.0 + self.dir.0 * self.step_length,
88 self.xyz.1 + self.dir.1 * self.step_length,
89 self.xyz.2 + self.dir.2 * self.step_length,
90 );
91
92 let (mut ndir_x, mut ndir_y, mut ndir_z) = self.dir;
93 match self.style {
94 MeanderStyle::Cylinder => {
95 nz = nz.clamp(-1.0, 1.0);
96 let nrm = (nx * nx + ny * ny).sqrt();
97 if nrm > 1.0 {
98 nx /= nrm;
99 ny /= nrm;
100 self.dir = self.normalize((nx - self.xyz.0, ny - self.xyz.1, nz - self.xyz.2));
101 }
102 ndir_x += rng.gen_range(-self.noise_level..self.noise_level);
103 ndir_y += rng.gen_range(-self.noise_level..self.noise_level);
104 ndir_z += rng.gen_range(-self.noise_level..self.noise_level);
105 if (nz + ndir_z).abs() > 1.0 {
106 let sgn = if nz + ndir_z > 0.0 { 1.0 } else { -1.0 };
107 let delta = (1.0 - (sgn - nz).powi(2)).sqrt();
108 let nrm = (ndir_x * ndir_x + ndir_y * ndir_y).sqrt();
109 ndir_x = ndir_x * delta / nrm;
110 ndir_y = ndir_y * delta / nrm;
111 ndir_z = sgn - nz;
112 }
113 }
114 MeanderStyle::Surface => {
115 let nrm = (nx * nx + ny * ny + nz * nz).sqrt();
116 nx /= nrm;
117 ny /= nrm;
118 nz /= nrm;
119 self.dir = self.normalize((nx - self.xyz.0, ny - self.xyz.1, nz - self.xyz.2));
120 ndir_x += rng.gen_range(-self.noise_level..self.noise_level);
121 ndir_y += rng.gen_range(-self.noise_level..self.noise_level);
122 ndir_z += rng.gen_range(-self.noise_level..self.noise_level);
123 }
124 _ => {
125 let nrm = (nx * nx + ny * ny + nz * nz).sqrt();
126 if nrm > 1.0 {
127 nx /= nrm * nrm;
128 ny /= nrm * nrm;
129 nz /= nrm * nrm;
130 self.dir = self.normalize((nx - self.xyz.0, ny - self.xyz.1, nz - self.xyz.2));
131 }
132 ndir_x += rng.gen_range(-self.noise_level..self.noise_level);
133 ndir_y += rng.gen_range(-self.noise_level..self.noise_level);
134 ndir_z += rng.gen_range(-self.noise_level..self.noise_level);
135 }
136 }
137
138 let nrm = (ndir_x * ndir_x + ndir_y * ndir_y + ndir_z * ndir_z).sqrt();
139 if nrm != 0.0 {
140 self.dir = (ndir_x / nrm, ndir_y / nrm, ndir_z / nrm);
141 }
142
143 self.xyz = (nx, ny, nz);
144 }
145}