use core::default::Default;
use num_traits::{ToPrimitive, Zero};
use crate::basenum::Primitive;
use crate::vec::traits::GenVec;
use crate::vec::vec::*;
pub trait ToPrim: Primitive {
fn to_i32(&self) -> Option<i32>;
fn to_u32(&self) -> Option<u32>;
fn to_f32(&self) -> Option<f32>;
fn to_f64(&self) -> Option<f64>;
fn to_bool(&self) -> Option<bool>;
}
macro_rules! impl_ToPrim_for {
($($t: ident),+) => {
$(
impl ToPrim for $t {
#[inline]
fn to_i32(&self) -> Option<i32> {
ToPrimitive::to_i32(self)
}
#[inline]
fn to_u32(&self) -> Option<u32> {
ToPrimitive::to_u32(self)
}
#[inline]
fn to_f32(&self) -> Option<f32> {
ToPrimitive::to_f32(self)
}
#[inline]
fn to_f64(&self) -> Option<f64> {
ToPrimitive::to_f64(self)
}
#[inline]
fn to_bool(&self) -> Option<bool> {
let b = if self.is_zero() { false } else { true };
Some(b)
}
}
)+
};
}
impl_ToPrim_for! { i32, u32, f32, f64 }
impl ToPrim for bool {
#[inline]
fn to_i32(&self) -> Option<i32> {
let i = if *self { 1 } else { 0 };
Some(i)
}
#[inline]
fn to_u32(&self) -> Option<u32> {
let i = if *self { 1 } else { 0 };
Some(i)
}
#[inline]
fn to_f32(&self) -> Option<f32> {
let i = if *self { 1. } else { 0. };
Some(i)
}
#[inline]
fn to_f64(&self) -> Option<f64> {
let i = if *self { 1. } else { 0. };
Some(i)
}
#[inline]
fn to_bool(&self) -> Option<bool> {
Some(*self)
}
}
pub trait PrimCast: ToPrim {
fn from<F: ToPrim>(p: F) -> Option<Self>;
}
macro_rules! impl_PrimCast_for {
($({ $t: ident, $cf: ident }),+) => {
$(
impl PrimCast for $t {
#[inline]
fn from<F: ToPrim>(p: F) -> Option<$t> {
p.$cf()
}
}
)+
}
}
impl_PrimCast_for! {
{ i32, to_i32 },
{ u32, to_u32 },
{ f32, to_f32 },
{ f64, to_f64 },
{ bool, to_bool }
}
pub trait GenPrimitive {
type BaseType: PrimCast;
}
macro_rules! impl_GenPrimitive_for_scalar {
($($t: ident),+) => {
$(
impl GenPrimitive for $t {
type BaseType = $t;
}
)+
}
}
impl_GenPrimitive_for_scalar! { i32, u32, f32, f64, bool }
macro_rules! impl_GenPrimitive_for_vector {
($($t: ident),+) => {
$(
impl<T: PrimCast> GenPrimitive for $t<T> {
type BaseType = T;
}
)+
}
}
impl_GenPrimitive_for_vector! {
Vector2, Vector3, Vector4
}
pub trait ToScalar<F: PrimCast, T: PrimCast>: GenPrimitive<BaseType = F> {
fn to(self) -> Option<T>;
}
macro_rules! impl_ToScalar_for_scalar {
($($t: ident),+) => {
$(
impl<T: PrimCast> ToScalar<$t, T> for $t {
#[inline(always)]
fn to(self) -> Option<T> {
T::from(self)
}
}
)+
}
}
impl_ToScalar_for_scalar! { i32, u32, f32, f64, bool }
macro_rules! impl_ToScalar_for_vector {
($($t: ident),+) => {
$(
impl<F: PrimCast, T: PrimCast> ToScalar<F, T> for $t<F> {
#[inline(always)]
fn to(self) -> Option<T> {
T::from(self[0])
}
}
)+
}
}
impl_ToScalar_for_vector! { Vector2, Vector3, Vector4 }
macro_rules! def_cast_scalar_fun {
($({ $nm: ident, $t: ty }),+) => {
$(
pub fn $nm<B: PrimCast, F: ToScalar<B, $t>>(from: F) -> $t {
from.to().unwrap()
}
)+
}
}
def_cast_scalar_fun! {
{ int, i32 },
{ uint, u32 },
{ float, f32 },
{ double, f64 },
{ boolean, bool }
}
pub trait ToVector<F: PrimCast, T: PrimCast, GT: GenPrimitive<BaseType = T> + GenVec<T>>:
GenPrimitive<BaseType = F>
{
fn to(self) -> Option<GT>;
}
macro_rules! impl_ToVector_for_scalar {
($t: ident, $v: ident, $($field: ident),+) => {
impl<T: PrimCast> ToVector<$t, T, $v<T>> for $t {
#[inline]
fn to(self) -> Option<$v<T>> {
PrimCast::from(self).map(|t| -> $v<T> {
$v { $($field: t),+ }
})
}
}
}
}
macro_rules! impl_ToVectors_for_scalar {
($($t: ident),+) => {
$(
impl_ToVector_for_scalar! { $t, Vector2, x, y }
impl_ToVector_for_scalar! { $t, Vector3, x, y, z }
impl_ToVector_for_scalar! { $t, Vector4, x, y, z, w }
)+
}
}
impl_ToVectors_for_scalar! { i32, u32, f32, f64, bool }
macro_rules! impl_ToVector_for_vector {
($({ $v: ident, $($field: ident),+ }),+) => {
$(
impl<F: PrimCast, T: PrimCast + Default> ToVector<F, T, $v<T>> for $v<F> {
#[inline]
fn to(self) -> Option<$v<T>> {
let os = [$(T::from(self.$field)),+];
if os.iter().any(|&o| -> bool { o.is_none() }) {
None
} else {
let mut zero: $v<T> = $v { $($field: Default::default()),+ };
os.iter().fold(0, |i, &o| -> usize {
zero[i] = o.unwrap();
i + 1
});
Some(zero)
}
}
}
)+
}
}
impl_ToVector_for_vector! {
{ Vector2, x, y },
{ Vector3, x, y, z },
{ Vector4, x, y, z, w }
}
macro_rules! def_cast_vector_fun {
($({ $nm: ident, $s: ty, $v: ty }),+) => {
$(
#[inline]
pub fn $nm<B: PrimCast, F: ToVector<B, $s, $v>>(gp: F) -> $v {
gp.to().unwrap()
}
)+
}
}
def_cast_vector_fun! {
{ to_ivec2, i32, IVec2 },
{ to_ivec3, i32, IVec3 },
{ to_ivec4, i32, IVec4 },
{ to_uvec2, u32, UVec2 },
{ to_uvec3, u32, UVec3 },
{ to_uvec4, u32, UVec4 },
{ to_vec2, f32, Vec2 },
{ to_vec3, f32, Vec3 },
{ to_vec4, f32, Vec4 },
{ to_dvec2, f64, DVec2 },
{ to_dvec3, f64, DVec3 },
{ to_dvec4, f64, DVec4 },
{ to_bvec2, bool, BVec2 },
{ to_bvec3, bool, BVec3 },
{ to_bvec4, bool, BVec4 }
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_int() {
assert_eq!(int(1_f32), 1_i32);
assert_eq!(int(true), 1_i32);
assert_eq!(int(false), 0_i32);
}
#[test]
fn test_boolean() {
assert_eq!(boolean(uvec2(0, 1)), false);
assert_eq!(boolean(vec3(1., -1., 0.)), true);
}
#[test]
fn test_float() {
assert_eq!(float(bvec2(true, false)), 1.);
assert_eq!(float(123_u32), 123.);
assert_eq!(float(0_f64), 0.);
}
#[test]
fn test_to_vec() {
assert_eq!(to_vec2(bvec2(true, false)), vec2(1., 0.));
assert_eq!(to_bvec3(ivec3(0, 1, -1)), bvec3(false, true, true));
}
}