// Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms.
#[cfg(feature = "bytemuck")] use bytemuck::{Pod, Zeroable}; use num_traits::{Float, NumCast}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize};
use core::borrow::Borrow; use core::cmp::PartialOrd; use core::fmt; use core::hash::{Hash, Hasher}; use core::ops::{Add, Div, DivAssign, Mul, MulAssign, Range, Sub};
/// A 2d Rectangle optionally tagged with a unit. /// /// # Representation /// /// `Rect` is represented by an origin point and a size. /// /// See [`Box2D`] for a rectangle represented by two endpoints. /// /// # Empty rectangle /// /// A rectangle is considered empty (see [`is_empty`]) if any of the following is true: /// - it's area is empty, /// - it's area is negative (`size.x < 0` or `size.y < 0`), /// - it contains NaNs. /// /// [`is_empty`]: #method.is_empty /// [`Box2D`]: struct.Box2D.html #[repr(C)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(
feature = "serde",
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
)] pubstruct Rect<T, U> { pub origin: Point2D<T, U>, pub size: Size2D<T, U>,
}
impl<T, U> Rect<T, U> where
T: Zero,
{ /// Constructor, setting all sides to zero. #[inline] pubfn zero() -> Self {
Rect::new(Point2D::origin(), Size2D::zero())
}
/// Creates a rect of the given size, at offset zero. #[inline] pubfn from_size(size: Size2D<T, U>) -> Self {
Rect {
origin: Point2D::zero(),
size,
}
}
}
impl<T, U> Rect<T, U> where
T: Copy + PartialOrd + Add<T, Output = T>,
{ /// Returns true if this rectangle contains the point. Points are considered /// in the rectangle if they are on the left or top edge, but outside if they /// are on the right or bottom edge. #[inline] pubfn contains(&self, p: Point2D<T, U>) -> bool { self.to_box2d().contains(p)
}
impl<T, U> Rect<T, U> where
T: Copy + Zero + PartialOrd + Add<T, Output = T>,
{ /// Returns true if this rectangle contains the interior of rect. Always /// returns true if rect is empty, and always returns false if rect is /// nonempty but this rectangle is empty. #[inline] pubfn contains_rect(&self, rect: &Self) -> bool {
rect.is_empty()
|| (self.min_x() <= rect.min_x()
&& rect.max_x() <= self.max_x()
&& self.min_y() <= rect.min_y()
&& rect.max_y() <= self.max_y())
}
}
impl<T, U> Rect<T, U> where
T: Copy + Zero + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
{ /// Calculate the size and position of an inner rectangle. /// /// Subtracts the side offsets from all sides. The horizontal and vertical /// offsets must not be larger than the original side length. /// This method assumes y oriented downward. pubfn inner_rect(&self, offsets: SideOffsets2D<T, U>) -> Self { let rect = Rect::new(
Point2D::new(self.origin.x + offsets.left, self.origin.y + offsets.top),
Size2D::new( self.size.width - offsets.horizontal(), self.size.height - offsets.vertical(),
),
);
debug_assert!(rect.size.width >= Zero::zero());
debug_assert!(rect.size.height >= Zero::zero());
rect
}
}
impl<T, U> Rect<T, U> where
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
{ /// Calculate the size and position of an outer rectangle. /// /// Add the offsets to all sides. The expanded rectangle is returned. /// This method assumes y oriented downward. pubfn outer_rect(&self, offsets: SideOffsets2D<T, U>) -> Self {
Rect::new(
Point2D::new(self.origin.x - offsets.left, self.origin.y - offsets.top),
Size2D::new( self.size.width + offsets.horizontal(), self.size.height + offsets.vertical(),
),
)
}
}
impl<T, U> Rect<T, U> where
T: Copy + Zero + PartialOrd + Sub<T, Output = T>,
{ /// Returns the smallest rectangle defined by the top/bottom/left/right-most /// points provided as parameter. /// /// Note: This function has a behavior that can be surprising because /// the right-most and bottom-most points are exactly on the edge /// of the rectangle while the `contains` function is has exclusive /// semantic on these edges. This means that the right-most and bottom-most /// points provided to `from_points` will count as not contained by the rect. /// This behavior may change in the future. pubfn from_points<I>(points: I) -> Self where
I: IntoIterator,
I::Item: Borrow<Point2D<T, U>>,
{
Box2D::from_points(points).to_rect()
}
}
impl<T, U> Rect<T, U> where
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{ /// Linearly interpolate between this rectangle and another rectangle. #[inline] pubfn lerp(&self, other: Self, t: T) -> Self { Self::new( self.origin.lerp(other.origin, t), self.size.lerp(other.size, t),
)
}
}
impl<T, U> Rect<T, U> where
T: Copy + One + Add<Output = T> + Div<Output = T>,
{ pubfn center(&self) -> Point2D<T, U> { let two = T::one() + T::one(); self.origin + self.size.to_vector() / two
}
}
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Rect<T, U> { #[inline] fn div_assign(&mutself, scale: Scale<T, U, U>) { self.origin /= scale.clone(); self.size /= scale;
}
}
impl<T: Copy, U> Rect<T, U> { /// Drop the units, preserving only the numeric value. #[inline] pubfn to_untyped(&self) -> Rect<T, UnknownUnit> {
Rect::new(self.origin.to_untyped(), self.size.to_untyped())
}
/// Tag a unitless value with units. #[inline] pubfn from_untyped(r: &Rect<T, UnknownUnit>) -> Rect<T, U> {
Rect::new(
Point2D::from_untyped(r.origin),
Size2D::from_untyped(r.size),
)
}
/// Cast the unit #[inline] pubfn cast_unit<V>(&self) -> Rect<T, V> {
Rect::new(self.origin.cast_unit(), self.size.cast_unit())
}
}
impl<T: NumCast + Copy, U> Rect<T, U> { /// Cast from one numeric representation to another, preserving the units. /// /// When casting from floating point to integer coordinates, the decimals are truncated /// as one would expect from a simple cast, but this behavior does not always make sense /// geometrically. Consider using round(), round_in or round_out() before casting. #[inline] pubfn cast<NewT: NumCast>(&self) -> Rect<NewT, U> {
Rect::new(self.origin.cast(), self.size.cast())
}
/// Fallible cast from one numeric representation to another, preserving the units. /// /// When casting from floating point to integer coordinates, the decimals are truncated /// as one would expect from a simple cast, but this behavior does not always make sense /// geometrically. Consider using round(), round_in or round_out() before casting. pubfn try_cast<NewT: NumCast>(&self) -> Option<Rect<NewT, U>> { match (self.origin.try_cast(), self.size.try_cast()) {
(Some(origin), Some(size)) => Some(Rect::new(origin, size)),
_ => None,
}
}
// Convenience functions for common casts
/// Cast into an `f32` rectangle. #[inline] pubfn to_f32(&self) -> Rect<f32, U> { self.cast()
}
/// Cast into an `f64` rectangle. #[inline] pubfn to_f64(&self) -> Rect<f64, U> { self.cast()
}
/// Cast into an `usize` rectangle, truncating decimals if any. /// /// When casting from floating point rectangles, it is worth considering whether /// to `round()`, `round_in()` or `round_out()` before the cast in order to /// obtain the desired conversion behavior. #[inline] pubfn to_usize(&self) -> Rect<usize, U> { self.cast()
}
/// Cast into an `u32` rectangle, truncating decimals if any. /// /// When casting from floating point rectangles, it is worth considering whether /// to `round()`, `round_in()` or `round_out()` before the cast in order to /// obtain the desired conversion behavior. #[inline] pubfn to_u32(&self) -> Rect<u32, U> { self.cast()
}
/// Cast into an `u64` rectangle, truncating decimals if any. /// /// When casting from floating point rectangles, it is worth considering whether /// to `round()`, `round_in()` or `round_out()` before the cast in order to /// obtain the desired conversion behavior. #[inline] pubfn to_u64(&self) -> Rect<u64, U> { self.cast()
}
/// Cast into an `i32` rectangle, truncating decimals if any. /// /// When casting from floating point rectangles, it is worth considering whether /// to `round()`, `round_in()` or `round_out()` before the cast in order to /// obtain the desired conversion behavior. #[inline] pubfn to_i32(&self) -> Rect<i32, U> { self.cast()
}
/// Cast into an `i64` rectangle, truncating decimals if any. /// /// When casting from floating point rectangles, it is worth considering whether /// to `round()`, `round_in()` or `round_out()` before the cast in order to /// obtain the desired conversion behavior. #[inline] pubfn to_i64(&self) -> Rect<i64, U> { self.cast()
}
}
impl<T: Float, U> Rect<T, U> { /// Returns true if all members are finite. #[inline] pubfn is_finite(self) -> bool { self.origin.is_finite() && self.size.is_finite()
}
}
impl<T: Floor + Ceil + Round + Add<T, Output = T> + Sub<T, Output = T>, U> Rect<T, U> { /// Return a rectangle with edges rounded to integer coordinates, such that /// the returned rectangle has the same set of pixel centers as the original /// one. /// Edges at offset 0.5 round up. /// Suitable for most places where integral device coordinates /// are needed, but note that any translation should be applied first to /// avoid pixel rounding errors. /// Note that this is *not* rounding to nearest integer if the values are negative. /// They are always rounding as floor(n + 0.5). /// /// # Usage notes /// Note, that when using with floating-point `T` types that method can significantly /// loose precision for large values, so if you need to call this method very often it /// is better to use [`Box2D`]. /// /// [`Box2D`]: struct.Box2D.html #[must_use] pubfn round(&self) -> Self { self.to_box2d().round().to_rect()
}
/// Return a rectangle with edges rounded to integer coordinates, such that /// the original rectangle contains the resulting rectangle. /// /// # Usage notes /// Note, that when using with floating-point `T` types that method can significantly /// loose precision for large values, so if you need to call this method very often it /// is better to use [`Box2D`]. /// /// [`Box2D`]: struct.Box2D.html #[must_use] pubfn round_in(&self) -> Self { self.to_box2d().round_in().to_rect()
}
/// Return a rectangle with edges rounded to integer coordinates, such that /// the original rectangle is contained in the resulting rectangle. /// /// # Usage notes /// Note, that when using with floating-point `T` types that method can significantly /// loose precision for large values, so if you need to call this method very often it /// is better to use [`Box2D`]. /// /// [`Box2D`]: struct.Box2D.html #[must_use] pubfn round_out(&self) -> Self { self.to_box2d().round_out().to_rect()
}
}
#[test] fn test_union() { let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40)); let q = Rect::new(Point2D::new(20, 20), Size2D::new(5, 5)); let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15)); let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200));
#[test] fn test_intersection() { let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20)); let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10)); let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8));
let pq = p.intersection(&q);
assert!(pq.is_some()); let pq = pq.unwrap();
assert!(pq.origin == Point2D::new(5, 15));
assert!(pq.size == Size2D::new(5, 5));
let pr = p.intersection(&r);
assert!(pr.is_some()); let pr = pr.unwrap();
assert!(pr.origin == Point2D::new(0, 0));
assert!(pr.size == Size2D::new(3, 3));
let qr = q.intersection(&r);
assert!(qr.is_none());
}
#[test] fn test_intersection_overflow() { // test some scenarios where the intersection can overflow but // the min_x() and max_x() don't. Gecko currently fails these cases let p = Rect::new(Point2D::new(-2147483648, -2147483648), Size2D::new(0, 0)); let q = Rect::new(
Point2D::new(2136893440, 2136893440),
Size2D::new(279552, 279552),
); let r = Rect::new(Point2D::new(-2147483648, -2147483648), Size2D::new(1, 1));
assert!(p.is_empty()); let pq = p.intersection(&q);
assert!(pq.is_none());
let qr = q.intersection(&r);
assert!(qr.is_none());
}
#[test] fn test_contains() { let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
// The `contains` method is inclusive of the top/left edges, but not the // bottom/right edges.
assert!(r.contains(Point2D::new(-20, 15)));
assert!(!r.contains(Point2D::new(80, 15)));
assert!(!r.contains(Point2D::new(80, 215)));
assert!(!r.contains(Point2D::new(-20, 215)));
// Points beyond the top-left corner.
assert!(!r.contains(Point2D::new(-25, 15)));
assert!(!r.contains(Point2D::new(-15, 10)));
// Points beyond the top-right corner.
assert!(!r.contains(Point2D::new(85, 20)));
assert!(!r.contains(Point2D::new(75, 10)));
// Points beyond the bottom-right corner.
assert!(!r.contains(Point2D::new(85, 210)));
assert!(!r.contains(Point2D::new(75, 220)));
// Points beyond the bottom-left corner.
assert!(!r.contains(Point2D::new(-25, 210)));
assert!(!r.contains(Point2D::new(-15, 220)));
let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
assert!(r.contains_rect(&r));
assert!(!r.contains_rect(&r.translate(vec2(0.1, 0.0))));
assert!(!r.contains_rect(&r.translate(vec2(-0.1, 0.0))));
assert!(!r.contains_rect(&r.translate(vec2(0.0, 0.1))));
assert!(!r.contains_rect(&r.translate(vec2(0.0, -0.1)))); // Empty rectangles are always considered as contained in other rectangles, // even if their origin is not. let p = Point2D::new(1.0, 1.0);
assert!(!r.contains(p));
assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
}
#[test] fn test_scale() { let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32)); let pp = p.scale(10, 15);
#[test] fn test_round() { letmut x = -2.0; letmut y = -2.0; letmut w = -2.0; letmut h = -2.0; while x < 2.0 { while y < 2.0 { while w < 2.0 { while h < 2.0 { let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h));
#[test] fn test_nan() { let r1: Rect<f32> = rect(-2.0, 5.0, 4.0, std::f32::NAN); let r2: Rect<f32> = rect(std::f32::NAN, -1.0, 3.0, 10.0);
assert_eq!(r1.intersection(&r2), None);
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.18Bemerkung:
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.