euclid/
box2d.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
10use super::UnknownUnit;
11use crate::approxord::{max, min};
12use crate::num::*;
13use crate::point::{point2, Point2D};
14use crate::rect::Rect;
15use crate::scale::Scale;
16use crate::side_offsets::SideOffsets2D;
17use crate::size::Size2D;
18use crate::vector::{vec2, Vector2D};
19
20#[cfg(feature = "bytemuck")]
21use bytemuck::{Pod, Zeroable};
22#[cfg(feature = "malloc_size_of")]
23use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
24use num_traits::{Float, NumCast};
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28use core::borrow::Borrow;
29use core::cmp::PartialOrd;
30use core::fmt;
31use core::hash::{Hash, Hasher};
32use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
33
34/// A 2d axis aligned rectangle represented by its minimum and maximum coordinates.
35///
36/// # Representation
37///
38/// This struct is similar to [`Rect`], but stores rectangle as two endpoints
39/// instead of origin point and size. Such representation has several advantages over
40/// [`Rect`] representation:
41/// - Several operations are more efficient with `Box2D`, including [`intersection`],
42///   [`union`], and point-in-rect.
43/// - The representation is less susceptible to overflow. With [`Rect`], computation
44///   of second point can overflow for a large range of values of origin and size.
45///   However, with `Box2D`, computation of [`size`] cannot overflow if the coordinates
46///   are signed and the resulting size is unsigned.
47///
48/// A known disadvantage of `Box2D` is that translating the rectangle requires translating
49/// both points, whereas translating [`Rect`] only requires translating one point.
50///
51/// # Empty box
52///
53/// A box is considered empty (see [`is_empty`]) if any of the following is true:
54/// - it's area is empty,
55/// - it's area is negative (`min.x > max.x` or `min.y > max.y`),
56/// - it contains NaNs.
57///
58/// [`intersection`]: Self::intersection
59/// [`is_empty`]: Self::is_empty
60/// [`union`]: Self::union
61/// [`size`]: Self::size
62#[repr(C)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64#[cfg_attr(
65    feature = "serde",
66    serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
67)]
68pub struct Box2D<T, U> {
69    pub min: Point2D<T, U>,
70    pub max: Point2D<T, U>,
71}
72
73impl<T: Hash, U> Hash for Box2D<T, U> {
74    fn hash<H: Hasher>(&self, h: &mut H) {
75        self.min.hash(h);
76        self.max.hash(h);
77    }
78}
79
80impl<T: Copy, U> Copy for Box2D<T, U> {}
81
82impl<T: Clone, U> Clone for Box2D<T, U> {
83    fn clone(&self) -> Self {
84        Self::new(self.min.clone(), self.max.clone())
85    }
86}
87
88impl<T: PartialEq, U> PartialEq for Box2D<T, U> {
89    fn eq(&self, other: &Self) -> bool {
90        self.min.eq(&other.min) && self.max.eq(&other.max)
91    }
92}
93
94impl<T: Eq, U> Eq for Box2D<T, U> {}
95
96impl<T: fmt::Debug, U> fmt::Debug for Box2D<T, U> {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        f.debug_tuple("Box2D")
99            .field(&self.min)
100            .field(&self.max)
101            .finish()
102    }
103}
104
105#[cfg(feature = "arbitrary")]
106impl<'a, T, U> arbitrary::Arbitrary<'a> for Box2D<T, U>
107where
108    T: arbitrary::Arbitrary<'a>,
109{
110    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
111        Ok(Box2D::new(
112            arbitrary::Arbitrary::arbitrary(u)?,
113            arbitrary::Arbitrary::arbitrary(u)?,
114        ))
115    }
116}
117
118#[cfg(feature = "bytemuck")]
119unsafe impl<T: Zeroable, U> Zeroable for Box2D<T, U> {}
120
121#[cfg(feature = "bytemuck")]
122unsafe impl<T: Pod, U: 'static> Pod for Box2D<T, U> {}
123
124impl<T, U> Box2D<T, U> {
125    /// Constructor.
126    #[inline]
127    pub const fn new(min: Point2D<T, U>, max: Point2D<T, U>) -> Self {
128        Box2D { min, max }
129    }
130
131    /// Constructor.
132    #[inline]
133    pub fn from_origin_and_size(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self
134    where
135        T: Copy + Add<T, Output = T>,
136    {
137        Box2D {
138            min: origin,
139            max: point2(origin.x + size.width, origin.y + size.height),
140        }
141    }
142
143    /// Creates a `Box2D` of the given size, at offset zero.
144    #[inline]
145    pub fn from_size(size: Size2D<T, U>) -> Self
146    where
147        T: Zero,
148    {
149        Box2D {
150            min: Point2D::zero(),
151            max: point2(size.width, size.height),
152        }
153    }
154}
155
156#[cfg(feature = "malloc_size_of")]
157impl<T: MallocSizeOf, U> MallocSizeOf for Box2D<T, U> {
158    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
159        self.min.size_of(ops) + self.max.size_of(ops)
160    }
161}
162
163impl<T, U> Box2D<T, U>
164where
165    T: PartialOrd,
166{
167    /// Returns `true` if the box has a negative area.
168    ///
169    /// The common interpretation for a negative box is to consider it empty. It can be obtained
170    /// by calculating the intersection of two boxes that do not intersect.
171    #[inline]
172    pub fn is_negative(&self) -> bool {
173        self.max.x < self.min.x || self.max.y < self.min.y
174    }
175
176    /// Returns `true` if the size is zero, negative or NaN.
177    #[inline]
178    pub fn is_empty(&self) -> bool {
179        !(self.max.x > self.min.x && self.max.y > self.min.y)
180    }
181
182    /// Returns `true` if the two boxes intersect.
183    #[inline]
184    pub fn intersects(&self, other: &Self) -> bool {
185        // Use bitwise and instead of && to avoid emitting branches.
186        (self.min.x < other.max.x)
187            & (self.max.x > other.min.x)
188            & (self.min.y < other.max.y)
189            & (self.max.y > other.min.y)
190    }
191
192    /// Returns `true` if this [`Box2D`] contains the point `p`.
193    ///
194    /// Points on the top and left edges are inside the box, whereas
195    /// points on the bottom and right edges are outside the box.
196    /// See [`Box2D::contains_inclusive`] for a variant that also includes those
197    /// latter points.
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// use euclid::default::{Box2D, Point2D};
203    ///
204    /// let rect = Box2D::new(Point2D::origin(), Point2D::new(2, 2));
205    ///
206    /// assert!(rect.contains(Point2D::new(1, 1)));
207    ///
208    /// assert!(rect.contains(Point2D::new(0, 1))); // left edge
209    /// assert!(rect.contains(Point2D::new(1, 0))); // top edge
210    /// assert!(rect.contains(Point2D::origin()));
211    ///
212    /// assert!(!rect.contains(Point2D::new(2, 1))); // right edge
213    /// assert!(!rect.contains(Point2D::new(1, 2))); // bottom edge
214    /// assert!(!rect.contains(Point2D::new(2, 2)));
215    /// ```
216    #[inline]
217    pub fn contains(&self, p: Point2D<T, U>) -> bool {
218        // Use bitwise and instead of && to avoid emitting branches.
219        (self.min.x <= p.x) & (p.x < self.max.x) & (self.min.y <= p.y) & (p.y < self.max.y)
220    }
221
222    /// Returns `true` if this box contains the point `p`.
223    ///
224    /// This is like [`Box2D::contains`], but points on the bottom and right
225    /// edges are also inside the box.
226    ///
227    /// # Examples
228    /// ```
229    /// use euclid::default::{Box2D, Point2D};
230    ///
231    /// let rect = Box2D::new(Point2D::origin(), Point2D::new(2, 2));
232    ///
233    /// assert!(rect.contains_inclusive(Point2D::new(1, 1)));
234    ///
235    /// assert!(rect.contains_inclusive(Point2D::new(0, 1))); // left edge
236    /// assert!(rect.contains_inclusive(Point2D::new(1, 0))); // top edge
237    /// assert!(rect.contains_inclusive(Point2D::origin()));
238    ///
239    /// assert!(rect.contains_inclusive(Point2D::new(2, 1))); // right edge
240    /// assert!(rect.contains_inclusive(Point2D::new(1, 2))); // bottom edge
241    /// assert!(rect.contains_inclusive(Point2D::new(2, 2)));
242    /// ```
243    #[inline]
244    pub fn contains_inclusive(&self, p: Point2D<T, U>) -> bool {
245        // Use bitwise and instead of && to avoid emitting branches.
246        (self.min.x <= p.x) & (p.x <= self.max.x) & (self.min.y <= p.y) & (p.y <= self.max.y)
247    }
248
249    /// Returns `true` if this box contains the interior of the other box. Always
250    /// returns `true` if other is empty, and always returns `false` if other is
251    /// nonempty but this box is empty.
252    #[inline]
253    pub fn contains_box(&self, other: &Self) -> bool {
254        other.is_empty()
255            || ((self.min.x <= other.min.x)
256                & (other.max.x <= self.max.x)
257                & (self.min.y <= other.min.y)
258                & (other.max.y <= self.max.y))
259    }
260}
261
262impl<T, U> Box2D<T, U>
263where
264    T: Copy + PartialOrd,
265{
266    #[inline]
267    pub fn to_non_empty(&self) -> Option<Self> {
268        if self.is_empty() {
269            return None;
270        }
271
272        Some(*self)
273    }
274
275    /// Computes the intersection of two boxes, returning `None` if the boxes do not intersect.
276    #[inline]
277    pub fn intersection(&self, other: &Self) -> Option<Self> {
278        let b = self.intersection_unchecked(other);
279
280        if b.is_empty() {
281            return None;
282        }
283
284        Some(b)
285    }
286
287    /// Computes the intersection of two boxes without check whether they do intersect.
288    ///
289    /// The result is a negative box if the boxes do not intersect.
290    /// This can be useful for computing the intersection of more than two boxes, as
291    /// it is possible to chain multiple `intersection_unchecked` calls and check for
292    /// empty/negative result at the end.
293    #[inline]
294    pub fn intersection_unchecked(&self, other: &Self) -> Self {
295        Box2D {
296            min: point2(max(self.min.x, other.min.x), max(self.min.y, other.min.y)),
297            max: point2(min(self.max.x, other.max.x), min(self.max.y, other.max.y)),
298        }
299    }
300
301    /// Computes the union of two boxes.
302    ///
303    /// If either of the boxes is empty, the other one is returned.
304    #[inline]
305    pub fn union(&self, other: &Self) -> Self {
306        if other.is_empty() {
307            return *self;
308        }
309        if self.is_empty() {
310            return *other;
311        }
312
313        Box2D {
314            min: point2(min(self.min.x, other.min.x), min(self.min.y, other.min.y)),
315            max: point2(max(self.max.x, other.max.x), max(self.max.y, other.max.y)),
316        }
317    }
318}
319
320impl<T, U> Box2D<T, U>
321where
322    T: Copy + Add<T, Output = T>,
323{
324    /// Returns the same box, translated by a vector.
325    #[inline]
326    pub fn translate(&self, by: Vector2D<T, U>) -> Self {
327        Box2D {
328            min: self.min + by,
329            max: self.max + by,
330        }
331    }
332}
333
334impl<T, U> Box2D<T, U>
335where
336    T: Copy + Sub<T, Output = T>,
337{
338    #[inline]
339    pub fn size(&self) -> Size2D<T, U> {
340        (self.max - self.min).to_size()
341    }
342
343    /// Change the size of the box by adjusting the max endpoint
344    /// without modifying the min endpoint.
345    #[inline]
346    pub fn set_size(&mut self, size: Size2D<T, U>) {
347        let diff = (self.size() - size).to_vector();
348        self.max -= diff;
349    }
350
351    #[inline]
352    pub fn width(&self) -> T {
353        self.max.x - self.min.x
354    }
355
356    #[inline]
357    pub fn height(&self) -> T {
358        self.max.y - self.min.y
359    }
360
361    #[inline]
362    pub fn to_rect(&self) -> Rect<T, U> {
363        Rect {
364            origin: self.min,
365            size: self.size(),
366        }
367    }
368}
369
370impl<T, U> Box2D<T, U>
371where
372    T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
373{
374    /// Inflates the box by the specified sizes on each side respectively.
375    #[inline]
376    #[must_use]
377    pub fn inflate(&self, width: T, height: T) -> Self {
378        Box2D {
379            min: point2(self.min.x - width, self.min.y - height),
380            max: point2(self.max.x + width, self.max.y + height),
381        }
382    }
383
384    /// Calculate the size and position of an inner box.
385    ///
386    /// Subtracts the side offsets from all sides. The horizontal, vertical
387    /// and applicate offsets must not be larger than the original side length.
388    pub fn inner_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
389        Box2D {
390            min: self.min + vec2(offsets.left, offsets.top),
391            max: self.max - vec2(offsets.right, offsets.bottom),
392        }
393    }
394
395    /// Calculate the b and position of an outer box.
396    ///
397    /// Add the offsets to all sides. The expanded box is returned.
398    pub fn outer_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
399        Box2D {
400            min: self.min - vec2(offsets.left, offsets.top),
401            max: self.max + vec2(offsets.right, offsets.bottom),
402        }
403    }
404}
405
406impl<T, U> Box2D<T, U>
407where
408    T: Copy + Zero + PartialOrd,
409{
410    /// Returns the smallest box enclosing all of the provided points.
411    ///
412    /// The top/bottom/left/right-most points are exactly on the box's edges.
413    /// Since [`Box2D::contains`] excludes points that are on the right-most and
414    /// bottom-most edges, not all points passed to [`Box2D::from_points`] are
415    /// contained in the returned [`Box2D`] when probed with [`Box2D::contains`], but
416    /// are when probed with [`Box2D::contains_inclusive`].
417    ///
418    /// For example:
419    ///
420    /// ```
421    /// use euclid::default::{Point2D, Box2D};
422    ///
423    /// let a = Point2D::origin();
424    /// let b = Point2D::new(1, 2);
425    /// let rect = Box2D::from_points([a, b]);
426    ///
427    /// assert_eq!(rect.width(), 1);
428    /// assert_eq!(rect.height(), 2);
429    ///
430    /// assert!(rect.contains(a));
431    /// assert!(!rect.contains(b));
432    /// assert!(rect.contains_inclusive(b));
433    /// ```
434    ///
435    /// In particular, calling [`Box2D::from_points`] with a single point
436    /// results in an empty [`Box2D`]:
437    ///
438    /// ```
439    /// use euclid::default::{Point2D, Box2D};
440    ///
441    /// let a = Point2D::new(1, 0);
442    /// let rect = Box2D::from_points([a]);
443    ///
444    /// assert!(rect.is_empty());
445    /// assert!(!rect.contains(a));
446    /// assert!(rect.contains_inclusive(a));
447    /// ```
448    ///
449    /// The [`Box2D`] enclosing no points is also empty:
450    ///
451    /// ```
452    /// use euclid::default::{Box2D, Point2D};
453    ///
454    /// let rect = Box2D::from_points(std::iter::empty::<Point2D<i32>>());
455    /// assert!(rect.is_empty());
456    /// ```
457    pub fn from_points<I>(points: I) -> Self
458    where
459        I: IntoIterator,
460        I::Item: Borrow<Point2D<T, U>>,
461    {
462        let mut points = points.into_iter();
463
464        let (mut min_x, mut min_y) = match points.next() {
465            Some(first) => first.borrow().to_tuple(),
466            None => return Box2D::zero(),
467        };
468
469        let (mut max_x, mut max_y) = (min_x, min_y);
470        for point in points {
471            let p = point.borrow();
472            if p.x < min_x {
473                min_x = p.x;
474            }
475            if p.x > max_x {
476                max_x = p.x;
477            }
478            if p.y < min_y {
479                min_y = p.y;
480            }
481            if p.y > max_y {
482                max_y = p.y;
483            }
484        }
485
486        Box2D {
487            min: point2(min_x, min_y),
488            max: point2(max_x, max_y),
489        }
490    }
491}
492
493impl<T, U> Box2D<T, U>
494where
495    T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
496{
497    /// Linearly interpolate between this box and another box.
498    #[inline]
499    pub fn lerp(&self, other: Self, t: T) -> Self {
500        Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
501    }
502}
503
504impl<T, U> Box2D<T, U>
505where
506    T: Copy + One + Add<Output = T> + Div<Output = T>,
507{
508    pub fn center(&self) -> Point2D<T, U> {
509        let two = T::one() + T::one();
510        (self.min + self.max.to_vector()) / two
511    }
512}
513
514impl<T, U> Box2D<T, U>
515where
516    T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
517{
518    #[inline]
519    pub fn area(&self) -> T {
520        let size = self.size();
521        size.width * size.height
522    }
523}
524
525impl<T, U> Box2D<T, U>
526where
527    T: Zero,
528{
529    /// Constructor, setting all sides to zero.
530    pub fn zero() -> Self {
531        Box2D::new(Point2D::zero(), Point2D::zero())
532    }
533}
534
535impl<T: Copy + Mul, U> Mul<T> for Box2D<T, U> {
536    type Output = Box2D<T::Output, U>;
537
538    #[inline]
539    fn mul(self, scale: T) -> Self::Output {
540        Box2D::new(self.min * scale, self.max * scale)
541    }
542}
543
544impl<T: Copy + MulAssign, U> MulAssign<T> for Box2D<T, U> {
545    #[inline]
546    fn mul_assign(&mut self, scale: T) {
547        *self *= Scale::new(scale);
548    }
549}
550
551impl<T: Copy + Div, U> Div<T> for Box2D<T, U> {
552    type Output = Box2D<T::Output, U>;
553
554    #[inline]
555    fn div(self, scale: T) -> Self::Output {
556        Box2D::new(self.min / scale, self.max / scale)
557    }
558}
559
560impl<T: Copy + DivAssign, U> DivAssign<T> for Box2D<T, U> {
561    #[inline]
562    fn div_assign(&mut self, scale: T) {
563        *self /= Scale::new(scale);
564    }
565}
566
567impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1> {
568    type Output = Box2D<T::Output, U2>;
569
570    #[inline]
571    fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
572        Box2D::new(self.min * scale, self.max * scale)
573    }
574}
575
576impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box2D<T, U> {
577    #[inline]
578    fn mul_assign(&mut self, scale: Scale<T, U, U>) {
579        self.min *= scale;
580        self.max *= scale;
581    }
582}
583
584impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2> {
585    type Output = Box2D<T::Output, U1>;
586
587    #[inline]
588    fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
589        Box2D::new(self.min / scale, self.max / scale)
590    }
591}
592
593impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box2D<T, U> {
594    #[inline]
595    fn div_assign(&mut self, scale: Scale<T, U, U>) {
596        self.min /= scale;
597        self.max /= scale;
598    }
599}
600
601impl<T, U> Box2D<T, U>
602where
603    T: Copy,
604{
605    #[inline]
606    pub fn x_range(&self) -> Range<T> {
607        self.min.x..self.max.x
608    }
609
610    #[inline]
611    pub fn y_range(&self) -> Range<T> {
612        self.min.y..self.max.y
613    }
614
615    /// Drop the units, preserving only the numeric value.
616    #[inline]
617    pub fn to_untyped(&self) -> Box2D<T, UnknownUnit> {
618        Box2D::new(self.min.to_untyped(), self.max.to_untyped())
619    }
620
621    /// Tag a unitless value with units.
622    #[inline]
623    pub fn from_untyped(c: &Box2D<T, UnknownUnit>) -> Box2D<T, U> {
624        Box2D::new(Point2D::from_untyped(c.min), Point2D::from_untyped(c.max))
625    }
626
627    /// Cast the unit
628    #[inline]
629    pub fn cast_unit<V>(&self) -> Box2D<T, V> {
630        Box2D::new(self.min.cast_unit(), self.max.cast_unit())
631    }
632
633    #[inline]
634    pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
635    where
636        T: Mul<S, Output = T>,
637    {
638        Box2D {
639            min: point2(self.min.x * x, self.min.y * y),
640            max: point2(self.max.x * x, self.max.y * y),
641        }
642    }
643}
644
645impl<T: NumCast + Copy, U> Box2D<T, U> {
646    /// Cast from one numeric representation to another, preserving the units.
647    ///
648    /// When casting from floating point to integer coordinates, the decimals are truncated
649    /// as one would expect from a simple cast, but this behavior does not always make sense
650    /// geometrically. Consider using [`round`], [`round_in`] or [`round_out`] before casting.
651    ///
652    /// [`round`]: Self::round
653    /// [`round_in`]: Self::round_in
654    /// [`round_out`]: Self::round_out
655    #[inline]
656    pub fn cast<NewT: NumCast>(&self) -> Box2D<NewT, U> {
657        Box2D::new(self.min.cast(), self.max.cast())
658    }
659
660    /// Fallible cast from one numeric representation to another, preserving the units.
661    ///
662    /// When casting from floating point to integer coordinates, the decimals are truncated
663    /// as one would expect from a simple cast, but this behavior does not always make sense
664    /// geometrically. Consider using [`round`], [`round_in`] or [`round_out`] before casting.
665    ///
666    /// [`round`]: Self::round
667    /// [`round_in`]: Self::round_in
668    /// [`round_out`]: Self::round_out
669    pub fn try_cast<NewT: NumCast>(&self) -> Option<Box2D<NewT, U>> {
670        match (self.min.try_cast(), self.max.try_cast()) {
671            (Some(a), Some(b)) => Some(Box2D::new(a, b)),
672            _ => None,
673        }
674    }
675
676    // Convenience functions for common casts
677
678    /// Cast into an `f32` box.
679    #[inline]
680    pub fn to_f32(&self) -> Box2D<f32, U> {
681        self.cast()
682    }
683
684    /// Cast into an `f64` box.
685    #[inline]
686    pub fn to_f64(&self) -> Box2D<f64, U> {
687        self.cast()
688    }
689
690    /// Cast into an `usize` box, truncating decimals if any.
691    ///
692    /// When casting from floating point boxes, it is worth considering whether
693    /// to `round()`, `round_in()` or `round_out()` before the cast in order to
694    /// obtain the desired conversion behavior.
695    #[inline]
696    pub fn to_usize(&self) -> Box2D<usize, U> {
697        self.cast()
698    }
699
700    /// Cast into an `u32` box, truncating decimals if any.
701    ///
702    /// When casting from floating point boxes, it is worth considering whether
703    /// to `round()`, `round_in()` or `round_out()` before the cast in order to
704    /// obtain the desired conversion behavior.
705    #[inline]
706    pub fn to_u32(&self) -> Box2D<u32, U> {
707        self.cast()
708    }
709
710    /// Cast into an `i32` box, truncating decimals if any.
711    ///
712    /// When casting from floating point boxes, it is worth considering whether
713    /// to `round()`, `round_in()` or `round_out()` before the cast in order to
714    /// obtain the desired conversion behavior.
715    #[inline]
716    pub fn to_i32(&self) -> Box2D<i32, U> {
717        self.cast()
718    }
719
720    /// Cast into an `i64` box, truncating decimals if any.
721    ///
722    /// When casting from floating point boxes, it is worth considering whether
723    /// to `round()`, `round_in()` or `round_out()` before the cast in order to
724    /// obtain the desired conversion behavior.
725    #[inline]
726    pub fn to_i64(&self) -> Box2D<i64, U> {
727        self.cast()
728    }
729}
730
731impl<T: Float, U> Box2D<T, U> {
732    /// Returns `true` if all members are finite.
733    #[inline]
734    pub fn is_finite(self) -> bool {
735        self.min.is_finite() && self.max.is_finite()
736    }
737}
738
739impl<T, U> Box2D<T, U>
740where
741    T: Round,
742{
743    /// Return a box with edges rounded to integer coordinates, such that
744    /// the returned box has the same set of pixel centers as the original
745    /// one.
746    /// Values equal to 0.5 round up.
747    /// Suitable for most places where integral device coordinates
748    /// are needed, but note that any translation should be applied first to
749    /// avoid pixel rounding errors.
750    /// Note that this is *not* rounding to nearest integer if the values are negative.
751    /// They are always rounding as floor(n + 0.5).
752    #[must_use]
753    pub fn round(&self) -> Self {
754        Box2D::new(self.min.round(), self.max.round())
755    }
756}
757
758impl<T, U> Box2D<T, U>
759where
760    T: Floor + Ceil,
761{
762    /// Return a box with faces/edges rounded to integer coordinates, such that
763    /// the original box contains the resulting box.
764    #[must_use]
765    pub fn round_in(&self) -> Self {
766        let min = self.min.ceil();
767        let max = self.max.floor();
768        Box2D { min, max }
769    }
770
771    /// Return a box with faces/edges rounded to integer coordinates, such that
772    /// the original box is contained in the resulting box.
773    #[must_use]
774    pub fn round_out(&self) -> Self {
775        let min = self.min.floor();
776        let max = self.max.ceil();
777        Box2D { min, max }
778    }
779}
780
781impl<T, U> From<Size2D<T, U>> for Box2D<T, U>
782where
783    T: Copy + Zero + PartialOrd,
784{
785    fn from(b: Size2D<T, U>) -> Self {
786        Self::from_size(b)
787    }
788}
789
790impl<T, U> From<Rect<T, U>> for Box2D<T, U>
791where
792    T: Copy + Add<T, Output = T>,
793{
794    fn from(r: Rect<T, U>) -> Self {
795        r.to_box2d()
796    }
797}
798
799impl<T: Default, U> Default for Box2D<T, U> {
800    fn default() -> Self {
801        Box2D {
802            min: Default::default(),
803            max: Default::default(),
804        }
805    }
806}
807
808#[cfg(test)]
809mod tests {
810    use crate::default::Box2D;
811    use crate::side_offsets::SideOffsets2D;
812    use crate::{point2, size2, vec2, Point2D};
813    //use super::*;
814
815    #[test]
816    fn test_size() {
817        let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
818        assert_eq!(b.size().width, 20.0);
819        assert_eq!(b.size().height, 20.0);
820    }
821
822    #[test]
823    fn test_width_height() {
824        let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
825        assert!(b.width() == 20.0);
826        assert!(b.height() == 20.0);
827    }
828
829    #[test]
830    fn test_center() {
831        let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
832        assert_eq!(b.center(), Point2D::zero());
833    }
834
835    #[test]
836    fn test_area() {
837        let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
838        assert_eq!(b.area(), 400.0);
839    }
840
841    #[test]
842    fn test_from_points() {
843        let b = Box2D::from_points(&[point2(50.0, 160.0), point2(100.0, 25.0)]);
844        assert_eq!(b.min, point2(50.0, 25.0));
845        assert_eq!(b.max, point2(100.0, 160.0));
846    }
847
848    #[test]
849    fn test_round_in() {
850        let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_in();
851        assert_eq!(b.min.x, -25.0);
852        assert_eq!(b.min.y, -40.0);
853        assert_eq!(b.max.x, 60.0);
854        assert_eq!(b.max.y, 36.0);
855    }
856
857    #[test]
858    fn test_round_out() {
859        let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_out();
860        assert_eq!(b.min.x, -26.0);
861        assert_eq!(b.min.y, -41.0);
862        assert_eq!(b.max.x, 61.0);
863        assert_eq!(b.max.y, 37.0);
864    }
865
866    #[test]
867    fn test_round() {
868        let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
869        assert_eq!(b.min.x, -25.0);
870        assert_eq!(b.min.y, -40.0);
871        assert_eq!(b.max.x, 60.0);
872        assert_eq!(b.max.y, 37.0);
873    }
874
875    #[test]
876    fn test_from_size() {
877        let b = Box2D::from_size(size2(30.0, 40.0));
878        assert!(b.min == Point2D::zero());
879        assert!(b.size().width == 30.0);
880        assert!(b.size().height == 40.0);
881    }
882
883    #[test]
884    fn test_inner_box() {
885        let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
886        let b = b.inner_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
887        assert_eq!(b.max.x, 80.0);
888        assert_eq!(b.max.y, 155.0);
889        assert_eq!(b.min.x, 60.0);
890        assert_eq!(b.min.y, 35.0);
891    }
892
893    #[test]
894    fn test_outer_box() {
895        let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
896        let b = b.outer_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
897        assert_eq!(b.max.x, 120.0);
898        assert_eq!(b.max.y, 165.0);
899        assert_eq!(b.min.x, 40.0);
900        assert_eq!(b.min.y, 15.0);
901    }
902
903    #[test]
904    fn test_translate() {
905        let size = size2(15.0, 15.0);
906        let mut center = (size / 2.0).to_vector().to_point();
907        let b = Box2D::from_size(size);
908        assert_eq!(b.center(), center);
909        let translation = vec2(10.0, 2.5);
910        let b = b.translate(translation);
911        center += translation;
912        assert_eq!(b.center(), center);
913        assert_eq!(b.max.x, 25.0);
914        assert_eq!(b.max.y, 17.5);
915        assert_eq!(b.min.x, 10.0);
916        assert_eq!(b.min.y, 2.5);
917    }
918
919    #[test]
920    fn test_union() {
921        let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(0.0, 20.0)]);
922        let b2 = Box2D::from_points(&[point2(0.0, 20.0), point2(20.0, -20.0)]);
923        let b = b1.union(&b2);
924        assert_eq!(b.max.x, 20.0);
925        assert_eq!(b.max.y, 20.0);
926        assert_eq!(b.min.x, -20.0);
927        assert_eq!(b.min.y, -20.0);
928    }
929
930    #[test]
931    fn test_intersects() {
932        let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
933        let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
934        assert!(b1.intersects(&b2));
935    }
936
937    #[test]
938    fn test_intersection_unchecked() {
939        let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
940        let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
941        let b = b1.intersection_unchecked(&b2);
942        assert_eq!(b.max.x, 10.0);
943        assert_eq!(b.max.y, 20.0);
944        assert_eq!(b.min.x, -10.0);
945        assert_eq!(b.min.y, -20.0);
946    }
947
948    #[test]
949    fn test_intersection() {
950        let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
951        let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
952        assert!(b1.intersection(&b2).is_some());
953
954        let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
955        let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
956        assert!(b1.intersection(&b2).is_none());
957    }
958
959    #[test]
960    fn test_scale() {
961        let b = Box2D::from_points(&[point2(-10.0, -10.0), point2(10.0, 10.0)]);
962        let b = b.scale(0.5, 0.5);
963        assert_eq!(b.max.x, 5.0);
964        assert_eq!(b.max.y, 5.0);
965        assert_eq!(b.min.x, -5.0);
966        assert_eq!(b.min.y, -5.0);
967    }
968
969    #[test]
970    fn test_lerp() {
971        let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(-10.0, -10.0)]);
972        let b2 = Box2D::from_points(&[point2(10.0, 10.0), point2(20.0, 20.0)]);
973        let b = b1.lerp(b2, 0.5);
974        assert_eq!(b.center(), Point2D::zero());
975        assert_eq!(b.size().width, 10.0);
976        assert_eq!(b.size().height, 10.0);
977    }
978
979    #[test]
980    fn test_contains() {
981        let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
982        assert!(b.contains(point2(-15.3, 10.5)));
983    }
984
985    #[test]
986    fn test_contains_box() {
987        let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
988        let b2 = Box2D::from_points(&[point2(-14.3, -16.5), point2(6.7, 17.6)]);
989        assert!(b1.contains_box(&b2));
990    }
991
992    #[test]
993    fn test_inflate() {
994        let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
995        let b = b.inflate(10.0, 5.0);
996        assert_eq!(b.size().width, 60.0);
997        assert_eq!(b.size().height, 50.0);
998        assert_eq!(b.center(), Point2D::zero());
999    }
1000
1001    #[test]
1002    fn test_is_empty() {
1003        for i in 0..2 {
1004            let mut coords_neg = [-20.0, -20.0];
1005            let mut coords_pos = [20.0, 20.0];
1006            coords_neg[i] = 0.0;
1007            coords_pos[i] = 0.0;
1008            let b = Box2D::from_points(&[Point2D::from(coords_neg), Point2D::from(coords_pos)]);
1009            assert!(b.is_empty());
1010        }
1011    }
1012
1013    #[test]
1014    #[rustfmt::skip]
1015    fn test_nan_empty() {
1016        use std::f32::NAN;
1017        assert!(Box2D { min: point2(NAN, 2.0), max: point2(1.0, 3.0) }.is_empty());
1018        assert!(Box2D { min: point2(0.0, NAN), max: point2(1.0, 2.0) }.is_empty());
1019        assert!(Box2D { min: point2(1.0, -2.0), max: point2(NAN, 2.0) }.is_empty());
1020        assert!(Box2D { min: point2(1.0, -2.0), max: point2(0.0, NAN) }.is_empty());
1021    }
1022
1023    #[test]
1024    fn test_from_origin_and_size() {
1025        let b = Box2D::from_origin_and_size(point2(1.0, 2.0), size2(3.0, 4.0));
1026        assert_eq!(b.min, point2(1.0, 2.0));
1027        assert_eq!(b.size(), size2(3.0, 4.0));
1028    }
1029
1030    #[test]
1031    fn test_set_size() {
1032        let mut b = Box2D {
1033            min: point2(1.0, 2.0),
1034            max: point2(3.0, 4.0),
1035        };
1036        b.set_size(size2(5.0, 6.0));
1037
1038        assert_eq!(b.min, point2(1.0, 2.0));
1039        assert_eq!(b.size(), size2(5.0, 6.0));
1040    }
1041}