use std::num::Wrapping;
use std::ops::*;
use std::cmp;
use num_traits::{Zero, One, FloatConst};
pub fn partial_min<T: PartialOrd + Sized>(a: T, b: T) -> T {
if a <= b { a } else { b }
}
pub fn partial_max<T: PartialOrd + Sized>(a: T, b: T) -> T {
if a >= b { a } else { b }
}
pub trait IsBetween<Bound=Self>: Sized {
type Output;
fn is_between(self, lower: Bound, upper: Bound) -> Self::Output;
fn is_between01(self) -> Self::Output where Bound: Zero + One {
self.is_between(Bound::zero(), Bound::one())
}
}
pub trait IsBetween01: IsBetween + Zero + One {}
impl<T: IsBetween + Zero + One> IsBetween01 for T {}
pub trait Clamp<Bound=Self>: Sized {
fn clamped(self, lower: Bound, upper: Bound) -> Self;
fn clamp(val: Self, lower: Bound, upper: Bound) -> Self {
val.clamped(lower, upper)
}
fn clamped01(self) -> Self where Bound: Zero + One {
self.clamped(Bound::zero(), Bound::one())
}
fn clamp01(val: Self) -> Self where Bound: Zero + One {
Self::clamp(val, Bound::zero(), Bound::one())
}
}
pub trait Clamp01: Clamp + Zero + One {}
impl<T: Clamp + Zero + One> Clamp01 for T {}
macro_rules! impl_clamp_float {
($($T:ty)+) => {
$(
impl Clamp for $T {
fn clamped(self, lower: Self, upper: Self) -> Self {
assert!(lower <= upper);
partial_min(partial_max(self, lower), upper)
}
}
impl IsBetween for $T {
type Output = bool;
fn is_between(self, lower: Self, upper: Self) -> bool {
assert!(lower <= upper);
lower <= self && self <= upper
}
}
)+
}
}
macro_rules! impl_clamp_integer {
($($T:ty)+) => {
$(
impl Clamp for $T {
fn clamped(self, lower: Self, upper: Self) -> Self {
assert!(lower <= upper);
cmp::min(cmp::max(self, lower), upper)
}
}
impl IsBetween for $T {
type Output = bool;
fn is_between(self, lower: Self, upper: Self) -> bool {
assert!(lower <= upper);
lower <= self && self <= upper
}
}
)+
}
}
impl_clamp_float!{
f32 f64
}
impl_clamp_integer!{
i8 i16 i32 i64 isize u8 u16 u32 u64 usize
Wrapping<i8>
Wrapping<i16>
Wrapping<i32>
Wrapping<i64>
Wrapping<isize>
Wrapping<u8>
Wrapping<u16>
Wrapping<u32>
Wrapping<u64>
Wrapping<usize>
}
pub trait MulAdd<MulRhs=Self, AddRhs=Self> {
type Output;
fn mul_add(self, mul: MulRhs, add: AddRhs) -> Self::Output;
}
pub fn mul_add<Output,V,M,A>(val: V, mul: M, add: A) -> Output where V: MulAdd<M,A,Output=Output> {
val.mul_add(mul, add)
}
macro_rules! impl_muladd_content {
(float, $Out:ty, $MulRhs:ty, $AddRhs:ty) => {
type Output = $Out;
fn mul_add(self, mul: $MulRhs, add: $AddRhs) -> Self::Output {
self * mul + add
}
};
(integer, $Out:ty, $MulRhs:ty, $AddRhs:ty) => {
type Output = $Out;
fn mul_add(self, mul: $MulRhs, add: $AddRhs) -> Self::Output {
self * mul + add
}
};
}
macro_rules! impl_muladd {
($kind:ident $($ty:ty)+) => {
$(
impl MulAdd< $ty, $ty> for $ty { impl_muladd_content!{$kind, $ty, $ty, $ty} }
impl< 'c> MulAdd< $ty, &'c $ty> for $ty { impl_muladd_content!{$kind, $ty, $ty, &'c $ty} }
impl< 'b > MulAdd<&'b $ty, $ty> for $ty { impl_muladd_content!{$kind, $ty, &'b $ty, $ty} }
impl< 'b, 'c> MulAdd<&'b $ty, &'c $ty> for $ty { impl_muladd_content!{$kind, $ty, &'b $ty, &'c $ty} }
impl<'a, > MulAdd< $ty, $ty> for &'a $ty { impl_muladd_content!{$kind, $ty, $ty, $ty} }
impl<'a, 'c> MulAdd< $ty, &'c $ty> for &'a $ty { impl_muladd_content!{$kind, $ty, $ty, &'c $ty} }
impl<'a, 'b, > MulAdd<&'b $ty, $ty> for &'a $ty { impl_muladd_content!{$kind, $ty, &'b $ty, $ty} }
impl<'a, 'b, 'c> MulAdd<&'b $ty, &'c $ty> for &'a $ty { impl_muladd_content!{$kind, $ty, &'b $ty, &'c $ty} }
)+
}
}
impl_muladd!{float f32 f64}
impl_muladd!{integer
i8 i16 i32 i64 isize u8 u16 u32 u64 usize
Wrapping<i8>
Wrapping<i16>
Wrapping<i32>
Wrapping<i64>
Wrapping<isize>
Wrapping<u8>
Wrapping<u16>
Wrapping<u32>
Wrapping<u64>
Wrapping<usize>
}
pub trait Lerp<Factor=f32>: Sized
{
type Output;
fn lerp_unclamped(from: Self, to: Self, factor: Factor) -> Self::Output;
fn lerp_unclamped_precise(from: Self, to: Self, factor: Factor) -> Self::Output {
Self::lerp_unclamped(from, to, factor)
}
fn lerp(from: Self, to: Self, factor: Factor) -> Self::Output where Factor: Clamp + Zero + One
{
Self::lerp_unclamped(from, to, factor.clamped01())
}
fn lerp_precise(from: Self, to: Self, factor: Factor) -> Self::Output where Factor: Clamp + Zero + One
{
Self::lerp_unclamped_precise(from, to, factor.clamped01())
}
}
macro_rules! lerp_impl_float {
($($T:ty)+) => {
$(
impl Lerp<$T> for $T {
type Output = $T;
fn lerp_unclamped_precise(from: Self, to: Self, factor: Self) -> Self {
from*(Self::one()-factor) + to*factor
}
fn lerp_unclamped(from: Self, to: Self, factor: Self) -> Self {
factor.mul_add(to - from, from)
}
}
impl<'a> Lerp<$T> for &'a $T {
type Output = $T;
fn lerp_unclamped_precise(from: Self, to: Self, factor: $T) -> $T {
Lerp::lerp_unclamped_precise(*from, *to, factor)
}
fn lerp_unclamped(from: Self, to: Self, factor: $T) -> $T {
Lerp::lerp_unclamped(*from, *to, factor)
}
}
)+
}
}
macro_rules! lerp_impl_integer {
($($T:ty)+) => {
$(
impl Lerp<f32> for $T {
type Output = $T;
fn lerp_unclamped_precise(from: Self, to: Self, factor: f32) -> Self {
((from as f32)*((1f32)-factor) + (to as f32)*factor).round() as Self
}
fn lerp_unclamped(from: Self, to: Self, factor: f32) -> Self {
factor.mul_add((to - from) as f32, from as f32).round() as Self
}
}
impl Lerp<f64> for $T {
type Output = $T;
fn lerp_unclamped_precise(from: Self, to: Self, factor: f64) -> Self {
((from as f64)*((1f64)-factor) + (to as f64)*factor).round() as Self
}
fn lerp_unclamped(from: Self, to: Self, factor: f64) -> Self {
factor.mul_add((to - from) as f64, from as f64).round() as Self
}
}
impl<'a> Lerp<f32> for &'a $T {
type Output = $T;
fn lerp_unclamped_precise(from: Self, to: Self, factor: f32) -> $T {
Lerp::lerp_unclamped_precise(*from, *to, factor)
}
fn lerp_unclamped(from: Self, to: Self, factor: f32) -> $T {
Lerp::lerp_unclamped(*from, *to, factor)
}
}
impl<'a> Lerp<f64> for &'a $T {
type Output = $T;
fn lerp_unclamped_precise(from: Self, to: Self, factor: f64) -> $T {
Lerp::lerp_unclamped_precise(*from, *to, factor)
}
fn lerp_unclamped(from: Self, to: Self, factor: f64) -> $T {
Lerp::lerp_unclamped(*from, *to, factor)
}
}
)+
}
}
lerp_impl_float!{f32 f64}
lerp_impl_integer!{
i8 i16 i32 i64 isize u8 u16 u32 u64 usize
}
pub trait Slerp<Factor=f32>: Sized {
type Output;
fn slerp_unclamped(from: Self, to: Self, factor: Factor) -> Self::Output;
fn slerp(from: Self, to: Self, factor: Factor) -> Self::Output where Factor: Clamp + Zero + One {
Self::slerp_unclamped(from, to, factor.clamped01())
}
}
pub trait Wrap<Bound=Self>: Sized {
fn wrapped(self, upper: Bound) -> Self;
fn wrap(val: Self, upper: Bound) -> Self {
val.wrapped(upper)
}
fn wrapped_2pi(self) -> Self where Bound: FloatConst + Add<Output=Bound> {
self.wrapped(Bound::PI() + Bound::PI())
}
fn wrap_2pi(val: Self) -> Self where Bound: FloatConst + Add<Output=Bound> {
val.wrapped_2pi()
}
fn wrapped_between(self, lower: Bound, upper: Bound) -> Self;
fn wrap_between(val: Self, lower: Bound, upper: Bound) -> Self
where Self: Sub<Output=Self> + Add<Output=Self> + From<Bound>,
Bound: Copy + Sub<Output=Bound> + PartialOrd
{
val.wrapped_between(lower, upper)
}
fn pingpong(self, upper: Bound) -> Self;
fn delta_angle(self, target: Self) -> Self
where Self: From<Bound> + Sub<Output=Self> + PartialOrd,
Bound: FloatConst + Add<Output=Bound>
{
let num = Self::wrap(target - self, Bound::PI() + Bound::PI());
if num > Self::from(Bound::PI()) {
return num - Self::from(Bound::PI() + Bound::PI());
}
num
}
fn delta_angle_degrees(self, target: Self) -> Self
where Self: From<Bound> + Sub<Output=Self> + PartialOrd,
Bound: From<u16>
{
let num = Self::wrap(target - self, Bound::from(360));
if num > Self::from(Bound::from(180)) {
return num - Self::from(Bound::from(360));
}
num
}
}
macro_rules! wrap_impl_float {
($($T:ty)+) => {
$(
impl Wrap for $T {
fn wrapped(self, upper: Self) -> Self {
assert!(upper > Self::zero());
self - (self/upper).floor() * upper
}
fn wrapped_between(self, lower: Self, upper: Self) -> Self {
assert!(lower < upper);
assert!(lower >= Self::zero());
assert!(upper > Self::zero());
let out = self - lower;
let out = out.wrapped(upper - lower);
out + lower
}
fn pingpong(self, upper: Self) -> Self {
assert!(upper > Self::zero());
let t = self.wrapped(upper + upper);
let upper = || Self::from(upper);
upper() - (t - upper()).abs()
}
}
)+
}
}
macro_rules! wrap_impl_uint {
($($T:ty)+) => {
$(
impl Wrap for $T {
fn wrapped_between(mut self, lower: Self, upper: Self) -> Self {
assert!(lower < upper);
assert!(lower >= Self::zero());
assert!(upper > Self::zero());
let range_size = upper - lower ;
if self < lower {
self += range_size * ((lower-self)/range_size + Self::one());
}
lower + (self - lower) % range_size
}
fn wrapped(self, upper: Self) -> Self {
assert!(upper > Self::zero());
self % upper
}
fn pingpong(self, upper: Self) -> Self {
assert!(upper > Self::zero());
let r = self % (upper+upper);
if r < upper {
r
} else {
upper+upper-r
}
}
}
)+
}
}
macro_rules! wrap_impl_sint {
($($T:ty)+) => {
$(
impl Wrap for $T {
fn wrapped_between(mut self, lower: Self, upper: Self) -> Self {
assert!(lower < upper);
assert!(lower >= Self::zero());
assert!(upper > Self::zero());
let range_size = upper - lower ;
if self < lower {
self += range_size * ((lower-self)/range_size + Self::one());
}
lower + (self - lower) % range_size
}
fn wrapped(self, upper: Self) -> Self {
assert!(upper > Self::zero());
self.wrapped_between(Self::zero(), upper)
}
fn pingpong(self, upper: Self) -> Self {
assert!(upper > Self::zero());
let r = self.wrapped(upper+upper);
if r <= upper {
r
} else {
upper+upper-r
}
}
}
)+
}
}
wrap_impl_float!{f32 f64}
wrap_impl_uint!{
u8 u16 u32 u64 usize
Wrapping<u8>
Wrapping<u16>
Wrapping<u32>
Wrapping<u64>
Wrapping<usize>
}
wrap_impl_sint!{
i8 i16 i32 i64 isize
Wrapping<i8>
Wrapping<i16>
Wrapping<i32>
Wrapping<i64>
Wrapping<isize>
}
pub trait ColorComponent : Zero {
fn full() -> Self;
}
impl ColorComponent for f32 { fn full() -> Self { 1f32 } }
impl ColorComponent for f64 { fn full() -> Self { 1f64 } }
impl ColorComponent for u8 { fn full() -> Self { ::std::u8 ::MAX } }
impl ColorComponent for u16 { fn full() -> Self { ::std::u16 ::MAX } }
impl ColorComponent for u32 { fn full() -> Self { ::std::u32 ::MAX } }
impl ColorComponent for u64 { fn full() -> Self { ::std::u64 ::MAX } }
impl ColorComponent for i8 { fn full() -> Self { ::std::i8 ::MAX } }
impl ColorComponent for i16 { fn full() -> Self { ::std::i16 ::MAX } }
impl ColorComponent for i32 { fn full() -> Self { ::std::i32 ::MAX } }
impl ColorComponent for i64 { fn full() -> Self { ::std::i64 ::MAX } }
impl ColorComponent for Wrapping<u8 > { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<u16> { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<u32> { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<u64> { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<i8 > { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<i16> { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<i32> { fn full() -> Self { Wrapping(ColorComponent::full()) } }
impl ColorComponent for Wrapping<i64> { fn full() -> Self { Wrapping(ColorComponent::full()) } }
#[cfg(test)]
mod tests {
use super::*;
macro_rules! for_each_unsigned_type {
($($T:ident)+) => {
$(mod $T {
use super::Wrap;
#[test]
fn wrapped() {
assert_eq!((0 as $T).wrapped(3 as $T), 0 as $T);
assert_eq!((1 as $T).wrapped(3 as $T), 1 as $T);
assert_eq!((2 as $T).wrapped(3 as $T), 2 as $T);
assert_eq!((3 as $T).wrapped(3 as $T), 0 as $T);
assert_eq!((4 as $T).wrapped(3 as $T), 1 as $T);
assert_eq!((5 as $T).wrapped(3 as $T), 2 as $T);
}
#[test]
fn wrapped_between() {
assert_eq!((0 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_eq!((1 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_eq!((2 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_eq!((3 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_eq!((4 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_eq!((5 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_eq!((6 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
}
#[test]
fn pingpong() {
assert_eq!((0 as $T).pingpong(3 as $T), 0 as $T);
assert_eq!((1 as $T).pingpong(3 as $T), 1 as $T);
assert_eq!((2 as $T).pingpong(3 as $T), 2 as $T);
assert_eq!((3 as $T).pingpong(3 as $T), 3 as $T);
assert_eq!((4 as $T).pingpong(3 as $T), 2 as $T);
assert_eq!((5 as $T).pingpong(3 as $T), 1 as $T);
assert_eq!((6 as $T).pingpong(3 as $T), 0 as $T);
assert_eq!((7 as $T).pingpong(3 as $T), 1 as $T);
}
})+
};
}
macro_rules! for_each_signed_type {
($($T:ident)+) => {
$(mod $T {
use super::Wrap;
#[test]
fn wrapped() {
assert_eq!((-5 as $T).wrapped(3 as $T), 1 as $T);
assert_eq!((-4 as $T).wrapped(3 as $T), 2 as $T);
assert_eq!((-3 as $T).wrapped(3 as $T), 0 as $T);
assert_eq!((-2 as $T).wrapped(3 as $T), 1 as $T);
assert_eq!((-1 as $T).wrapped(3 as $T), 2 as $T);
assert_eq!(( 0 as $T).wrapped(3 as $T), 0 as $T);
assert_eq!(( 1 as $T).wrapped(3 as $T), 1 as $T);
assert_eq!(( 2 as $T).wrapped(3 as $T), 2 as $T);
assert_eq!(( 3 as $T).wrapped(3 as $T), 0 as $T);
assert_eq!(( 4 as $T).wrapped(3 as $T), 1 as $T);
assert_eq!(( 5 as $T).wrapped(3 as $T), 2 as $T);
}
#[test]
fn wrapped_between() {
assert_eq!((-4 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_eq!((-3 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_eq!((-2 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_eq!((-1 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_eq!(( 0 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_eq!(( 1 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_eq!(( 2 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_eq!(( 3 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_eq!(( 4 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_eq!(( 5 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_eq!(( 6 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
}
#[test]
fn pingpong() {
assert_eq!((-4 as $T).pingpong(3 as $T), 2 as $T);
assert_eq!((-3 as $T).pingpong(3 as $T), 3 as $T);
assert_eq!((-2 as $T).pingpong(3 as $T), 2 as $T);
assert_eq!((-1 as $T).pingpong(3 as $T), 1 as $T);
assert_eq!(( 0 as $T).pingpong(3 as $T), 0 as $T);
assert_eq!(( 1 as $T).pingpong(3 as $T), 1 as $T);
assert_eq!(( 2 as $T).pingpong(3 as $T), 2 as $T);
assert_eq!(( 3 as $T).pingpong(3 as $T), 3 as $T);
assert_eq!(( 4 as $T).pingpong(3 as $T), 2 as $T);
assert_eq!(( 5 as $T).pingpong(3 as $T), 1 as $T);
assert_eq!(( 6 as $T).pingpong(3 as $T), 0 as $T);
assert_eq!(( 7 as $T).pingpong(3 as $T), 1 as $T);
}
})+
};
}
macro_rules! for_each_float_type {
($($T:ident)+) => {
$(mod $T {
use super::Wrap;
#[test]
fn wrapped() {
assert_relative_eq!((-5 as $T).wrapped(3 as $T), 1 as $T);
assert_relative_eq!((-4 as $T).wrapped(3 as $T), 2 as $T);
assert_relative_eq!((-3 as $T).wrapped(3 as $T), 0 as $T);
assert_relative_eq!((-2 as $T).wrapped(3 as $T), 1 as $T);
assert_relative_eq!((-1 as $T).wrapped(3 as $T), 2 as $T);
assert_relative_eq!(( 0 as $T).wrapped(3 as $T), 0 as $T);
assert_relative_eq!(( 1 as $T).wrapped(3 as $T), 1 as $T);
assert_relative_eq!(( 2 as $T).wrapped(3 as $T), 2 as $T);
assert_relative_eq!(( 3 as $T).wrapped(3 as $T), 0 as $T);
assert_relative_eq!(( 4 as $T).wrapped(3 as $T), 1 as $T);
assert_relative_eq!(( 5 as $T).wrapped(3 as $T), 2 as $T);
}
#[test]
fn wrapped_between() {
assert_relative_eq!((-4 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_relative_eq!((-3 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_relative_eq!((-2 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_relative_eq!((-1 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_relative_eq!(( 0 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_relative_eq!(( 1 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_relative_eq!(( 2 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_relative_eq!(( 3 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
assert_relative_eq!(( 4 as $T).wrapped_between(2 as $T, 5 as $T), 4 as $T);
assert_relative_eq!(( 5 as $T).wrapped_between(2 as $T, 5 as $T), 2 as $T);
assert_relative_eq!(( 6 as $T).wrapped_between(2 as $T, 5 as $T), 3 as $T);
}
#[test]
fn pingpong() {
assert_relative_eq!((-4 as $T).pingpong(3 as $T), 2 as $T);
assert_relative_eq!((-3 as $T).pingpong(3 as $T), 3 as $T);
assert_relative_eq!((-2 as $T).pingpong(3 as $T), 2 as $T);
assert_relative_eq!((-1 as $T).pingpong(3 as $T), 1 as $T);
assert_relative_eq!(( 0 as $T).pingpong(3 as $T), 0 as $T);
assert_relative_eq!(( 1 as $T).pingpong(3 as $T), 1 as $T);
assert_relative_eq!(( 2 as $T).pingpong(3 as $T), 2 as $T);
assert_relative_eq!(( 3 as $T).pingpong(3 as $T), 3 as $T);
assert_relative_eq!(( 4 as $T).pingpong(3 as $T), 2 as $T);
assert_relative_eq!(( 5 as $T).pingpong(3 as $T), 1 as $T);
assert_relative_eq!(( 6 as $T).pingpong(3 as $T), 0 as $T);
assert_relative_eq!(( 7 as $T).pingpong(3 as $T), 1 as $T);
}
})+
};
}
for_each_float_type!{f32 f64}
for_each_signed_type!{i8 i16 i32 i64 isize}
for_each_unsigned_type!{u8 u16 u32 u64 usize}
}