use num::One;
use num_traits::NumCast;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use core::fmt;
use core::ops::{Add, Div, Mul, Neg, Sub};
use core::marker::PhantomData;
use {TypedPoint2D, TypedRect, TypedSize2D, TypedVector2D};
#[repr(C)]
pub struct TypedScale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
#[cfg(feature = "serde")]
impl<'de, T, Src, Dst> Deserialize<'de> for TypedScale<T, Src, Dst>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<TypedScale<T, Src, Dst>, D::Error>
where
D: Deserializer<'de>,
{
Ok(TypedScale(
try!(Deserialize::deserialize(deserializer)),
PhantomData,
))
}
}
#[cfg(feature = "serde")]
impl<T, Src, Dst> Serialize for TypedScale<T, Src, Dst>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<T, Src, Dst> TypedScale<T, Src, Dst> {
pub fn new(x: T) -> Self {
TypedScale(x, PhantomData)
}
}
impl<T: Clone, Src, Dst> TypedScale<T, Src, Dst> {
pub fn get(&self) -> T {
self.0.clone()
}
}
impl<Src, Dst> TypedScale<f32, Src, Dst> {
pub const ONE: Self = TypedScale(1.0, PhantomData);
}
impl<T: Clone + One + Div<T, Output = T>, Src, Dst> TypedScale<T, Src, Dst> {
pub fn inv(&self) -> TypedScale<T, Dst, Src> {
let one: T = One::one();
TypedScale::new(one / self.get())
}
}
impl<T: Clone + Mul<T, Output = T>, A, B, C> Mul<TypedScale<T, B, C>> for TypedScale<T, A, B> {
type Output = TypedScale<T, A, C>;
#[inline]
fn mul(self, other: TypedScale<T, B, C>) -> TypedScale<T, A, C> {
TypedScale::new(self.get() * other.get())
}
}
impl<T: Clone + Add<T, Output = T>, Src, Dst> Add for TypedScale<T, Src, Dst> {
type Output = TypedScale<T, Src, Dst>;
#[inline]
fn add(self, other: TypedScale<T, Src, Dst>) -> TypedScale<T, Src, Dst> {
TypedScale::new(self.get() + other.get())
}
}
impl<T: Clone + Sub<T, Output = T>, Src, Dst> Sub for TypedScale<T, Src, Dst> {
type Output = TypedScale<T, Src, Dst>;
#[inline]
fn sub(self, other: TypedScale<T, Src, Dst>) -> TypedScale<T, Src, Dst> {
TypedScale::new(self.get() - other.get())
}
}
impl<T: NumCast + Clone, Src, Dst0> TypedScale<T, Src, Dst0> {
pub fn cast<T1: NumCast + Clone>(&self) -> TypedScale<T1, Src, Dst0> {
self.try_cast().unwrap()
}
pub fn try_cast<T1: NumCast + Clone>(&self) -> Option<TypedScale<T1, Src, Dst0>> {
NumCast::from(self.get()).map(TypedScale::new)
}
}
impl<T, Src, Dst> TypedScale<T, Src, Dst>
where
T: Copy + Clone + Mul<T, Output = T> + Neg<Output = T> + PartialEq + One,
{
#[inline]
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
TypedPoint2D::new(point.x * self.get(), point.y * self.get())
}
#[inline]
pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
TypedVector2D::new(vec.x * self.get(), vec.y * self.get())
}
#[inline]
pub fn transform_size(&self, size: &TypedSize2D<T, Src>) -> TypedSize2D<T, Dst> {
TypedSize2D::new(size.width * self.get(), size.height * self.get())
}
#[inline]
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
TypedRect::new(
self.transform_point(&rect.origin),
self.transform_size(&rect.size),
)
}
#[inline]
pub fn inverse(&self) -> TypedScale<T, Dst, Src> {
TypedScale::new(-self.get())
}
#[inline]
pub fn is_identity(&self) -> bool {
self.get() == T::one()
}
}
impl<T: PartialEq, Src, Dst> PartialEq for TypedScale<T, Src, Dst> {
fn eq(&self, other: &TypedScale<T, Src, Dst>) -> bool {
self.0 == other.0
}
}
impl<T: Clone, Src, Dst> Clone for TypedScale<T, Src, Dst> {
fn clone(&self) -> TypedScale<T, Src, Dst> {
TypedScale::new(self.get())
}
}
impl<T: Copy, Src, Dst> Copy for TypedScale<T, Src, Dst> {}
impl<T: fmt::Debug, Src, Dst> fmt::Debug for TypedScale<T, Src, Dst> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: fmt::Display, Src, Dst> fmt::Display for TypedScale<T, Src, Dst> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::TypedScale;
enum Inch {}
enum Cm {}
enum Mm {}
#[test]
fn test_scale() {
let mm_per_inch: TypedScale<f32, Inch, Mm> = TypedScale::new(25.4);
let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
let mm_per_cm: TypedScale<f32, Cm, Mm> = cm_per_mm.inv();
assert_eq!(mm_per_cm.get(), 10.0);
let cm_per_inch: TypedScale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
assert_eq!(cm_per_inch, TypedScale::new(2.54));
let a: TypedScale<isize, Inch, Inch> = TypedScale::new(2);
let b: TypedScale<isize, Inch, Inch> = TypedScale::new(3);
assert!(a != b);
assert_eq!(a, a.clone());
assert_eq!(a.clone() + b.clone(), TypedScale::new(5));
assert_eq!(a - b, TypedScale::new(-1));
}
}