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 
13 use length::Length;
14 use num::Zero;
15 use core::fmt;
16 use core::ops::{Add, Neg};
17 use core::marker::PhantomData;
18 use core::cmp::{Eq, PartialEq};
19 use core::hash::{Hash};
20 #[cfg(feature = "serde")]
21 use serde::{Deserialize, Serialize};
22 use crate::Vector2D;
23 
24 /// A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding,
25 /// and margins in CSS, optionally tagged with a unit.
26 #[repr(C)]
27 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28 #[cfg_attr(feature = "serde", serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>")))]
29 pub struct SideOffsets2D<T, U> {
30     pub top: T,
31     pub right: T,
32     pub bottom: T,
33     pub left: T,
34     #[doc(hidden)]
35     pub _unit: PhantomData<U>,
36 }
37 
38 impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
39 
40 impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
clone(&self) -> Self41     fn clone(&self) -> Self {
42         SideOffsets2D {
43             top: self.top.clone(),
44             right: self.right.clone(),
45             bottom: self.bottom.clone(),
46             left: self.left.clone(),
47             _unit: PhantomData,
48         }
49     }
50 }
51 
52 impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
53 
54 impl<T, U> PartialEq for SideOffsets2D<T, U>
55     where T: PartialEq
56 {
eq(&self, other: &Self) -> bool57     fn eq(&self, other: &Self) -> bool {
58         self.top == other.top &&
59             self.right == other.right &&
60             self.bottom == other.bottom &&
61             self.left == other.left
62     }
63 }
64 
65 impl<T, U> Hash for SideOffsets2D<T, U>
66     where T: Hash
67 {
hash<H: ::core::hash::Hasher>(&self, h: &mut H)68     fn hash<H: ::core::hash::Hasher>(&self, h: &mut H) {
69         self.top.hash(h);
70         self.right.hash(h);
71         self.bottom.hash(h);
72         self.left.hash(h);
73     }
74 }
75 
76 impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78         write!(
79             f,
80             "({:?},{:?},{:?},{:?})",
81             self.top, self.right, self.bottom, self.left
82         )
83     }
84 }
85 
86 impl<T: Default, U> Default for SideOffsets2D<T, U> {
default() -> Self87     fn default() -> Self {
88         SideOffsets2D {
89             top: Default::default(),
90             right: Default::default(),
91             bottom: Default::default(),
92             left: Default::default(),
93             _unit: PhantomData,
94         }
95     }
96 }
97 
98 impl<T, U> SideOffsets2D<T, U> {
99     /// Constructor taking a scalar for each side.
100     ///
101     /// Sides are specified in top-right-bottom-left order following
102     /// CSS's convention.
new(top: T, right: T, bottom: T, left: T) -> Self103     pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
104         SideOffsets2D {
105             top,
106             right,
107             bottom,
108             left,
109             _unit: PhantomData,
110         }
111     }
112 
113     /// Constructor taking a typed Length for each side.
114     ///
115     /// Sides are specified in top-right-bottom-left order following
116     /// CSS's convention.
from_lengths( top: Length<T, U>, right: Length<T, U>, bottom: Length<T, U>, left: Length<T, U>, ) -> Self117     pub fn from_lengths(
118         top: Length<T, U>,
119         right: Length<T, U>,
120         bottom: Length<T, U>,
121         left: Length<T, U>,
122     ) -> Self {
123         SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
124     }
125 
126     /// Construct side offsets from min and a max vector offsets.
127     ///
128     /// The outer rect of the resulting side offsets is equivalent to translating
129     /// a rectangle's upper-left corner with the min vector and translating the
130     /// bottom-right corner with the max vector.
from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self where T: Neg<Output = T>131     pub fn from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self
132     where
133         T: Neg<Output = T>
134     {
135         SideOffsets2D {
136             left: -min.x,
137             top: -min.y,
138             right: max.x,
139             bottom: max.y,
140             _unit: PhantomData,
141         }
142     }
143 
144     /// Construct side offsets from min and a max vector offsets.
145     ///
146     /// The inner rect of the resulting side offsets is equivalent to translating
147     /// a rectangle's upper-left corner with the min vector and translating the
148     /// bottom-right corner with the max vector.
from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self where T: Neg<Output = T>149     pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self
150     where
151         T: Neg<Output = T>
152     {
153         SideOffsets2D {
154             left: min.x,
155             top: min.y,
156             right: -max.x,
157             bottom: -max.y,
158             _unit: PhantomData,
159         }
160     }
161 }
162 
163 impl<T: Copy, U> SideOffsets2D<T, U> {
164     /// Constructor setting the same value to all sides, taking a scalar value directly.
new_all_same(all: T) -> Self165     pub fn new_all_same(all: T) -> Self {
166         SideOffsets2D::new(all, all, all, all)
167     }
168 
169     /// Constructor setting the same value to all sides, taking a typed Length.
from_length_all_same(all: Length<T, U>) -> Self170     pub fn from_length_all_same(all: Length<T, U>) -> Self {
171         SideOffsets2D::new_all_same(all.0)
172     }
173 }
174 
175 impl<T, U> SideOffsets2D<T, U>
176 where
177     T: Add<T, Output = T> + Copy,
178 {
horizontal(&self) -> T179     pub fn horizontal(&self) -> T {
180         self.left + self.right
181     }
182 
vertical(&self) -> T183     pub fn vertical(&self) -> T {
184         self.top + self.bottom
185     }
186 }
187 
188 impl<T, U> Add for SideOffsets2D<T, U>
189 where
190     T: Add<T, Output = T>,
191 {
192     type Output = Self;
add(self, other: Self) -> Self193     fn add(self, other: Self) -> Self {
194         SideOffsets2D::new(
195             self.top + other.top,
196             self.right + other.right,
197             self.bottom + other.bottom,
198             self.left + other.left,
199         )
200     }
201 }
202 
203 impl<T: Zero, U> SideOffsets2D<T, U> {
204     /// Constructor, setting all sides to zero.
zero() -> Self205     pub fn zero() -> Self {
206         SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
207     }
208 }
209 
210 #[test]
from_vectors()211 fn from_vectors() {
212     use crate::{vec2, point2};
213     type Box2D = crate::default::Box2D<i32>;
214 
215     let b = Box2D {
216         min: point2(10, 10),
217         max: point2(20, 20),
218     };
219 
220     let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
221     let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
222 
223     assert_eq!(outer, Box2D { min: point2(9, 8), max: point2(23, 24) });
224     assert_eq!(inner, Box2D { min: point2(11, 12), max: point2(17, 16) });
225 }
226