1use emath::{vec2, Rect, Vec2};
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
15pub struct Margin {
16 pub left: i8,
17 pub right: i8,
18 pub top: i8,
19 pub bottom: i8,
20}
21
22impl Margin {
23 pub const ZERO: Self = Self {
24 left: 0,
25 right: 0,
26 top: 0,
27 bottom: 0,
28 };
29
30 #[doc(alias = "symmetric")]
32 #[inline]
33 pub const fn same(margin: i8) -> Self {
34 Self {
35 left: margin,
36 right: margin,
37 top: margin,
38 bottom: margin,
39 }
40 }
41
42 #[inline]
44 pub const fn symmetric(x: i8, y: i8) -> Self {
45 Self {
46 left: x,
47 right: x,
48 top: y,
49 bottom: y,
50 }
51 }
52
53 #[inline]
55 pub const fn leftf(self) -> f32 {
56 self.left as _
57 }
58
59 #[inline]
61 pub const fn rightf(self) -> f32 {
62 self.right as _
63 }
64
65 #[inline]
67 pub const fn topf(self) -> f32 {
68 self.top as _
69 }
70
71 #[inline]
73 pub const fn bottomf(self) -> f32 {
74 self.bottom as _
75 }
76
77 #[inline]
79 pub fn sum(self) -> Vec2 {
80 vec2(self.leftf() + self.rightf(), self.topf() + self.bottomf())
81 }
82
83 #[inline]
84 pub const fn left_top(self) -> Vec2 {
85 vec2(self.leftf(), self.topf())
86 }
87
88 #[inline]
89 pub const fn right_bottom(self) -> Vec2 {
90 vec2(self.rightf(), self.bottomf())
91 }
92
93 #[doc(alias = "symmetric")]
95 #[inline]
96 pub const fn is_same(self) -> bool {
97 self.left == self.right && self.left == self.top && self.left == self.bottom
98 }
99
100 #[deprecated = "Use `rect + margin` instead"]
101 #[inline]
102 pub fn expand_rect(self, rect: Rect) -> Rect {
103 Rect::from_min_max(rect.min - self.left_top(), rect.max + self.right_bottom())
104 }
105
106 #[deprecated = "Use `rect - margin` instead"]
107 #[inline]
108 pub fn shrink_rect(self, rect: Rect) -> Rect {
109 Rect::from_min_max(rect.min + self.left_top(), rect.max - self.right_bottom())
110 }
111}
112
113impl From<i8> for Margin {
114 #[inline]
115 fn from(v: i8) -> Self {
116 Self::same(v)
117 }
118}
119
120impl From<f32> for Margin {
121 #[inline]
122 fn from(v: f32) -> Self {
123 Self::same(v.round() as _)
124 }
125}
126
127impl From<Vec2> for Margin {
128 #[inline]
129 fn from(v: Vec2) -> Self {
130 Self::symmetric(v.x.round() as _, v.y.round() as _)
131 }
132}
133
134impl std::ops::Add for Margin {
136 type Output = Self;
137
138 #[inline]
139 fn add(self, other: Self) -> Self {
140 Self {
141 left: self.left.saturating_add(other.left),
142 right: self.right.saturating_add(other.right),
143 top: self.top.saturating_add(other.top),
144 bottom: self.bottom.saturating_add(other.bottom),
145 }
146 }
147}
148
149impl std::ops::Add<i8> for Margin {
151 type Output = Self;
152
153 #[inline]
154 fn add(self, v: i8) -> Self {
155 Self {
156 left: self.left.saturating_add(v),
157 right: self.right.saturating_add(v),
158 top: self.top.saturating_add(v),
159 bottom: self.bottom.saturating_add(v),
160 }
161 }
162}
163
164impl std::ops::AddAssign<i8> for Margin {
166 #[inline]
167 fn add_assign(&mut self, v: i8) {
168 *self = *self + v;
169 }
170}
171
172impl std::ops::Mul<f32> for Margin {
174 type Output = Self;
175
176 #[inline]
177 fn mul(self, v: f32) -> Self {
178 Self {
179 left: (self.leftf() * v).round() as _,
180 right: (self.rightf() * v).round() as _,
181 top: (self.topf() * v).round() as _,
182 bottom: (self.bottomf() * v).round() as _,
183 }
184 }
185}
186
187impl std::ops::MulAssign<f32> for Margin {
189 #[inline]
190 fn mul_assign(&mut self, v: f32) {
191 *self = *self * v;
192 }
193}
194
195impl std::ops::Div<f32> for Margin {
197 type Output = Self;
198
199 #[inline]
200 fn div(self, v: f32) -> Self {
201 #![allow(clippy::suspicious_arithmetic_impl)]
202 self * v.recip()
203 }
204}
205
206impl std::ops::DivAssign<f32> for Margin {
208 #[inline]
209 fn div_assign(&mut self, v: f32) {
210 *self = *self / v;
211 }
212}
213
214impl std::ops::Sub for Margin {
216 type Output = Self;
217
218 #[inline]
219 fn sub(self, other: Self) -> Self {
220 Self {
221 left: self.left.saturating_sub(other.left),
222 right: self.right.saturating_sub(other.right),
223 top: self.top.saturating_sub(other.top),
224 bottom: self.bottom.saturating_sub(other.bottom),
225 }
226 }
227}
228
229impl std::ops::Sub<i8> for Margin {
231 type Output = Self;
232
233 #[inline]
234 fn sub(self, v: i8) -> Self {
235 Self {
236 left: self.left.saturating_sub(v),
237 right: self.right.saturating_sub(v),
238 top: self.top.saturating_sub(v),
239 bottom: self.bottom.saturating_sub(v),
240 }
241 }
242}
243
244impl std::ops::SubAssign<i8> for Margin {
246 #[inline]
247 fn sub_assign(&mut self, v: i8) {
248 *self = *self - v;
249 }
250}
251
252impl std::ops::Add<Margin> for Rect {
254 type Output = Self;
255
256 #[inline]
257 fn add(self, margin: Margin) -> Self {
258 Self::from_min_max(
259 self.min - margin.left_top(),
260 self.max + margin.right_bottom(),
261 )
262 }
263}
264
265impl std::ops::AddAssign<Margin> for Rect {
267 #[inline]
268 fn add_assign(&mut self, margin: Margin) {
269 *self = *self + margin;
270 }
271}
272
273impl std::ops::Sub<Margin> for Rect {
275 type Output = Self;
276
277 #[inline]
278 fn sub(self, margin: Margin) -> Self {
279 Self::from_min_max(
280 self.min + margin.left_top(),
281 self.max - margin.right_bottom(),
282 )
283 }
284}
285
286impl std::ops::SubAssign<Margin> for Rect {
288 #[inline]
289 fn sub_assign(&mut self, margin: Margin) {
290 *self = *self - margin;
291 }
292}