Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

383
vendor/euclid/src/angle.rs vendored Normal file
View File

@@ -0,0 +1,383 @@
// 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.
use crate::approxeq::ApproxEq;
use crate::trig::Trig;
use core::cmp::{Eq, PartialEq};
use core::hash::Hash;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
use num_traits::real::Real;
use num_traits::{Float, FloatConst, NumCast, One, Zero};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An angle in radians
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Angle<T> {
pub radians: T,
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable> Zeroable for Angle<T> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod> Pod for Angle<T> {}
#[cfg(feature = "arbitrary")]
impl<'a, T> arbitrary::Arbitrary<'a> for Angle<T>
where
T: arbitrary::Arbitrary<'a>,
{
// This implementation could be derived, but the derive would require an `extern crate std`.
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Angle {
radians: arbitrary::Arbitrary::arbitrary(u)?,
})
}
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<T as arbitrary::Arbitrary>::size_hint(depth)
}
}
impl<T> Angle<T> {
#[inline]
pub fn radians(radians: T) -> Self {
Angle { radians }
}
#[inline]
pub fn get(self) -> T {
self.radians
}
}
impl<T> Angle<T>
where
T: Trig,
{
#[inline]
pub fn degrees(deg: T) -> Self {
Angle {
radians: T::degrees_to_radians(deg),
}
}
#[inline]
pub fn to_degrees(self) -> T {
T::radians_to_degrees(self.radians)
}
}
impl<T> Angle<T>
where
T: Rem<Output = T> + Sub<Output = T> + Add<Output = T> + Zero + FloatConst + PartialOrd + Copy,
{
/// Returns this angle in the [0..2*PI[ range.
pub fn positive(&self) -> Self {
let two_pi = T::PI() + T::PI();
let mut a = self.radians % two_pi;
if a < T::zero() {
a = a + two_pi;
}
Angle::radians(a)
}
/// Returns this angle in the ]-PI..PI] range.
pub fn signed(&self) -> Self {
Angle::pi() - (Angle::pi() - *self).positive()
}
}
impl<T> Angle<T>
where
T: Rem<Output = T>
+ Mul<Output = T>
+ Sub<Output = T>
+ Add<Output = T>
+ One
+ FloatConst
+ Copy,
{
/// Returns the shortest signed angle between two angles.
///
/// Takes wrapping and signs into account.
pub fn angle_to(&self, to: Self) -> Self {
let two = T::one() + T::one();
let max = T::PI() * two;
let d = (to.radians - self.radians) % max;
Angle::radians(two * d % max - d)
}
/// Linear interpolation between two angles, using the shortest path.
pub fn lerp(&self, other: Self, t: T) -> Self {
*self + self.angle_to(other) * t
}
}
impl<T> Angle<T>
where
T: Float,
{
/// Returns `true` if the angle is a finite number.
#[inline]
pub fn is_finite(self) -> bool {
self.radians.is_finite()
}
}
impl<T> Angle<T>
where
T: Real,
{
/// Returns `(sin(self), cos(self))`.
pub fn sin_cos(self) -> (T, T) {
self.radians.sin_cos()
}
}
impl<T> Angle<T>
where
T: Zero,
{
pub fn zero() -> Self {
Angle::radians(T::zero())
}
}
impl<T> Angle<T>
where
T: FloatConst + Add<Output = T>,
{
pub fn pi() -> Self {
Angle::radians(T::PI())
}
pub fn two_pi() -> Self {
Angle::radians(T::PI() + T::PI())
}
pub fn frac_pi_2() -> Self {
Angle::radians(T::FRAC_PI_2())
}
pub fn frac_pi_3() -> Self {
Angle::radians(T::FRAC_PI_3())
}
pub fn frac_pi_4() -> Self {
Angle::radians(T::FRAC_PI_4())
}
}
impl<T> Angle<T>
where
T: NumCast + Copy,
{
/// Cast from one numeric representation to another.
#[inline]
pub fn cast<NewT: NumCast>(&self) -> Angle<NewT> {
self.try_cast().unwrap()
}
/// Fallible cast from one numeric representation to another.
pub fn try_cast<NewT: NumCast>(&self) -> Option<Angle<NewT>> {
NumCast::from(self.radians).map(|radians| Angle { radians })
}
// Convenience functions for common casts.
/// Cast angle to `f32`.
#[inline]
pub fn to_f32(&self) -> Angle<f32> {
self.cast()
}
/// Cast angle `f64`.
#[inline]
pub fn to_f64(&self) -> Angle<f64> {
self.cast()
}
}
impl<T: Add<T, Output = T>> Add for Angle<T> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self::radians(self.radians + other.radians)
}
}
impl<T: Copy + Add<T, Output = T>> Add<&Self> for Angle<T> {
type Output = Self;
fn add(self, other: &Self) -> Self {
Self::radians(self.radians + other.radians)
}
}
impl<T: Add + Zero> Sum for Angle<T> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
impl<'a, T: 'a + Add + Copy + Zero> Sum<&'a Self> for Angle<T> {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
impl<T: AddAssign<T>> AddAssign for Angle<T> {
fn add_assign(&mut self, other: Angle<T>) {
self.radians += other.radians;
}
}
impl<T: Sub<T, Output = T>> Sub<Angle<T>> for Angle<T> {
type Output = Angle<T>;
fn sub(self, other: Angle<T>) -> <Self as Sub>::Output {
Angle::radians(self.radians - other.radians)
}
}
impl<T: SubAssign<T>> SubAssign for Angle<T> {
fn sub_assign(&mut self, other: Angle<T>) {
self.radians -= other.radians;
}
}
impl<T: Div<T, Output = T>> Div<Angle<T>> for Angle<T> {
type Output = T;
#[inline]
fn div(self, other: Angle<T>) -> T {
self.radians / other.radians
}
}
impl<T: Div<T, Output = T>> Div<T> for Angle<T> {
type Output = Angle<T>;
#[inline]
fn div(self, factor: T) -> Angle<T> {
Angle::radians(self.radians / factor)
}
}
impl<T: DivAssign<T>> DivAssign<T> for Angle<T> {
fn div_assign(&mut self, factor: T) {
self.radians /= factor;
}
}
impl<T: Mul<T, Output = T>> Mul<T> for Angle<T> {
type Output = Angle<T>;
#[inline]
fn mul(self, factor: T) -> Angle<T> {
Angle::radians(self.radians * factor)
}
}
impl<T: MulAssign<T>> MulAssign<T> for Angle<T> {
fn mul_assign(&mut self, factor: T) {
self.radians *= factor;
}
}
impl<T: Neg<Output = T>> Neg for Angle<T> {
type Output = Self;
fn neg(self) -> Self {
Angle::radians(-self.radians)
}
}
impl<T: ApproxEq<T>> ApproxEq<T> for Angle<T> {
#[inline]
fn approx_epsilon() -> T {
T::approx_epsilon()
}
#[inline]
fn approx_eq_eps(&self, other: &Angle<T>, approx_epsilon: &T) -> bool {
self.radians.approx_eq_eps(&other.radians, approx_epsilon)
}
}
#[test]
fn wrap_angles() {
use core::f32::consts::{FRAC_PI_2, PI};
assert!(Angle::radians(0.0).positive().approx_eq(&Angle::zero()));
assert!(Angle::radians(FRAC_PI_2)
.positive()
.approx_eq(&Angle::frac_pi_2()));
assert!(Angle::radians(-FRAC_PI_2)
.positive()
.approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
assert!(Angle::radians(3.0 * FRAC_PI_2)
.positive()
.approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
assert!(Angle::radians(5.0 * FRAC_PI_2)
.positive()
.approx_eq(&Angle::frac_pi_2()));
assert!(Angle::radians(2.0 * PI)
.positive()
.approx_eq(&Angle::zero()));
assert!(Angle::radians(-2.0 * PI)
.positive()
.approx_eq(&Angle::zero()));
assert!(Angle::radians(PI).positive().approx_eq(&Angle::pi()));
assert!(Angle::radians(-PI).positive().approx_eq(&Angle::pi()));
assert!(Angle::radians(FRAC_PI_2)
.signed()
.approx_eq(&Angle::frac_pi_2()));
assert!(Angle::radians(3.0 * FRAC_PI_2)
.signed()
.approx_eq(&-Angle::frac_pi_2()));
assert!(Angle::radians(5.0 * FRAC_PI_2)
.signed()
.approx_eq(&Angle::frac_pi_2()));
assert!(Angle::radians(2.0 * PI).signed().approx_eq(&Angle::zero()));
assert!(Angle::radians(-2.0 * PI).signed().approx_eq(&Angle::zero()));
assert!(Angle::radians(-PI).signed().approx_eq(&Angle::pi()));
assert!(Angle::radians(PI).signed().approx_eq(&Angle::pi()));
}
#[test]
fn lerp() {
type A = Angle<f32>;
let a = A::radians(1.0);
let b = A::radians(2.0);
assert!(a.lerp(b, 0.25).approx_eq(&Angle::radians(1.25)));
assert!(a.lerp(b, 0.5).approx_eq(&Angle::radians(1.5)));
assert!(a.lerp(b, 0.75).approx_eq(&Angle::radians(1.75)));
assert!(a
.lerp(b + A::two_pi(), 0.75)
.approx_eq(&Angle::radians(1.75)));
assert!(a
.lerp(b - A::two_pi(), 0.75)
.approx_eq(&Angle::radians(1.75)));
assert!(a
.lerp(b + A::two_pi() * 5.0, 0.75)
.approx_eq(&Angle::radians(1.75)));
}
#[test]
fn sum() {
type A = Angle<f32>;
let angles = [A::radians(1.0), A::radians(2.0), A::radians(3.0)];
let sum = A::radians(6.0);
assert_eq!(angles.iter().sum::<A>(), sum);
}

42
vendor/euclid/src/approxeq.rs vendored Normal file
View File

@@ -0,0 +1,42 @@
// 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.
/// Trait for testing approximate equality
pub trait ApproxEq<Eps> {
/// Default epsilon value
fn approx_epsilon() -> Eps;
/// Returns `true` if this object is approximately equal to the other one, using
/// a provided epsilon value.
fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool;
/// Returns `true` if this object is approximately equal to the other one, using
/// the [`approx_epsilon`](ApproxEq::approx_epsilon) epsilon value.
fn approx_eq(&self, other: &Self) -> bool {
self.approx_eq_eps(other, &Self::approx_epsilon())
}
}
macro_rules! approx_eq {
($ty:ty, $eps:expr) => {
impl ApproxEq<$ty> for $ty {
#[inline]
fn approx_epsilon() -> $ty {
$eps
}
#[inline]
fn approx_eq_eps(&self, other: &$ty, approx_epsilon: &$ty) -> bool {
num_traits::Float::abs(*self - *other) < *approx_epsilon
}
}
};
}
approx_eq!(f32, 1.0e-6);
approx_eq!(f64, 1.0e-6);

44
vendor/euclid/src/approxord.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
// 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.
//! Utilities for testing approximate ordering - especially true for
//! floating point types, where NaN's cannot be ordered.
pub fn min<T: PartialOrd>(x: T, y: T) -> T {
if x <= y {
x
} else {
y
}
}
pub fn max<T: PartialOrd>(x: T, y: T) -> T {
if x >= y {
x
} else {
y
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_min() {
assert!(min(0u32, 1u32) == 0u32);
assert!(min(-1.0f32, 0.0f32) == -1.0f32);
}
#[test]
fn test_max() {
assert!(max(0u32, 1u32) == 1u32);
assert!(max(-1.0f32, 0.0f32) == 0.0f32);
}
}

937
vendor/euclid/src/box2d.rs vendored Normal file
View File

@@ -0,0 +1,937 @@
// 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.
use super::UnknownUnit;
use crate::approxord::{max, min};
use crate::num::*;
use crate::point::{point2, Point2D};
use crate::rect::Rect;
use crate::scale::Scale;
use crate::side_offsets::SideOffsets2D;
use crate::size::Size2D;
use crate::vector::{vec2, Vector2D};
#[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 axis aligned rectangle represented by its minimum and maximum coordinates.
///
/// # Representation
///
/// This struct is similar to [`Rect`], but stores rectangle as two endpoints
/// instead of origin point and size. Such representation has several advantages over
/// [`Rect`] representation:
/// - Several operations are more efficient with `Box2D`, including [`intersection`],
/// [`union`], and point-in-rect.
/// - The representation is less susceptible to overflow. With [`Rect`], computation
/// of second point can overflow for a large range of values of origin and size.
/// However, with `Box2D`, computation of [`size`] cannot overflow if the coordinates
/// are signed and the resulting size is unsigned.
///
/// A known disadvantage of `Box2D` is that translating the rectangle requires translating
/// both points, whereas translating [`Rect`] only requires translating one point.
///
/// # Empty box
///
/// A box is considered empty (see [`is_empty`]) if any of the following is true:
/// - it's area is empty,
/// - it's area is negative (`min.x > max.x` or `min.y > max.y`),
/// - it contains NaNs.
///
/// [`intersection`]: Self::intersection
/// [`is_empty`]: Self::is_empty
/// [`union`]: Self::union
/// [`size`]: Self::size
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
)]
pub struct Box2D<T, U> {
pub min: Point2D<T, U>,
pub max: Point2D<T, U>,
}
impl<T: Hash, U> Hash for Box2D<T, U> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.min.hash(h);
self.max.hash(h);
}
}
impl<T: Copy, U> Copy for Box2D<T, U> {}
impl<T: Clone, U> Clone for Box2D<T, U> {
fn clone(&self) -> Self {
Self::new(self.min.clone(), self.max.clone())
}
}
impl<T: PartialEq, U> PartialEq for Box2D<T, U> {
fn eq(&self, other: &Self) -> bool {
self.min.eq(&other.min) && self.max.eq(&other.max)
}
}
impl<T: Eq, U> Eq for Box2D<T, U> {}
impl<T: fmt::Debug, U> fmt::Debug for Box2D<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Box2D")
.field(&self.min)
.field(&self.max)
.finish()
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, U> arbitrary::Arbitrary<'a> for Box2D<T, U>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Box2D::new(
arbitrary::Arbitrary::arbitrary(u)?,
arbitrary::Arbitrary::arbitrary(u)?,
))
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, U> Zeroable for Box2D<T, U> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, U: 'static> Pod for Box2D<T, U> {}
impl<T, U> Box2D<T, U> {
/// Constructor.
#[inline]
pub const fn new(min: Point2D<T, U>, max: Point2D<T, U>) -> Self {
Box2D { min, max }
}
/// Constructor.
#[inline]
pub fn from_origin_and_size(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self
where
T: Copy + Add<T, Output = T>,
{
Box2D {
min: origin,
max: point2(origin.x + size.width, origin.y + size.height),
}
}
/// Creates a `Box2D` of the given size, at offset zero.
#[inline]
pub fn from_size(size: Size2D<T, U>) -> Self
where
T: Zero,
{
Box2D {
min: Point2D::zero(),
max: point2(size.width, size.height),
}
}
}
impl<T, U> Box2D<T, U>
where
T: PartialOrd,
{
/// Returns `true` if the box has a negative area.
///
/// The common interpretation for a negative box is to consider it empty. It can be obtained
/// by calculating the intersection of two boxes that do not intersect.
#[inline]
pub fn is_negative(&self) -> bool {
self.max.x < self.min.x || self.max.y < self.min.y
}
/// Returns `true` if the size is zero, negative or NaN.
#[inline]
pub fn is_empty(&self) -> bool {
!(self.max.x > self.min.x && self.max.y > self.min.y)
}
/// Returns `true` if the two boxes intersect.
#[inline]
pub fn intersects(&self, other: &Self) -> bool {
// Use bitwise and instead of && to avoid emitting branches.
(self.min.x < other.max.x)
& (self.max.x > other.min.x)
& (self.min.y < other.max.y)
& (self.max.y > other.min.y)
}
/// Returns `true` if this box2d contains the point `p`. A point is considered
/// in the box2d if it lies on the left or top edges, but outside if it lies
/// on the right or bottom edges.
#[inline]
pub fn contains(&self, p: Point2D<T, U>) -> bool {
// Use bitwise and instead of && to avoid emitting branches.
(self.min.x <= p.x) & (p.x < self.max.x) & (self.min.y <= p.y) & (p.y < self.max.y)
}
/// Returns `true` if this box contains the point `p`. A point is considered
/// in the box2d if it lies on any edge of the box2d.
#[inline]
pub fn contains_inclusive(&self, p: Point2D<T, U>) -> bool {
// Use bitwise and instead of && to avoid emitting branches.
(self.min.x <= p.x) & (p.x <= self.max.x) & (self.min.y <= p.y) & (p.y <= self.max.y)
}
/// Returns `true` if this box contains the interior of the other box. Always
/// returns `true` if other is empty, and always returns `false` if other is
/// nonempty but this box is empty.
#[inline]
pub fn contains_box(&self, other: &Self) -> bool {
other.is_empty()
|| ((self.min.x <= other.min.x)
& (other.max.x <= self.max.x)
& (self.min.y <= other.min.y)
& (other.max.y <= self.max.y))
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + PartialOrd,
{
#[inline]
pub fn to_non_empty(&self) -> Option<Self> {
if self.is_empty() {
return None;
}
Some(*self)
}
/// Computes the intersection of two boxes, returning `None` if the boxes do not intersect.
#[inline]
pub fn intersection(&self, other: &Self) -> Option<Self> {
let b = self.intersection_unchecked(other);
if b.is_empty() {
return None;
}
Some(b)
}
/// Computes the intersection of two boxes without check whether they do intersect.
///
/// The result is a negative box if the boxes do not intersect.
/// This can be useful for computing the intersection of more than two boxes, as
/// it is possible to chain multiple `intersection_unchecked` calls and check for
/// empty/negative result at the end.
#[inline]
pub fn intersection_unchecked(&self, other: &Self) -> Self {
Box2D {
min: point2(max(self.min.x, other.min.x), max(self.min.y, other.min.y)),
max: point2(min(self.max.x, other.max.x), min(self.max.y, other.max.y)),
}
}
/// Computes the union of two boxes.
///
/// If either of the boxes is empty, the other one is returned.
#[inline]
pub fn union(&self, other: &Self) -> Self {
if other.is_empty() {
return *self;
}
if self.is_empty() {
return *other;
}
Box2D {
min: point2(min(self.min.x, other.min.x), min(self.min.y, other.min.y)),
max: point2(max(self.max.x, other.max.x), max(self.max.y, other.max.y)),
}
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + Add<T, Output = T>,
{
/// Returns the same box, translated by a vector.
#[inline]
pub fn translate(&self, by: Vector2D<T, U>) -> Self {
Box2D {
min: self.min + by,
max: self.max + by,
}
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + Sub<T, Output = T>,
{
#[inline]
pub fn size(&self) -> Size2D<T, U> {
(self.max - self.min).to_size()
}
/// Change the size of the box by adjusting the max endpoint
/// without modifying the min endpoint.
#[inline]
pub fn set_size(&mut self, size: Size2D<T, U>) {
let diff = (self.size() - size).to_vector();
self.max -= diff;
}
#[inline]
pub fn width(&self) -> T {
self.max.x - self.min.x
}
#[inline]
pub fn height(&self) -> T {
self.max.y - self.min.y
}
#[inline]
pub fn to_rect(&self) -> Rect<T, U> {
Rect {
origin: self.min,
size: self.size(),
}
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
{
/// Inflates the box by the specified sizes on each side respectively.
#[inline]
#[must_use]
pub fn inflate(&self, width: T, height: T) -> Self {
Box2D {
min: point2(self.min.x - width, self.min.y - height),
max: point2(self.max.x + width, self.max.y + height),
}
}
/// Calculate the size and position of an inner box.
///
/// Subtracts the side offsets from all sides. The horizontal, vertical
/// and applicate offsets must not be larger than the original side length.
pub fn inner_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
Box2D {
min: self.min + vec2(offsets.left, offsets.top),
max: self.max - vec2(offsets.right, offsets.bottom),
}
}
/// Calculate the b and position of an outer box.
///
/// Add the offsets to all sides. The expanded box is returned.
pub fn outer_box(&self, offsets: SideOffsets2D<T, U>) -> Self {
Box2D {
min: self.min - vec2(offsets.left, offsets.top),
max: self.max + vec2(offsets.right, offsets.bottom),
}
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + Zero + PartialOrd,
{
/// Returns the smallest box containing all of the provided points.
pub fn from_points<I>(points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Point2D<T, U>>,
{
let mut points = points.into_iter();
let (mut min_x, mut min_y) = match points.next() {
Some(first) => first.borrow().to_tuple(),
None => return Box2D::zero(),
};
let (mut max_x, mut max_y) = (min_x, min_y);
for point in points {
let p = point.borrow();
if p.x < min_x {
min_x = p.x
}
if p.x > max_x {
max_x = p.x
}
if p.y < min_y {
min_y = p.y
}
if p.y > max_y {
max_y = p.y
}
}
Box2D {
min: point2(min_x, min_y),
max: point2(max_x, max_y),
}
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
/// Linearly interpolate between this box and another box.
#[inline]
pub fn lerp(&self, other: Self, t: T) -> Self {
Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + One + Add<Output = T> + Div<Output = T>,
{
pub fn center(&self) -> Point2D<T, U> {
let two = T::one() + T::one();
(self.min + self.max.to_vector()) / two
}
}
impl<T, U> Box2D<T, U>
where
T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
{
#[inline]
pub fn area(&self) -> T {
let size = self.size();
size.width * size.height
}
}
impl<T, U> Box2D<T, U>
where
T: Zero,
{
/// Constructor, setting all sides to zero.
pub fn zero() -> Self {
Box2D::new(Point2D::zero(), Point2D::zero())
}
}
impl<T: Copy + Mul, U> Mul<T> for Box2D<T, U> {
type Output = Box2D<T::Output, U>;
#[inline]
fn mul(self, scale: T) -> Self::Output {
Box2D::new(self.min * scale, self.max * scale)
}
}
impl<T: Copy + MulAssign, U> MulAssign<T> for Box2D<T, U> {
#[inline]
fn mul_assign(&mut self, scale: T) {
*self *= Scale::new(scale);
}
}
impl<T: Copy + Div, U> Div<T> for Box2D<T, U> {
type Output = Box2D<T::Output, U>;
#[inline]
fn div(self, scale: T) -> Self::Output {
Box2D::new(self.min / scale, self.max / scale)
}
}
impl<T: Copy + DivAssign, U> DivAssign<T> for Box2D<T, U> {
#[inline]
fn div_assign(&mut self, scale: T) {
*self /= Scale::new(scale);
}
}
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box2D<T, U1> {
type Output = Box2D<T::Output, U2>;
#[inline]
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
Box2D::new(self.min * scale, self.max * scale)
}
}
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box2D<T, U> {
#[inline]
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
self.min *= scale;
self.max *= scale;
}
}
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box2D<T, U2> {
type Output = Box2D<T::Output, U1>;
#[inline]
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
Box2D::new(self.min / scale, self.max / scale)
}
}
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box2D<T, U> {
#[inline]
fn div_assign(&mut self, scale: Scale<T, U, U>) {
self.min /= scale;
self.max /= scale;
}
}
impl<T, U> Box2D<T, U>
where
T: Copy,
{
#[inline]
pub fn x_range(&self) -> Range<T> {
self.min.x..self.max.x
}
#[inline]
pub fn y_range(&self) -> Range<T> {
self.min.y..self.max.y
}
/// Drop the units, preserving only the numeric value.
#[inline]
pub fn to_untyped(&self) -> Box2D<T, UnknownUnit> {
Box2D::new(self.min.to_untyped(), self.max.to_untyped())
}
/// Tag a unitless value with units.
#[inline]
pub fn from_untyped(c: &Box2D<T, UnknownUnit>) -> Box2D<T, U> {
Box2D::new(Point2D::from_untyped(c.min), Point2D::from_untyped(c.max))
}
/// Cast the unit
#[inline]
pub fn cast_unit<V>(&self) -> Box2D<T, V> {
Box2D::new(self.min.cast_unit(), self.max.cast_unit())
}
#[inline]
pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
where
T: Mul<S, Output = T>,
{
Box2D {
min: point2(self.min.x * x, self.min.y * y),
max: point2(self.max.x * x, self.max.y * y),
}
}
}
impl<T: NumCast + Copy, U> Box2D<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.
///
/// [`round`]: Self::round
/// [`round_in`]: Self::round_in
/// [`round_out`]: Self::round_out
#[inline]
pub fn cast<NewT: NumCast>(&self) -> Box2D<NewT, U> {
Box2D::new(self.min.cast(), self.max.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.
///
/// [`round`]: Self::round
/// [`round_in`]: Self::round_in
/// [`round_out`]: Self::round_out
pub fn try_cast<NewT: NumCast>(&self) -> Option<Box2D<NewT, U>> {
match (self.min.try_cast(), self.max.try_cast()) {
(Some(a), Some(b)) => Some(Box2D::new(a, b)),
_ => None,
}
}
// Convenience functions for common casts
/// Cast into an `f32` box.
#[inline]
pub fn to_f32(&self) -> Box2D<f32, U> {
self.cast()
}
/// Cast into an `f64` box.
#[inline]
pub fn to_f64(&self) -> Box2D<f64, U> {
self.cast()
}
/// Cast into an `usize` box, truncating decimals if any.
///
/// When casting from floating point boxes, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_usize(&self) -> Box2D<usize, U> {
self.cast()
}
/// Cast into an `u32` box, truncating decimals if any.
///
/// When casting from floating point boxes, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_u32(&self) -> Box2D<u32, U> {
self.cast()
}
/// Cast into an `i32` box, truncating decimals if any.
///
/// When casting from floating point boxes, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_i32(&self) -> Box2D<i32, U> {
self.cast()
}
/// Cast into an `i64` box, truncating decimals if any.
///
/// When casting from floating point boxes, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_i64(&self) -> Box2D<i64, U> {
self.cast()
}
}
impl<T: Float, U> Box2D<T, U> {
/// Returns `true` if all members are finite.
#[inline]
pub fn is_finite(self) -> bool {
self.min.is_finite() && self.max.is_finite()
}
}
impl<T, U> Box2D<T, U>
where
T: Round,
{
/// Return a box with edges rounded to integer coordinates, such that
/// the returned box has the same set of pixel centers as the original
/// one.
/// Values equal to 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).
#[must_use]
pub fn round(&self) -> Self {
Box2D::new(self.min.round(), self.max.round())
}
}
impl<T, U> Box2D<T, U>
where
T: Floor + Ceil,
{
/// Return a box with faces/edges rounded to integer coordinates, such that
/// the original box contains the resulting box.
#[must_use]
pub fn round_in(&self) -> Self {
let min = self.min.ceil();
let max = self.max.floor();
Box2D { min, max }
}
/// Return a box with faces/edges rounded to integer coordinates, such that
/// the original box is contained in the resulting box.
#[must_use]
pub fn round_out(&self) -> Self {
let min = self.min.floor();
let max = self.max.ceil();
Box2D { min, max }
}
}
impl<T, U> From<Size2D<T, U>> for Box2D<T, U>
where
T: Copy + Zero + PartialOrd,
{
fn from(b: Size2D<T, U>) -> Self {
Self::from_size(b)
}
}
impl<T: Default, U> Default for Box2D<T, U> {
fn default() -> Self {
Box2D {
min: Default::default(),
max: Default::default(),
}
}
}
#[cfg(test)]
mod tests {
use crate::default::Box2D;
use crate::side_offsets::SideOffsets2D;
use crate::{point2, size2, vec2, Point2D};
//use super::*;
#[test]
fn test_size() {
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
assert_eq!(b.size().width, 20.0);
assert_eq!(b.size().height, 20.0);
}
#[test]
fn test_width_height() {
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
assert!(b.width() == 20.0);
assert!(b.height() == 20.0);
}
#[test]
fn test_center() {
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
assert_eq!(b.center(), Point2D::zero());
}
#[test]
fn test_area() {
let b = Box2D::new(point2(-10.0, -10.0), point2(10.0, 10.0));
assert_eq!(b.area(), 400.0);
}
#[test]
fn test_from_points() {
let b = Box2D::from_points(&[point2(50.0, 160.0), point2(100.0, 25.0)]);
assert_eq!(b.min, point2(50.0, 25.0));
assert_eq!(b.max, point2(100.0, 160.0));
}
#[test]
fn test_round_in() {
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_in();
assert_eq!(b.min.x, -25.0);
assert_eq!(b.min.y, -40.0);
assert_eq!(b.max.x, 60.0);
assert_eq!(b.max.y, 36.0);
}
#[test]
fn test_round_out() {
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round_out();
assert_eq!(b.min.x, -26.0);
assert_eq!(b.min.y, -41.0);
assert_eq!(b.max.x, 61.0);
assert_eq!(b.max.y, 37.0);
}
#[test]
fn test_round() {
let b = Box2D::from_points(&[point2(-25.5, -40.4), point2(60.3, 36.5)]).round();
assert_eq!(b.min.x, -25.0);
assert_eq!(b.min.y, -40.0);
assert_eq!(b.max.x, 60.0);
assert_eq!(b.max.y, 37.0);
}
#[test]
fn test_from_size() {
let b = Box2D::from_size(size2(30.0, 40.0));
assert!(b.min == Point2D::zero());
assert!(b.size().width == 30.0);
assert!(b.size().height == 40.0);
}
#[test]
fn test_inner_box() {
let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
let b = b.inner_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
assert_eq!(b.max.x, 80.0);
assert_eq!(b.max.y, 155.0);
assert_eq!(b.min.x, 60.0);
assert_eq!(b.min.y, 35.0);
}
#[test]
fn test_outer_box() {
let b = Box2D::from_points(&[point2(50.0, 25.0), point2(100.0, 160.0)]);
let b = b.outer_box(SideOffsets2D::new(10.0, 20.0, 5.0, 10.0));
assert_eq!(b.max.x, 120.0);
assert_eq!(b.max.y, 165.0);
assert_eq!(b.min.x, 40.0);
assert_eq!(b.min.y, 15.0);
}
#[test]
fn test_translate() {
let size = size2(15.0, 15.0);
let mut center = (size / 2.0).to_vector().to_point();
let b = Box2D::from_size(size);
assert_eq!(b.center(), center);
let translation = vec2(10.0, 2.5);
let b = b.translate(translation);
center += translation;
assert_eq!(b.center(), center);
assert_eq!(b.max.x, 25.0);
assert_eq!(b.max.y, 17.5);
assert_eq!(b.min.x, 10.0);
assert_eq!(b.min.y, 2.5);
}
#[test]
fn test_union() {
let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(0.0, 20.0)]);
let b2 = Box2D::from_points(&[point2(0.0, 20.0), point2(20.0, -20.0)]);
let b = b1.union(&b2);
assert_eq!(b.max.x, 20.0);
assert_eq!(b.max.y, 20.0);
assert_eq!(b.min.x, -20.0);
assert_eq!(b.min.y, -20.0);
}
#[test]
fn test_intersects() {
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
assert!(b1.intersects(&b2));
}
#[test]
fn test_intersection_unchecked() {
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
let b = b1.intersection_unchecked(&b2);
assert_eq!(b.max.x, 10.0);
assert_eq!(b.max.y, 20.0);
assert_eq!(b.min.x, -10.0);
assert_eq!(b.min.y, -20.0);
}
#[test]
fn test_intersection() {
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(10.0, 20.0)]);
let b2 = Box2D::from_points(&[point2(-10.0, 20.0), point2(15.0, -20.0)]);
assert!(b1.intersection(&b2).is_some());
let b1 = Box2D::from_points(&[point2(-15.0, -20.0), point2(-10.0, 20.0)]);
let b2 = Box2D::from_points(&[point2(10.0, 20.0), point2(15.0, -20.0)]);
assert!(b1.intersection(&b2).is_none());
}
#[test]
fn test_scale() {
let b = Box2D::from_points(&[point2(-10.0, -10.0), point2(10.0, 10.0)]);
let b = b.scale(0.5, 0.5);
assert_eq!(b.max.x, 5.0);
assert_eq!(b.max.y, 5.0);
assert_eq!(b.min.x, -5.0);
assert_eq!(b.min.y, -5.0);
}
#[test]
fn test_lerp() {
let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(-10.0, -10.0)]);
let b2 = Box2D::from_points(&[point2(10.0, 10.0), point2(20.0, 20.0)]);
let b = b1.lerp(b2, 0.5);
assert_eq!(b.center(), Point2D::zero());
assert_eq!(b.size().width, 10.0);
assert_eq!(b.size().height, 10.0);
}
#[test]
fn test_contains() {
let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
assert!(b.contains(point2(-15.3, 10.5)));
}
#[test]
fn test_contains_box() {
let b1 = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
let b2 = Box2D::from_points(&[point2(-14.3, -16.5), point2(6.7, 17.6)]);
assert!(b1.contains_box(&b2));
}
#[test]
fn test_inflate() {
let b = Box2D::from_points(&[point2(-20.0, -20.0), point2(20.0, 20.0)]);
let b = b.inflate(10.0, 5.0);
assert_eq!(b.size().width, 60.0);
assert_eq!(b.size().height, 50.0);
assert_eq!(b.center(), Point2D::zero());
}
#[test]
fn test_is_empty() {
for i in 0..2 {
let mut coords_neg = [-20.0, -20.0];
let mut coords_pos = [20.0, 20.0];
coords_neg[i] = 0.0;
coords_pos[i] = 0.0;
let b = Box2D::from_points(&[Point2D::from(coords_neg), Point2D::from(coords_pos)]);
assert!(b.is_empty());
}
}
#[test]
#[rustfmt::skip]
fn test_nan_empty() {
use std::f32::NAN;
assert!(Box2D { min: point2(NAN, 2.0), max: point2(1.0, 3.0) }.is_empty());
assert!(Box2D { min: point2(0.0, NAN), max: point2(1.0, 2.0) }.is_empty());
assert!(Box2D { min: point2(1.0, -2.0), max: point2(NAN, 2.0) }.is_empty());
assert!(Box2D { min: point2(1.0, -2.0), max: point2(0.0, NAN) }.is_empty());
}
#[test]
fn test_from_origin_and_size() {
let b = Box2D::from_origin_and_size(point2(1.0, 2.0), size2(3.0, 4.0));
assert_eq!(b.min, point2(1.0, 2.0));
assert_eq!(b.size(), size2(3.0, 4.0));
}
#[test]
fn test_set_size() {
let mut b = Box2D {
min: point2(1.0, 2.0),
max: point2(3.0, 4.0),
};
b.set_size(size2(5.0, 6.0));
assert_eq!(b.min, point2(1.0, 2.0));
assert_eq!(b.size(), size2(5.0, 6.0));
}
}

984
vendor/euclid/src/box3d.rs vendored Normal file
View File

@@ -0,0 +1,984 @@
// 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.
use super::UnknownUnit;
use crate::approxord::{max, min};
use crate::num::*;
use crate::point::{point3, Point3D};
use crate::scale::Scale;
use crate::size::Size3D;
use crate::vector::Vector3D;
#[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};
/// An axis aligned 3D box represented by its minimum and maximum coordinates.
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
)]
pub struct Box3D<T, U> {
pub min: Point3D<T, U>,
pub max: Point3D<T, U>,
}
impl<T: Hash, U> Hash for Box3D<T, U> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.min.hash(h);
self.max.hash(h);
}
}
impl<T: Copy, U> Copy for Box3D<T, U> {}
impl<T: Clone, U> Clone for Box3D<T, U> {
fn clone(&self) -> Self {
Self::new(self.min.clone(), self.max.clone())
}
}
impl<T: PartialEq, U> PartialEq for Box3D<T, U> {
fn eq(&self, other: &Self) -> bool {
self.min.eq(&other.min) && self.max.eq(&other.max)
}
}
impl<T: Eq, U> Eq for Box3D<T, U> {}
impl<T: fmt::Debug, U> fmt::Debug for Box3D<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Box3D")
.field(&self.min)
.field(&self.max)
.finish()
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, U> arbitrary::Arbitrary<'a> for Box3D<T, U>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Box3D::new(
arbitrary::Arbitrary::arbitrary(u)?,
arbitrary::Arbitrary::arbitrary(u)?,
))
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, U> Zeroable for Box3D<T, U> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, U: 'static> Pod for Box3D<T, U> {}
impl<T, U> Box3D<T, U> {
/// Constructor.
#[inline]
pub const fn new(min: Point3D<T, U>, max: Point3D<T, U>) -> Self {
Box3D { min, max }
}
/// Constructor.
#[inline]
pub fn from_origin_and_size(origin: Point3D<T, U>, size: Size3D<T, U>) -> Self
where
T: Copy + Add<T, Output = T>,
{
Box3D {
min: origin,
max: point3(
origin.x + size.width,
origin.y + size.height,
origin.z + size.depth,
),
}
}
/// Creates a `Box3D` of the given size, at offset zero.
#[inline]
pub fn from_size(size: Size3D<T, U>) -> Self
where
T: Zero,
{
Box3D {
min: Point3D::zero(),
max: point3(size.width, size.height, size.depth),
}
}
}
impl<T, U> Box3D<T, U>
where
T: PartialOrd,
{
/// Returns `true` if the box has a negative volume.
///
/// The common interpretation for a negative box is to consider it empty. It can be obtained
/// by calculating the intersection of two boxes that do not intersect.
#[inline]
pub fn is_negative(&self) -> bool {
self.max.x < self.min.x || self.max.y < self.min.y || self.max.z < self.min.z
}
/// Returns `true` if the size is zero, negative or NaN.
#[inline]
pub fn is_empty(&self) -> bool {
!(self.max.x > self.min.x && self.max.y > self.min.y && self.max.z > self.min.z)
}
#[inline]
pub fn intersects(&self, other: &Self) -> bool {
self.min.x < other.max.x
&& self.max.x > other.min.x
&& self.min.y < other.max.y
&& self.max.y > other.min.y
&& self.min.z < other.max.z
&& self.max.z > other.min.z
}
/// Returns `true` if this box3d contains the point `p`. A point is considered
/// in the box3d if it lies on the front, left or top faces, but outside if it lies
/// on the back, right or bottom faces.
#[inline]
pub fn contains(&self, other: Point3D<T, U>) -> bool {
(self.min.x <= other.x)
& (other.x < self.max.x)
& (self.min.y <= other.y)
& (other.y < self.max.y)
& (self.min.z <= other.z)
& (other.z < self.max.z)
}
/// Returns `true` if this box3d contains the point `p`. A point is considered
/// in the box3d if it lies on any face of the box3d.
#[inline]
pub fn contains_inclusive(&self, other: Point3D<T, U>) -> bool {
(self.min.x <= other.x)
& (other.x <= self.max.x)
& (self.min.y <= other.y)
& (other.y <= self.max.y)
& (self.min.z <= other.z)
& (other.z <= self.max.z)
}
/// Returns `true` if this box3d contains the interior of the other box3d. Always
/// returns `true` if other is empty, and always returns `false` if other is
/// nonempty but this box3d is empty.
#[inline]
pub fn contains_box(&self, other: &Self) -> bool {
other.is_empty()
|| ((self.min.x <= other.min.x)
& (other.max.x <= self.max.x)
& (self.min.y <= other.min.y)
& (other.max.y <= self.max.y)
& (self.min.z <= other.min.z)
& (other.max.z <= self.max.z))
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + PartialOrd,
{
#[inline]
pub fn to_non_empty(&self) -> Option<Self> {
if self.is_empty() {
return None;
}
Some(*self)
}
#[inline]
pub fn intersection(&self, other: &Self) -> Option<Self> {
let b = self.intersection_unchecked(other);
if b.is_empty() {
return None;
}
Some(b)
}
pub fn intersection_unchecked(&self, other: &Self) -> Self {
let intersection_min = Point3D::new(
max(self.min.x, other.min.x),
max(self.min.y, other.min.y),
max(self.min.z, other.min.z),
);
let intersection_max = Point3D::new(
min(self.max.x, other.max.x),
min(self.max.y, other.max.y),
min(self.max.z, other.max.z),
);
Box3D::new(intersection_min, intersection_max)
}
/// Computes the union of two boxes.
///
/// If either of the boxes is empty, the other one is returned.
#[inline]
pub fn union(&self, other: &Self) -> Self {
if other.is_empty() {
return *self;
}
if self.is_empty() {
return *other;
}
Box3D::new(
Point3D::new(
min(self.min.x, other.min.x),
min(self.min.y, other.min.y),
min(self.min.z, other.min.z),
),
Point3D::new(
max(self.max.x, other.max.x),
max(self.max.y, other.max.y),
max(self.max.z, other.max.z),
),
)
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + Add<T, Output = T>,
{
/// Returns the same box3d, translated by a vector.
#[inline]
#[must_use]
pub fn translate(&self, by: Vector3D<T, U>) -> Self {
Box3D {
min: self.min + by,
max: self.max + by,
}
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + Sub<T, Output = T>,
{
#[inline]
pub fn size(&self) -> Size3D<T, U> {
Size3D::new(
self.max.x - self.min.x,
self.max.y - self.min.y,
self.max.z - self.min.z,
)
}
#[inline]
pub fn width(&self) -> T {
self.max.x - self.min.x
}
#[inline]
pub fn height(&self) -> T {
self.max.y - self.min.y
}
#[inline]
pub fn depth(&self) -> T {
self.max.z - self.min.z
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
{
/// Inflates the box by the specified sizes on each side respectively.
#[inline]
#[must_use]
pub fn inflate(&self, width: T, height: T, depth: T) -> Self {
Box3D::new(
Point3D::new(self.min.x - width, self.min.y - height, self.min.z - depth),
Point3D::new(self.max.x + width, self.max.y + height, self.max.z + depth),
)
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + Zero + PartialOrd,
{
/// Returns the smallest box containing all of the provided points.
pub fn from_points<I>(points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Point3D<T, U>>,
{
let mut points = points.into_iter();
let (mut min_x, mut min_y, mut min_z) = match points.next() {
Some(first) => first.borrow().to_tuple(),
None => return Box3D::zero(),
};
let (mut max_x, mut max_y, mut max_z) = (min_x, min_y, min_z);
for point in points {
let p = point.borrow();
if p.x < min_x {
min_x = p.x
}
if p.x > max_x {
max_x = p.x
}
if p.y < min_y {
min_y = p.y
}
if p.y > max_y {
max_y = p.y
}
if p.z < min_z {
min_z = p.z
}
if p.z > max_z {
max_z = p.z
}
}
Box3D {
min: point3(min_x, min_y, min_z),
max: point3(max_x, max_y, max_z),
}
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
/// Linearly interpolate between this box3d and another box3d.
#[inline]
pub fn lerp(&self, other: Self, t: T) -> Self {
Self::new(self.min.lerp(other.min, t), self.max.lerp(other.max, t))
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + One + Add<Output = T> + Div<Output = T>,
{
pub fn center(&self) -> Point3D<T, U> {
let two = T::one() + T::one();
(self.min + self.max.to_vector()) / two
}
}
impl<T, U> Box3D<T, U>
where
T: Copy + Mul<T, Output = T> + Sub<T, Output = T>,
{
#[inline]
pub fn volume(&self) -> T {
let size = self.size();
size.width * size.height * size.depth
}
#[inline]
pub fn xy_area(&self) -> T {
let size = self.size();
size.width * size.height
}
#[inline]
pub fn yz_area(&self) -> T {
let size = self.size();
size.depth * size.height
}
#[inline]
pub fn xz_area(&self) -> T {
let size = self.size();
size.depth * size.width
}
}
impl<T, U> Box3D<T, U>
where
T: Zero,
{
/// Constructor, setting all sides to zero.
pub fn zero() -> Self {
Box3D::new(Point3D::zero(), Point3D::zero())
}
}
impl<T: Copy + Mul, U> Mul<T> for Box3D<T, U> {
type Output = Box3D<T::Output, U>;
#[inline]
fn mul(self, scale: T) -> Self::Output {
Box3D::new(self.min * scale, self.max * scale)
}
}
impl<T: Copy + MulAssign, U> MulAssign<T> for Box3D<T, U> {
#[inline]
fn mul_assign(&mut self, scale: T) {
self.min *= scale;
self.max *= scale;
}
}
impl<T: Copy + Div, U> Div<T> for Box3D<T, U> {
type Output = Box3D<T::Output, U>;
#[inline]
fn div(self, scale: T) -> Self::Output {
Box3D::new(self.min / scale.clone(), self.max / scale)
}
}
impl<T: Copy + DivAssign, U> DivAssign<T> for Box3D<T, U> {
#[inline]
fn div_assign(&mut self, scale: T) {
self.min /= scale;
self.max /= scale;
}
}
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Box3D<T, U1> {
type Output = Box3D<T::Output, U2>;
#[inline]
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
Box3D::new(self.min * scale.clone(), self.max * scale)
}
}
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Box3D<T, U> {
#[inline]
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
self.min *= scale.clone();
self.max *= scale;
}
}
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Box3D<T, U2> {
type Output = Box3D<T::Output, U1>;
#[inline]
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
Box3D::new(self.min / scale.clone(), self.max / scale)
}
}
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Box3D<T, U> {
#[inline]
fn div_assign(&mut self, scale: Scale<T, U, U>) {
self.min /= scale.clone();
self.max /= scale;
}
}
impl<T, U> Box3D<T, U>
where
T: Copy,
{
#[inline]
pub fn x_range(&self) -> Range<T> {
self.min.x..self.max.x
}
#[inline]
pub fn y_range(&self) -> Range<T> {
self.min.y..self.max.y
}
#[inline]
pub fn z_range(&self) -> Range<T> {
self.min.z..self.max.z
}
/// Drop the units, preserving only the numeric value.
#[inline]
pub fn to_untyped(&self) -> Box3D<T, UnknownUnit> {
Box3D {
min: self.min.to_untyped(),
max: self.max.to_untyped(),
}
}
/// Tag a unitless value with units.
#[inline]
pub fn from_untyped(c: &Box3D<T, UnknownUnit>) -> Box3D<T, U> {
Box3D {
min: Point3D::from_untyped(c.min),
max: Point3D::from_untyped(c.max),
}
}
/// Cast the unit
#[inline]
pub fn cast_unit<V>(&self) -> Box3D<T, V> {
Box3D::new(self.min.cast_unit(), self.max.cast_unit())
}
#[inline]
pub fn scale<S: Copy>(&self, x: S, y: S, z: S) -> Self
where
T: Mul<S, Output = T>,
{
Box3D::new(
Point3D::new(self.min.x * x, self.min.y * y, self.min.z * z),
Point3D::new(self.max.x * x, self.max.y * y, self.max.z * z),
)
}
}
impl<T: NumCast + Copy, U> Box3D<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.
///
/// [`round`]: Self::round
/// [`round_in`]: Self::round_in
/// [`round_out`]: Self::round_out
#[inline]
pub fn cast<NewT: NumCast>(&self) -> Box3D<NewT, U> {
Box3D::new(self.min.cast(), self.max.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.
///
/// [`round`]: Self::round
/// [`round_in`]: Self::round_in
/// [`round_out`]: Self::round_out
pub fn try_cast<NewT: NumCast>(&self) -> Option<Box3D<NewT, U>> {
match (self.min.try_cast(), self.max.try_cast()) {
(Some(a), Some(b)) => Some(Box3D::new(a, b)),
_ => None,
}
}
// Convenience functions for common casts
/// Cast into an `f32` box3d.
#[inline]
pub fn to_f32(&self) -> Box3D<f32, U> {
self.cast()
}
/// Cast into an `f64` box3d.
#[inline]
pub fn to_f64(&self) -> Box3D<f64, U> {
self.cast()
}
/// Cast into an `usize` box3d, truncating decimals if any.
///
/// When casting from floating point cuboids, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_usize(&self) -> Box3D<usize, U> {
self.cast()
}
/// Cast into an `u32` box3d, truncating decimals if any.
///
/// When casting from floating point cuboids, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_u32(&self) -> Box3D<u32, U> {
self.cast()
}
/// Cast into an `i32` box3d, truncating decimals if any.
///
/// When casting from floating point cuboids, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_i32(&self) -> Box3D<i32, U> {
self.cast()
}
/// Cast into an `i64` box3d, truncating decimals if any.
///
/// When casting from floating point cuboids, it is worth considering whether
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
/// obtain the desired conversion behavior.
#[inline]
pub fn to_i64(&self) -> Box3D<i64, U> {
self.cast()
}
}
impl<T: Float, U> Box3D<T, U> {
/// Returns `true` if all members are finite.
#[inline]
pub fn is_finite(self) -> bool {
self.min.is_finite() && self.max.is_finite()
}
}
impl<T, U> Box3D<T, U>
where
T: Round,
{
/// Return a box3d with edges rounded to integer coordinates, such that
/// the returned box3d has the same set of pixel centers as the original
/// one.
/// Values equal to 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).
#[must_use]
pub fn round(&self) -> Self {
Box3D::new(self.min.round(), self.max.round())
}
}
impl<T, U> Box3D<T, U>
where
T: Floor + Ceil,
{
/// Return a box3d with faces/edges rounded to integer coordinates, such that
/// the original box3d contains the resulting box3d.
#[must_use]
pub fn round_in(&self) -> Self {
Box3D {
min: self.min.ceil(),
max: self.max.floor(),
}
}
/// Return a box3d with faces/edges rounded to integer coordinates, such that
/// the original box3d is contained in the resulting box3d.
#[must_use]
pub fn round_out(&self) -> Self {
Box3D {
min: self.min.floor(),
max: self.max.ceil(),
}
}
}
impl<T, U> From<Size3D<T, U>> for Box3D<T, U>
where
T: Copy + Zero + PartialOrd,
{
fn from(b: Size3D<T, U>) -> Self {
Self::from_size(b)
}
}
impl<T: Default, U> Default for Box3D<T, U> {
fn default() -> Self {
Box3D {
min: Default::default(),
max: Default::default(),
}
}
}
/// Shorthand for `Box3D::new(Point3D::new(x1, y1, z1), Point3D::new(x2, y2, z2))`.
pub fn box3d<T: Copy, U>(
min_x: T,
min_y: T,
min_z: T,
max_x: T,
max_y: T,
max_z: T,
) -> Box3D<T, U> {
Box3D::new(
Point3D::new(min_x, min_y, min_z),
Point3D::new(max_x, max_y, max_z),
)
}
#[cfg(test)]
mod tests {
use crate::default::{Box3D, Point3D};
use crate::{point3, size3, vec3};
#[test]
fn test_new() {
let b = Box3D::new(point3(-1.0, -1.0, -1.0), point3(1.0, 1.0, 1.0));
assert!(b.min.x == -1.0);
assert!(b.min.y == -1.0);
assert!(b.min.z == -1.0);
assert!(b.max.x == 1.0);
assert!(b.max.y == 1.0);
assert!(b.max.z == 1.0);
}
#[test]
fn test_size() {
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
assert!(b.size().width == 20.0);
assert!(b.size().height == 20.0);
assert!(b.size().depth == 20.0);
}
#[test]
fn test_width_height_depth() {
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
assert!(b.width() == 20.0);
assert!(b.height() == 20.0);
assert!(b.depth() == 20.0);
}
#[test]
fn test_center() {
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
assert!(b.center() == Point3D::zero());
}
#[test]
fn test_volume() {
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
assert!(b.volume() == 8000.0);
}
#[test]
fn test_area() {
let b = Box3D::new(point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0));
assert!(b.xy_area() == 400.0);
assert!(b.yz_area() == 400.0);
assert!(b.xz_area() == 400.0);
}
#[test]
fn test_from_points() {
let b = Box3D::from_points(&[point3(50.0, 160.0, 12.5), point3(100.0, 25.0, 200.0)]);
assert!(b.min == point3(50.0, 25.0, 12.5));
assert!(b.max == point3(100.0, 160.0, 200.0));
}
#[test]
fn test_min_max() {
let b = Box3D::from_points(&[point3(50.0, 25.0, 12.5), point3(100.0, 160.0, 200.0)]);
assert!(b.min.x == 50.0);
assert!(b.min.y == 25.0);
assert!(b.min.z == 12.5);
assert!(b.max.x == 100.0);
assert!(b.max.y == 160.0);
assert!(b.max.z == 200.0);
}
#[test]
fn test_round_in() {
let b =
Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round_in();
assert!(b.min.x == -25.0);
assert!(b.min.y == -40.0);
assert!(b.min.z == -70.0);
assert!(b.max.x == 60.0);
assert!(b.max.y == 36.0);
assert!(b.max.z == 89.0);
}
#[test]
fn test_round_out() {
let b = Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)])
.round_out();
assert!(b.min.x == -26.0);
assert!(b.min.y == -41.0);
assert!(b.min.z == -71.0);
assert!(b.max.x == 61.0);
assert!(b.max.y == 37.0);
assert!(b.max.z == 90.0);
}
#[test]
fn test_round() {
let b =
Box3D::from_points(&[point3(-25.5, -40.4, -70.9), point3(60.3, 36.5, 89.8)]).round();
assert!(b.min.x == -25.0);
assert!(b.min.y == -40.0);
assert!(b.min.z == -71.0);
assert!(b.max.x == 60.0);
assert!(b.max.y == 37.0);
assert!(b.max.z == 90.0);
}
#[test]
fn test_from_size() {
let b = Box3D::from_size(size3(30.0, 40.0, 50.0));
assert!(b.min == Point3D::zero());
assert!(b.size().width == 30.0);
assert!(b.size().height == 40.0);
assert!(b.size().depth == 50.0);
}
#[test]
fn test_translate() {
let size = size3(15.0, 15.0, 200.0);
let mut center = (size / 2.0).to_vector().to_point();
let b = Box3D::from_size(size);
assert!(b.center() == center);
let translation = vec3(10.0, 2.5, 9.5);
let b = b.translate(translation);
center += translation;
assert!(b.center() == center);
assert!(b.max.x == 25.0);
assert!(b.max.y == 17.5);
assert!(b.max.z == 209.5);
assert!(b.min.x == 10.0);
assert!(b.min.y == 2.5);
assert!(b.min.z == 9.5);
}
#[test]
fn test_union() {
let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(0.0, 20.0, 20.0)]);
let b2 = Box3D::from_points(&[point3(0.0, 20.0, 20.0), point3(20.0, -20.0, -20.0)]);
let b = b1.union(&b2);
assert!(b.max.x == 20.0);
assert!(b.max.y == 20.0);
assert!(b.max.z == 20.0);
assert!(b.min.x == -20.0);
assert!(b.min.y == -20.0);
assert!(b.min.z == -20.0);
assert!(b.volume() == (40.0 * 40.0 * 40.0));
}
#[test]
fn test_intersects() {
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
assert!(b1.intersects(&b2));
}
#[test]
fn test_intersection_unchecked() {
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
let b = b1.intersection_unchecked(&b2);
assert!(b.max.x == 10.0);
assert!(b.max.y == 20.0);
assert!(b.max.z == 20.0);
assert!(b.min.x == -10.0);
assert!(b.min.y == -20.0);
assert!(b.min.z == -20.0);
assert!(b.volume() == (20.0 * 40.0 * 40.0));
}
#[test]
fn test_intersection() {
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(10.0, 20.0, 20.0)]);
let b2 = Box3D::from_points(&[point3(-10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
assert!(b1.intersection(&b2).is_some());
let b1 = Box3D::from_points(&[point3(-15.0, -20.0, -20.0), point3(-10.0, 20.0, 20.0)]);
let b2 = Box3D::from_points(&[point3(10.0, 20.0, 20.0), point3(15.0, -20.0, -20.0)]);
assert!(b1.intersection(&b2).is_none());
}
#[test]
fn test_scale() {
let b = Box3D::from_points(&[point3(-10.0, -10.0, -10.0), point3(10.0, 10.0, 10.0)]);
let b = b.scale(0.5, 0.5, 0.5);
assert!(b.max.x == 5.0);
assert!(b.max.y == 5.0);
assert!(b.max.z == 5.0);
assert!(b.min.x == -5.0);
assert!(b.min.y == -5.0);
assert!(b.min.z == -5.0);
}
#[test]
fn test_zero() {
let b = Box3D::<f64>::zero();
assert!(b.max.x == 0.0);
assert!(b.max.y == 0.0);
assert!(b.max.z == 0.0);
assert!(b.min.x == 0.0);
assert!(b.min.y == 0.0);
assert!(b.min.z == 0.0);
}
#[test]
fn test_lerp() {
let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(-10.0, -10.0, -10.0)]);
let b2 = Box3D::from_points(&[point3(10.0, 10.0, 10.0), point3(20.0, 20.0, 20.0)]);
let b = b1.lerp(b2, 0.5);
assert!(b.center() == Point3D::zero());
assert!(b.size().width == 10.0);
assert!(b.size().height == 10.0);
assert!(b.size().depth == 10.0);
}
#[test]
fn test_contains() {
let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
assert!(b.contains(point3(-15.3, 10.5, 18.4)));
}
#[test]
fn test_contains_box() {
let b1 = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
let b2 = Box3D::from_points(&[point3(-14.3, -16.5, -19.3), point3(6.7, 17.6, 2.5)]);
assert!(b1.contains_box(&b2));
}
#[test]
fn test_inflate() {
let b = Box3D::from_points(&[point3(-20.0, -20.0, -20.0), point3(20.0, 20.0, 20.0)]);
let b = b.inflate(10.0, 5.0, 2.0);
assert!(b.size().width == 60.0);
assert!(b.size().height == 50.0);
assert!(b.size().depth == 44.0);
assert!(b.center() == Point3D::zero());
}
#[test]
fn test_is_empty() {
for i in 0..3 {
let mut coords_neg = [-20.0, -20.0, -20.0];
let mut coords_pos = [20.0, 20.0, 20.0];
coords_neg[i] = 0.0;
coords_pos[i] = 0.0;
let b = Box3D::from_points(&[Point3D::from(coords_neg), Point3D::from(coords_pos)]);
assert!(b.is_empty());
}
}
#[test]
#[rustfmt::skip]
fn test_nan_empty_or_negative() {
use std::f32::NAN;
assert!(Box3D { min: point3(NAN, 2.0, 1.0), max: point3(1.0, 3.0, 5.0) }.is_empty());
assert!(Box3D { min: point3(0.0, NAN, 1.0), max: point3(1.0, 2.0, 5.0) }.is_empty());
assert!(Box3D { min: point3(1.0, -2.0, NAN), max: point3(3.0, 2.0, 5.0) }.is_empty());
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(NAN, 2.0, 5.0) }.is_empty());
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, NAN, 5.0) }.is_empty());
assert!(Box3D { min: point3(1.0, -2.0, 1.0), max: point3(0.0, 1.0, NAN) }.is_empty());
}
}

