#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
#![cfg_attr(not(feature = "std"), doc = "no-std stand in")]
#![deny(unsafe_code)]
#![deny(rust_2018_idioms)]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![no_std]
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(all(feature = "std", feature = "libm"))]
compile_error!("Please disable feature `libm` or disable default features -- both cannot be active at once.");
#[macro_use]
mod macros;
mod math;
mod tweener;
mod tweens;
pub use tweener::*;
pub use tweens::*;
pub trait Tween<Value> {
fn tween(&mut self, value_delta: Value, percent: f32) -> Value;
#[inline(always)]
fn is_finite(&self) -> bool {
true
}
}
#[cfg(test)]
static_assertions::assert_obj_safe!(Tween<i32>);
#[cfg(feature = "std")]
impl<'a, Value> Tween<Value> for &'a mut dyn Tween<Value>
where
Value: TweenValue,
{
#[inline(always)]
fn tween(&mut self, value_delta: Value, percent: f32) -> Value {
(**self).tween(value_delta, percent)
}
fn is_finite(&self) -> bool {
true
}
}
impl_tween_for_box!();
impl_tween_for_box!(Send);
impl_tween_for_box!(Sync);
impl_tween_for_box!(Send, Sync);
impl_tween_for_box!(Unpin);
impl_tween_for_box!(Send, Unpin);
impl_tween_for_box!(Send, Sync, Unpin);
impl<Value, F> Tween<Value> for F
where
F: FnMut(Value, f32) -> Value,
Value: TweenValue,
{
#[inline(always)]
fn tween(&mut self, value_delta: Value, percent: f32) -> Value {
self(value_delta, percent)
}
}
pub trait TweenValue: Copy + core::fmt::Debug + core::ops::Add<Output = Self> + core::ops::Sub<Output = Self> {
fn scale(self, scale: f32) -> Self;
}
pub trait TweenTime:
Copy
+ PartialEq
+ PartialOrd
+ core::fmt::Debug
+ core::ops::Add<Output = Self>
+ core::ops::AddAssign
+ core::ops::Rem<Output = Self>
+ core::ops::Sub<Output = Self>
{
const ZERO: Self;
fn to_f32(self) -> f32;
}
declare_time!(u8, i8, i16, u16, i32, i64, u32, u64, i128, u128, usize, isize);
impl TweenTime for f32 {
const ZERO: Self = 0.0;
#[inline(always)]
fn to_f32(self) -> f32 {
self
}
}
impl TweenTime for f64 {
const ZERO: Self = 0.0;
#[inline(always)]
fn to_f32(self) -> f32 {
self as f32
}
}
declare_value!(u8, i8, i16, u16, i32, i64, u32, u64, i128, u128, usize, isize);
impl TweenValue for f32 {
#[inline(always)]
fn scale(self, scale: f32) -> Self {
self * scale
}
}
impl TweenValue for f64 {
#[inline(always)]
fn scale(self, scale: f32) -> Self {
(self as f32 * scale) as Self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lambda_test() {
let (start, end) = (0, 1); let length = 4;
let mut pct_50_or_over = false;
let mut tweener = Tweener::new(start, end, length, |_vd, pct| {
if pct >= 0.5 {
pct_50_or_over = true;
}
100
});
assert_eq!(tweener.move_by(1), 100);
assert_eq!(tweener.move_by(1), 100);
assert_eq!(tweener.move_by(1), 100);
assert_eq!(tweener.move_by(1), 100);
assert_eq!(tweener.move_by(1), 1);
assert!(tweener.is_finished());
assert!(pct_50_or_over);
}
#[test]
fn cubic_bezier() {
use core::ops::{Add, Sub};
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point(f32, f32);
impl Add for Point {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl Sub for Point {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0, self.1 - rhs.1)
}
}
impl TweenValue for Point {
fn scale(self, scale: f32) -> Self {
Self(self.0 * scale, self.1 * scale)
}
}
let start = Point(0.0, 0.0);
let destination = Point(10.0, 0.0);
let ctrl_one = Point(2.5, 10.0);
let ctrl_two = Point(7.5, 10.0);
let mut tweener = Tweener::new(start, destination, 10.0, |delta, t| {
fn lerp(a: Point, b: Point, t: f32) -> Point {
a.scale(1.0 - t) + b.scale(t)
}
let a = lerp(Point(0.0, 0.0), ctrl_one, t);
let b = lerp(ctrl_two, ctrl_one, t);
let c = lerp(ctrl_two, delta, t);
let d = lerp(a, b, t);
let e = lerp(b, c, t);
lerp(d, e, t)
});
assert_eq!(tweener.move_to(5.0), Point(5.0, 7.5));
assert_eq!(tweener.move_to(10.0), Point(10.0, 0.0));
}
}