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