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 crate::length::Length;
14 use crate::num::Zero;
15 use crate::scale::Scale;
16 use crate::Vector2D;
17 use core::cmp::{Eq, PartialEq};
18 use core::fmt;
19 use core::hash::Hash;
20 use core::marker::PhantomData;
21 use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Neg};
22 #[cfg(feature = "serde")]
23 use serde::{Deserialize, Serialize};
24 
25 /// A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding,
26 /// and margins in CSS, optionally tagged with a unit.
27 #[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 )]
33 pub 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 
42 #[cfg(feature = "arbitrary")]
43 impl<'a, T, U> arbitrary::Arbitrary<'a> for SideOffsets2D<T, U>
44 where
45     T: arbitrary::Arbitrary<'a>,
46 {
arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self>47     fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self>
48     {
49         let (top, right, bottom, left) = arbitrary::Arbitrary::arbitrary(u)?;
50         Ok(SideOffsets2D {
51             top,
52             right,
53             bottom,
54             left,
55             _unit: PhantomData,
56         })
57     }
58 }
59 
60 impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
61 
62 impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
clone(&self) -> Self63     fn clone(&self) -> Self {
64         SideOffsets2D {
65             top: self.top.clone(),
66             right: self.right.clone(),
67             bottom: self.bottom.clone(),
68             left: self.left.clone(),
69             _unit: PhantomData,
70         }
71     }
72 }
73 
74 impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
75 
76 impl<T, U> PartialEq for SideOffsets2D<T, U>
77 where
78     T: PartialEq,
79 {
eq(&self, other: &Self) -> bool80     fn eq(&self, other: &Self) -> bool {
81         self.top == other.top
82             && self.right == other.right
83             && self.bottom == other.bottom
84             && self.left == other.left
85     }
86 }
87 
88 impl<T, U> Hash for SideOffsets2D<T, U>
89 where
90     T: Hash,
91 {
hash<H: core::hash::Hasher>(&self, h: &mut H)92     fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
93         self.top.hash(h);
94         self.right.hash(h);
95         self.bottom.hash(h);
96         self.left.hash(h);
97     }
98 }
99 
100 impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result101     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102         write!(
103             f,
104             "({:?},{:?},{:?},{:?})",
105             self.top, self.right, self.bottom, self.left
106         )
107     }
108 }
109 
110 impl<T: Default, U> Default for SideOffsets2D<T, U> {
default() -> Self111     fn default() -> Self {
112         SideOffsets2D {
113             top: Default::default(),
114             right: Default::default(),
115             bottom: Default::default(),
116             left: Default::default(),
117             _unit: PhantomData,
118         }
119     }
120 }
121 
122 impl<T, U> SideOffsets2D<T, U> {
123     /// Constructor taking a scalar for each side.
124     ///
125     /// Sides are specified in top-right-bottom-left order following
126     /// CSS's convention.
new(top: T, right: T, bottom: T, left: T) -> Self127     pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
128         SideOffsets2D {
129             top,
130             right,
131             bottom,
132             left,
133             _unit: PhantomData,
134         }
135     }
136 
137     /// Constructor taking a typed Length for each side.
138     ///
139     /// Sides are specified in top-right-bottom-left order following
140     /// CSS's convention.
from_lengths( top: Length<T, U>, right: Length<T, U>, bottom: Length<T, U>, left: Length<T, U>, ) -> Self141     pub fn from_lengths(
142         top: Length<T, U>,
143         right: Length<T, U>,
144         bottom: Length<T, U>,
145         left: Length<T, U>,
146     ) -> Self {
147         SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
148     }
149 
150     /// Construct side offsets from min and a max vector offsets.
151     ///
152     /// The outer rect of the resulting side offsets is equivalent to translating
153     /// a rectangle's upper-left corner with the min vector and translating the
154     /// bottom-right corner with the max vector.
from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self where T: Neg<Output = T>,155     pub fn from_vectors_outer(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     /// Construct side offsets from min and a max vector offsets.
169     ///
170     /// The inner rect of the resulting side offsets is equivalent to translating
171     /// a rectangle's upper-left corner with the min vector and translating the
172     /// bottom-right corner with the max vector.
from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self where T: Neg<Output = T>,173     pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
174     where
175         T: Neg<Output = T>,
176     {
177         SideOffsets2D {
178             left: min.x,
179             top: min.y,
180             right: -max.x,
181             bottom: -max.y,
182             _unit: PhantomData,
183         }
184     }
185 
186     /// Constructor, setting all sides to zero.
zero() -> Self where T: Zero,187     pub fn zero() -> Self
188         where T: Zero,
189     {
190         SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
191     }
192 
193     /// Returns `true` if all side offsets are zero.
is_zero(&self) -> bool where T: Zero + PartialEq,194     pub fn is_zero(&self) -> bool
195     where
196         T: Zero + PartialEq,
197     {
198         let zero = T::zero();
199         self.top == zero && self.right == zero && self.bottom == zero && self.left == zero
200     }
201 
202     /// Constructor setting the same value to all sides, taking a scalar value directly.
new_all_same(all: T) -> Self where T : Copy203     pub fn new_all_same(all: T) -> Self
204         where T : Copy
205     {
206         SideOffsets2D::new(all, all, all, all)
207     }
208 
209     /// Constructor setting the same value to all sides, taking a typed Length.
from_length_all_same(all: Length<T, U>) -> Self where T : Copy210     pub fn from_length_all_same(all: Length<T, U>) -> Self
211         where T : Copy
212     {
213         SideOffsets2D::new_all_same(all.0)
214     }
215 
horizontal(&self) -> T where T: Copy + Add<T, Output = T>216     pub fn horizontal(&self) -> T
217         where T: Copy + Add<T, Output = T>
218     {
219         self.left + self.right
220     }
221 
vertical(&self) -> T where T: Copy + Add<T, Output = T>222     pub fn vertical(&self) -> T
223         where T: Copy + Add<T, Output = T>
224     {
225         self.top + self.bottom
226     }
227 }
228 
229 impl<T, U> Add for SideOffsets2D<T, U>
230 where
231     T: Add<T, Output = T>,
232 {
233     type Output = Self;
add(self, other: Self) -> Self234     fn add(self, other: Self) -> Self {
235         SideOffsets2D::new(
236             self.top + other.top,
237             self.right + other.right,
238             self.bottom + other.bottom,
239             self.left + other.left,
240         )
241     }
242 }
243 
244 impl<T: Copy + Mul, U> Mul<T> for SideOffsets2D<T, U> {
245     type Output = SideOffsets2D<T::Output, U>;
246 
247     #[inline]
mul(self, scale: T) -> Self::Output248     fn mul(self, scale: T) -> Self::Output {
249         SideOffsets2D::new(
250             self.top * scale,
251             self.right * scale,
252             self.bottom * scale,
253             self.left * scale,
254         )
255     }
256 }
257 
258 impl<T: Copy + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
259     #[inline]
mul_assign(&mut self, other: T)260     fn mul_assign(&mut self, other: T) {
261         self.top *= other;
262         self.right *= other;
263         self.bottom *= other;
264         self.left *= other;
265     }
266 }
267 
268 impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
269     type Output = SideOffsets2D<T::Output, U2>;
270 
271     #[inline]
mul(self, scale: Scale<T, U1, U2>) -> Self::Output272     fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
273         SideOffsets2D::new(
274             self.top * scale.0,
275             self.right * scale.0,
276             self.bottom * scale.0,
277             self.left * scale.0,
278         )
279     }
280 }
281 
282 impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
283     #[inline]
mul_assign(&mut self, other: Scale<T, U, U>)284     fn mul_assign(&mut self, other: Scale<T, U, U>) {
285         *self *= other.0;
286     }
287 }
288 
289 impl<T: Copy + Div, U> Div<T> for SideOffsets2D<T, U> {
290     type Output = SideOffsets2D<T::Output, U>;
291 
292     #[inline]
div(self, scale: T) -> Self::Output293     fn div(self, scale: T) -> Self::Output {
294         SideOffsets2D::new(
295             self.top / scale,
296             self.right / scale,
297             self.bottom / scale,
298             self.left / scale,
299         )
300     }
301 }
302 
303 impl<T: Copy + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
304     #[inline]
div_assign(&mut self, other: T)305     fn div_assign(&mut self, other: T) {
306         self.top /= other;
307         self.right /= other;
308         self.bottom /= other;
309         self.left /= other;
310     }
311 }
312 
313 impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
314     type Output = SideOffsets2D<T::Output, U1>;
315 
316     #[inline]
div(self, scale: Scale<T, U1, U2>) -> Self::Output317     fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
318         SideOffsets2D::new(
319             self.top / scale.0,
320             self.right / scale.0,
321             self.bottom / scale.0,
322             self.left / scale.0,
323         )
324     }
325 }
326 
327 impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
div_assign(&mut self, other: Scale<T, U, U>)328     fn div_assign(&mut self, other: Scale<T, U, U>) {
329         *self /= other.0;
330     }
331 }
332 
333 #[test]
from_vectors()334 fn from_vectors() {
335     use crate::{point2, vec2};
336     type Box2D = crate::default::Box2D<i32>;
337 
338     let b = Box2D {
339         min: point2(10, 10),
340         max: point2(20, 20),
341     };
342 
343     let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
344     let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
345 
346     assert_eq!(
347         outer,
348         Box2D {
349             min: point2(9, 8),
350             max: point2(23, 24)
351         }
352     );
353     assert_eq!(
354         inner,
355         Box2D {
356             min: point2(11, 12),
357             max: point2(17, 16)
358         }
359     );
360 }
361 
362 #[test]
test_is_zero()363 fn test_is_zero() {
364     let s1: SideOffsets2D<f32, ()> = SideOffsets2D::new_all_same(0.0);
365     assert!(s1.is_zero());
366 
367     let s2: SideOffsets2D<f32, ()> = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
368     assert!(!s2.is_zero());
369 }
370 
371 #[cfg(test)]
372 mod ops {
373     use crate::Scale;
374 
375     pub enum Mm {}
376     pub enum Cm {}
377 
378     type SideOffsets2D<T> = crate::default::SideOffsets2D<T>;
379     type SideOffsets2DMm<T> = crate::SideOffsets2D<T, Mm>;
380     type SideOffsets2DCm<T> = crate::SideOffsets2D<T, Cm>;
381 
382     #[test]
test_mul_scalar()383     fn test_mul_scalar() {
384         let s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
385 
386         let result = s * 3.0;
387 
388         assert_eq!(result, SideOffsets2D::new(3.0, 6.0, 9.0, 12.0));
389     }
390 
391     #[test]
test_mul_assign_scalar()392     fn test_mul_assign_scalar() {
393         let mut s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
394 
395         s *= 2.0;
396 
397         assert_eq!(s, SideOffsets2D::new(2.0, 4.0, 6.0, 8.0));
398     }
399 
400     #[test]
test_mul_scale()401     fn test_mul_scale() {
402         let s = SideOffsets2DMm::new(0.0, 1.0, 3.0, 2.0);
403         let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
404 
405         let result = s * cm_per_mm;
406 
407         assert_eq!(result, SideOffsets2DCm::new(0.0, 0.1, 0.3, 0.2));
408     }
409 
410     #[test]
test_mul_assign_scale()411     fn test_mul_assign_scale() {
412         let mut s = SideOffsets2DMm::new(2.0, 4.0, 6.0, 8.0);
413         let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
414 
415         s *= scale;
416 
417         assert_eq!(s, SideOffsets2DMm::new(0.2, 0.4, 0.6, 0.8));
418     }
419 
420     #[test]
test_div_scalar()421     fn test_div_scalar() {
422         let s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
423 
424         let result = s / 10.0;
425 
426         assert_eq!(result, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
427     }
428 
429     #[test]
test_div_assign_scalar()430     fn test_div_assign_scalar() {
431         let mut s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
432 
433         s /= 10.0;
434 
435         assert_eq!(s, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
436     }
437 
438     #[test]
test_div_scale()439     fn test_div_scale() {
440         let s = SideOffsets2DCm::new(0.1, 0.2, 0.3, 0.4);
441         let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
442 
443         let result = s / cm_per_mm;
444 
445         assert_eq!(result, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
446     }
447 
448     #[test]
test_div_assign_scale()449     fn test_div_assign_scale() {
450         let mut s = SideOffsets2DMm::new(0.1, 0.2, 0.3, 0.4);
451         let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
452 
453         s /= scale;
454 
455         assert_eq!(s, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
456     }
457 }
458