use crate::geometry::Rect;
#[cfg(not(feature = "std"))]
use crate::math::F32Ext as _;
#[inline]
pub fn circle_chord_width(radius: u32, y_offset_from_center: i32) -> u32 {
let r = radius as i32;
let y = y_offset_from_center.abs();
if y >= r {
return 0;
}
let r2 = r * r;
let y2 = y * y;
let half_w = ((r2 - y2) as f32).sqrt() as u32;
half_w * 2
}
pub fn round_screen_line_bounds(diameter: u32, line_y: i32, line_height: u32) -> Rect {
let radius = (diameter / 2) as i32;
let center_y = radius;
let y_top = line_y - center_y;
let y_bottom = (line_y + line_height as i32) - center_y;
let w_top = circle_chord_width(radius as u32, y_top);
let w_bottom = circle_chord_width(radius as u32, y_bottom);
let min_width = w_top.min(w_bottom);
let offset_x = (radius - (min_width as i32 / 2)).max(0);
Rect::new(offset_x, line_y, min_width, line_height)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UnobstructedArea {
pub screen_size: Rect,
pub inset_top: u16,
pub inset_bottom: u16,
pub inset_left: u16,
pub inset_right: u16,
}
impl UnobstructedArea {
pub const fn new(screen_size: Rect) -> Self {
Self {
screen_size,
inset_top: 0,
inset_bottom: 0,
inset_left: 0,
inset_right: 0,
}
}
pub fn set_insets(&mut self, top: u16, bottom: u16, left: u16, right: u16) {
self.inset_top = top;
self.inset_bottom = bottom;
self.inset_left = left;
self.inset_right = right;
}
pub fn visible_rect(&self) -> Rect {
let x = self.screen_size.x + self.inset_left as i32;
let y = self.screen_size.y + self.inset_top as i32;
let w = self
.screen_size
.w
.saturating_sub((self.inset_left + self.inset_right) as u32);
let h = self
.screen_size
.h
.saturating_sub((self.inset_top + self.inset_bottom) as u32);
Rect::new(x, y, w, h)
}
pub const fn is_obstructed(&self) -> bool {
self.inset_top > 0 || self.inset_bottom > 0 || self.inset_left > 0 || self.inset_right > 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle_chord_calculations() {
let radius = 90; assert_eq!(circle_chord_width(radius, 0), 180);
let w = circle_chord_width(radius, 80);
assert!(w > 0 && w < 180);
assert_eq!(circle_chord_width(radius, 95), 0);
}
#[test]
fn test_round_screen_line_bounds() {
let diameter = 180;
let center_line = round_screen_line_bounds(diameter, 80, 20);
assert!(center_line.w > 160);
assert_eq!(center_line.y, 80);
}
#[test]
fn test_unobstructed_area_rect() {
let mut area = UnobstructedArea::new(Rect::new(0, 0, 144, 168));
assert_eq!(area.visible_rect(), Rect::new(0, 0, 144, 168));
assert!(!area.is_obstructed());
area.set_insets(16, 24, 0, 0);
assert!(area.is_obstructed());
assert_eq!(area.visible_rect(), Rect::new(0, 16, 144, 128));
}
}