poline_rs/
point.rs

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}