1use crate::geometry::Rect;
7#[cfg(not(feature = "std"))]
8use crate::math::F32Ext as _;
9
10#[inline]
14pub fn circle_chord_width(radius: u32, y_offset_from_center: i32) -> u32 {
15 let r = radius as i32;
16 let y = y_offset_from_center.abs();
17 if y >= r {
18 return 0;
19 }
20 let r2 = r * r;
21 let y2 = y * y;
22 let half_w = ((r2 - y2) as f32).sqrt() as u32;
23 half_w * 2
24}
25
26pub fn round_screen_line_bounds(diameter: u32, line_y: i32, line_height: u32) -> Rect {
30 let radius = (diameter / 2) as i32;
31 let center_y = radius;
32
33 let y_top = line_y - center_y;
35 let y_bottom = (line_y + line_height as i32) - center_y;
36
37 let w_top = circle_chord_width(radius as u32, y_top);
38 let w_bottom = circle_chord_width(radius as u32, y_bottom);
39 let min_width = w_top.min(w_bottom);
40
41 let offset_x = (radius - (min_width as i32 / 2)).max(0);
42 Rect::new(offset_x, line_y, min_width, line_height)
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub struct UnobstructedArea {
49 pub screen_size: Rect,
50 pub inset_top: u16,
51 pub inset_bottom: u16,
52 pub inset_left: u16,
53 pub inset_right: u16,
54}
55
56impl UnobstructedArea {
57 pub const fn new(screen_size: Rect) -> Self {
58 Self {
59 screen_size,
60 inset_top: 0,
61 inset_bottom: 0,
62 inset_left: 0,
63 inset_right: 0,
64 }
65 }
66
67 pub fn set_insets(&mut self, top: u16, bottom: u16, left: u16, right: u16) {
68 self.inset_top = top;
69 self.inset_bottom = bottom;
70 self.inset_left = left;
71 self.inset_right = right;
72 }
73
74 pub fn visible_rect(&self) -> Rect {
76 let x = self.screen_size.x + self.inset_left as i32;
77 let y = self.screen_size.y + self.inset_top as i32;
78 let w = self
79 .screen_size
80 .w
81 .saturating_sub((self.inset_left + self.inset_right) as u32);
82 let h = self
83 .screen_size
84 .h
85 .saturating_sub((self.inset_top + self.inset_bottom) as u32);
86 Rect::new(x, y, w, h)
87 }
88
89 pub const fn is_obstructed(&self) -> bool {
91 self.inset_top > 0 || self.inset_bottom > 0 || self.inset_left > 0 || self.inset_right > 0
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_circle_chord_calculations() {
101 let radius = 90; assert_eq!(circle_chord_width(radius, 0), 180);
104
105 let w = circle_chord_width(radius, 80);
107 assert!(w > 0 && w < 180);
108
109 assert_eq!(circle_chord_width(radius, 95), 0);
111 }
112
113 #[test]
114 fn test_round_screen_line_bounds() {
115 let diameter = 180;
116 let center_line = round_screen_line_bounds(diameter, 80, 20);
117 assert!(center_line.w > 160);
118 assert_eq!(center_line.y, 80);
119 }
120
121 #[test]
122 fn test_unobstructed_area_rect() {
123 let mut area = UnobstructedArea::new(Rect::new(0, 0, 144, 168));
124 assert_eq!(area.visible_rect(), Rect::new(0, 0, 144, 168));
125 assert!(!area.is_obstructed());
126
127 area.set_insets(16, 24, 0, 0);
128 assert!(area.is_obstructed());
129 assert_eq!(area.visible_rect(), Rect::new(0, 16, 144, 128));
130 }
131}