turtlers/src/turtle_movement.rs

120 lines
3.8 KiB
Rust

use std::{f32::consts::PI, time::Duration};
use bevy::prelude::{Quat, Transform, Vec2, Vec3};
use bevy_tweening::{
lens::{TransformPositionLens, TransformRotateAxisLens, TransformRotateZLens},
EaseFunction, Tween, TweeningType,
};
use crate::{
datatypes::angle::Angle,
primitives::{CircleAnimationLens, CircleMovementLens},
turtle::{TurtleGraphElement, TurtleState},
};
pub fn turtle_turn(
state: &mut TurtleState,
angle_to_turn: Angle<f32>,
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
let start = state.heading;
let end = state.heading + angle_to_turn;
let animation = Tween::new(
// Use a quadratic easing on both endpoints
EaseFunction::QuadraticInOut,
TweeningType::Once,
// Animation time
Duration::from_millis(state.speed),
// Rotate the turtle
TransformRotateZLens {
start: start.to_radians().value(),
end: end.to_radians().value(),
},
)
.with_completed_event(state.index as u64);
// Dont move and draw
let line = TurtleGraphElement::Noop;
// Update the state
state.heading = end.limit_smaller_than_full_circle();
(Some(animation), Some(line))
}
pub fn turtle_move(
state: &mut TurtleState,
length: f32,
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
let start = state.start;
let end = state.start + (Vec2::from_angle(state.heading.to_radians().value()) * length);
let turtle_movement_animation = Tween::new(
// accelerate and decelerate
EaseFunction::QuadraticInOut,
TweeningType::Once,
// later to be controlled by speed
Duration::from_millis(state.speed),
// set the start and end of the animation
TransformPositionLens {
start: start.extend(0.),
end: end.extend(0.),
},
)
.with_completed_event(state.index as u64);
// The line for animating and drawing
let line = if state.drawing {
TurtleGraphElement::TurtleLine { start, end }
} else {
TurtleGraphElement::Noop
};
state.start = end;
(Some(turtle_movement_animation), Some(line))
}
pub fn turtle_circle(
state: &mut TurtleState,
radius: f32,
angle: Angle<f32>,
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
let radius_tuple = Vec2::ONE * radius.abs();
let left_right = Angle::degrees(if radius >= 0. { 90. } else { -90. });
let center = state.start
+ (Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle(
((state.heading + left_right).to_radians()).value(),
)));
let turtle_movement_animation = Tween::new(
// accelerate and decelerate
EaseFunction::QuadraticInOut,
TweeningType::Once,
// later to be controlled by speed
Duration::from_millis(state.speed),
// set the start and end of the animation
CircleMovementLens {
start: Transform {
translation: state.start.extend(0.),
rotation: Quat::from_rotation_z(state.heading.to_radians().value()),
scale: Vec3::ONE,
},
end: angle,
center,
},
)
.with_completed_event(state.index as u64);
// The line for animating and drawing
let line = if state.drawing {
TurtleGraphElement::TurtleCircle {
center,
radii: radius_tuple,
angle,
start: state.start,
end: state.start,
}
} else {
TurtleGraphElement::Noop
};
let end_pos = center
+ Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle(
(state.heading + angle - left_right).to_radians().value(),
));
state.start = end_pos;
state.heading = state.heading + angle;
(Some(turtle_movement_animation), Some(line))
}