use crate::geometry::Size;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct CornerRadii {
pub top_left: Size,
pub top_right: Size,
pub bottom_right: Size,
pub bottom_left: Size,
}
impl CornerRadii {
pub const fn new(radius: Size) -> Self {
Self {
top_left: radius,
top_right: radius,
bottom_right: radius,
bottom_left: radius,
}
}
pub(in crate::primitives) fn confine(self, bounding_box: Size) -> Self {
let mut overlap = 0;
let mut size = 0;
let mut corner_size = 0;
let top_radii = self.top_left.width + self.top_right.width;
let right_radii = self.top_right.height + self.bottom_right.height;
let bottom_radii = self.bottom_left.width + self.bottom_right.width;
let left_radii = self.top_left.height + self.bottom_left.height;
let o = top_radii.saturating_sub(bounding_box.width);
if o > overlap {
size = bounding_box.width;
corner_size = top_radii;
overlap = o;
}
let o = right_radii.saturating_sub(bounding_box.height);
if o > overlap {
size = bounding_box.height;
corner_size = right_radii;
overlap = o;
}
let o = bottom_radii.saturating_sub(bounding_box.width);
if o > overlap {
size = bounding_box.width;
corner_size = bottom_radii;
overlap = o;
}
let o = left_radii.saturating_sub(bounding_box.height);
if o > overlap {
size = bounding_box.height;
corner_size = left_radii;
overlap = o;
}
if overlap > 0 && corner_size > 0 {
Self {
top_left: (self.top_left * size) / corner_size,
top_right: (self.top_right * size) / corner_size,
bottom_right: (self.bottom_right * size) / corner_size,
bottom_left: (self.bottom_left * size) / corner_size,
}
} else {
self
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct CornerRadiiBuilder {
corners: CornerRadii,
}
impl CornerRadiiBuilder {
pub const fn new() -> Self {
Self {
corners: CornerRadii::new(Size::zero()),
}
}
pub const fn all(mut self, radius: Size) -> Self {
self.corners = CornerRadii::new(radius);
self
}
pub const fn top(mut self, radius: Size) -> Self {
self.corners.top_left = radius;
self.corners.top_right = radius;
self
}
pub const fn right(mut self, radius: Size) -> Self {
self.corners.top_right = radius;
self.corners.bottom_right = radius;
self
}
pub const fn bottom(mut self, radius: Size) -> Self {
self.corners.bottom_left = radius;
self.corners.bottom_right = radius;
self
}
pub const fn left(mut self, radius: Size) -> Self {
self.corners.top_left = radius;
self.corners.bottom_left = radius;
self
}
pub const fn top_left(mut self, radius: Size) -> Self {
self.corners.top_left = radius;
self
}
pub const fn top_right(mut self, radius: Size) -> Self {
self.corners.top_right = radius;
self
}
pub const fn bottom_right(mut self, radius: Size) -> Self {
self.corners.bottom_right = radius;
self
}
pub const fn bottom_left(mut self, radius: Size) -> Self {
self.corners.bottom_left = radius;
self
}
pub const fn build(self) -> CornerRadii {
self.corners
}
}
impl From<&CornerRadii> for CornerRadiiBuilder {
fn from(corners: &CornerRadii) -> Self {
Self { corners: *corners }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_radii_to_builder() {
let radii = CornerRadii {
top_left: Size::new(1, 2),
top_right: Size::new(3, 4),
bottom_right: Size::new(5, 6),
bottom_left: Size::new(7, 8),
};
let builder: CornerRadiiBuilder = (&radii).into();
assert_eq!(builder.build(), radii);
}
#[test]
fn corner_radii_exact_size() {
let corners = CornerRadii {
top_left: Size::new(10, 15),
top_right: Size::new(10, 15),
bottom_right: Size::new(10, 15),
bottom_left: Size::new(10, 15),
};
assert_eq!(corners.confine(Size::new(20, 30)), corners);
}
#[test]
fn corner_radii_single_overlap() {
let corners = CornerRadii {
top_left: Size::new(10, 20),
top_right: Size::new(10, 15),
bottom_right: Size::new(10, 15),
bottom_left: Size::new(10, 15),
};
assert_eq!(
corners.confine(Size::new(20, 30)),
CornerRadii {
top_left: Size::new(8, 17),
top_right: Size::new(8, 12),
bottom_right: Size::new(8, 12),
bottom_left: Size::new(8, 12)
}
);
}
#[test]
fn corner_radii_1px_overlap() {
let corners = CornerRadii {
top_left: Size::new(10, 16),
top_right: Size::new(11, 15),
bottom_right: Size::new(10, 15),
bottom_left: Size::new(10, 15),
};
assert_eq!(
corners.confine(Size::new(20, 30)),
CornerRadii {
top_left: Size::new(9, 15),
top_right: Size::new(10, 14),
bottom_right: Size::new(9, 14),
bottom_left: Size::new(9, 14),
}
);
}
#[test]
fn corner_radii_multiple_overlap() {
let corners = CornerRadii {
top_left: Size::new(10, 20),
top_right: Size::new(10, 15),
bottom_right: Size::new(18, 15),
bottom_left: Size::new(10, 15),
};
assert_eq!(
corners.confine(Size::new(20, 30)),
CornerRadii {
top_left: Size::new(7, 14),
top_right: Size::new(7, 10),
bottom_right: Size::new(12, 10),
bottom_left: Size::new(7, 10),
}
);
}
}