240
vendor/euclid/src/homogen.rs vendored Normal file
View File

@@ -0,0 +1,240 @@
// Copyright 2018 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.
use crate::point::{Point2D, Point3D};
use crate::vector::{Vector2D, Vector3D};
use crate::num::{One, Zero};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
use core::cmp::{Eq, PartialEq};
use core::fmt;
use core::hash::Hash;
use core::marker::PhantomData;
use core::ops::Div;
#[cfg(feature = "serde")]
use serde;
/// Homogeneous vector in 3D space.
#[repr(C)]
pub struct HomogeneousVector<T, U> {
pub x: T,
pub y: T,
pub z: T,
pub w: T,
#[doc(hidden)]
pub _unit: PhantomData<U>,
}
impl<T: Copy, U> Copy for HomogeneousVector<T, U> {}
impl<T: Clone, U> Clone for HomogeneousVector<T, U> {
fn clone(&self) -> Self {
HomogeneousVector {
x: self.x.clone(),
y: self.y.clone(),
z: self.z.clone(),
w: self.w.clone(),
_unit: PhantomData,
}
}
}
#[cfg(feature = "serde")]
impl<'de, T, U> serde::Deserialize<'de> for HomogeneousVector<T, U>
where
T: serde::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let (x, y, z, w) = serde::Deserialize::deserialize(deserializer)?;
Ok(HomogeneousVector {
x,
y,
z,
w,
_unit: PhantomData,
})
}
}
#[cfg(feature = "serde")]
impl<T, U> serde::Serialize for HomogeneousVector<T, U>
where
T: serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
(&self.x, &self.y, &self.z, &self.w).serialize(serializer)
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, U> arbitrary::Arbitrary<'a> for HomogeneousVector<T, U>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let (x, y, z, w) = arbitrary::Arbitrary::arbitrary(u)?;
Ok(HomogeneousVector {
x,
y,
z,
w,
_unit: PhantomData,
})
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, U> Zeroable for HomogeneousVector<T, U> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, U: 'static> Pod for HomogeneousVector<T, U> {}
impl<T, U> Eq for HomogeneousVector<T, U> where T: Eq {}
impl<T, U> PartialEq for HomogeneousVector<T, U>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w
}
}
impl<T, U> Hash for HomogeneousVector<T, U>
where
T: Hash,
{
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
self.x.hash(h);
self.y.hash(h);
self.z.hash(h);
self.w.hash(h);
}
}
impl<T, U> HomogeneousVector<T, U> {
/// Constructor taking scalar values directly.
#[inline]
pub const fn new(x: T, y: T, z: T, w: T) -> Self {
HomogeneousVector {
x,
y,
z,
w,
_unit: PhantomData,
}
}
}
impl<T: Copy + Div<T, Output = T> + Zero + PartialOrd, U> HomogeneousVector<T, U> {
/// Convert into Cartesian 2D point.
///
/// Returns `None` if the point is on or behind the W=0 hemisphere.
#[inline]
pub fn to_point2d(self) -> Option<Point2D<T, U>> {
if self.w > T::zero() {
Some(Point2D::new(self.x / self.w, self.y / self.w))
} else {
None
}
}
/// Convert into Cartesian 3D point.
///
/// Returns `None` if the point is on or behind the W=0 hemisphere.
#[inline]
pub fn to_point3d(self) -> Option<Point3D<T, U>> {
if self.w > T::zero() {
Some(Point3D::new(
self.x / self.w,
self.y / self.w,
self.z / self.w,
))
} else {
None
}
}
}
impl<T: Zero, U> From<Vector2D<T, U>> for HomogeneousVector<T, U> {
#[inline]
fn from(v: Vector2D<T, U>) -> Self {
HomogeneousVector::new(v.x, v.y, T::zero(), T::zero())
}
}
impl<T: Zero, U> From<Vector3D<T, U>> for HomogeneousVector<T, U> {
#[inline]
fn from(v: Vector3D<T, U>) -> Self {
HomogeneousVector::new(v.x, v.y, v.z, T::zero())
}
}
impl<T: Zero + One, U> From<Point2D<T, U>> for HomogeneousVector<T, U> {
#[inline]
fn from(p: Point2D<T, U>) -> Self {
HomogeneousVector::new(p.x, p.y, T::zero(), T::one())
}
}
impl<T: One, U> From<Point3D<T, U>> for HomogeneousVector<T, U> {
#[inline]
fn from(p: Point3D<T, U>) -> Self {
HomogeneousVector::new(p.x, p.y, p.z, T::one())
}
}
impl<T: fmt::Debug, U> fmt::Debug for HomogeneousVector<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("")
.field(&self.x)
.field(&self.y)
.field(&self.z)
.field(&self.w)
.finish()
}
}
#[cfg(test)]
mod homogeneous {
use super::HomogeneousVector;
use crate::default::{Point2D, Point3D};
#[test]
fn roundtrip() {
assert_eq!(
Some(Point2D::new(1.0, 2.0)),
HomogeneousVector::from(Point2D::new(1.0, 2.0)).to_point2d()
);
assert_eq!(
Some(Point3D::new(1.0, -2.0, 0.1)),
HomogeneousVector::from(Point3D::new(1.0, -2.0, 0.1)).to_point3d()
);
}
#[test]
fn negative() {
assert_eq!(
None,
HomogeneousVector::<f32, ()>::new(1.0, 2.0, 3.0, 0.0).to_point2d()
);
assert_eq!(
None,
HomogeneousVector::<f32, ()>::new(1.0, -2.0, -3.0, -2.0).to_point3d()
);
}
}

