1use crate::length::Length;
14use crate::num::Zero;
15use crate::scale::Scale;
16use crate::Vector2D;
17
18use core::cmp::{Eq, PartialEq};
19use core::fmt;
20use core::hash::Hash;
21use core::marker::PhantomData;
22use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
23
24#[cfg(feature = "bytemuck")]
25use bytemuck::{Pod, Zeroable};
26#[cfg(feature = "malloc_size_of")]
27use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
28#[cfg(feature = "serde")]
29use serde::{Deserialize, Serialize};
30
31#[repr(C)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(
36 feature = "serde",
37 serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
38)]
39pub struct SideOffsets2D<T, U> {
40 pub top: T,
41 pub right: T,
42 pub bottom: T,
43 pub left: T,
44 #[doc(hidden)]
45 pub _unit: PhantomData<U>,
46}
47
48#[cfg(feature = "arbitrary")]
49impl<'a, T, U> arbitrary::Arbitrary<'a> for SideOffsets2D<T, U>
50where
51 T: arbitrary::Arbitrary<'a>,
52{
53 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
54 let (top, right, bottom, left) = arbitrary::Arbitrary::arbitrary(u)?;
55 Ok(SideOffsets2D {
56 top,
57 right,
58 bottom,
59 left,
60 _unit: PhantomData,
61 })
62 }
63}
64
65#[cfg(feature = "bytemuck")]
66unsafe impl<T: Zeroable, U> Zeroable for SideOffsets2D<T, U> {}
67
68#[cfg(feature = "bytemuck")]
69unsafe impl<T: Pod, U: 'static> Pod for SideOffsets2D<T, U> {}
70
71#[cfg(feature = "malloc_size_of")]
72impl<T: MallocSizeOf, U> MallocSizeOf for SideOffsets2D<T, U> {
73 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
74 self.top.size_of(ops)
75 + self.right.size_of(ops)
76 + self.bottom.size_of(ops)
77 + self.left.size_of(ops)
78 }
79}
80
81impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
82
83impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
84 fn clone(&self) -> Self {
85 SideOffsets2D {
86 top: self.top.clone(),
87 right: self.right.clone(),
88 bottom: self.bottom.clone(),
89 left: self.left.clone(),
90 _unit: PhantomData,
91 }
92 }
93}
94
95impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
96
97impl<T, U> PartialEq for SideOffsets2D<T, U>
98where
99 T: PartialEq,
100{
101 fn eq(&self, other: &Self) -> bool {
102 self.top == other.top
103 && self.right == other.right
104 && self.bottom == other.bottom
105 && self.left == other.left
106 }
107}
108
109impl<T, U> Hash for SideOffsets2D<T, U>
110where
111 T: Hash,
112{
113 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
114 self.top.hash(h);
115 self.right.hash(h);
116 self.bottom.hash(h);
117 self.left.hash(h);
118 }
119}
120
121impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 write!(
124 f,
125 "({:?},{:?},{:?},{:?})",
126 self.top, self.right, self.bottom, self.left
127 )
128 }
129}
130
131impl<T: Default, U> Default for SideOffsets2D<T, U> {
132 fn default() -> Self {
133 SideOffsets2D {
134 top: Default::default(),
135 right: Default::default(),
136 bottom: Default::default(),
137 left: Default::default(),
138 _unit: PhantomData,
139 }
140 }
141}
142
143impl<T, U> SideOffsets2D<T, U> {
144 pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
149 SideOffsets2D {
150 top,
151 right,
152 bottom,
153 left,
154 _unit: PhantomData,
155 }
156 }
157
158 pub fn from_lengths(
163 top: Length<T, U>,
164 right: Length<T, U>,
165 bottom: Length<T, U>,
166 left: Length<T, U>,
167 ) -> Self {
168 SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
169 }
170
171 pub fn from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
177 where
178 T: Neg<Output = T>,
179 {
180 SideOffsets2D {
181 left: -min.x,
182 top: -min.y,
183 right: max.x,
184 bottom: max.y,
185 _unit: PhantomData,
186 }
187 }
188
189 pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
195 where
196 T: Neg<Output = T>,
197 {
198 SideOffsets2D {
199 left: min.x,
200 top: min.y,
201 right: -max.x,
202 bottom: -max.y,
203 _unit: PhantomData,
204 }
205 }
206
207 pub fn zero() -> Self
209 where
210 T: Zero,
211 {
212 SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
213 }
214
215 pub fn is_zero(&self) -> bool
217 where
218 T: Zero + PartialEq,
219 {
220 let zero = T::zero();
221 self.top == zero && self.right == zero && self.bottom == zero && self.left == zero
222 }
223
224 pub fn new_all_same(all: T) -> Self
226 where
227 T: Copy,
228 {
229 SideOffsets2D::new(all, all, all, all)
230 }
231
232 pub fn from_length_all_same(all: Length<T, U>) -> Self
234 where
235 T: Copy,
236 {
237 SideOffsets2D::new_all_same(all.0)
238 }
239
240 pub fn horizontal(&self) -> T
241 where
242 T: Copy + Add<T, Output = T>,
243 {
244 self.left + self.right
245 }
246
247 pub fn vertical(&self) -> T
248 where
249 T: Copy + Add<T, Output = T>,
250 {
251 self.top + self.bottom
252 }
253}
254
255impl<T, U> Add for SideOffsets2D<T, U>
256where
257 T: Add<T, Output = T>,
258{
259 type Output = Self;
260 fn add(self, other: Self) -> Self {
261 SideOffsets2D::new(
262 self.top + other.top,
263 self.right + other.right,
264 self.bottom + other.bottom,
265 self.left + other.left,
266 )
267 }
268}
269
270impl<T, U> AddAssign<Self> for SideOffsets2D<T, U>
271where
272 T: AddAssign<T>,
273{
274 fn add_assign(&mut self, other: Self) {
275 self.top += other.top;
276 self.right += other.right;
277 self.bottom += other.bottom;
278 self.left += other.left;
279 }
280}
281
282impl<T, U> Sub for SideOffsets2D<T, U>
283where
284 T: Sub<T, Output = T>,
285{
286 type Output = Self;
287 fn sub(self, other: Self) -> Self {
288 SideOffsets2D::new(
289 self.top - other.top,
290 self.right - other.right,
291 self.bottom - other.bottom,
292 self.left - other.left,
293 )
294 }
295}
296
297impl<T, U> SubAssign<Self> for SideOffsets2D<T, U>
298where
299 T: SubAssign<T>,
300{
301 fn sub_assign(&mut self, other: Self) {
302 self.top -= other.top;
303 self.right -= other.right;
304 self.bottom -= other.bottom;
305 self.left -= other.left;
306 }
307}
308
309impl<T, U> Neg for SideOffsets2D<T, U>
310where
311 T: Neg<Output = T>,
312{
313 type Output = Self;
314 fn neg(self) -> Self {
315 SideOffsets2D {
316 top: -self.top,
317 right: -self.right,
318 bottom: -self.bottom,
319 left: -self.left,
320 _unit: PhantomData,
321 }
322 }
323}
324
325impl<T: Copy + Mul, U> Mul<T> for SideOffsets2D<T, U> {
326 type Output = SideOffsets2D<T::Output, U>;
327
328 #[inline]
329 fn mul(self, scale: T) -> Self::Output {
330 SideOffsets2D::new(
331 self.top * scale,
332 self.right * scale,
333 self.bottom * scale,
334 self.left * scale,
335 )
336 }
337}
338
339impl<T: Copy + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
340 #[inline]
341 fn mul_assign(&mut self, other: T) {
342 self.top *= other;
343 self.right *= other;
344 self.bottom *= other;
345 self.left *= other;
346 }
347}
348
349impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
350 type Output = SideOffsets2D<T::Output, U2>;
351
352 #[inline]
353 fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
354 SideOffsets2D::new(
355 self.top * scale.0,
356 self.right * scale.0,
357 self.bottom * scale.0,
358 self.left * scale.0,
359 )
360 }
361}
362
363impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
364 #[inline]
365 fn mul_assign(&mut self, other: Scale<T, U, U>) {
366 *self *= other.0;
367 }
368}
369
370impl<T: Copy + Div, U> Div<T> for SideOffsets2D<T, U> {
371 type Output = SideOffsets2D<T::Output, U>;
372
373 #[inline]
374 fn div(self, scale: T) -> Self::Output {
375 SideOffsets2D::new(
376 self.top / scale,
377 self.right / scale,
378 self.bottom / scale,
379 self.left / scale,
380 )
381 }
382}
383
384impl<T: Copy + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
385 #[inline]
386 fn div_assign(&mut self, other: T) {
387 self.top /= other;
388 self.right /= other;
389 self.bottom /= other;
390 self.left /= other;
391 }
392}
393
394impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
395 type Output = SideOffsets2D<T::Output, U1>;
396
397 #[inline]
398 fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
399 SideOffsets2D::new(
400 self.top / scale.0,
401 self.right / scale.0,
402 self.bottom / scale.0,
403 self.left / scale.0,
404 )
405 }
406}
407
408impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
409 fn div_assign(&mut self, other: Scale<T, U, U>) {
410 *self /= other.0;
411 }
412}
413
414#[test]
415fn from_vectors() {
416 use crate::{point2, vec2};
417 type Box2D = crate::default::Box2D<i32>;
418
419 let b = Box2D {
420 min: point2(10, 10),
421 max: point2(20, 20),
422 };
423
424 let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
425 let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
426
427 assert_eq!(
428 outer,
429 Box2D {
430 min: point2(9, 8),
431 max: point2(23, 24)
432 }
433 );
434 assert_eq!(
435 inner,
436 Box2D {
437 min: point2(11, 12),
438 max: point2(17, 16)
439 }
440 );
441}
442
443#[test]
444fn test_is_zero() {
445 let s1: SideOffsets2D<f32, ()> = SideOffsets2D::new_all_same(0.0);
446 assert!(s1.is_zero());
447
448 let s2: SideOffsets2D<f32, ()> = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
449 assert!(!s2.is_zero());
450}
451
452#[cfg(test)]
453mod ops {
454 use crate::Scale;
455
456 pub enum Mm {}
457 pub enum Cm {}
458
459 type SideOffsets2D<T> = crate::default::SideOffsets2D<T>;
460 type SideOffsets2DMm<T> = crate::SideOffsets2D<T, Mm>;
461 type SideOffsets2DCm<T> = crate::SideOffsets2D<T, Cm>;
462
463 #[test]
464 fn test_mul_scalar() {
465 let s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
466
467 let result = s * 3.0;
468
469 assert_eq!(result, SideOffsets2D::new(3.0, 6.0, 9.0, 12.0));
470 }
471
472 #[test]
473 fn test_mul_assign_scalar() {
474 let mut s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
475
476 s *= 2.0;
477
478 assert_eq!(s, SideOffsets2D::new(2.0, 4.0, 6.0, 8.0));
479 }
480
481 #[test]
482 fn test_mul_scale() {
483 let s = SideOffsets2DMm::new(0.0, 1.0, 3.0, 2.0);
484 let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
485
486 let result = s * cm_per_mm;
487
488 assert_eq!(result, SideOffsets2DCm::new(0.0, 0.1, 0.3, 0.2));
489 }
490
491 #[test]
492 fn test_mul_assign_scale() {
493 let mut s = SideOffsets2DMm::new(2.0, 4.0, 6.0, 8.0);
494 let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
495
496 s *= scale;
497
498 assert_eq!(s, SideOffsets2DMm::new(0.2, 0.4, 0.6, 0.8));
499 }
500
501 #[test]
502 fn test_div_scalar() {
503 let s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
504
505 let result = s / 10.0;
506
507 assert_eq!(result, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
508 }
509
510 #[test]
511 fn test_div_assign_scalar() {
512 let mut s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
513
514 s /= 10.0;
515
516 assert_eq!(s, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
517 }
518
519 #[test]
520 fn test_div_scale() {
521 let s = SideOffsets2DCm::new(0.1, 0.2, 0.3, 0.4);
522 let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
523
524 let result = s / cm_per_mm;
525
526 assert_eq!(result, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
527 }
528
529 #[test]
530 fn test_div_assign_scale() {
531 let mut s = SideOffsets2DMm::new(0.1, 0.2, 0.3, 0.4);
532 let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
533
534 s /= scale;
535
536 assert_eq!(s, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
537 }
538}