1use crate::types::Transformer;
2use std::convert::From;
3use std::f64::consts::PI;
4
5#[derive(Debug, Clone, Copy, Default)]
6pub struct Point2 {
7 x: f64,
8 y: f64,
9}
10
11impl Point2 {
12 pub fn random() -> Self {
13 Self {
14 x: rand::random::<f64>(),
15 y: rand::random::<f64>(),
16 }
17 }
18}
19
20#[derive(Debug, Clone, Copy, Default)]
21pub struct Point3 {
22 x: f64,
23 y: f64,
24 z: f64,
25}
26
27impl Point3 {
28 pub fn random() -> Self {
29 Self {
30 x: rand::random::<f64>(),
31 y: rand::random::<f64>(),
32 z: rand::random::<f64>(),
33 }
34 }
35}
36
37impl From<(Hsl, bool)> for Point3 {
38 fn from((hsl, inverted): (Hsl, bool)) -> Self {
39 let Hsl { h, s, l } = hsl;
40
41 let cx = 0.5f64;
42 let cy = 0.5f64;
43
44 let radians = h / (180f64 / PI);
45
46 let dist = (if inverted { 1.0 - l } else { l }) * cx;
47
48 let x = cx + dist * radians.cos();
49 let y = cy + dist * radians.sin();
50
51 let z = s;
52
53 Point3 { x, y, z }
54 }
55}
56
57#[derive(Debug, Clone, Copy)]
58pub struct Hsl {
59 pub h: f64,
60 pub s: f64,
61 pub l: f64,
62}
63
64impl From<(Point3, bool)> for Hsl {
65 fn from((point, inverted): (Point3, bool)) -> Self {
66 let Point3 { x, y, z } = point;
67
68 let cx = 0.5f64;
69 let cy = 0.5f64;
70
71 let radians = (y - cy).atan2(x - cx);
72
73 let mut deg = radians * (180f64 / PI);
74 deg = (360f64 + deg) % 360f64;
75
76 let dist = ((x - cx).powi(2) + (y - cy).powi(2)).sqrt();
77 let l = dist / cx;
78 let s = z;
79
80 Hsl {
81 h: deg,
82 s,
83 l: if inverted { 1.0 - l } else { l },
84 }
85 }
86}
87
88pub struct HslPairInit {
89 start_hue: f64,
90 saturation: Point2,
91 lightness: Point2,
92}
93
94impl Default for HslPairInit {
95 fn default() -> Self {
96 Self {
97 start_hue: rand::random::<f64>() * 360f64,
98 saturation: Point2::random(),
99 lightness: Point2 {
100 x: 0.75 + rand::random::<f64>() * 0.2,
101 y: 0.75 + rand::random::<f64>() * 0.2,
102 },
103 }
104 }
105}
106
107pub struct HslTripleInit {
108 start_hue: f64,
109 saturation: Point3,
110 lightness: Point3,
111}
112
113impl Default for HslTripleInit {
114 fn default() -> Self {
115 Self {
116 start_hue: rand::random::<f64>() * 360f64,
117 saturation: Point3::random(),
118 lightness: Point3 {
119 x: 0.75 + rand::random::<f64>() * 0.2,
120 y: rand::random::<f64>() * 0.2,
121 z: 0.75 + rand::random::<f64>() * 0.2,
122 },
123 }
124 }
125}
126
127impl Hsl {
128 pub fn random_pair(init: HslPairInit) -> [Self; 2] {
129 let HslPairInit {
130 start_hue: h,
131 saturation: s,
132 lightness: l,
133 } = init;
134 [
135 Hsl { h, s: s.x, l: l.x },
136 Hsl {
137 h: (h + 60f64 + rand::random::<f64>() * 180f64) % 360f64,
138 s: s.y,
139 l: l.y,
140 },
141 ]
142 }
143
144 pub fn random_triple(init: HslTripleInit) -> [Self; 3] {
145 let HslTripleInit {
146 start_hue: h,
147 saturation: s,
148 lightness: l,
149 } = init;
150
151 [
152 Hsl { h, s: s.x, l: l.x },
153 Hsl {
154 h: (h + 60f64 + rand::random::<f64>() * 180f64) % 360f64,
155 s: s.y,
156 l: l.y,
157 },
158 Hsl {
159 h: (h + 60f64 + rand::random::<f64>() * 180f64) % 360f64,
160 s: s.z,
161 l: l.z,
162 },
163 ]
164 }
165}
166
167fn vector_on_line(
168 t: f64,
169 p1: &Point3,
170 p2: &Point3,
171 inverted: bool,
172 fx: Transformer,
173 fy: Transformer,
174 fz: Transformer,
175) -> Point3 {
176 let t_modified_x = fx(t, inverted);
177 let t_modified_y = fy(t, inverted);
178 let t_modified_z = fz(t, inverted);
179
180 let x = (1f64 - t_modified_x) * p1.x + t_modified_x * p2.x;
181 let y = (1f64 - t_modified_y) * p1.y + t_modified_y * p2.y;
182 let z = (1f64 - t_modified_z) * p1.z + t_modified_z * p2.z;
183
184 Point3 { x, y, z }
185}
186
187pub fn vectors_on_line(
188 p1: &Point3,
189 p2: &Point3,
190 num_points: i32,
191 inverted: bool,
192 fx: Transformer,
193 fy: Transformer,
194 fz: Transformer,
195) -> Vec<Point3> {
196 (0..num_points)
197 .map(move |i| {
198 let t: f64 = i as f64 / (num_points - 1) as f64;
199 vector_on_line(t, p1, p2, inverted, fx, fy, fz)
200 })
201 .collect()
202}
203
204pub struct PartialPoint3(Option<f64>, Option<f64>, Option<f64>);
205
206impl PartialPoint3 {
207 pub fn distance(&self, other: &PartialPoint3, hue_mode: Option<bool>) -> f64 {
208 let a1 = self.0;
209 let a2 = other.0;
210
211 let a = match (hue_mode.unwrap_or(false), a1, a2) {
212 (true, Some(a), Some(b)) => (a - b).abs().min(360f64 - (a - b).abs()) / 360f64,
213 (false, Some(a), Some(b)) => a - b,
214 _ => 0f64,
215 };
216
217 let b = match (self.1, other.1) {
218 (Some(a), Some(b)) => b - a,
219 _ => 0f64,
220 };
221
222 let c = match (self.2, other.2) {
223 (Some(a), Some(b)) => b - a,
224 _ => 0f64,
225 };
226
227 (a * a + b * b + c * c).sqrt()
228 }
229}
230
231impl From<&Hsl> for PartialPoint3 {
232 fn from(value: &Hsl) -> Self {
233 Self(Some(value.h), Some(value.s), Some(value.l))
234 }
235}
236
237impl From<&Point3> for PartialPoint3 {
238 fn from(value: &Point3) -> Self {
239 Self(Some(value.x), Some(value.y), Some(value.z))
240 }
241}
242
243pub enum PointOrHsl {
244 Point(Point3),
245 Hsl(Hsl),
246}
247
248#[derive(Debug, Clone, Copy)]
249#[non_exhaustive]
250pub struct ColorPoint {
251 pub color: Hsl,
252 pub point: Point3,
253 pub inverted: bool,
254}
255
256impl ColorPoint {
257 pub fn set_inverted(&mut self, inverted: bool) {
258 self.inverted = inverted;
259 }
260
261 pub fn set_postion(&mut self, point: Point3) {
262 let new_color = Hsl::from((point, self.inverted));
263 self.color = new_color;
264 self.point = point;
265 }
266
267 pub fn set_hsl(&mut self, hsl: Hsl) {
268 let new_point = Point3::from((hsl, self.inverted));
269 self.color = hsl;
270 self.point = new_point;
271 }
272
273 pub fn css_string(&self) -> String {
274 format!(
275 "hsl({:06.2}, {}%, {}%)",
276 self.color.h,
277 self.color.s * 100f64,
278 self.color.l * 100f64
279 )
280 }
281
282 pub fn shift_hue(&mut self, angle: f64) {
283 self.color.h = (360f64 + (self.color.h + angle)) % 360f64;
284 self.point = Point3::from((self.color, self.inverted));
285 }
286}
287
288impl From<(Hsl, bool)> for ColorPoint {
289 fn from((hsl, inverted): (Hsl, bool)) -> Self {
290 ColorPoint {
291 point: Point3::from((hsl, inverted)),
292 color: hsl,
293 inverted,
294 }
295 }
296}
297
298impl From<(Point3, bool)> for ColorPoint {
299 fn from((point, inverted): (Point3, bool)) -> Self {
300 ColorPoint {
301 color: Hsl::from((point, inverted)),
302 point,
303 inverted,
304 }
305 }
306}