iced_aw/core/
clock.rs

1//! Helper functions for calculating the clock
2
3use iced_core::Point;
4use std::fmt::Display;
5
6/// The size of the period on the clock based on the clock's size.
7pub const PERIOD_PERCENTAGE: f32 = 0.1;
8
9/// The radius of the hour on the clock based on the clock's size.
10pub const HOUR_RADIUS_PERCENTAGE: f32 = 0.4;
11
12/// The radius of the minute on the clock based on the clock's size.
13pub const MINUTE_RADIUS_PERCENTAGE: f32 = 0.65;
14
15/// The radius of the second on the clock based on the clock's size.
16pub const SECOND_RADIUS_PERCENTAGE: f32 = 0.9;
17
18/// The radius of the hour without the seconds on the clock based on the
19/// clock's size.
20pub const HOUR_RADIUS_PERCENTAGE_NO_SECONDS: f32 = 0.5;
21
22/// The radius of the minute without the seconds on the clock based on the
23/// clock's size.
24pub const MINUTE_RADIUS_PERCENTAGE_NO_SECONDS: f32 = 0.9;
25
26/// The current period of the clock.
27#[derive(Clone, Debug)]
28pub enum Period {
29    /// Ante meridiem: Before noon.
30    AM,
31    /// Post meridiem: After noon.
32    PM,
33}
34
35impl Display for Period {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(
38            f,
39            "{}",
40            match self {
41                Self::AM => "AM",
42                Self::PM => "PM",
43            }
44        )
45    }
46}
47
48/// The radius nearest to the cursor position.
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum NearestRadius {
51    /// Nothing is near to the cursor position.
52    None,
53    /// Period is the nearest radius to the cursor position.
54    Period,
55    /// Hour is the nearest radius to the cursor position.
56    Hour,
57    /// Minute is the nearest radius to the cursor position.
58    Minute,
59    /// Second is the nearest radius to the cursor position.
60    Second,
61}
62
63/// Distributes the amount of points on a circle with the given radius around the
64/// center.
65#[must_use]
66pub fn circle_points(distance_radius: f32, center: Point, amount: u16) -> Vec<Point> {
67    let part = std::f32::consts::TAU / f32::from(amount);
68
69    let rotation =
70        |(x, y): (f32, f32), t: f32| (x * t.cos() - y * t.sin(), x * t.sin() + y * t.cos());
71
72    let points: Vec<(f32, f32)> = (0..amount).fold(Vec::new(), |mut v, i| {
73        v.push(rotation((0.0, -distance_radius), part * f32::from(i)));
74        v
75    });
76
77    let points: Vec<Point> = points
78        .iter()
79        .map(|p| Point::new(p.0 + center.x, p.1 + center.y))
80        .collect();
81
82    points
83}
84
85/// # Panics
86/// Determines the nearest point with the smallest distance to the cursor
87/// position. The index of the point is returned.
88/// Will panic if distance vec can not compare a and b
89#[must_use]
90pub fn nearest_point(points: &[Point], cursor_position: Point) -> usize {
91    let mut distance_vec: Vec<(usize, f32)> = points
92        .iter()
93        .enumerate()
94        .map(|(i, p)| (i, p.distance(cursor_position)))
95        .collect();
96
97    distance_vec.sort_by(|a, b| a.1.partial_cmp(&b.1).expect("Should be comparable"));
98
99    distance_vec[0].0
100}
101
102/// # Panics
103/// Determining the nearest radius to the position of the cursor position based
104/// on the distance to the center.
105/// Will panic if distance vec can not compare a and b
106#[must_use]
107pub fn nearest_radius(
108    radii: &[(f32, NearestRadius)],
109    cursor_position: Point,
110    center: Point,
111) -> NearestRadius {
112    let distance = cursor_position.distance(center);
113
114    let mut distance_vec: Vec<(f32, &NearestRadius)> = radii
115        .iter()
116        .map(|(r, n)| ((r - distance).abs(), n))
117        .collect();
118
119    distance_vec.sort_by(|a, b| a.0.partial_cmp(&b.0).expect("Should be comparable"));
120
121    distance_vec[0].1.clone()
122}
123
124#[cfg(test)]
125mod tests {
126    use iced_core::{Point, Vector};
127
128    use super::{NearestRadius, circle_points, nearest_point, nearest_radius};
129
130    #[test]
131    fn circle_points_test() {
132        let points = circle_points(10.0, Point::new(0.0, 0.0), 10);
133        let expected = vec![
134            Point { x: 0.0, y: -10.0 },
135            Point {
136                x: 5.877_852_4,
137                y: -8.09017,
138            },
139            Point {
140                x: 9.510_566,
141                y: -3.090_169_7,
142            },
143            Point {
144                x: 9.510_565,
145                y: 3.090_170_4,
146            },
147            Point {
148                x: 5.877_852,
149                y: 8.090_171,
150            },
151            Point {
152                x: -0.000_000_874_227_8,
153                y: 10.0,
154            },
155            Point {
156                x: -5.877_853_4,
157                y: 8.09017,
158            },
159            Point {
160                x: -9.510_565,
161                y: 3.090_170_9,
162            },
163            Point {
164                x: -9.510_565,
165                y: -3.090_171_3,
166            },
167            Point {
168                x: -5.877_849_6,
169                y: -8.090_173,
170            },
171        ];
172
173        assert_eq!(points, expected);
174    }
175
176    #[test]
177    fn nearest_radius_test() {
178        let radii = vec![
179            (10.0, NearestRadius::Period),
180            (20.0, NearestRadius::Hour),
181            (30.0, NearestRadius::Minute),
182            (40.0, NearestRadius::Second),
183        ];
184
185        let center = Point::new(0.0, 0.0);
186
187        let cursor_position = Point::new(5.0, 0.0);
188        let result = nearest_radius(&radii, cursor_position, center);
189        assert_eq!(result, NearestRadius::Period);
190
191        let cursor_position = Point::new(16.0, 0.0);
192        let result = nearest_radius(&radii, cursor_position, center);
193        assert_eq!(result, NearestRadius::Hour);
194
195        let cursor_position = Point::new(26.0, 0.0);
196        let result = nearest_radius(&radii, cursor_position, center);
197        assert_eq!(result, NearestRadius::Minute);
198
199        let cursor_position = Point::new(36.0, 0.0);
200        let result = nearest_radius(&radii, cursor_position, center);
201        assert_eq!(result, NearestRadius::Second);
202    }
203
204    #[test]
205    fn nearest_point_test() {
206        let points = circle_points(10.0, Point::new(0.0, 0.0), 10);
207
208        let mut index = 5;
209        let cursor_position = points[index] + Vector::new(1.0, -1.0);
210        let mut result = nearest_point(&points, cursor_position);
211        assert_eq!(index, result);
212
213        index = 0;
214        let cursor_position = points[index] + Vector::new(-1.0, 1.0);
215        result = nearest_point(&points, cursor_position);
216        assert_eq!(index, result);
217    }
218}