1use crate::length::Length;
14use crate::num::Zero;
15use crate::scale::Scale;
16use crate::Vector2D;
17use core::cmp::{Eq, PartialEq};
18use core::fmt;
19use core::hash::Hash;
20use core::marker::PhantomData;
21use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Neg};
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25#[repr(C)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29#[cfg_attr(
30 feature = "serde",
31 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
32)]
33pub struct SideOffsets2D<T, U> {
34 pub top: T,
35 pub right: T,
36 pub bottom: T,
37 pub left: T,
38 #[doc(hidden)]
39 pub _unit: PhantomData<U>,
40}
41
42impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
43
44impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
45 fn clone(&self) -> Self {
46 SideOffsets2D {
47 top: self.top.clone(),
48 right: self.right.clone(),
49 bottom: self.bottom.clone(),
50 left: self.left.clone(),
51 _unit: PhantomData,
52 }
53 }
54}
55
56impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
57
58impl<T, U> PartialEq for SideOffsets2D<T, U>
59where
60 T: PartialEq,
61{
62 fn eq(&self, other: &Self) -> bool {
63 self.top == other.top
64 && self.right == other.right
65 && self.bottom == other.bottom
66 && self.left == other.left
67 }
68}
69
70impl<T, U> Hash for SideOffsets2D<T, U>
71where
72 T: Hash,
73{
74 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
75 self.top.hash(h);
76 self.right.hash(h);
77 self.bottom.hash(h);
78 self.left.hash(h);
79 }
80}
81
82impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 write!(
85 f,
86 "({:?},{:?},{:?},{:?})",
87 self.top, self.right, self.bottom, self.left
88 )
89 }
90}
91
92impl<T: Default, U> Default for SideOffsets2D<T, U> {
93 fn default() -> Self {
94 SideOffsets2D {
95 top: Default::default(),
96 right: Default::default(),
97 bottom: Default::default(),
98 left: Default::default(),
99 _unit: PhantomData,
100 }
101 }
102}
103
104impl<T, U> SideOffsets2D<T, U> {
105 pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
110 SideOffsets2D {
111 top,
112 right,
113 bottom,
114 left,
115 _unit: PhantomData,
116 }
117 }
118
119 pub fn from_lengths(
124 top: Length<T, U>,
125 right: Length<T, U>,
126 bottom: Length<T, U>,
127 left: Length<T, U>,
128 ) -> Self {
129 SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
130 }
131
132 pub fn from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
138 where
139 T: Neg<Output = T>,
140 {
141 SideOffsets2D {
142 left: -min.x,
143 top: -min.y,
144 right: max.x,
145 bottom: max.y,
146 _unit: PhantomData,
147 }
148 }
149
150 pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
156 where
157 T: Neg<Output = T>,
158 {
159 SideOffsets2D {
160 left: min.x,
161 top: min.y,
162 right: -max.x,
163 bottom: -max.y,
164 _unit: PhantomData,
165 }
166 }
167
168 pub fn zero() -> Self
170 where T: Zero,
171 {
172 SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
173 }
174
175 pub fn is_zero(&self) -> bool
177 where
178 T: Zero + PartialEq,
179 {
180 let zero = T::zero();
181 self.top == zero && self.right == zero && self.bottom == zero && self.left == zero
182 }
183
184 pub fn new_all_same(all: T) -> Self
186 where T : Copy
187 {
188 SideOffsets2D::new(all, all, all, all)
189 }
190
191 pub fn from_length_all_same(all: Length<T, U>) -> Self
193 where T : Copy
194 {
195 SideOffsets2D::new_all_same(all.0)
196 }
197
198 pub fn horizontal(&self) -> T
199 where T: Copy + Add<T, Output = T>
200 {
201 self.left + self.right
202 }
203
204 pub fn vertical(&self) -> T
205 where T: Copy + Add<T, Output = T>
206 {
207 self.top + self.bottom
208 }
209}
210
211impl<T, U> Add for SideOffsets2D<T, U>
212where
213 T: Add<T, Output = T>,
214{
215 type Output = Self;
216 fn add(self, other: Self) -> Self {
217 SideOffsets2D::new(
218 self.top + other.top,
219 self.right + other.right,
220 self.bottom + other.bottom,
221 self.left + other.left,
222 )
223 }
224}
225
226impl<T: Copy + Mul, U> Mul<T> for SideOffsets2D<T, U> {
227 type Output = SideOffsets2D<T::Output, U>;
228
229 #[inline]
230 fn mul(self, scale: T) -> Self::Output {
231 SideOffsets2D::new(
232 self.top * scale,
233 self.right * scale,
234 self.bottom * scale,
235 self.left * scale,
236 )
237 }
238}
239
240impl<T: Copy + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
241 #[inline]
242 fn mul_assign(&mut self, other: T) {
243 self.top *= other;
244 self.right *= other;
245 self.bottom *= other;
246 self.left *= other;
247 }
248}
249
250impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
251 type Output = SideOffsets2D<T::Output, U2>;
252
253 #[inline]
254 fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
255 SideOffsets2D::new(
256 self.top * scale.0,
257 self.right * scale.0,
258 self.bottom * scale.0,
259 self.left * scale.0,
260 )
261 }
262}
263
264impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
265 #[inline]
266 fn mul_assign(&mut self, other: Scale<T, U, U>) {
267 *self *= other.0;
268 }
269}
270
271impl<T: Copy + Div, U> Div<T> for SideOffsets2D<T, U> {
272 type Output = SideOffsets2D<T::Output, U>;
273
274 #[inline]
275 fn div(self, scale: T) -> Self::Output {
276 SideOffsets2D::new(
277 self.top / scale,
278 self.right / scale,
279 self.bottom / scale,
280 self.left / scale,
281 )
282 }
283}
284
285impl<T: Copy + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
286 #[inline]
287 fn div_assign(&mut self, other: T) {
288 self.top /= other;
289 self.right /= other;
290 self.bottom /= other;
291 self.left /= other;
292 }
293}
294
295impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
296 type Output = SideOffsets2D<T::Output, U1>;
297
298 #[inline]
299 fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
300 SideOffsets2D::new(
301 self.top / scale.0,
302 self.right / scale.0,
303 self.bottom / scale.0,
304 self.left / scale.0,
305 )
306 }
307}
308
309impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
310 fn div_assign(&mut self, other: Scale<T, U, U>) {
311 *self /= other.0;
312 }
313}
314
315#[test]
316fn from_vectors() {
317 use crate::{point2, vec2};
318 type Box2D = crate::default::Box2D<i32>;
319
320 let b = Box2D {
321 min: point2(10, 10),
322 max: point2(20, 20),
323 };
324
325 let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
326 let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
327
328 assert_eq!(
329 outer,
330 Box2D {
331 min: point2(9, 8),
332 max: point2(23, 24)
333 }
334 );
335 assert_eq!(
336 inner,
337 Box2D {
338 min: point2(11, 12),
339 max: point2(17, 16)
340 }
341 );
342}
343
344#[test]
345fn test_is_zero() {
346 let s1: SideOffsets2D<f32, ()> = SideOffsets2D::new_all_same(0.0);
347 assert!(s1.is_zero());
348
349 let s2: SideOffsets2D<f32, ()> = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
350 assert!(!s2.is_zero());
351}
352
353#[cfg(test)]
354mod ops {
355 use crate::Scale;
356
357 pub enum Mm {}
358 pub enum Cm {}
359
360 type SideOffsets2D<T> = crate::default::SideOffsets2D<T>;
361 type SideOffsets2DMm<T> = crate::SideOffsets2D<T, Mm>;
362 type SideOffsets2DCm<T> = crate::SideOffsets2D<T, Cm>;
363
364 #[test]
365 fn test_mul_scalar() {
366 let s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
367
368 let result = s * 3.0;
369
370 assert_eq!(result, SideOffsets2D::new(3.0, 6.0, 9.0, 12.0));
371 }
372
373 #[test]
374 fn test_mul_assign_scalar() {
375 let mut s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
376
377 s *= 2.0;
378
379 assert_eq!(s, SideOffsets2D::new(2.0, 4.0, 6.0, 8.0));
380 }
381
382 #[test]
383 fn test_mul_scale() {
384 let s = SideOffsets2DMm::new(0.0, 1.0, 3.0, 2.0);
385 let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
386
387 let result = s * cm_per_mm;
388
389 assert_eq!(result, SideOffsets2DCm::new(0.0, 0.1, 0.3, 0.2));
390 }
391
392 #[test]
393 fn test_mul_assign_scale() {
394 let mut s = SideOffsets2DMm::new(2.0, 4.0, 6.0, 8.0);
395 let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
396
397 s *= scale;
398
399 assert_eq!(s, SideOffsets2DMm::new(0.2, 0.4, 0.6, 0.8));
400 }
401
402 #[test]
403 fn test_div_scalar() {
404 let s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
405
406 let result = s / 10.0;
407
408 assert_eq!(result, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
409 }
410
411 #[test]
412 fn test_div_assign_scalar() {
413 let mut s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
414
415 s /= 10.0;
416
417 assert_eq!(s, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
418 }
419
420 #[test]
421 fn test_div_scale() {
422 let s = SideOffsets2DCm::new(0.1, 0.2, 0.3, 0.4);
423 let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
424
425 let result = s / cm_per_mm;
426
427 assert_eq!(result, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
428 }
429
430 #[test]
431 fn test_div_assign_scale() {
432 let mut s = SideOffsets2DMm::new(0.1, 0.2, 0.3, 0.4);
433 let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
434
435 s /= scale;
436
437 assert_eq!(s, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
438 }
439}