617
vendor/euclid/src/length.rs vendored Normal file
View File

@@ -0,0 +1,617 @@
// Copyright 2014 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.
//! A one-dimensional length, tagged with its units.
use crate::approxeq::ApproxEq;
use crate::approxord::{max, min};
use crate::num::Zero;
use crate::scale::Scale;
use crate::num::One;
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::iter::Sum;
use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Neg, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, SubAssign};
use num_traits::{NumCast, Saturating};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// A one-dimensional distance, with value represented by `T` and unit of measurement `Unit`.
///
/// `T` can be any numeric type, for example a primitive type like `u64` or `f32`.
///
/// `Unit` is not used in the representation of a `Length` value. It is used only at compile time
/// to ensure that a `Length` stored with one unit is converted explicitly before being used in an
/// expression that requires a different unit. It may be a type without values, such as an empty
/// enum.
///
/// You can multiply a `Length` by a [`Scale`] to convert it from one unit to
/// another. See the [`Scale`] docs for an example.
#[repr(C)]
pub struct Length<T, Unit>(pub T, #[doc(hidden)] pub PhantomData<Unit>);
impl<T: Clone, U> Clone for Length<T, U> {
fn clone(&self) -> Self {
Length(self.0.clone(), PhantomData)
}
}
impl<T: Copy, U> Copy for Length<T, U> {}
#[cfg(feature = "serde")]
impl<'de, T, U> Deserialize<'de> for Length<T, U>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Length(Deserialize::deserialize(deserializer)?, PhantomData))
}
}
#[cfg(feature = "serde")]
impl<T, U> Serialize for Length<T, U>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, U> arbitrary::Arbitrary<'a> for Length<T, U>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Length(arbitrary::Arbitrary::arbitrary(u)?, PhantomData))
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, U> Zeroable for Length<T, U> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, U: 'static> Pod for Length<T, U> {}
impl<T, U> Length<T, U> {
/// Associate a value with a unit of measure.
#[inline]
pub const fn new(x: T) -> Self {
Length(x, PhantomData)
}
}
impl<T: Clone, U> Length<T, U> {
/// Unpack the underlying value from the wrapper.
pub fn get(self) -> T {
self.0
}
/// Cast the unit
#[inline]
pub fn cast_unit<V>(self) -> Length<T, V> {
Length::new(self.0)
}
/// Linearly interpolate between this length and another length.
///
/// # Example
///
/// ```rust
/// use euclid::default::Length;
///
/// let from = Length::new(0.0);
/// let to = Length::new(8.0);
///
/// assert_eq!(from.lerp(to, -1.0), Length::new(-8.0));
/// assert_eq!(from.lerp(to, 0.0), Length::new( 0.0));
/// assert_eq!(from.lerp(to, 0.5), Length::new( 4.0));
/// assert_eq!(from.lerp(to, 1.0), Length::new( 8.0));
/// assert_eq!(from.lerp(to, 2.0), Length::new(16.0));
/// ```
#[inline]
pub fn lerp(self, other: Self, t: T) -> Self
where
T: One + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
{
let one_t = T::one() - t.clone();
Length::new(one_t * self.0.clone() + t * other.0)
}
}
impl<T: PartialOrd, U> Length<T, U> {
/// Returns minimum between this length and another length.
#[inline]
pub fn min(self, other: Self) -> Self {
min(self, other)
}
/// Returns maximum between this length and another length.
#[inline]
pub fn max(self, other: Self) -> Self {
max(self, other)
}
}
impl<T: NumCast + Clone, U> Length<T, U> {
/// Cast from one numeric representation to another, preserving the units.
#[inline]
pub fn cast<NewT: NumCast>(self) -> Length<NewT, U> {
self.try_cast().unwrap()
}
/// Fallible cast from one numeric representation to another, preserving the units.
pub fn try_cast<NewT: NumCast>(self) -> Option<Length<NewT, U>> {
NumCast::from(self.0).map(Length::new)
}
}
impl<T: fmt::Debug, U> fmt::Debug for Length<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: Default, U> Default for Length<T, U> {
#[inline]
fn default() -> Self {
Length::new(Default::default())
}
}
impl<T: Hash, U> Hash for Length<T, U> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.0.hash(h);
}
}
// length + length
impl<T: Add, U> Add for Length<T, U> {
type Output = Length<T::Output, U>;
fn add(self, other: Self) -> Self::Output {
Length::new(self.0 + other.0)
}
}
// length + &length
impl<T: Add + Copy, U> Add<&Self> for Length<T, U> {
type Output = Length<T::Output, U>;
fn add(self, other: &Self) -> Self::Output {
Length::new(self.0 + other.0)
}
}
// length_iter.copied().sum()
impl<T: Add<Output = T> + Zero, U> Sum for Length<T, U> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
// length_iter.sum()
impl<'a, T: 'a + Add<Output = T> + Copy + Zero, U: 'a> Sum<&'a Self> for Length<T, U> {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
// length += length
impl<T: AddAssign, U> AddAssign for Length<T, U> {
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
}
}
// length - length
impl<T: Sub, U> Sub for Length<T, U> {
type Output = Length<T::Output, U>;
fn sub(self, other: Length<T, U>) -> Self::Output {
Length::new(self.0 - other.0)
}
}
// length -= length
impl<T: SubAssign, U> SubAssign for Length<T, U> {
fn sub_assign(&mut self, other: Self) {
self.0 -= other.0;
}
}
// Saturating length + length and length - length.
impl<T: Saturating, U> Saturating for Length<T, U> {
fn saturating_add(self, other: Self) -> Self {
Length::new(self.0.saturating_add(other.0))
}
fn saturating_sub(self, other: Self) -> Self {
Length::new(self.0.saturating_sub(other.0))
}
}
// length / length
impl<Src, Dst, T: Div> Div<Length<T, Src>> for Length<T, Dst> {
type Output = Scale<T::Output, Src, Dst>;
#[inline]
fn div(self, other: Length<T, Src>) -> Self::Output {
Scale::new(self.0 / other.0)
}
}
// length * scalar
impl<T: Mul, U> Mul<T> for Length<T, U> {
type Output = Length<T::Output, U>;
#[inline]
fn mul(self, scale: T) -> Self::Output {
Length::new(self.0 * scale)
}
}
// length *= scalar
impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for Length<T, U> {
#[inline]
fn mul_assign(&mut self, scale: T) {
*self = *self * scale
}
}
// length / scalar
impl<T: Div, U> Div<T> for Length<T, U> {
type Output = Length<T::Output, U>;
#[inline]
fn div(self, scale: T) -> Self::Output {
Length::new(self.0 / scale)
}
}
// length /= scalar
impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for Length<T, U> {
#[inline]
fn div_assign(&mut self, scale: T) {
*self = *self / scale
}
}
// length * scaleFactor
impl<Src, Dst, T: Mul> Mul<Scale<T, Src, Dst>> for Length<T, Src> {
type Output = Length<T::Output, Dst>;
#[inline]
fn mul(self, scale: Scale<T, Src, Dst>) -> Self::Output {
Length::new(self.0 * scale.0)
}
}
// length / scaleFactor
impl<Src, Dst, T: Div> Div<Scale<T, Src, Dst>> for Length<T, Dst> {
type Output = Length<T::Output, Src>;
#[inline]
fn div(self, scale: Scale<T, Src, Dst>) -> Self::Output {
Length::new(self.0 / scale.0)
}
}
// -length
impl<U, T: Neg> Neg for Length<T, U> {
type Output = Length<T::Output, U>;
#[inline]
fn neg(self) -> Self::Output {
Length::new(-self.0)
}
}
impl<T: PartialEq, U> PartialEq for Length<T, U> {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl<T: PartialOrd, U> PartialOrd for Length<T, U> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: Eq, U> Eq for Length<T, U> {}
impl<T: Ord, U> Ord for Length<T, U> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T: Zero, U> Zero for Length<T, U> {
#[inline]
fn zero() -> Self {
Length::new(Zero::zero())
}
}
impl<U, T: ApproxEq<T>> ApproxEq<T> for Length<T, U> {
#[inline]
fn approx_epsilon() -> T {
T::approx_epsilon()
}
#[inline]
fn approx_eq_eps(&self, other: &Length<T, U>, approx_epsilon: &T) -> bool {
self.0.approx_eq_eps(&other.0, approx_epsilon)
}
}
#[cfg(test)]
mod tests {
use super::Length;
use crate::num::Zero;
use crate::scale::Scale;
use core::f32::INFINITY;
use num_traits::Saturating;
enum Inch {}
enum Mm {}
enum Cm {}
enum Second {}
#[cfg(feature = "serde")]
mod serde {
use super::*;
extern crate serde_test;
use self::serde_test::assert_tokens;
use self::serde_test::Token;
#[test]
fn test_length_serde() {
let one_cm: Length<f32, Mm> = Length::new(10.0);
assert_tokens(&one_cm, &[Token::F32(10.0)]);
}
}
#[test]
fn test_clone() {
// A cloned Length is a separate length with the state matching the
// original Length at the point it was cloned.
let mut variable_length: Length<f32, Inch> = Length::new(12.0);
let one_foot = variable_length.clone();
variable_length.0 = 24.0;
assert_eq!(one_foot.get(), 12.0);
assert_eq!(variable_length.get(), 24.0);
}
#[test]
fn test_add() {
let length1: Length<u8, Mm> = Length::new(250);
let length2: Length<u8, Mm> = Length::new(5);
assert_eq!((length1 + length2).get(), 255);
assert_eq!((length1 + &length2).get(), 255);
}
#[test]
fn test_sum() {
type L = Length<f32, Mm>;
let lengths = [L::new(1.0), L::new(2.0), L::new(3.0)];
assert_eq!(lengths.iter().sum::<L>(), L::new(6.0));
}
#[test]
fn test_addassign() {
let one_cm: Length<f32, Mm> = Length::new(10.0);
let mut measurement: Length<f32, Mm> = Length::new(5.0);
measurement += one_cm;
assert_eq!(measurement.get(), 15.0);
}
#[test]
fn test_sub() {
let length1: Length<u8, Mm> = Length::new(250);
let length2: Length<u8, Mm> = Length::new(5);
let result = length1 - length2;
assert_eq!(result.get(), 245);
}
#[test]
fn test_subassign() {
let one_cm: Length<f32, Mm> = Length::new(10.0);
let mut measurement: Length<f32, Mm> = Length::new(5.0);
measurement -= one_cm;
assert_eq!(measurement.get(), -5.0);
}
#[test]
fn test_saturating_add() {
let length1: Length<u8, Mm> = Length::new(250);
let length2: Length<u8, Mm> = Length::new(6);
let result = length1.saturating_add(length2);
assert_eq!(result.get(), 255);
}
#[test]
fn test_saturating_sub() {
let length1: Length<u8, Mm> = Length::new(5);
let length2: Length<u8, Mm> = Length::new(10);
let result = length1.saturating_sub(length2);
assert_eq!(result.get(), 0);
}
#[test]
fn test_division_by_length() {
// Division results in a Scale from denominator units
// to numerator units.
let length: Length<f32, Cm> = Length::new(5.0);
let duration: Length<f32, Second> = Length::new(10.0);
let result = length / duration;
let expected: Scale<f32, Second, Cm> = Scale::new(0.5);
assert_eq!(result, expected);
}
#[test]
fn test_multiplication() {
let length_mm: Length<f32, Mm> = Length::new(10.0);
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
let result = length_mm * cm_per_mm;
let expected: Length<f32, Cm> = Length::new(1.0);
assert_eq!(result, expected);
}
#[test]
fn test_multiplication_with_scalar() {
let length_mm: Length<f32, Mm> = Length::new(10.0);
let result = length_mm * 2.0;
let expected: Length<f32, Mm> = Length::new(20.0);
assert_eq!(result, expected);
}
#[test]
fn test_multiplication_assignment() {
let mut length: Length<f32, Mm> = Length::new(10.0);
length *= 2.0;
let expected: Length<f32, Mm> = Length::new(20.0);
assert_eq!(length, expected);
}
#[test]
fn test_division_by_scalefactor() {
let length: Length<f32, Cm> = Length::new(5.0);
let cm_per_second: Scale<f32, Second, Cm> = Scale::new(10.0);
let result = length / cm_per_second;
let expected: Length<f32, Second> = Length::new(0.5);
assert_eq!(result, expected);
}
#[test]
fn test_division_by_scalar() {
let length: Length<f32, Cm> = Length::new(5.0);
let result = length / 2.0;
let expected: Length<f32, Cm> = Length::new(2.5);
assert_eq!(result, expected);
}
#[test]
fn test_division_assignment() {
let mut length: Length<f32, Mm> = Length::new(10.0);
length /= 2.0;
let expected: Length<f32, Mm> = Length::new(5.0);
assert_eq!(length, expected);
}
#[test]
fn test_negation() {
let length: Length<f32, Cm> = Length::new(5.0);
let result = -length;
let expected: Length<f32, Cm> = Length::new(-5.0);
assert_eq!(result, expected);
}
#[test]
fn test_cast() {
let length_as_i32: Length<i32, Cm> = Length::new(5);
let result: Length<f32, Cm> = length_as_i32.cast();
let length_as_f32: Length<f32, Cm> = Length::new(5.0);
assert_eq!(result, length_as_f32);
}
#[test]
fn test_equality() {
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
assert!(length_5_point_0 == length_5_point_1 - length_0_point_1);
assert!(length_5_point_0 != length_5_point_1);
}
#[test]
fn test_order() {
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
assert!(length_5_point_0 < length_5_point_1);
assert!(length_5_point_0 <= length_5_point_1);
assert!(length_5_point_0 <= length_5_point_1 - length_0_point_1);
assert!(length_5_point_1 > length_5_point_0);
assert!(length_5_point_1 >= length_5_point_0);
assert!(length_5_point_0 >= length_5_point_1 - length_0_point_1);
}
#[test]
fn test_zero_add() {
type LengthCm = Length<f32, Cm>;
let length: LengthCm = Length::new(5.0);
let result = length - LengthCm::zero();
assert_eq!(result, length);
}
#[test]
fn test_zero_division() {
type LengthCm = Length<f32, Cm>;
let length: LengthCm = Length::new(5.0);
let length_zero: LengthCm = Length::zero();
let result = length / length_zero;
let expected: Scale<f32, Cm, Cm> = Scale::new(INFINITY);
assert_eq!(result, expected);
}
}

115
vendor/euclid/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,115 @@
// 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_attr(not(test), no_std)]
//! A collection of strongly typed math tools for computer graphics with an inclination
//! towards 2d graphics and layout.
//!
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
//! and tagged with a generic Unit parameter which is useful to prevent mixing
//! values from different spaces. For example it should not be legal to translate
//! a screen-space position by a world-space vector and this can be expressed using
//! the generic Unit parameter.
//!
//! This unit system is not mandatory and all structures have an alias
//! with the default unit: `UnknownUnit`.
//! for example ```default::Point2D<T>``` is equivalent to ```Point2D<T, UnknownUnit>```.
//! Client code typically creates a set of aliases for each type and doesn't need
//! to deal with the specifics of typed units further. For example:
//!
//! ```rust
//! use euclid::*;
//! pub struct ScreenSpace;
//! pub type ScreenPoint = Point2D<f32, ScreenSpace>;
//! pub type ScreenSize = Size2D<f32, ScreenSpace>;
//! pub struct WorldSpace;
//! pub type WorldPoint = Point3D<f32, WorldSpace>;
//! pub type ProjectionMatrix = Transform3D<f32, WorldSpace, ScreenSpace>;
//! // etc...
//! ```
//!
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
//!
#![deny(unconditional_recursion)]
pub use crate::angle::Angle;
pub use crate::box2d::Box2D;
pub use crate::homogen::HomogeneousVector;
pub use crate::length::Length;
pub use crate::point::{point2, point3, Point2D, Point3D};
pub use crate::scale::Scale;
pub use crate::transform2d::Transform2D;
pub use crate::transform3d::Transform3D;
pub use crate::vector::{bvec2, bvec3, BoolVector2D, BoolVector3D};
pub use crate::vector::{vec2, vec3, Vector2D, Vector3D};
pub use crate::box3d::{box3d, Box3D};
pub use crate::rect::{rect, Rect};
pub use crate::rigid::RigidTransform3D;
pub use crate::rotation::{Rotation2D, Rotation3D};
pub use crate::side_offsets::SideOffsets2D;
pub use crate::size::{size2, size3, Size2D, Size3D};
pub use crate::translation::{Translation2D, Translation3D};
pub use crate::trig::Trig;
#[macro_use]
mod macros;
mod angle;
pub mod approxeq;
pub mod approxord;
mod box2d;
mod box3d;
mod homogen;
mod length;
pub mod num;
mod point;
mod rect;
mod rigid;
mod rotation;
mod scale;
mod side_offsets;
mod size;
mod transform2d;
mod transform3d;
mod translation;
mod trig;
mod vector;
/// The default unit.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownUnit;
pub mod default {
//! A set of aliases for all types, tagged with the default unknown unit.
use super::UnknownUnit;
pub type Length<T> = super::Length<T, UnknownUnit>;
pub type Point2D<T> = super::Point2D<T, UnknownUnit>;
pub type Point3D<T> = super::Point3D<T, UnknownUnit>;
pub type Vector2D<T> = super::Vector2D<T, UnknownUnit>;
pub type Vector3D<T> = super::Vector3D<T, UnknownUnit>;
pub type HomogeneousVector<T> = super::HomogeneousVector<T, UnknownUnit>;
pub type Size2D<T> = super::Size2D<T, UnknownUnit>;
pub type Size3D<T> = super::Size3D<T, UnknownUnit>;
pub type Rect<T> = super::Rect<T, UnknownUnit>;
pub type Box2D<T> = super::Box2D<T, UnknownUnit>;
pub type Box3D<T> = super::Box3D<T, UnknownUnit>;
pub type SideOffsets2D<T> = super::SideOffsets2D<T, UnknownUnit>;
pub type Transform2D<T> = super::Transform2D<T, UnknownUnit, UnknownUnit>;
pub type Transform3D<T> = super::Transform3D<T, UnknownUnit, UnknownUnit>;
pub type Rotation2D<T> = super::Rotation2D<T, UnknownUnit, UnknownUnit>;
pub type Rotation3D<T> = super::Rotation3D<T, UnknownUnit, UnknownUnit>;
pub type Translation2D<T> = super::Translation2D<T, UnknownUnit, UnknownUnit>;
pub type Translation3D<T> = super::Translation3D<T, UnknownUnit, UnknownUnit>;
pub type Scale<T> = super::Scale<T, UnknownUnit, UnknownUnit>;
pub type RigidTransform3D<T> = super::RigidTransform3D<T, UnknownUnit, UnknownUnit>;
}

30
vendor/euclid/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,30 @@
// 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.
macro_rules! mint_vec {
($name:ident [ $($field:ident),* ] = $std_name:ident) => {
#[cfg(feature = "mint")]
impl<T, U> From<mint::$std_name<T>> for $name<T, U> {
fn from(v: mint::$std_name<T>) -> Self {
$name {
$( $field: v.$field, )*
_unit: PhantomData,
}
}
}
#[cfg(feature = "mint")]
impl<T, U> From<$name<T, U>> for mint::$std_name<T> {
fn from(v: $name<T, U>) -> Self {
mint::$std_name {
$( $field: v.$field, )*
}
}
}
}
}

128
vendor/euclid/src/num.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2014 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.
//! A one-dimensional length, tagged with its units.
use num_traits;
// Euclid has its own Zero and One traits instead of of using the num_traits equivalents.
// Unfortunately, num_traits::Zero requires Add, which opens a bag of sad things:
// - Most importantly, for Point2D to implement Zero it would need to implement Add<Self> which we
// don't want (we allow "Point + Vector" and "Vector + Vector" semantics and purposefully disallow
// "Point + Point".
// - Some operations that require, say, One and Div (for example Scale::inv) currently return a
// type parameterized over T::Output which is ambiguous with num_traits::One because it inherits
// Mul which also has an Output associated type. To fix it need to complicate type signatures
// by using <T as Trait>::Output which makes the code and documentation harder to read.
//
// On the other hand, euclid::num::Zero/One are automatically implemented for all types that
// implement their num_traits counterpart. Euclid users never need to explicitly use
// euclid::num::Zero/One and can/should only manipulate the num_traits equivalents without risk
// of compatibility issues with euclid.
pub trait Zero {
fn zero() -> Self;
}
impl<T: num_traits::Zero> Zero for T {
fn zero() -> T {
num_traits::Zero::zero()
}
}
pub trait One {
fn one() -> Self;
}
impl<T: num_traits::One> One for T {
fn one() -> T {
num_traits::One::one()
}
}
/// Defines the nearest integer value to the original value.
pub trait Round: Copy {
/// Rounds to the nearest integer value.
///
/// This behavior is preserved for negative values (unlike the basic cast).
#[must_use]
fn round(self) -> Self;
}
/// Defines the biggest integer equal or lower than the original value.
pub trait Floor: Copy {
/// Rounds to the biggest integer equal or lower than the original value.
///
/// This behavior is preserved for negative values (unlike the basic cast).
#[must_use]
fn floor(self) -> Self;
}
/// Defines the smallest integer equal or greater than the original value.
pub trait Ceil: Copy {
/// Rounds to the smallest integer equal or greater than the original value.
///
/// This behavior is preserved for negative values (unlike the basic cast).
#[must_use]
fn ceil(self) -> Self;
}
macro_rules! num_int {
($ty:ty) => {
impl Round for $ty {
#[inline]
fn round(self) -> $ty {
self
}
}
impl Floor for $ty {
#[inline]
fn floor(self) -> $ty {
self
}
}
impl Ceil for $ty {
#[inline]
fn ceil(self) -> $ty {
self
}
}
};
}
macro_rules! num_float {
($ty:ty) => {
impl Round for $ty {
#[inline]
fn round(self) -> $ty {
(self + 0.5).floor()
}
}
impl Floor for $ty {
#[inline]
fn floor(self) -> $ty {
num_traits::Float::floor(self)
}
}
impl Ceil for $ty {
#[inline]
fn ceil(self) -> $ty {
num_traits::Float::ceil(self)
}
}
};
}
num_int!(i16);
num_int!(u16);
num_int!(i32);
num_int!(u32);
num_int!(i64);
num_int!(u64);
num_int!(isize);
num_int!(usize);
num_float!(f32);
num_float!(f64);

2258
vendor/euclid/src/point.rs vendored Normal file

File diff suppressed because it is too large Load Diff

928
vendor/euclid/src/rect.rs vendored Normal file
View File

@@ -0,0 +1,928 @@
// 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.
use super::UnknownUnit;
use crate::box2d::Box2D;
use crate::num::*;
use crate::point::Point2D;
use crate::scale::Scale;
use crate::side_offsets::SideOffsets2D;
use crate::size::Size2D;
use crate::vector::Vector2D;
#[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`]: Self::is_empty
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
)]
pub struct Rect<T, U> {
pub origin: Point2D<T, U>,
pub size: Size2D<T, U>,
}
#[cfg(feature = "arbitrary")]
impl<'a, T, U> arbitrary::Arbitrary<'a> for Rect<T, U>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let (origin, size) = arbitrary::Arbitrary::arbitrary(u)?;
Ok(Rect { origin, size })
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, U> Zeroable for Rect<T, U> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, U: 'static> Pod for Rect<T, U> {}
impl<T: Hash, U> Hash for Rect<T, U> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.origin.hash(h);
self.size.hash(h);
}
}
impl<T: Copy, U> Copy for Rect<T, U> {}
impl<T: Clone, U> Clone for Rect<T, U> {
fn clone(&self) -> Self {
Self::new(self.origin.clone(), self.size.clone())
}
}
impl<T: PartialEq, U> PartialEq for Rect<T, U> {
fn eq(&self, other: &Self) -> bool {
self.origin.eq(&other.origin) && self.size.eq(&other.size)
}
}
impl<T: Eq, U> Eq for Rect<T, U> {}
impl<T: fmt::Debug, U> fmt::Debug for Rect<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Rect(")?;
fmt::Debug::fmt(&self.size, f)?;
write!(f, " at ")?;
fmt::Debug::fmt(&self.origin, f)?;
write!(f, ")")
}
}
impl<T: Default, U> Default for Rect<T, U> {
fn default() -> Self {
Rect::new(Default::default(), Default::default())
}
}
impl<T, U> Rect<T, U> {
/// Constructor.
#[inline]
pub const fn new(origin: Point2D<T, U>, size: Size2D<T, U>) -> Self {
Rect { origin, size }
}
}
impl<T, U> Rect<T, U>
where
T: Zero,
{
/// Constructor, setting all sides to zero.
#[inline]
pub fn zero() -> Self {
Rect::new(Point2D::origin(), Size2D::zero())
}
/// Creates a rect of the given size, at offset zero.
#[inline]
pub fn from_size(size: Size2D<T, U>) -> Self {
Rect {
origin: Point2D::zero(),
size,
}
}
}
impl<T, U> Rect<T, U>
where
T: Copy + Add<T, Output = T>,
{
#[inline]
pub fn min(&self) -> Point2D<T, U> {
self.origin
}
#[inline]
pub fn max(&self) -> Point2D<T, U> {
self.origin + self.size
}
#[inline]
pub fn max_x(&self) -> T {
self.origin.x + self.size.width
}
#[inline]
pub fn min_x(&self) -> T {
self.origin.x
}
#[inline]
pub fn max_y(&self) -> T {
self.origin.y + self.size.height
}
#[inline]
pub fn min_y(&self) -> T {
self.origin.y
}
#[inline]
pub fn width(&self) -> T {
self.size.width
}
#[inline]
pub fn height(&self) -> T {
self.size.height
}
#[inline]
pub fn x_range(&self) -> Range<T> {
self.min_x()..self.max_x()
}
#[inline]
pub fn y_range(&self) -> Range<T> {
self.min_y()..self.max_y()
}
/// Returns the same rectangle, translated by a vector.
#[inline]
#[must_use]
pub fn translate(&self, by: Vector2D<T, U>) -> Self {
Self::new(self.origin + by, self.size)
}
#[inline]
pub fn to_box2d(&self) -> Box2D<T, U> {
Box2D {
min: self.min(),
max: self.max(),
}
}
}
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]
pub fn contains(&self, p: Point2D<T, U>) -> bool {
self.to_box2d().contains(p)
}
#[inline]
pub fn intersects(&self, other: &Self) -> bool {
self.to_box2d().intersects(&other.to_box2d())
}
}
impl<T, U> Rect<T, U>
where
T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
{
#[inline]
pub fn intersection(&self, other: &Self) -> Option<Self> {
let box2d = self.to_box2d().intersection_unchecked(&other.to_box2d());
if box2d.is_empty() {
return None;
}
Some(box2d.to_rect())
}
}
impl<T, U> Rect<T, U>
where
T: Copy + Add<T, Output = T> + Sub<T, Output = T>,
{
#[inline]
#[must_use]
pub fn inflate(&self, width: T, height: T) -> Self {
Rect::new(
Point2D::new(self.origin.x - width, self.origin.y - height),
Size2D::new(
self.size.width + width + width,
self.size.height + height + height,
),
)
}
}
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]
pub fn 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.
pub fn 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.
pub fn 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.
pub fn 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]
pub fn 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>,
{
pub fn center(&self) -> Point2D<T, U> {
let two = T::one() + T::one();
self.origin + self.size.to_vector() / two
}
}
impl<T, U> Rect<T, U>
where
T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Zero,
{
#[inline]
pub fn union(&self, other: &Self) -> Self {
self.to_box2d().union(&other.to_box2d()).to_rect()
}
}
impl<T, U> Rect<T, U> {
#[inline]
pub fn scale<S: Copy>(&self, x: S, y: S) -> Self
where
T: Copy + Mul<S, Output = T>,
{
Rect::new(
Point2D::new(self.origin.x * x, self.origin.y * y),
Size2D::new(self.size.width * x, self.size.height * y),
)
}
}
impl<T: Copy + Mul<T, Output = T>, U> Rect<T, U> {
#[inline]
pub fn area(&self) -> T {
self.size.area()
}
}
impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
#[inline]
pub fn is_empty(&self) -> bool {
self.size.is_empty()
}
}
impl<T: Copy + Zero + PartialOrd, U> Rect<T, U> {
#[inline]
pub fn to_non_empty(&self) -> Option<Self> {
if self.is_empty() {
return None;
}
Some(*self)
}
}
impl<T: Copy + Mul, U> Mul<T> for Rect<T, U> {
type Output = Rect<T::Output, U>;
#[inline]
fn mul(self, scale: T) -> Self::Output {
Rect::new(self.origin * scale, self.size * scale)
}
}
impl<T: Copy + MulAssign, U> MulAssign<T> for Rect<T, U> {
#[inline]
fn mul_assign(&mut self, scale: T) {
*self *= Scale::new(scale);
}
}
impl<T: Copy + Div, U> Div<T> for Rect<T, U> {
type Output = Rect<T::Output, U>;
#[inline]
fn div(self, scale: T) -> Self::Output {
Rect::new(self.origin / scale.clone(), self.size / scale)
}
}
impl<T: Copy + DivAssign, U> DivAssign<T> for Rect<T, U> {
#[inline]
fn div_assign(&mut self, scale: T) {
*self /= Scale::new(scale);
}
}
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for Rect<T, U1> {
type Output = Rect<T::Output, U2>;
#[inline]
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
Rect::new(self.origin * scale.clone(), self.size * scale)
}
}
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for Rect<T, U> {
#[inline]
fn mul_assign(&mut self, scale: Scale<T, U, U>) {
self.origin *= scale.clone();
self.size *= scale;
}
}
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for Rect<T, U2> {
type Output = Rect<T::Output, U1>;
#[inline]
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
Rect::new(self.origin / scale.clone(), self.size / scale)
}
}
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for Rect<T, U> {
#[inline]
fn div_assign(&mut self, 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]
pub fn to_untyped(&self) -> Rect<T, UnknownUnit> {
Rect::new(self.origin.to_untyped(), self.size.to_untyped())
}
/// Tag a unitless value with units.
#[inline]
pub fn 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]
pub fn 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.
///
/// [`round`]: Self::round
/// [`round_in`]: Self::round_in
/// [`round_out`]: Self::round_out
#[inline]
pub fn 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.
///
/// [`round`]: Self::round
/// [`round_in`]: Self::round_in
/// [`round_out`]: Self::round_out
pub fn 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]
pub fn to_f32(&self) -> Rect<f32, U> {
self.cast()
}
/// Cast into an `f64` rectangle.
#[inline]
pub fn 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]
pub fn 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]
pub fn 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]
pub fn 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]
pub fn 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]
pub fn to_i64(&self) -> Rect<i64, U> {
self.cast()
}
}
impl<T: Float, U> Rect<T, U> {
/// Returns `true` if all members are finite.
#[inline]
pub fn 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
/// lose precision for large values, so if you need to call this method very often it
/// is better to use [`Box2D`].
#[must_use]
pub fn 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
/// lose precision for large values, so if you need to call this method very often it
/// is better to use [`Box2D`].
#[must_use]
pub fn 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
/// lose precision for large values, so if you need to call this method very often it
/// is better to use [`Box2D`].
#[must_use]
pub fn round_out(&self) -> Self {
self.to_box2d().round_out().to_rect()
}
}
impl<T, U> From<Size2D<T, U>> for Rect<T, U>
where
T: Zero,
{
fn from(size: Size2D<T, U>) -> Self {
Self::from_size(size)
}
}
/// Shorthand for `Rect::new(Point2D::new(x, y), Size2D::new(w, h))`.
pub const fn rect<T, U>(x: T, y: T, w: T, h: T) -> Rect<T, U> {
Rect::new(Point2D::new(x, y), Size2D::new(w, h))
}
#[cfg(test)]
mod tests {
use crate::default::{Point2D, Rect, Size2D};
use crate::side_offsets::SideOffsets2D;
use crate::{point2, rect, size2, vec2};
#[test]
fn test_translate() {
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
let pp = p.translate(vec2(10, 15));
assert!(pp.size.width == 50);
assert!(pp.size.height == 40);
assert!(pp.origin.x == 10);
assert!(pp.origin.y == 15);
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
let rr = r.translate(vec2(0, -10));
assert!(rr.size.width == 50);
assert!(rr.size.height == 40);
assert!(rr.origin.x == -10);
assert!(rr.origin.y == -15);
}
#[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));
let pq = p.union(&q);
assert!(pq.origin == Point2D::new(0, 0));
assert!(pq.size == Size2D::new(50, 40));
let pr = p.union(&r);
assert!(pr.origin == Point2D::new(-15, -30));
assert!(pr.size == Size2D::new(200, 70));
let ps = p.union(&s);
assert!(ps.origin == Point2D::new(0, -15));
assert!(ps.size == Size2D::new(270, 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));
assert!(r.contains(Point2D::new(0, 50)));
assert!(r.contains(Point2D::new(-10, 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);
assert!(pp.size.width == 500);
assert!(pp.size.height == 600);
assert!(pp.origin.x == 0);
assert!(pp.origin.y == 0);
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
let rr = r.scale(1, 20);
assert!(rr.size.width == 50);
assert!(rr.size.height == 800);
assert!(rr.origin.x == -10);
assert!(rr.origin.y == -100);
}
#[test]
fn test_inflate() {
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
let pp = p.inflate(10, 20);
assert!(pp.size.width == 30);
assert!(pp.size.height == 50);
assert!(pp.origin.x == -10);
assert!(pp.origin.y == -20);
let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
let rr = r.inflate(-2, -5);
assert!(rr.size.width == 6);
assert!(rr.size.height == 10);
assert!(rr.origin.x == 2);
assert!(rr.origin.y == 5);
}
#[test]
fn test_inner_outer_rect() {
let inner_rect = Rect::new(point2(20, 40), size2(80, 100));
let offsets = SideOffsets2D::new(20, 10, 10, 10);
let outer_rect = inner_rect.outer_rect(offsets);
assert_eq!(outer_rect.origin.x, 10);
assert_eq!(outer_rect.origin.y, 20);
assert_eq!(outer_rect.size.width, 100);
assert_eq!(outer_rect.size.height, 130);
assert_eq!(outer_rect.inner_rect(offsets), inner_rect);
}
#[test]
fn test_min_max_x_y() {
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
assert!(p.max_y() == 40);
assert!(p.min_y() == 0);
assert!(p.max_x() == 50);
assert!(p.min_x() == 0);
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
assert!(r.max_y() == 35);
assert!(r.min_y() == -5);
assert!(r.max_x() == 40);
assert!(r.min_x() == -10);
}
#[test]
fn test_width_height() {
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
assert!(r.width() == 50);
assert!(r.height() == 40);
}
#[test]
fn test_is_empty() {
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty());
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty());
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty());
assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty());
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty());
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty());
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty());
assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty());
}
#[test]
fn test_round() {
let mut x = -2.0;
let mut y = -2.0;
let mut w = -2.0;
let mut 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));
assert!(rect.contains_rect(&rect.round_in()));
assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect));
assert!(rect.round_out().contains_rect(&rect));
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out()));
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round()));
assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect));
h += 0.1;
}
w += 0.1;
}
y += 0.1;
}
x += 0.1
}
}
#[test]
fn test_center() {
let r: Rect<i32> = rect(-2, 5, 4, 10);
assert_eq!(r.center(), point2(0, 10));
let r: Rect<f32> = rect(1.0, 2.0, 3.0, 4.0);
assert_eq!(r.center(), point2(2.5, 4.0));
}
#[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);
}
}

