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};
#[cfg(feature = "malloc_size_of")]
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use num_traits::NumCast;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[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)
}
#[inline]
pub fn identity() -> Self
where
T: One,
{
Scale::new(T::one())
}
#[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)
}
#[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)
}
#[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)
}
#[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)
}
#[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),
)
}
#[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),
}
}
#[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),
}
}
#[inline]
pub fn is_identity(self) -> bool
where
T: PartialEq + One,
{
self.0 == T::one()
}
#[inline]
pub fn get(self) -> T {
self.0
}
pub fn inverse(self) -> Scale<T::Output, Dst, Src>
where
T: One + Div,
{
let one: T = One::one();
Scale::new(one / self.0)
}
#[inline]
pub fn with_source<NewSrc>(self) -> Scale<T, NewSrc, Dst> {
Scale::new(self.0)
}
#[inline]
pub fn with_destination<NewDst>(self) -> Scale<T, Src, NewDst> {
Scale::new(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))
}
#[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> {
#[inline]
pub fn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> {
self.try_cast().unwrap()
}
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> {}
#[cfg(feature = "malloc_size_of")]
impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for Scale<T, Src, Dst> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.0.size_of(ops)
}
}
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)
}
}
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)
}
}
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)
}
}
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)]
#[cfg(any(feature = "std", feature = "libm"))]
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;
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));
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);
}
}