zng_unit/
corner_radius.rs

1use std::{cmp, fmt, ops};
2
3use serde::{Deserialize, Serialize};
4
5use crate::SideOffsets2D;
6
7/// Ellipses that define the radius of the four corners of a 2D box.
8#[derive(Serialize, Deserialize)]
9#[serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))]
10pub struct CornerRadius2D<T, U> {
11    /// Top-left corner radius.
12    pub top_left: euclid::Size2D<T, U>,
13    /// Top-right corner radius.
14    pub top_right: euclid::Size2D<T, U>,
15    /// Bottom-right corner radius.
16    pub bottom_right: euclid::Size2D<T, U>,
17    /// Bottom-left corner radius.
18    pub bottom_left: euclid::Size2D<T, U>,
19}
20impl<T: Default, U> Default for CornerRadius2D<T, U> {
21    fn default() -> Self {
22        Self {
23            top_left: Default::default(),
24            top_right: Default::default(),
25            bottom_right: Default::default(),
26            bottom_left: Default::default(),
27        }
28    }
29}
30impl<T: Clone, U> Clone for CornerRadius2D<T, U> {
31    fn clone(&self) -> Self {
32        Self {
33            top_left: self.top_left.clone(),
34            top_right: self.top_right.clone(),
35            bottom_right: self.bottom_right.clone(),
36            bottom_left: self.bottom_left.clone(),
37        }
38    }
39}
40impl<T: Copy, U> Copy for CornerRadius2D<T, U> {}
41impl<T: Copy + num_traits::Zero, U> CornerRadius2D<T, U> {
42    /// New with distinct values.
43    pub fn new(
44        top_left: euclid::Size2D<T, U>,
45        top_right: euclid::Size2D<T, U>,
46        bottom_right: euclid::Size2D<T, U>,
47        bottom_left: euclid::Size2D<T, U>,
48    ) -> Self {
49        Self {
50            top_left,
51            top_right,
52            bottom_right,
53            bottom_left,
54        }
55    }
56
57    /// New all corners same radius.
58    pub fn new_all(radius: euclid::Size2D<T, U>) -> Self {
59        Self::new(radius, radius, radius, radius)
60    }
61
62    /// All zeros.
63    pub fn zero() -> Self {
64        Self::new_all(euclid::Size2D::zero())
65    }
66
67    /// Calculate the corner radius of an outer border around `self` to perfectly fit.
68    pub fn inflate(self, offsets: SideOffsets2D<T, U>) -> Self
69    where
70        T: ops::AddAssign,
71    {
72        let mut r = self;
73
74        r.top_left.width += offsets.left;
75        r.top_left.height += offsets.top;
76
77        r.top_right.width += offsets.right;
78        r.top_right.height += offsets.top;
79
80        r.bottom_right.width += offsets.right;
81        r.bottom_right.height += offsets.bottom;
82
83        r.bottom_left.width += offsets.left;
84        r.bottom_left.height += offsets.bottom;
85
86        r
87    }
88
89    /// Calculate the corner radius of an inner border inside `self` to perfectly fit.
90    pub fn deflate(self, offsets: SideOffsets2D<T, U>) -> Self
91    where
92        T: ops::SubAssign + cmp::PartialOrd,
93    {
94        let mut r = self;
95
96        if r.top_left.width >= offsets.left {
97            r.top_left.width -= offsets.left;
98        } else {
99            r.top_left.width = T::zero();
100        }
101        if r.top_left.height >= offsets.top {
102            r.top_left.height -= offsets.top;
103        } else {
104            r.top_left.height = T::zero();
105        }
106
107        if r.top_right.width >= offsets.right {
108            r.top_right.width -= offsets.right;
109        } else {
110            r.top_right.width = T::zero();
111        }
112        if r.top_right.height >= offsets.top {
113            r.top_right.height -= offsets.top;
114        } else {
115            r.top_right.height = T::zero();
116        }
117
118        if r.bottom_right.width >= offsets.right {
119            r.bottom_right.width -= offsets.right;
120        } else {
121            r.bottom_right.width = T::zero();
122        }
123        if r.bottom_right.height >= offsets.bottom {
124            r.bottom_right.height -= offsets.bottom;
125        } else {
126            r.bottom_right.height = T::zero();
127        }
128
129        if r.bottom_left.width >= offsets.left {
130            r.bottom_left.width -= offsets.left;
131        } else {
132            r.bottom_left.width = T::zero();
133        }
134        if r.bottom_left.height >= offsets.bottom {
135            r.bottom_left.height -= offsets.bottom;
136        } else {
137            r.bottom_left.height = T::zero();
138        }
139
140        r
141    }
142}
143impl<T: fmt::Debug, U> fmt::Debug for CornerRadius2D<T, U> {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        f.debug_struct("CornerRadius2D")
146            .field("top_left", &self.top_left)
147            .field("top_right", &self.top_right)
148            .field("bottom_right", &self.bottom_right)
149            .field("bottom_left", &self.bottom_left)
150            .finish()
151    }
152}
153impl<T: PartialEq, U> PartialEq for CornerRadius2D<T, U> {
154    fn eq(&self, other: &Self) -> bool {
155        self.top_left == other.top_left
156            && self.top_right == other.top_right
157            && self.bottom_right == other.bottom_right
158            && self.bottom_left == other.bottom_left
159    }
160}
161impl<T: Eq, U> Eq for CornerRadius2D<T, U> {}