326
vendor/euclid/src/rigid.rs vendored Normal file
View File

@@ -0,0 +1,326 @@
//! All matrix multiplication in this module is in row-vector notation,
//! i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
//! before `T2` you use `T1 * T2`
use crate::approxeq::ApproxEq;
use crate::trig::Trig;
use crate::{Rotation3D, Transform3D, UnknownUnit, Vector3D};
use core::{fmt, hash};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
use num_traits::real::Real;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A rigid transformation. All lengths are preserved under such a transformation.
///
///
/// Internally, this is a rotation and a translation, with the rotation
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
///
/// This can be more efficient to use over full matrices, especially if you
/// have to deal with the decomposed quantities often.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct RigidTransform3D<T, Src, Dst> {
pub rotation: Rotation3D<T, Src, Dst>,
pub translation: Vector3D<T, Dst>,
}
impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
/// Construct a new rigid transformation, where the `rotation` applies first
#[inline]
pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
Self {
rotation,
translation,
}
}
}
impl<T: Copy, Src, Dst> RigidTransform3D<T, Src, Dst> {
pub fn cast_unit<Src2, Dst2>(&self) -> RigidTransform3D<T, Src2, Dst2> {
RigidTransform3D {
rotation: self.rotation.cast_unit(),
translation: self.translation.cast_unit(),
}
}
}
impl<T: Real + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
/// Construct an identity transform
#[inline]
pub fn identity() -> Self {
Self {
rotation: Rotation3D::identity(),
translation: Vector3D::zero(),
}
}
/// Construct a new rigid transformation, where the `translation` applies first
#[inline]
pub fn new_from_reversed(
translation: Vector3D<T, Src>,
rotation: Rotation3D<T, Src, Dst>,
) -> Self {
// T * R
// = (R * R^-1) * T * R
// = R * (R^-1 * T * R)
// = R * T'
//
// T' = (R^-1 * T * R) is also a translation matrix
// It is equivalent to the translation matrix obtained by rotating the
// translation by R
let translation = rotation.transform_vector3d(translation);
Self {
rotation,
translation,
}
}
#[inline]
pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
Self {
rotation,
translation: Vector3D::zero(),
}
}
#[inline]
pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
Self {
translation,
rotation: Rotation3D::identity(),
}
}
/// Decompose this into a translation and an rotation to be applied in the opposite order
///
/// i.e., the translation is applied _first_
#[inline]
pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
// self = R * T
// = R * T * (R^-1 * R)
// = (R * T * R^-1) * R)
// = T' * R
//
// T' = (R^ * T * R^-1) is T rotated by R^-1
let translation = self.rotation.inverse().transform_vector3d(self.translation);
(translation, self.rotation)
}
/// Returns the multiplication of the two transforms such that
/// other's transformation applies after self's transformation.
///
/// i.e., this produces `self * other` in row-vector notation
#[inline]
pub fn then<Dst2>(
&self,
other: &RigidTransform3D<T, Dst, Dst2>,
) -> RigidTransform3D<T, Src, Dst2> {
// self = R1 * T1
// other = R2 * T2
// result = R1 * T1 * R2 * T2
// = R1 * (R2 * R2^-1) * T1 * R2 * T2
// = (R1 * R2) * (R2^-1 * T1 * R2) * T2
// = R' * T' * T2
// = R' * T''
//
// (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
// R1 * R2 = R'
// T' * T2 = T'' = vector addition of translations T2 and T'
let t_prime = other.rotation.transform_vector3d(self.translation);
let r_prime = self.rotation.then(&other.rotation);
let t_prime2 = t_prime + other.translation;
RigidTransform3D {
rotation: r_prime,
translation: t_prime2,
}
}
/// Inverts the transformation
#[inline]
pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
// result = (self)^-1
// = (R * T)^-1
// = T^-1 * R^-1
// = (R^-1 * R) * T^-1 * R^-1
// = R^-1 * (R * T^-1 * R^-1)
// = R' * T'
//
// T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
// R' = R^-1
//
// An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1
RigidTransform3D::new_from_reversed(-self.translation, self.rotation.inverse())
}
pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
where
T: Trig,
{
self.rotation
.to_transform()
.then(&self.translation.to_transform())
}
/// Drop the units, preserving only the numeric value.
#[inline]
pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
RigidTransform3D {
rotation: self.rotation.to_untyped(),
translation: self.translation.to_untyped(),
}
}
/// Tag a unitless value with units.
#[inline]
pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
RigidTransform3D {
rotation: Rotation3D::from_untyped(&transform.rotation),
translation: Vector3D::from_untyped(transform.translation),
}
}
}
impl<T: fmt::Debug, Src, Dst> fmt::Debug for RigidTransform3D<T, Src, Dst> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RigidTransform3D")
.field("rotation", &self.rotation)
.field("translation", &self.translation)
.finish()
}
}
impl<T: PartialEq, Src, Dst> PartialEq for RigidTransform3D<T, Src, Dst> {
fn eq(&self, other: &Self) -> bool {
self.rotation == other.rotation && self.translation == other.translation
}
}
impl<T: Eq, Src, Dst> Eq for RigidTransform3D<T, Src, Dst> {}
impl<T: hash::Hash, Src, Dst> hash::Hash for RigidTransform3D<T, Src, Dst> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.rotation.hash(state);
self.translation.hash(state);
}
}
impl<T: Copy, Src, Dst> Copy for RigidTransform3D<T, Src, Dst> {}
impl<T: Clone, Src, Dst> Clone for RigidTransform3D<T, Src, Dst> {
fn clone(&self) -> Self {
RigidTransform3D {
rotation: self.rotation.clone(),
translation: self.translation.clone(),
}
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for RigidTransform3D<T, Src, Dst>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(RigidTransform3D {
rotation: arbitrary::Arbitrary::arbitrary(u)?,
translation: arbitrary::Arbitrary::arbitrary(u)?,
})
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, Src, Dst> Zeroable for RigidTransform3D<T, Src, Dst> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for RigidTransform3D<T, Src, Dst> {}
impl<T: Real + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
for RigidTransform3D<T, Src, Dst>
{
fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
Self::from_rotation(rot)
}
}
impl<T: Real + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>> for RigidTransform3D<T, Src, Dst> {
fn from(t: Vector3D<T, Dst>) -> Self {
Self::from_translation(t)
}
}
#[cfg(test)]
mod test {
use super::RigidTransform3D;
use crate::default::{Rotation3D, Transform3D, Vector3D};
#[test]
fn test_rigid_construction() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let rigid = RigidTransform3D::new(rotation, translation);
assert!(rigid
.to_transform()
.approx_eq(&rotation.to_transform().then(&translation.to_transform())));
let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
assert!(rigid
.to_transform()
.approx_eq(&translation.to_transform().then(&rotation.to_transform())));
}
#[test]
fn test_rigid_decomposition() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let rigid = RigidTransform3D::new(rotation, translation);
let (t2, r2) = rigid.decompose_reversed();
assert!(rigid
.to_transform()
.approx_eq(&t2.to_transform().then(&r2.to_transform())));
}
#[test]
fn test_rigid_inverse() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let rigid = RigidTransform3D::new(rotation, translation);
let inverse = rigid.inverse();
assert!(rigid
.then(&inverse)
.to_transform()
.approx_eq(&Transform3D::identity()));
assert!(inverse
.to_transform()
.approx_eq(&rigid.to_transform().inverse().unwrap()));
}
#[test]
fn test_rigid_multiply() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let translation2 = Vector3D::new(9.3, -3.9, 1.1);
let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
let rigid = RigidTransform3D::new(rotation, translation);
let rigid2 = RigidTransform3D::new(rotation2, translation2);
assert!(rigid
.then(&rigid2)
.to_transform()
.approx_eq(&rigid.to_transform().then(&rigid2.to_transform())));
assert!(rigid2
.then(&rigid)
.to_transform()
.approx_eq(&rigid2.to_transform().then(&rigid.to_transform())));
}
}

