Newer
Older
self.timer.tick(delta);
if self.timer.finished() {
TweenState::Completed
} else {
TweenState::Active
}
}
fn times_completed(&self) -> u32 {
if self.timer.finished() {
1
self.timer.reset();
}
}
#[cfg(test)]
mod tests {
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use bevy::ecs::{event::Events, system::SystemState};
use super::*;
use crate::{lens::*, test_utils::*};
#[derive(Default, Copy, Clone)]
struct CallbackMonitor {
invoke_count: u64,
last_reported_count: u32,
}
/// Utility to create a tween for testing.
fn make_test_tween() -> Tween<Transform> {
Tween::new(
EaseMethod::Linear,
Duration::from_secs(1),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
)
}
/// Utility to create a test environment to tick a tween.
fn make_test_env() -> (World, Entity) {
let mut world = World::new();
world.init_resource::<Events<TweenCompleted>>();
let entity = world.spawn().insert(Transform::default()).id();
(world, entity)
}
/// Manually tick a test tweenable targeting a component.
fn manual_tick_component<T: Component>(
duration: Duration,
tween: &mut dyn Tweenable<T>,
world: &mut World,
entity: Entity,
) -> TweenState {
world.resource_scope(
|world: &mut World, mut events: Mut<Events<TweenCompleted>>| {
let transform = world.get_mut::<T>(entity).unwrap();
let mut target = ComponentTarget::new(transform);
tween.tick(duration, &mut target, entity, &mut events)
},
)
#[test]
fn anim_clock_precision() {
let duration = Duration::from_millis(1);
let mut clock = AnimClock::new(duration);
clock.total_duration = TotalDuration::Infinite;
let test_ticks = [
Duration::from_micros(123),
Duration::from_millis(1),
Duration::from_secs_f32(1. / 24.),
Duration::from_secs_f32(1. / 30.),
Duration::from_secs_f32(1. / 60.),
Duration::from_secs_f32(1. / 120.),
Duration::from_secs_f32(1. / 144.),
Duration::from_secs_f32(1. / 240.),
];
let mut times_completed = 0;
let mut total_duration = Duration::ZERO;
for i in 0..10_000_000 {
let tick = test_ticks[i % test_ticks.len()];
times_completed += clock.tick(tick).1;
total_duration += tick;
}
assert_eq!(
(total_duration.as_secs_f64() / duration.as_secs_f64()) as i32,
times_completed
);
}
/// Test ticking of a single tween in isolation.
#[test]
fn tween_tick() {
for tweening_direction in &[TweeningDirection::Forward, TweeningDirection::Backward] {
for (count, strategy) in &[
(RepeatCount::Finite(1), RepeatStrategy::default()),
(RepeatCount::Infinite, RepeatStrategy::Repeat),
(RepeatCount::Finite(2), RepeatStrategy::Repeat),
(RepeatCount::Infinite, RepeatStrategy::MirroredRepeat),
(RepeatCount::Finite(2), RepeatStrategy::MirroredRepeat),
"TweeningType: count={count:?} strategy={strategy:?} dir={tweening_direction:?}",
// Create a linear tween over 1 second
let mut tween = make_test_tween()
.with_direction(*tweening_direction)
.with_repeat_count(*count)
.with_repeat_strategy(*strategy);
assert_eq!(tween.direction(), *tweening_direction);
assert!(tween.on_completed.is_none());
assert!(tween.event_data.is_none());
let (mut world, entity) = make_test_env();
let mut event_reader_system_state: SystemState<EventReader<TweenCompleted>> =
SystemState::new(&mut world);
// Register callbacks to count started/ended events
let callback_monitor = Arc::new(Mutex::new(CallbackMonitor::default()));
let cb_mon_ptr = Arc::clone(&callback_monitor);
let reference_entity = entity;
tween.set_completed(move |completed_entity, tween| {
assert_eq!(completed_entity, reference_entity);
let mut cb_mon = cb_mon_ptr.lock().unwrap();
cb_mon.invoke_count += 1;
cb_mon.last_reported_count = tween.times_completed();
});
assert!(tween.on_completed.is_some());
assert!(tween.event_data.is_none());
assert_eq!(callback_monitor.lock().unwrap().invoke_count, 0);
// Activate event sending
const USER_DATA: u64 = 54789; // dummy
tween.set_completed_event(USER_DATA);
assert!(tween.event_data.is_some());
assert_eq!(tween.event_data.unwrap(), USER_DATA);
// Loop over 2.2 seconds, so greater than one ping-pong loop
let tick_duration = Duration::from_millis(200);
for i in 1..=11 {
// Calculate expected values
let (progress, times_completed, mut direction, expected_state, just_completed) =
match count {
RepeatCount::Finite(1) => {
let progress = (i as f32 * 0.2).min(1.0);
let times_completed = if i >= 5 { 1 } else { 0 };
let state = if i < 5 {
TweenState::Active
} else {
TweenState::Completed
};
let just_completed = i == 5;
(
progress,
times_completed,
TweeningDirection::Forward,
state,
just_completed,
)
}
RepeatCount::Finite(count) => {
let total_progress = i as f32 * 0.2;
let progress = if total_progress >= *count as f32 {
1.
} else {
total_progress.fract()
};
if *strategy == RepeatStrategy::Repeat {
let times_completed = i / 5;
let just_completed = i % 5 == 0;
(
progress,
times_completed,
TweeningDirection::Forward,
if i < 10 {
TweenState::Active
} else {
TweenState::Completed
},
just_completed,
)
} else {
let i5 = i % 5;
let times_completed = i / 5;
// Once Completed, the direction doesn't change
let direction = if i >= 5 {
TweeningDirection::Backward
} else {
TweeningDirection::Forward
};
let just_completed = i5 == 0;
(
progress,
times_completed,
direction,
if i < 10 {
TweenState::Active
} else {
TweenState::Completed
},
just_completed,
)
}
RepeatCount::Infinite => {
let progress = (i as f32 * 0.2).fract();
if *strategy == RepeatStrategy::Repeat {
let times_completed = i / 5;
let just_completed = i % 5 == 0;
(
progress,
times_completed,
TweeningDirection::Forward,
TweenState::Active,
just_completed,
)
let i5 = i % 5;
let times_completed = i / 5;
let i10 = i % 10;
let direction = if i10 >= 5 {
TweeningDirection::Backward
} else {
TweeningDirection::Forward
};
let just_completed = i5 == 0;
(
progress,
times_completed,
direction,
TweenState::Active,
just_completed,
)
}
RepeatCount::For(_) => panic!("Untested"),
};
let factor = if tweening_direction.is_backward() {
direction = !direction;
1. - progress
} else {
progress
};
let expected_translation = if direction.is_forward() {
Vec3::splat(progress)
} else {
Vec3::splat(1. - progress)
};
println!(
"Expected: progress={} factor={} times_completed={} direction={:?} state={:?} just_completed={} translation={:?}",
progress, factor, times_completed, direction, expected_state, just_completed, expected_translation
);
// Tick the tween
let actual_state =
manual_tick_component(tick_duration, &mut tween, &mut world, entity);
// Propagate events
{
let mut events = world.resource_mut::<Events<TweenCompleted>>();
events.update();
}
// Check actual values
assert_eq!(tween.direction(), direction);
assert_eq!(actual_state, expected_state);
assert_approx_eq!(tween.progress(), progress);
assert_eq!(tween.times_completed(), times_completed);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform
.translation
.abs_diff_eq(expected_translation, 1e-5));
assert!(transform.rotation.abs_diff_eq(Quat::IDENTITY, 1e-5));
let cb_mon = callback_monitor.lock().unwrap();
assert_eq!(cb_mon.invoke_count, times_completed as u64);
assert_eq!(cb_mon.last_reported_count, times_completed);
{
let mut event_reader = event_reader_system_state.get_mut(&mut world);
let event = event_reader.iter().next();
if just_completed {
assert!(event.is_some());
if let Some(event) = event {
assert_eq!(event.entity, entity);
assert_eq!(event.user_data, USER_DATA);
}
} else {
assert!(event.is_none());
}
}
}
// Rewind
tween.rewind();
assert_eq!(tween.direction(), *tweening_direction); // does not change
assert_approx_eq!(tween.progress(), 0.);
assert_eq!(tween.times_completed(), 0);
// Dummy tick to update target
let actual_state =
manual_tick_component(Duration::ZERO, &mut tween, &mut world, entity);
assert_eq!(actual_state, TweenState::Active);
let expected_translation = if tweening_direction.is_backward() {
Vec3::ONE
} else {
Vec3::ZERO
};
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform
.translation
.abs_diff_eq(expected_translation, 1e-5));
assert!(transform.rotation.abs_diff_eq(Quat::IDENTITY, 1e-5));
// Clear callback
tween.clear_completed();
assert!(tween.on_completed.is_none());
}
#[test]
fn tween_dir() {
let mut tween = make_test_tween();
// Default
assert_eq!(tween.direction(), TweeningDirection::Forward);
assert_approx_eq!(tween.progress(), 0.0);
// no-op
tween.set_direction(TweeningDirection::Forward);
assert_eq!(tween.direction(), TweeningDirection::Forward);
assert_approx_eq!(tween.progress(), 0.0);
// Backward
tween.set_direction(TweeningDirection::Backward);
assert_eq!(tween.direction(), TweeningDirection::Backward);
// progress is independent of direction
assert_approx_eq!(tween.progress(), 0.0);
// Progress-invariant
tween.set_direction(TweeningDirection::Forward);
tween.set_progress(0.3);
assert_approx_eq!(tween.progress(), 0.3);
tween.set_direction(TweeningDirection::Backward);
// progress is independent of direction
assert_approx_eq!(tween.progress(), 0.3);
let (mut world, entity) = make_test_env();
// Progress always increases alongside the current direction
tween.set_direction(TweeningDirection::Backward);
assert_approx_eq!(tween.progress(), 0.3);
manual_tick_component(Duration::from_millis(100), &mut tween, &mut world, entity);
assert_approx_eq!(tween.progress(), 0.4);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.6), 1e-5));
}
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
#[test]
fn tween_elapsed() {
let mut tween = make_test_tween();
let duration = tween.duration();
let elapsed = tween.elapsed();
assert_eq!(elapsed, Duration::ZERO);
assert_eq!(duration, Duration::from_secs(1));
for ms in [0, 1, 500, 100, 300, 999, 847, 1000, 900] {
let elapsed = Duration::from_millis(ms);
tween.set_elapsed(elapsed);
assert_eq!(tween.elapsed(), elapsed);
let progress = (elapsed.as_secs_f64() / duration.as_secs_f64()) as f32;
assert_approx_eq!(tween.progress(), progress);
let times_completed = if ms == 1000 { 1 } else { 0 };
assert_eq!(tween.times_completed(), times_completed);
}
}
/// Test ticking a sequence of tweens.
#[test]
fn seq_tick() {
let tween1 = Tween::new(
EaseMethod::Linear,
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
);
let tween2 = Tween::new(
EaseMethod::Linear,
TransformRotationLens {
start: Quat::IDENTITY,
},
);
let mut seq = tween1.then(tween2);
let (mut world, entity) = make_test_env();
let state =
manual_tick_component(Duration::from_millis(200), &mut seq, &mut world, entity);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert_eq!(state, TweenState::Active);
assert_eq!(*transform, Transform::from_translation(Vec3::splat(r)));
assert_eq!(state, TweenState::Active);
assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
assert!(transform
.rotation
.abs_diff_eq(Quat::from_rotation_x(alpha_deg.to_radians()), 1e-5));
} else {
assert_eq!(state, TweenState::Completed);
assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
.abs_diff_eq(Quat::from_rotation_x(90_f32.to_radians()), 1e-5));
/// Test crossing tween boundaries in one tick.
#[test]
fn seq_tick_boundaries() {
let mut seq = Sequence::new((0..3).map(|i| {
Tween::new(
EaseMethod::Linear,
Duration::from_secs(1),
TransformPositionLens {
start: Vec3::splat(i as f32),
end: Vec3::splat((i + 1) as f32),
},
)
.with_repeat_count(RepeatCount::Finite(1))
}));
let (mut world, entity) = make_test_env();
// Tick halfway through the first tween, then in one tick:
// - Finish the first tween
// - Start and finish the second tween
// - Start the third tween
for delta_ms in [500, 2000] {
manual_tick_component(
Duration::from_millis(delta_ms),
&mut seq,
&mut world,
);
}
assert_eq!(seq.index(), 2);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(2.5), 1e-5));
}
/// Sequence::new() and various Sequence-specific methods
#[test]
fn seq_iter() {
let mut seq = Sequence::new((1..5).map(|i| {
Tween::new(
EaseMethod::Linear,
Duration::from_millis(200 * i),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
)
}));
let mut progress = 0.;
for i in 1..5 {
assert_eq!(seq.index(), i - 1);
assert_approx_eq!(seq.progress(), progress);
let duration = Duration::from_millis(200 * i as u64);
assert_eq!(seq.current().duration(), duration);
progress += 0.25;
seq.set_progress(progress);
assert_eq!(seq.times_completed(), if i == 4 { 1 } else { 0 });
}
seq.rewind();
assert_eq!(seq.progress(), 0.);
assert_eq!(seq.times_completed(), 0);
}
/// Sequence::from_single()
#[test]
fn seq_from_single() {
let tween = Tween::new(
EaseMethod::Linear,
Duration::from_secs(1),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
);
let seq = Sequence::from_single(tween);
assert_eq!(seq.duration(), Duration::from_secs(1));
}
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
#[test]
fn seq_elapsed() {
let mut seq = Sequence::new((1..5).map(|i| {
Tween::new(
EaseMethod::Linear,
Duration::from_millis(200 * i),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
)
}));
let mut elapsed = Duration::ZERO;
for i in 1..5 {
assert_eq!(seq.index(), i - 1);
assert_eq!(seq.elapsed(), elapsed);
let duration = Duration::from_millis(200 * i as u64);
assert_eq!(seq.current().duration(), duration);
elapsed += duration;
seq.set_elapsed(elapsed);
assert_eq!(seq.times_completed(), if i == 4 { 1 } else { 0 });
}
}
/// Test ticking parallel tracks of tweens.
#[test]
fn tracks_tick() {
let tween1 = Tween::new(
EaseMethod::Linear,
Duration::from_millis(1000),
TransformPositionLens {
start: Vec3::ZERO,
end: Vec3::ONE,
},
);
let tween2 = Tween::new(
EaseMethod::Linear,
Duration::from_millis(800), // shorter
TransformRotationLens {
start: Quat::IDENTITY,
},
);
let mut tracks = Tracks::new([tween1, tween2]);
assert_eq!(tracks.duration(), Duration::from_secs(1)); // max(1., 0.8)
let (mut world, entity) = make_test_env();
let state =
manual_tick_component(Duration::from_millis(200), &mut tracks, &mut world, entity);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert_eq!(state, TweenState::Active);
assert_eq!(tracks.times_completed(), 0);
assert_approx_eq!(tracks.progress(), r);
assert!(transform.translation.abs_diff_eq(Vec3::splat(r), 1e-5));
assert!(transform
.rotation
.abs_diff_eq(Quat::from_rotation_x(alpha_deg.to_radians()), 1e-5));
} else {
assert_eq!(state, TweenState::Completed);
assert_eq!(tracks.times_completed(), 1);
assert_approx_eq!(tracks.progress(), 1.);
assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
.abs_diff_eq(Quat::from_rotation_x(90_f32.to_radians()), 1e-5));
tracks.rewind();
assert_eq!(tracks.times_completed(), 0);
assert_approx_eq!(tracks.progress(), 0.);
tracks.set_progress(0.9);
assert_approx_eq!(tracks.progress(), 0.9);
// tick to udpate state (set_progress() does not update state)
let state = manual_tick_component(Duration::ZERO, &mut tracks, &mut world, entity);
assert_eq!(state, TweenState::Active);
assert_eq!(tracks.times_completed(), 0);
tracks.set_progress(3.2);
assert_approx_eq!(tracks.progress(), 1.);
// tick to udpate state (set_progress() does not update state)
let state = manual_tick_component(Duration::ZERO, &mut tracks, &mut world, entity);
assert_eq!(state, TweenState::Completed);
assert_eq!(tracks.times_completed(), 1); // no looping
tracks.set_progress(-0.5);
assert_approx_eq!(tracks.progress(), 0.);
// tick to udpate state (set_progress() does not update state)
let state = manual_tick_component(Duration::ZERO, &mut tracks, &mut world, entity);
assert_eq!(state, TweenState::Active);
assert_eq!(tracks.times_completed(), 0); // no looping
}
/// Delay::then()
#[test]
fn delay_then() {
let seq: Sequence<Transform> =
Delay::new(Duration::from_secs(1)).then(Delay::new(Duration::from_secs(2)));
assert_eq!(seq.duration(), Duration::from_secs(3));
assert_eq!(seq.tweens.len(), 2);
for (i, t) in seq.tweens.iter().enumerate() {
assert_eq!(t.duration(), Duration::from_secs(i as u64 + 1));
}
}
/// Test ticking a delay.
#[test]
fn delay_tick() {
let mut delay = Delay::new(duration);
{
let tweenable: &dyn Tweenable<Transform> = &delay;
assert_eq!(tweenable.duration(), duration);
assert_approx_eq!(tweenable.progress(), 0.);
}
// Dummy world and event writer
let (mut world, entity) = make_test_env();
for i in 1..=6 {
let state = manual_tick_component::<Transform>(
&mut delay,
&mut world,
);
{
let tweenable: &dyn Tweenable<Transform> = &delay;
if i < 5 {
assert_eq!(state, TweenState::Active);
let r = i as f32 * 0.2;
assert_approx_eq!(tweenable.progress(), r);
} else {
assert_eq!(state, TweenState::Completed);
assert_approx_eq!(tweenable.progress(), 1.);
let tweenable: &mut dyn Tweenable<Transform> = &mut delay;
tweenable.rewind();
assert_eq!(tweenable.times_completed(), 0);
assert_approx_eq!(tweenable.progress(), 0.);
let state = manual_tick_component(Duration::ZERO, tweenable, &mut world, entity);
assert_eq!(state, TweenState::Active);
tweenable.set_progress(0.3);
assert_eq!(tweenable.times_completed(), 0);
assert_approx_eq!(tweenable.progress(), 0.3);
tweenable.set_progress(1.);
assert_eq!(tweenable.times_completed(), 1);
assert_approx_eq!(tweenable.progress(), 1.);
#[test]
fn delay_elapsed() {
let mut delay = Delay::new(Duration::from_secs(1));
let tweenable: &mut dyn Tweenable<Transform> = &mut delay;
let duration = tweenable.duration();
for ms in [0, 1, 500, 100, 300, 999, 847, 1000, 900] {
let elapsed = Duration::from_millis(ms);
tweenable.set_elapsed(elapsed);
assert_eq!(tweenable.elapsed(), elapsed);
let progress = (elapsed.as_secs_f64() / duration.as_secs_f64()) as f32;
assert_approx_eq!(tweenable.progress(), progress);
let times_completed = if ms == 1000 { 1 } else { 0 };
assert_eq!(tweenable.times_completed(), times_completed);
}
}
#[test]
#[should_panic]
fn delay_zero_duration_panics() {
let _ = Delay::new(Duration::ZERO);
}
#[test]
fn tween_repeat() {
let mut tween = make_test_tween()
.with_repeat_count(RepeatCount::Finite(5))
.with_repeat_strategy(RepeatStrategy::Repeat);
assert_approx_eq!(tween.progress(), 0.);
let (mut world, entity) = make_test_env();
let state =
manual_tick_component(Duration::from_millis(100), &mut tween, &mut world, entity);
assert_eq!(TweenState::Active, state);
assert_eq!(0, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.1);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.1), 1e-5));
// 130%
let state =
manual_tick_component(Duration::from_millis(1200), &mut tween, &mut world, entity);
assert_eq!(TweenState::Active, state);
assert_eq!(1, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.3);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.3), 1e-5));
// 480%
let state =
manual_tick_component(Duration::from_millis(3500), &mut tween, &mut world, entity);
assert_eq!(TweenState::Active, state);
assert_eq!(4, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.8);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.8), 1e-5));
// 500% - done
let state =
manual_tick_component(Duration::from_millis(200), &mut tween, &mut world, entity);
assert_eq!(TweenState::Completed, state);
assert_eq!(5, tween.times_completed());
assert_approx_eq!(tween.progress(), 1.0);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::ONE, 1e-5));
}
#[test]
fn tween_mirrored_rewind() {
let mut tween = make_test_tween()
.with_repeat_count(RepeatCount::Finite(4))
.with_repeat_strategy(RepeatStrategy::MirroredRepeat);
assert_approx_eq!(tween.progress(), 0.);
let (mut world, entity) = make_test_env();
let state =
manual_tick_component(Duration::from_millis(100), &mut tween, &mut world, entity);
assert_eq!(TweenState::Active, state);
assert_eq!(TweeningDirection::Forward, tween.direction());
assert_eq!(0, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.1);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.1), 1e-5));
// rewind
tween.rewind();
assert_eq!(TweeningDirection::Forward, tween.direction());
assert_eq!(0, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.1), 1e-5)); // no-op, rewind doesn't apply Lens
// 120% - mirror
let state =
manual_tick_component(Duration::from_millis(1200), &mut tween, &mut world, entity);
assert_eq!(TweeningDirection::Backward, tween.direction());
assert_eq!(TweenState::Active, state);
assert_eq!(1, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.2);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.8), 1e-5));
// rewind
tween.rewind();
assert_eq!(TweeningDirection::Forward, tween.direction()); // restored
assert_eq!(0, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::splat(0.8), 1e-5)); // no-op, rewind doesn't apply Lens
// 400% - done mirror (because Completed freezes the state)
let state =
manual_tick_component(Duration::from_millis(4000), &mut tween, &mut world, entity);
assert_eq!(TweenState::Completed, state);
assert_eq!(TweeningDirection::Backward, tween.direction()); // frozen from last loop
assert_eq!(4, tween.times_completed());
assert_approx_eq!(tween.progress(), 1.); // Completed
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5));
// rewind
tween.rewind();
assert_eq!(TweeningDirection::Forward, tween.direction()); // restored
assert_eq!(0, tween.times_completed());
assert_approx_eq!(tween.progress(), 0.);
let transform = world.entity(entity).get::<Transform>().unwrap();
assert!(transform.translation.abs_diff_eq(Vec3::ZERO, 1e-5)); // no-op, rewind doesn't apply Lens
}