euclid/
side_offsets.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
11//! and margins in CSS.
12
13use 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/// A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding,
32/// and margins in CSS, optionally tagged with a unit.
33#[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    /// Constructor taking a scalar for each side.
145    ///
146    /// Sides are specified in top-right-bottom-left order following
147    /// CSS's convention.
148    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    /// Constructor taking a typed Length for each side.
159    ///
160    /// Sides are specified in top-right-bottom-left order following
161    /// CSS's convention.
162    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    /// Construct side offsets from min and a max vector offsets.
172    ///
173    /// The outer rect of the resulting side offsets is equivalent to translating
174    /// a rectangle's upper-left corner with the min vector and translating the
175    /// bottom-right corner with the max vector.
176    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    /// Construct side offsets from min and a max vector offsets.
190    ///
191    /// The inner rect of the resulting side offsets is equivalent to translating
192    /// a rectangle's upper-left corner with the min vector and translating the
193    /// bottom-right corner with the max vector.
194    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    /// Constructor, setting all sides to zero.
208    pub fn zero() -> Self
209    where
210        T: Zero,
211    {
212        SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
213    }
214
215    /// Returns `true` if all side offsets are zero.
216    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    /// Constructor setting the same value to all sides, taking a scalar value directly.
225    pub fn new_all_same(all: T) -> Self
226    where
227        T: Copy,
228    {
229        SideOffsets2D::new(all, all, all, all)
230    }
231
232    /// Constructor setting the same value to all sides, taking a typed Length.
233    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}