1051
vendor/euclid/src/rotation.rs vendored Normal file

File diff suppressed because it is too large Load Diff

473
vendor/euclid/src/scale.rs vendored Normal file
View File

@@ -0,0 +1,473 @@
// Copyright 2014 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.
//! A type-checked scaling factor between units.
use crate::num::One;
use crate::approxord::{max, min};
use crate::{Box2D, Box3D, Point2D, Point3D, Rect, Size2D, Vector2D};
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Sub};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
use num_traits::NumCast;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A scaling factor between two different units of measurement.
///
/// This is effectively a type-safe float, intended to be used in combination with other types like
/// `length::Length` to enforce conversion between systems of measurement at compile time.
///
/// `Src` and `Dst` represent the units before and after multiplying a value by a `Scale`. They
/// may be types without values, such as empty enums. For example:
///
/// ```rust
/// use euclid::Scale;
/// use euclid::Length;
/// enum Mm {};
/// enum Inch {};
///
/// let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
///
/// let one_foot: Length<f32, Inch> = Length::new(12.0);
/// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch;
/// ```
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "T: serde::Serialize",
deserialize = "T: serde::Deserialize<'de>"
))
)]
pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
impl<T, Src, Dst> Scale<T, Src, Dst> {
#[inline]
pub const fn new(x: T) -> Self {
Scale(x, PhantomData)
}
/// Creates an identity scale (1.0).
#[inline]
pub fn identity() -> Self
where
T: One,
{
Scale::new(T::one())
}
/// Returns the given point transformed by this scale.
///
/// # Example
///
/// ```rust
/// use euclid::{Scale, point2};
/// enum Mm {};
/// enum Cm {};
///
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
///
/// assert_eq!(to_mm.transform_point(point2(42, -42)), point2(420, -420));
/// ```
#[inline]
pub fn transform_point(self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
where
T: Copy + Mul,
{
Point2D::new(point.x * self.0, point.y * self.0)
}
/// Returns the given point transformed by this scale.
#[inline]
pub fn transform_point3d(self, point: Point3D<T, Src>) -> Point3D<T::Output, Dst>
where
T: Copy + Mul,
{
Point3D::new(point.x * self.0, point.y * self.0, point.z * self.0)
}
/// Returns the given vector transformed by this scale.
///
/// # Example
///
/// ```rust
/// use euclid::{Scale, vec2};
/// enum Mm {};
/// enum Cm {};
///
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
///
/// assert_eq!(to_mm.transform_vector(vec2(42, -42)), vec2(420, -420));
/// ```
#[inline]
pub fn transform_vector(self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
where
T: Copy + Mul,
{
Vector2D::new(vec.x * self.0, vec.y * self.0)
}
/// Returns the given size transformed by this scale.
///
/// # Example
///
/// ```rust
/// use euclid::{Scale, size2};
/// enum Mm {};
/// enum Cm {};
///
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
///
/// assert_eq!(to_mm.transform_size(size2(42, -42)), size2(420, -420));
/// ```
#[inline]
pub fn transform_size(self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
where
T: Copy + Mul,
{
Size2D::new(size.width * self.0, size.height * self.0)
}
/// Returns the given rect transformed by this scale.
///
/// # Example
///
/// ```rust
/// use euclid::{Scale, rect};
/// enum Mm {};
/// enum Cm {};
///
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
///
/// assert_eq!(to_mm.transform_rect(&rect(1, 2, 42, -42)), rect(10, 20, 420, -420));
/// ```
#[inline]
pub fn transform_rect(self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
where
T: Copy + Mul,
{
Rect::new(
self.transform_point(rect.origin),
self.transform_size(rect.size),
)
}
/// Returns the given box transformed by this scale.
#[inline]
pub fn transform_box2d(self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
where
T: Copy + Mul,
{
Box2D {
min: self.transform_point(b.min),
max: self.transform_point(b.max),
}
}
/// Returns the given box transformed by this scale.
#[inline]
pub fn transform_box3d(self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
where
T: Copy + Mul,
{
Box3D {
min: self.transform_point3d(b.min),
max: self.transform_point3d(b.max),
}
}
/// Returns `true` if this scale has no effect.
///
/// # Example
///
/// ```rust
/// use euclid::Scale;
/// use euclid::num::One;
/// enum Mm {};
/// enum Cm {};
///
/// let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
/// let mm_per_mm: Scale<f32, Mm, Mm> = Scale::new(1.0);
///
/// assert_eq!(cm_per_mm.is_identity(), false);
/// assert_eq!(mm_per_mm.is_identity(), true);
/// assert_eq!(mm_per_mm, Scale::one());
/// ```
#[inline]
pub fn is_identity(self) -> bool
where
T: PartialEq + One,
{
self.0 == T::one()
}
/// Returns the underlying scalar scale factor.
#[inline]
pub fn get(self) -> T {
self.0
}
/// The inverse Scale (1.0 / self).
///
/// # Example
///
/// ```rust
/// use euclid::Scale;
/// enum Mm {};
/// enum Cm {};
///
/// let cm_per_mm: Scale<f32, Cm, Mm> = Scale::new(0.1);
///
/// assert_eq!(cm_per_mm.inverse(), Scale::new(10.0));
/// ```
pub fn inverse(self) -> Scale<T::Output, Dst, Src>
where
T: One + Div,
{
let one: T = One::one();
Scale::new(one / self.0)
}
}
impl<T: PartialOrd, Src, Dst> Scale<T, Src, Dst> {
#[inline]
pub fn min(self, other: Self) -> Self {
Self::new(min(self.0, other.0))
}
#[inline]
pub fn max(self, other: Self) -> Self {
Self::new(max(self.0, other.0))
}
/// Returns the point each component of which clamped by corresponding
/// components of `start` and `end`.
///
/// Shortcut for `self.max(start).min(end)`.
#[inline]
pub fn clamp(self, start: Self, end: Self) -> Self
where
T: Copy,
{
self.max(start).min(end)
}
}
impl<T: NumCast, Src, Dst> Scale<T, Src, Dst> {
/// Cast from one numeric representation to another, preserving the units.
///
/// # Panics
///
/// If the source value cannot be represented by the target type `NewT`, then
/// method panics. Use `try_cast` if that must be case.
///
/// # Example
///
/// ```rust
/// use euclid::Scale;
/// enum Mm {};
/// enum Cm {};
///
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
///
/// assert_eq!(to_mm.cast::<f32>(), Scale::new(10.0));
/// ```
/// That conversion will panic, because `i32` not enough to store such big numbers:
/// ```rust,should_panic
/// use euclid::Scale;
/// enum Mm {};// millimeter = 10^-2 meters
/// enum Em {};// exameter = 10^18 meters
///
/// // Panics
/// let to_em: Scale<i32, Mm, Em> = Scale::new(10e20).cast();
/// ```
#[inline]
pub fn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> {
self.try_cast().unwrap()
}
/// Fallible cast from one numeric representation to another, preserving the units.
/// If the source value cannot be represented by the target type `NewT`, then `None`
/// is returned.
///
/// # Example
///
/// ```rust
/// use euclid::Scale;
/// enum Mm {};
/// enum Cm {};
/// enum Em {};// Exameter = 10^18 meters
///
/// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
/// let to_em: Scale<f32, Mm, Em> = Scale::new(10e20);
///
/// assert_eq!(to_mm.try_cast::<f32>(), Some(Scale::new(10.0)));
/// // Integer to small to store that number
/// assert_eq!(to_em.try_cast::<i32>(), None);
/// ```
pub fn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> {
NumCast::from(self.0).map(Scale::new)
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Scale<T, Src, Dst>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Scale::new(arbitrary::Arbitrary::arbitrary(u)?))
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, Src, Dst> Zeroable for Scale<T, Src, Dst> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Scale<T, Src, Dst> {}
// scale0 * scale1
// (A,B) * (B,C) = (A,C)
impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
type Output = Scale<T::Output, A, C>;
#[inline]
fn mul(self, other: Scale<T, B, C>) -> Self::Output {
Scale::new(self.0 * other.0)
}
}
// scale0 + scale1
impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> {
type Output = Scale<T::Output, Src, Dst>;
#[inline]
fn add(self, other: Scale<T, Src, Dst>) -> Self::Output {
Scale::new(self.0 + other.0)
}
}
// scale0 - scale1
impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> {
type Output = Scale<T::Output, Src, Dst>;
#[inline]
fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output {
Scale::new(self.0 - other.0)
}
}
// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
// https://github.com/rust-lang/rust/issues/26925
impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> {
fn eq(&self, other: &Scale<T, Src, Dst>) -> bool {
self.0 == other.0
}
}
impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
fn clone(&self) -> Scale<T, Src, Dst> {
Scale::new(self.0.clone())
}
}
impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T: One, Src, Dst> One for Scale<T, Src, Dst> {
#[inline]
fn one() -> Self {
Scale::new(T::one())
}
}
#[cfg(test)]
mod tests {
use super::Scale;
enum Inch {}
enum Cm {}
enum Mm {}
#[test]
fn test_scale() {
let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inverse();
assert_eq!(mm_per_cm.get(), 10.0);
let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
assert_eq!(one.get(), 1.0);
let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm;
assert_eq!(one.get(), 1.0);
let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
// mm cm cm
// ---- x ---- = ----
// inch mm inch
assert_eq!(cm_per_inch, Scale::new(2.54));
let a: Scale<isize, Inch, Inch> = Scale::new(2);
let b: Scale<isize, Inch, Inch> = Scale::new(3);
assert_ne!(a, b);
assert_eq!(a, a.clone());
assert_eq!(a.clone() + b.clone(), Scale::new(5));
assert_eq!(a - b, Scale::new(-1));
// Clamp
assert_eq!(Scale::identity().clamp(a, b), a);
assert_eq!(Scale::new(5).clamp(a, b), b);
let a = Scale::<f32, Inch, Inch>::new(2.0);
let b = Scale::<f32, Inch, Inch>::new(3.0);
let c = Scale::<f32, Inch, Inch>::new(2.5);
assert_eq!(c.clamp(a, b), c);
}
}

526
vendor/euclid/src/side_offsets.rs vendored Normal file
View File

@@ -0,0 +1,526 @@
// 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.
//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
//! and margins in CSS.
use crate::length::Length;
use crate::num::Zero;
use crate::scale::Scale;
use crate::Vector2D;
use core::cmp::{Eq, PartialEq};
use core::fmt;
use core::hash::Hash;
use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding,
/// and margins in CSS, optionally tagged with a unit.
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
)]
pub struct SideOffsets2D<T, U> {
pub top: T,
pub right: T,
pub bottom: T,
pub left: T,
#[doc(hidden)]
pub _unit: PhantomData<U>,
}
#[cfg(feature = "arbitrary")]
impl<'a, T, U> arbitrary::Arbitrary<'a> for SideOffsets2D<T, U>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let (top, right, bottom, left) = arbitrary::Arbitrary::arbitrary(u)?;
Ok(SideOffsets2D {
top,
right,
bottom,
left,
_unit: PhantomData,
})
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, U> Zeroable for SideOffsets2D<T, U> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, U: 'static> Pod for SideOffsets2D<T, U> {}
impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
fn clone(&self) -> Self {
SideOffsets2D {
top: self.top.clone(),
right: self.right.clone(),
bottom: self.bottom.clone(),
left: self.left.clone(),
_unit: PhantomData,
}
}
}
impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
impl<T, U> PartialEq for SideOffsets2D<T, U>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.top == other.top
&& self.right == other.right
&& self.bottom == other.bottom
&& self.left == other.left
}
}
impl<T, U> Hash for SideOffsets2D<T, U>
where
T: Hash,
{
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
self.top.hash(h);
self.right.hash(h);
self.bottom.hash(h);
self.left.hash(h);
}
}
impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({:?},{:?},{:?},{:?})",
self.top, self.right, self.bottom, self.left
)
}
}
impl<T: Default, U> Default for SideOffsets2D<T, U> {
fn default() -> Self {
SideOffsets2D {
top: Default::default(),
right: Default::default(),
bottom: Default::default(),
left: Default::default(),
_unit: PhantomData,
}
}
}
impl<T, U> SideOffsets2D<T, U> {
/// Constructor taking a scalar for each side.
///
/// Sides are specified in top-right-bottom-left order following
/// CSS's convention.
pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
SideOffsets2D {
top,
right,
bottom,
left,
_unit: PhantomData,
}
}
/// Constructor taking a typed Length for each side.
///
/// Sides are specified in top-right-bottom-left order following
/// CSS's convention.
pub fn from_lengths(
top: Length<T, U>,
right: Length<T, U>,
bottom: Length<T, U>,
left: Length<T, U>,
) -> Self {
SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
}
/// Construct side offsets from min and a max vector offsets.
///
/// The outer rect of the resulting side offsets is equivalent to translating
/// a rectangle's upper-left corner with the min vector and translating the
/// bottom-right corner with the max vector.
pub fn from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
where
T: Neg<Output = T>,
{
SideOffsets2D {
left: -min.x,
top: -min.y,
right: max.x,
bottom: max.y,
_unit: PhantomData,
}
}
/// Construct side offsets from min and a max vector offsets.
///
/// The inner rect of the resulting side offsets is equivalent to translating
/// a rectangle's upper-left corner with the min vector and translating the
/// bottom-right corner with the max vector.
pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T, U>) -> Self
where
T: Neg<Output = T>,
{
SideOffsets2D {
left: min.x,
top: min.y,
right: -max.x,
bottom: -max.y,
_unit: PhantomData,
}
}
/// Constructor, setting all sides to zero.
pub fn zero() -> Self
where
T: Zero,
{
SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
}
/// Returns `true` if all side offsets are zero.
pub fn is_zero(&self) -> bool
where
T: Zero + PartialEq,
{
let zero = T::zero();
self.top == zero && self.right == zero && self.bottom == zero && self.left == zero
}
/// Constructor setting the same value to all sides, taking a scalar value directly.
pub fn new_all_same(all: T) -> Self
where
T: Copy,
{
SideOffsets2D::new(all, all, all, all)
}
/// Constructor setting the same value to all sides, taking a typed Length.
pub fn from_length_all_same(all: Length<T, U>) -> Self
where
T: Copy,
{
SideOffsets2D::new_all_same(all.0)
}
pub fn horizontal(&self) -> T
where
T: Copy + Add<T, Output = T>,
{
self.left + self.right
}
pub fn vertical(&self) -> T
where
T: Copy + Add<T, Output = T>,
{
self.top + self.bottom
}
}
impl<T, U> Add for SideOffsets2D<T, U>
where
T: Add<T, Output = T>,
{
type Output = Self;
fn add(self, other: Self) -> Self {
SideOffsets2D::new(
self.top + other.top,
self.right + other.right,
self.bottom + other.bottom,
self.left + other.left,
)
}
}
impl<T, U> AddAssign<Self> for SideOffsets2D<T, U>
where
T: AddAssign<T>,
{
fn add_assign(&mut self, other: Self) {
self.top += other.top;
self.right += other.right;
self.bottom += other.bottom;
self.left += other.left;
}
}
impl<T, U> Sub for SideOffsets2D<T, U>
where
T: Sub<T, Output = T>,
{
type Output = Self;
fn sub(self, other: Self) -> Self {
SideOffsets2D::new(
self.top - other.top,
self.right - other.right,
self.bottom - other.bottom,
self.left - other.left,
)
}
}
impl<T, U> SubAssign<Self> for SideOffsets2D<T, U>
where
T: SubAssign<T>,
{
fn sub_assign(&mut self, other: Self) {
self.top -= other.top;
self.right -= other.right;
self.bottom -= other.bottom;
self.left -= other.left;
}
}
impl<T, U> Neg for SideOffsets2D<T, U>
where
T: Neg<Output = T>,
{
type Output = Self;
fn neg(self) -> Self {
SideOffsets2D {
top: -self.top,
right: -self.right,
bottom: -self.bottom,
left: -self.left,
_unit: PhantomData,
}
}
}
impl<T: Copy + Mul, U> Mul<T> for SideOffsets2D<T, U> {
type Output = SideOffsets2D<T::Output, U>;
#[inline]
fn mul(self, scale: T) -> Self::Output {
SideOffsets2D::new(
self.top * scale,
self.right * scale,
self.bottom * scale,
self.left * scale,
)
}
}
impl<T: Copy + MulAssign, U> MulAssign<T> for SideOffsets2D<T, U> {
#[inline]
fn mul_assign(&mut self, other: T) {
self.top *= other;
self.right *= other;
self.bottom *= other;
self.left *= other;
}
}
impl<T: Copy + Mul, U1, U2> Mul<Scale<T, U1, U2>> for SideOffsets2D<T, U1> {
type Output = SideOffsets2D<T::Output, U2>;
#[inline]
fn mul(self, scale: Scale<T, U1, U2>) -> Self::Output {
SideOffsets2D::new(
self.top * scale.0,
self.right * scale.0,
self.bottom * scale.0,
self.left * scale.0,
)
}
}
impl<T: Copy + MulAssign, U> MulAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
#[inline]
fn mul_assign(&mut self, other: Scale<T, U, U>) {
*self *= other.0;
}
}
impl<T: Copy + Div, U> Div<T> for SideOffsets2D<T, U> {
type Output = SideOffsets2D<T::Output, U>;
#[inline]
fn div(self, scale: T) -> Self::Output {
SideOffsets2D::new(
self.top / scale,
self.right / scale,
self.bottom / scale,
self.left / scale,
)
}
}
impl<T: Copy + DivAssign, U> DivAssign<T> for SideOffsets2D<T, U> {
#[inline]
fn div_assign(&mut self, other: T) {
self.top /= other;
self.right /= other;
self.bottom /= other;
self.left /= other;
}
}
impl<T: Copy + Div, U1, U2> Div<Scale<T, U1, U2>> for SideOffsets2D<T, U2> {
type Output = SideOffsets2D<T::Output, U1>;
#[inline]
fn div(self, scale: Scale<T, U1, U2>) -> Self::Output {
SideOffsets2D::new(
self.top / scale.0,
self.right / scale.0,
self.bottom / scale.0,
self.left / scale.0,
)
}
}
impl<T: Copy + DivAssign, U> DivAssign<Scale<T, U, U>> for SideOffsets2D<T, U> {
fn div_assign(&mut self, other: Scale<T, U, U>) {
*self /= other.0;
}
}
#[test]
fn from_vectors() {
use crate::{point2, vec2};
type Box2D = crate::default::Box2D<i32>;
let b = Box2D {
min: point2(10, 10),
max: point2(20, 20),
};
let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
assert_eq!(
outer,
Box2D {
min: point2(9, 8),
max: point2(23, 24)
}
);
assert_eq!(
inner,
Box2D {
min: point2(11, 12),
max: point2(17, 16)
}
);
}
#[test]
fn test_is_zero() {
let s1: SideOffsets2D<f32, ()> = SideOffsets2D::new_all_same(0.0);
assert!(s1.is_zero());
let s2: SideOffsets2D<f32, ()> = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
assert!(!s2.is_zero());
}
#[cfg(test)]
mod ops {
use crate::Scale;
pub enum Mm {}
pub enum Cm {}
type SideOffsets2D<T> = crate::default::SideOffsets2D<T>;
type SideOffsets2DMm<T> = crate::SideOffsets2D<T, Mm>;
type SideOffsets2DCm<T> = crate::SideOffsets2D<T, Cm>;
#[test]
fn test_mul_scalar() {
let s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
let result = s * 3.0;
assert_eq!(result, SideOffsets2D::new(3.0, 6.0, 9.0, 12.0));
}
#[test]
fn test_mul_assign_scalar() {
let mut s = SideOffsets2D::new(1.0, 2.0, 3.0, 4.0);
s *= 2.0;
assert_eq!(s, SideOffsets2D::new(2.0, 4.0, 6.0, 8.0));
}
#[test]
fn test_mul_scale() {
let s = SideOffsets2DMm::new(0.0, 1.0, 3.0, 2.0);
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
let result = s * cm_per_mm;
assert_eq!(result, SideOffsets2DCm::new(0.0, 0.1, 0.3, 0.2));
}
#[test]
fn test_mul_assign_scale() {
let mut s = SideOffsets2DMm::new(2.0, 4.0, 6.0, 8.0);
let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
s *= scale;
assert_eq!(s, SideOffsets2DMm::new(0.2, 0.4, 0.6, 0.8));
}
#[test]
fn test_div_scalar() {
let s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
let result = s / 10.0;
assert_eq!(result, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
}
#[test]
fn test_div_assign_scalar() {
let mut s = SideOffsets2D::new(10.0, 20.0, 30.0, 40.0);
s /= 10.0;
assert_eq!(s, SideOffsets2D::new(1.0, 2.0, 3.0, 4.0));
}
#[test]
fn test_div_scale() {
let s = SideOffsets2DCm::new(0.1, 0.2, 0.3, 0.4);
let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
let result = s / cm_per_mm;
assert_eq!(result, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
}
#[test]
fn test_div_assign_scale() {
let mut s = SideOffsets2DMm::new(0.1, 0.2, 0.3, 0.4);
let scale: Scale<f32, Mm, Mm> = Scale::new(0.1);
s /= scale;
assert_eq!(s, SideOffsets2DMm::new(1.0, 2.0, 3.0, 4.0));
}
}

1877
vendor/euclid/src/size.rs vendored Normal file

File diff suppressed because it is too large Load Diff

857
vendor/euclid/src/transform2d.rs vendored Normal file
View File

@@ -0,0 +1,857 @@
// 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.
#![allow(clippy::just_underscores_and_digits)]
use super::{Angle, UnknownUnit};
use crate::approxeq::ApproxEq;
use crate::box2d::Box2D;
use crate::num::{One, Zero};
use crate::point::{point2, Point2D};
use crate::rect::Rect;
use crate::transform3d::Transform3D;
use crate::trig::Trig;
use crate::vector::{vec2, Vector2D};
use core::cmp::{Eq, PartialEq};
use core::fmt;
use core::hash::Hash;
use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Sub};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
#[cfg(feature = "mint")]
use mint;
use num_traits::NumCast;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A 2d transform represented by a column-major 3 by 3 matrix, compressed down to 3 by 2.
///
/// Transforms can be parametrized over the source and destination units, to describe a
/// transformation from a space to another.
/// For example, `Transform2D<f32, WorldSpace, ScreenSpace>::transform_point4d`
/// takes a `Point2D<f32, WorldSpace>` and returns a `Point2D<f32, ScreenSpace>`.
///
/// Transforms expose a set of convenience methods for pre- and post-transformations.
/// Pre-transformations (`pre_*` methods) correspond to adding an operation that is
/// applied before the rest of the transformation, while post-transformations (`then_*`
/// methods) add an operation that is applied after.
///
/// The matrix representation is conceptually equivalent to a 3 by 3 matrix transformation
/// compressed to 3 by 2 with the components that aren't needed to describe the set of 2d
/// transformations we are interested in implicitly defined:
///
/// ```text
/// | m11 m21 m31 | |x| |x'|
/// | m12 m22 m32 | x |y| = |y'|
/// | 0 0 1 | |1| |1 |
/// ```
///
/// When translating `Transform2D` into general matrix representations, consider that the
/// representation follows the column-major notation with column vectors.
///
/// The translation terms are `m31` and `m32`.
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))
)]
#[rustfmt::skip]
pub struct Transform2D<T, Src, Dst> {
pub m11: T, pub m12: T,
pub m21: T, pub m22: T,
pub m31: T, pub m32: T,
#[doc(hidden)]
pub _unit: PhantomData<(Src, Dst)>,
}
#[cfg(feature = "arbitrary")]
impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Transform2D<T, Src, Dst>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let (m11, m12, m21, m22, m31, m32) = arbitrary::Arbitrary::arbitrary(u)?;
Ok(Transform2D {
m11,
m12,
m21,
m22,
m31,
m32,
_unit: PhantomData,
})
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, Src, Dst> Zeroable for Transform2D<T, Src, Dst> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Transform2D<T, Src, Dst> {}
impl<T: Copy, Src, Dst> Copy for Transform2D<T, Src, Dst> {}
impl<T: Clone, Src, Dst> Clone for Transform2D<T, Src, Dst> {
fn clone(&self) -> Self {
Transform2D {
m11: self.m11.clone(),
m12: self.m12.clone(),
m21: self.m21.clone(),
m22: self.m22.clone(),
m31: self.m31.clone(),
m32: self.m32.clone(),
_unit: PhantomData,
}
}
}
impl<T, Src, Dst> Eq for Transform2D<T, Src, Dst> where T: Eq {}
impl<T, Src, Dst> PartialEq for Transform2D<T, Src, Dst>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.m11 == other.m11
&& self.m12 == other.m12
&& self.m21 == other.m21
&& self.m22 == other.m22
&& self.m31 == other.m31
&& self.m32 == other.m32
}
}
impl<T, Src, Dst> Hash for Transform2D<T, Src, Dst>
where
T: Hash,
{
fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
self.m11.hash(h);
self.m12.hash(h);
self.m21.hash(h);
self.m22.hash(h);
self.m31.hash(h);
self.m32.hash(h);
}
}
impl<T, Src, Dst> Transform2D<T, Src, Dst> {
/// Create a transform specifying its components in using the column-major-column-vector
/// matrix notation.
///
/// For example, the translation terms m31 and m32 are the last two parameters parameters.
///
/// ```
/// use euclid::default::Transform2D;
/// let tx = 1.0;
/// let ty = 2.0;
/// let translation = Transform2D::new(
/// 1.0, 0.0,
/// 0.0, 1.0,
/// tx, ty,
/// );
/// ```
#[rustfmt::skip]
pub const fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
Transform2D {
m11, m12,
m21, m22,
m31, m32,
_unit: PhantomData,
}
}
/// Returns `true` if this transform is approximately equal to the other one, using
/// `T`'s default epsilon value.
///
/// The same as [`ApproxEq::approx_eq`] but available without importing trait.
#[inline]
pub fn approx_eq(&self, other: &Self) -> bool
where
T: ApproxEq<T>,
{
<Self as ApproxEq<T>>::approx_eq(&self, &other)
}
/// Returns `true` if this transform is approximately equal to the other one, using
/// a provided epsilon value.
///
/// The same as [`ApproxEq::approx_eq_eps`] but available without importing trait.
#[inline]
pub fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool
where
T: ApproxEq<T>,
{
<Self as ApproxEq<T>>::approx_eq_eps(&self, &other, &eps)
}
}
impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> {
/// Returns an array containing this transform's terms.
///
/// The terms are laid out in the same order as they are
/// specified in [`Transform2D::new`], that is following the
/// column-major-column-vector matrix notation.
///
/// For example the translation terms are found in the
/// last two slots of the array.
#[inline]
#[rustfmt::skip]
pub fn to_array(&self) -> [T; 6] {
[
self.m11, self.m12,
self.m21, self.m22,
self.m31, self.m32
]
}
/// Returns an array containing this transform's terms transposed.
///
/// The terms are laid out in transposed order from the same order of
/// `Transform3D::new` and `Transform3D::to_array`, that is following
/// the row-major-column-vector matrix notation.
///
/// For example the translation terms are found at indices 2 and 5
/// in the array.
#[inline]
#[rustfmt::skip]
pub fn to_array_transposed(&self) -> [T; 6] {
[
self.m11, self.m21, self.m31,
self.m12, self.m22, self.m32
]
}
/// Equivalent to `to_array` with elements packed two at a time
/// in an array of arrays.
#[inline]
pub fn to_arrays(&self) -> [[T; 2]; 3] {
[
[self.m11, self.m12],
[self.m21, self.m22],
[self.m31, self.m32],
]
}
/// Create a transform providing its components via an array
/// of 6 elements instead of as individual parameters.
///
/// The order of the components corresponds to the
/// column-major-column-vector matrix notation (the same order
/// as `Transform2D::new`).
#[inline]
#[rustfmt::skip]
pub fn from_array(array: [T; 6]) -> Self {
Self::new(
array[0], array[1],
array[2], array[3],
array[4], array[5],
)
}
/// Equivalent to `from_array` with elements packed two at a time
/// in an array of arrays.
///
/// The order of the components corresponds to the
/// column-major-column-vector matrix notation (the same order
/// as `Transform3D::new`).
#[inline]
#[rustfmt::skip]
pub fn from_arrays(array: [[T; 2]; 3]) -> Self {
Self::new(
array[0][0], array[0][1],
array[1][0], array[1][1],
array[2][0], array[2][1],
)
}
/// Drop the units, preserving only the numeric value.
#[inline]
#[rustfmt::skip]
pub fn to_untyped(&self) -> Transform2D<T, UnknownUnit, UnknownUnit> {
Transform2D::new(
self.m11, self.m12,
self.m21, self.m22,
self.m31, self.m32
)
}
/// Tag a unitless value with units.
#[inline]
#[rustfmt::skip]
pub fn from_untyped(p: &Transform2D<T, UnknownUnit, UnknownUnit>) -> Self {
Transform2D::new(
p.m11, p.m12,
p.m21, p.m22,
p.m31, p.m32
)
}
/// Returns the same transform with a different source unit.
#[inline]
#[rustfmt::skip]
pub fn with_source<NewSrc>(&self) -> Transform2D<T, NewSrc, Dst> {
Transform2D::new(
self.m11, self.m12,
self.m21, self.m22,
self.m31, self.m32,
)
}
/// Returns the same transform with a different destination unit.
#[inline]
#[rustfmt::skip]
pub fn with_destination<NewDst>(&self) -> Transform2D<T, Src, NewDst> {
Transform2D::new(
self.m11, self.m12,
self.m21, self.m22,
self.m31, self.m32,
)
}
/// Create a 3D transform from the current transform
pub fn to_3d(&self) -> Transform3D<T, Src, Dst>
where
T: Zero + One,
{
Transform3D::new_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
}
}
impl<T: NumCast + Copy, Src, Dst> Transform2D<T, Src, Dst> {
/// Cast from one numeric representation to another, preserving the units.
#[inline]
pub fn cast<NewT: NumCast>(&self) -> Transform2D<NewT, Src, Dst> {
self.try_cast().unwrap()
}
/// Fallible cast from one numeric representation to another, preserving the units.
#[rustfmt::skip]
pub fn try_cast<NewT: NumCast>(&self) -> Option<Transform2D<NewT, Src, Dst>> {
match (NumCast::from(self.m11), NumCast::from(self.m12),
NumCast::from(self.m21), NumCast::from(self.m22),
NumCast::from(self.m31), NumCast::from(self.m32)) {
(Some(m11), Some(m12),
Some(m21), Some(m22),
Some(m31), Some(m32)) => {
Some(Transform2D::new(
m11, m12,
m21, m22,
m31, m32
))
},
_ => None
}
}
}
impl<T, Src, Dst> Transform2D<T, Src, Dst>
where
T: Zero + One,
{
/// Create an identity matrix:
///
/// ```text
/// 1 0
/// 0 1
/// 0 0
/// ```
#[inline]
pub fn identity() -> Self {
Self::translation(T::zero(), T::zero())
}
/// Intentional not public, because it checks for exact equivalence
/// while most consumers will probably want some sort of approximate
/// equivalence to deal with floating-point errors.
fn is_identity(&self) -> bool
where
T: PartialEq,
{
*self == Self::identity()
}
}
/// Methods for combining generic transformations
impl<T, Src, Dst> Transform2D<T, Src, Dst>
where
T: Copy + Add<Output = T> + Mul<Output = T>,
{
/// Returns the multiplication of the two matrices such that mat's transformation
/// applies after self's transformation.
#[must_use]
#[rustfmt::skip]
pub fn then<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> {
Transform2D::new(
self.m11 * mat.m11 + self.m12 * mat.m21,
self.m11 * mat.m12 + self.m12 * mat.m22,
self.m21 * mat.m11 + self.m22 * mat.m21,
self.m21 * mat.m12 + self.m22 * mat.m22,
self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
)
}
}
/// Methods for creating and combining translation transformations
impl<T, Src, Dst> Transform2D<T, Src, Dst>
where
T: Zero + One,
{
/// Create a 2d translation transform:
///
/// ```text
/// 1 0
/// 0 1
/// x y
/// ```
#[inline]
#[rustfmt::skip]
pub fn translation(x: T, y: T) -> Self {
let _0 = || T::zero();
let _1 = || T::one();
Self::new(
_1(), _0(),
_0(), _1(),
x, y,
)
}
/// Applies a translation after self's transformation and returns the resulting transform.
#[inline]
#[must_use]
pub fn then_translate(&self, v: Vector2D<T, Dst>) -> Self
where
T: Copy + Add<Output = T> + Mul<Output = T>,
{
self.then(&Transform2D::translation(v.x, v.y))
}
/// Applies a translation before self's transformation and returns the resulting transform.
#[inline]
#[must_use]
pub fn pre_translate(&self, v: Vector2D<T, Src>) -> Self
where
T: Copy + Add<Output = T> + Mul<Output = T>,
{
Transform2D::translation(v.x, v.y).then(self)
}
}
/// Methods for creating and combining rotation transformations
impl<T, Src, Dst> Transform2D<T, Src, Dst>
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig,
{
/// Returns a rotation transform.
#[inline]
#[rustfmt::skip]
pub fn rotation(theta: Angle<T>) -> Self {
let _0 = Zero::zero();
let cos = theta.get().cos();
let sin = theta.get().sin();
Transform2D::new(
cos, sin,
_0 - sin, cos,
_0, _0
)
}
/// Applies a rotation after self's transformation and returns the resulting transform.
#[inline]
#[must_use]
pub fn then_rotate(&self, theta: Angle<T>) -> Self {
self.then(&Transform2D::rotation(theta))
}
/// Applies a rotation before self's transformation and returns the resulting transform.
#[inline]
#[must_use]
pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
Transform2D::rotation(theta).then(self)
}
}
/// Methods for creating and combining scale transformations
impl<T, Src, Dst> Transform2D<T, Src, Dst> {
/// Create a 2d scale transform:
///
/// ```text
/// x 0
/// 0 y
/// 0 0
/// ```
#[inline]
#[rustfmt::skip]
pub fn scale(x: T, y: T) -> Self
where
T: Zero,
{
let _0 = || Zero::zero();
Self::new(
x, _0(),
_0(), y,
_0(), _0(),
)
}
/// Applies a scale after self's transformation and returns the resulting transform.
#[inline]
#[must_use]
pub fn then_scale(&self, x: T, y: T) -> Self
where
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
{
self.then(&Transform2D::scale(x, y))
}
/// Applies a scale before self's transformation and returns the resulting transform.
#[inline]
#[must_use]
#[rustfmt::skip]
pub fn pre_scale(&self, x: T, y: T) -> Self
where
T: Copy + Mul<Output = T>,
{
Transform2D::new(
self.m11 * x, self.m12 * x,
self.m21 * y, self.m22 * y,
self.m31, self.m32
)
}
}
/// Methods for apply transformations to objects
impl<T, Src, Dst> Transform2D<T, Src, Dst>
where
T: Copy + Add<Output = T> + Mul<Output = T>,
{
/// Returns the given point transformed by this transform.
#[inline]
#[must_use]
pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> {
Point2D::new(
point.x * self.m11 + point.y * self.m21 + self.m31,
point.x * self.m12 + point.y * self.m22 + self.m32,
)
}
/// Returns the given vector transformed by this matrix.
#[inline]
#[must_use]
pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T, Dst> {
vec2(
vec.x * self.m11 + vec.y * self.m21,
vec.x * self.m12 + vec.y * self.m22,
)
}
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
/// transform.
#[inline]
#[must_use]
pub fn outer_transformed_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst>
where
T: Sub<Output = T> + Zero + PartialOrd,
{
let min = rect.min();
let max = rect.max();
Rect::from_points(&[
self.transform_point(min),
self.transform_point(max),
self.transform_point(point2(max.x, min.y)),
self.transform_point(point2(min.x, max.y)),
])
}
/// Returns a box that encompasses the result of transforming the given box by this
/// transform.
#[inline]
#[must_use]
pub fn outer_transformed_box(&self, b: &Box2D<T, Src>) -> Box2D<T, Dst>
where
T: Sub<Output = T> + Zero + PartialOrd,
{
Box2D::from_points(&[
self.transform_point(b.min),
self.transform_point(b.max),
self.transform_point(point2(b.max.x, b.min.y)),
self.transform_point(point2(b.min.x, b.max.y)),
])
}
}
impl<T, Src, Dst> Transform2D<T, Src, Dst>
where
T: Copy + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + PartialEq + Zero + One,
{
/// Computes and returns the determinant of this transform.
pub fn determinant(&self) -> T {
self.m11 * self.m22 - self.m12 * self.m21
}
/// Returns whether it is possible to compute the inverse transform.
#[inline]
pub fn is_invertible(&self) -> bool {
self.determinant() != Zero::zero()
}
/// Returns the inverse transform if possible.
#[must_use]
pub fn inverse(&self) -> Option<Transform2D<T, Dst, Src>> {
let det = self.determinant();
let _0: T = Zero::zero();
let _1: T = One::one();
if det == _0 {
return None;
}
let inv_det = _1 / det;
Some(Transform2D::new(
inv_det * self.m22,
inv_det * (_0 - self.m12),
inv_det * (_0 - self.m21),
inv_det * self.m11,
inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
))
}
}
impl<T, Src, Dst> Default for Transform2D<T, Src, Dst>
where
T: Zero + One,
{
/// Returns the [identity transform](Transform2D::identity).
fn default() -> Self {
Self::identity()
}
}
impl<T: ApproxEq<T>, Src, Dst> ApproxEq<T> for Transform2D<T, Src, Dst> {
#[inline]
fn approx_epsilon() -> T {
T::approx_epsilon()
}
/// Returns `true` if this transform is approximately equal to the other one, using
/// a provided epsilon value.
fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool {
self.m11.approx_eq_eps(&other.m11, eps)
&& self.m12.approx_eq_eps(&other.m12, eps)
&& self.m21.approx_eq_eps(&other.m21, eps)
&& self.m22.approx_eq_eps(&other.m22, eps)
&& self.m31.approx_eq_eps(&other.m31, eps)
&& self.m32.approx_eq_eps(&other.m32, eps)
}
}
impl<T, Src, Dst> fmt::Debug for Transform2D<T, Src, Dst>
where
T: Copy + fmt::Debug + PartialEq + One + Zero,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_identity() {
write!(f, "[I]")
} else {
self.to_array().fmt(f)
}
}
}
#[cfg(feature = "mint")]
impl<T, Src, Dst> From<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> {
#[rustfmt::skip]
fn from(m: mint::RowMatrix3x2<T>) -> Self {
Transform2D {
m11: m.x.x, m12: m.x.y,
m21: m.y.x, m22: m.y.y,
m31: m.z.x, m32: m.z.y,
_unit: PhantomData,
}
}
}
#[cfg(feature = "mint")]
impl<T, Src, Dst> From<Transform2D<T, Src, Dst>> for mint::RowMatrix3x2<T> {
fn from(t: Transform2D<T, Src, Dst>) -> Self {
mint::RowMatrix3x2 {
x: mint::Vector2 { x: t.m11, y: t.m12 },
y: mint::Vector2 { x: t.m21, y: t.m22 },
z: mint::Vector2 { x: t.m31, y: t.m32 },
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::approxeq::ApproxEq;
use crate::default;
#[cfg(feature = "mint")]
use mint;
use core::f32::consts::FRAC_PI_2;
type Mat = default::Transform2D<f32>;
fn rad(v: f32) -> Angle<f32> {
Angle::radians(v)
}
#[test]
pub fn test_translation() {
let t1 = Mat::translation(1.0, 2.0);
let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
let t3 = Mat::identity().then_translate(vec2(1.0, 2.0));
assert_eq!(t1, t2);
assert_eq!(t1, t3);
assert_eq!(
t1.transform_point(Point2D::new(1.0, 1.0)),
Point2D::new(2.0, 3.0)
);
assert_eq!(t1.then(&t1), Mat::translation(2.0, 4.0));
}
#[test]
pub fn test_rotation() {
let r1 = Mat::rotation(rad(FRAC_PI_2));
let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
let r3 = Mat::identity().then_rotate(rad(FRAC_PI_2));
assert_eq!(r1, r2);
assert_eq!(r1, r3);
assert!(r1
.transform_point(Point2D::new(1.0, 2.0))
.approx_eq(&Point2D::new(-2.0, 1.0)));
assert!(r1.then(&r1).approx_eq(&Mat::rotation(rad(FRAC_PI_2 * 2.0))));
}
#[test]
pub fn test_scale() {
let s1 = Mat::scale(2.0, 3.0);
let s2 = Mat::identity().pre_scale(2.0, 3.0);
let s3 = Mat::identity().then_scale(2.0, 3.0);
assert_eq!(s1, s2);
assert_eq!(s1, s3);
assert!(s1
.transform_point(Point2D::new(2.0, 2.0))
.approx_eq(&Point2D::new(4.0, 6.0)));
}
#[test]
pub fn test_pre_then_scale() {
let m = Mat::rotation(rad(FRAC_PI_2)).then_translate(vec2(6.0, 7.0));
let s = Mat::scale(2.0, 3.0);
assert_eq!(m.then(&s), m.then_scale(2.0, 3.0));
}
#[test]
pub fn test_inverse_simple() {
let m1 = Mat::identity();
let m2 = m1.inverse().unwrap();
assert!(m1.approx_eq(&m2));
}
#[test]
pub fn test_inverse_scale() {
let m1 = Mat::scale(1.5, 0.3);
let m2 = m1.inverse().unwrap();
assert!(m1.then(&m2).approx_eq(&Mat::identity()));
assert!(m2.then(&m1).approx_eq(&Mat::identity()));
}
#[test]
pub fn test_inverse_translate() {
let m1 = Mat::translation(-132.0, 0.3);
let m2 = m1.inverse().unwrap();
assert!(m1.then(&m2).approx_eq(&Mat::identity()));
assert!(m2.then(&m1).approx_eq(&Mat::identity()));
}
#[test]
fn test_inverse_none() {
assert!(Mat::scale(2.0, 0.0).inverse().is_none());
assert!(Mat::scale(2.0, 2.0).inverse().is_some());
}
#[test]
pub fn test_pre_post() {
let m1 = default::Transform2D::identity()
.then_scale(1.0, 2.0)
.then_translate(vec2(1.0, 2.0));
let m2 = default::Transform2D::identity()
.pre_translate(vec2(1.0, 2.0))
.pre_scale(1.0, 2.0);
assert!(m1.approx_eq(&m2));
let r = Mat::rotation(rad(FRAC_PI_2));
let t = Mat::translation(2.0, 3.0);
let a = Point2D::new(1.0, 1.0);
assert!(r
.then(&t)
.transform_point(a)
.approx_eq(&Point2D::new(1.0, 4.0)));
assert!(t
.then(&r)
.transform_point(a)
.approx_eq(&Point2D::new(-4.0, 3.0)));
assert!(t
.then(&r)
.transform_point(a)
.approx_eq(&r.transform_point(t.transform_point(a))));
}
#[test]
fn test_size_of() {
use core::mem::size_of;
assert_eq!(size_of::<default::Transform2D<f32>>(), 6 * size_of::<f32>());
assert_eq!(size_of::<default::Transform2D<f64>>(), 6 * size_of::<f64>());
}
#[test]
pub fn test_is_identity() {
let m1 = default::Transform2D::identity();
assert!(m1.is_identity());
let m2 = m1.then_translate(vec2(0.1, 0.0));
assert!(!m2.is_identity());
}
#[test]
pub fn test_transform_vector() {
// Translation does not apply to vectors.
let m1 = Mat::translation(1.0, 1.0);
let v1 = vec2(10.0, -10.0);
assert_eq!(v1, m1.transform_vector(v1));
}
#[cfg(feature = "mint")]
#[test]
pub fn test_mint() {
let m1 = Mat::rotation(rad(FRAC_PI_2));
let mm: mint::RowMatrix3x2<_> = m1.into();
let m2 = Mat::from(mm);
assert_eq!(m1, m2);
}
}

1531
vendor/euclid/src/transform3d.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1045
vendor/euclid/src/translation.rs vendored Normal file

File diff suppressed because it is too large Load Diff

80
vendor/euclid/src/trig.rs vendored Normal file
View File

@@ -0,0 +1,80 @@
// 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.
/// Trait for basic trigonometry functions, so they can be used on generic numeric types
pub trait Trig {
fn sin(self) -> Self;
fn cos(self) -> Self;
fn tan(self) -> Self;
fn fast_atan2(y: Self, x: Self) -> Self;
fn degrees_to_radians(deg: Self) -> Self;
fn radians_to_degrees(rad: Self) -> Self;
}
macro_rules! trig {
($ty:ident) => {
impl Trig for $ty {
#[inline]
fn sin(self) -> $ty {
num_traits::Float::sin(self)
}
#[inline]
fn cos(self) -> $ty {
num_traits::Float::cos(self)
}
#[inline]
fn tan(self) -> $ty {
num_traits::Float::tan(self)
}
/// A slightly faster approximation of `atan2`.
///
/// Note that it does not deal with the case where both x and y are 0.
#[inline]
fn fast_atan2(y: $ty, x: $ty) -> $ty {
// This macro is used with f32 and f64 and clippy warns about the extra
// precision with f32.
#![allow(clippy::excessive_precision)]
// See https://math.stackexchange.com/questions/1098487/atan2-faster-approximation#1105038
use core::$ty::consts;
let x_abs = num_traits::Float::abs(x);
let y_abs = num_traits::Float::abs(y);
let a = x_abs.min(y_abs) / x_abs.max(y_abs);
let s = a * a;
let mut result =
((-0.046_496_474_9 * s + 0.159_314_22) * s - 0.327_622_764) * s * a + a;
if y_abs > x_abs {
result = consts::FRAC_PI_2 - result;
}
if x < 0.0 {
result = consts::PI - result
}
if y < 0.0 {
result = -result
}
result
}
#[inline]
fn degrees_to_radians(deg: Self) -> Self {
deg.to_radians()
}
#[inline]
fn radians_to_degrees(rad: Self) -> Self {
rad.to_degrees()
}
}
};
}
trig!(f32);
trig!(f64);

2686
vendor/euclid/src/vector.rs vendored Normal file

File diff suppressed because it is too large Load Diff