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