turtlers/src/turtle.rs

366 lines
12 KiB
Rust
Raw Normal View History

2022-08-08 14:23:56 +02:00
use std::time::Duration;
2022-08-06 08:42:08 +02:00
use bevy::prelude::*;
2022-08-06 11:45:35 +02:00
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
2022-08-19 19:08:02 +02:00
use bevy_prototype_lyon::prelude::*;
2022-08-06 08:42:08 +02:00
use bevy_tweening::{
2022-08-19 19:08:02 +02:00
component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween,
2022-08-11 17:14:42 +02:00
TweenCompleted, TweeningPlugin, TweeningType,
2022-08-06 08:42:08 +02:00
};
2022-08-19 19:08:02 +02:00
use crate::{
2022-08-20 15:33:47 +02:00
datatypes::angle::Angle,
2022-08-19 22:30:28 +02:00
primitives::{CircleAnimationLens, LineAnimationLens, TurtleDrawCircle, TurtleDrawLine},
2022-08-19 19:08:02 +02:00
turtle_shapes,
};
2022-08-06 08:42:08 +02:00
pub struct TurtlePlugin;
impl Plugin for TurtlePlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_plugin(TweeningPlugin)
.add_startup_system(setup)
2022-08-06 10:28:12 +02:00
.add_system(keypresses)
2022-08-08 14:23:56 +02:00
.add_system(draw_lines)
2022-08-11 17:14:42 +02:00
.add_system(component_animator_system::<Path>)
2022-08-06 10:28:12 +02:00
.register_inspectable::<Colors>()
.register_inspectable::<TurtleCommands>();
2022-08-06 08:42:08 +02:00
}
}
#[derive(Bundle)]
pub struct Turtle {
colors: Colors,
commands: TurtleCommands,
2022-08-09 22:37:26 +02:00
name: Name,
2022-08-06 08:42:08 +02:00
}
impl Default for Turtle {
fn default() -> Self {
Self {
colors: Colors {
color: Color::DARK_GRAY,
fill_color: Color::BLACK,
},
2022-08-08 14:23:56 +02:00
commands: TurtleCommands::new(vec![
2022-08-19 22:30:28 +02:00
TurtleCommand::Forward(Length(-100.)),
2022-08-19 19:08:02 +02:00
TurtleCommand::Circle {
radius: Length(150.),
2022-08-20 15:33:47 +02:00
angle: Angle::degrees(30.),
2022-08-19 22:30:28 +02:00
},
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Circle {
radius: Length(70.),
2022-08-20 15:33:47 +02:00
angle: Angle::degrees(60.),
2022-08-19 19:08:02 +02:00
},
2022-08-19 22:30:28 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-20 15:33:47 +02:00
TurtleCommand::Right(Angle::degrees(70.)),
2022-08-19 22:30:28 +02:00
//TurtleCommand::PenDown,
2022-08-19 19:08:02 +02:00
TurtleCommand::Circle {
2022-08-19 22:30:28 +02:00
radius: Length(30.),
2022-08-20 15:33:47 +02:00
angle: Angle::degrees(360. - 46.),
2022-08-19 19:08:02 +02:00
},
2022-08-19 22:30:28 +02:00
/* TurtleCommand::Backward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(90.)),
2022-08-06 10:28:12 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(45.)),
2022-08-19 19:08:02 +02:00
//TurtleCommand::PenUp,
2022-08-06 10:28:12 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(90.)),
TurtleCommand::Forward(Length(50.)),
TurtleCommand::Right(Angle(90.)),
2022-08-06 10:28:12 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(90.)),
TurtleCommand::Forward(Length(50.)),
TurtleCommand::Right(Angle(45.)),
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Left(Angle(120.)),
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Left(Angle(120.)),
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Right(Angle(150.)),
2022-08-19 22:30:28 +02:00
TurtleCommand::Forward(Length(100.)), */
2022-08-08 14:23:56 +02:00
]),
2022-08-09 22:37:26 +02:00
name: Name::new("Turtle"),
2022-08-06 08:42:08 +02:00
}
}
}
impl Turtle {
2022-08-11 17:14:42 +02:00
/* pub fn set_color(&mut self, color: Color) {
2022-08-06 08:42:08 +02:00
self.colors.color = color;
}
pub fn set_fill_color(&mut self, color: Color) {
self.colors.fill_color = color;
}
pub fn get_colors(&self) -> &Colors {
&self.colors
}
pub fn forward(&mut self) -> &mut Self {
2022-08-08 14:23:56 +02:00
self.commands
.commands
.push(TurtleCommand::Forward(Length(100.0)));
2022-08-06 08:42:08 +02:00
self
2022-08-11 17:14:42 +02:00
} */
2022-08-06 08:42:08 +02:00
}
2022-08-06 10:28:12 +02:00
#[derive(Component, Inspectable)]
2022-08-08 14:23:56 +02:00
pub struct TurtleCommands {
commands: Vec<TurtleCommand>,
lines: Vec<TurtleGraphElement>,
2022-08-11 17:14:42 +02:00
state: TurtleState,
}
#[derive(Inspectable)]
pub struct TurtleState {
pub start: Vec2,
2022-08-20 15:33:47 +02:00
pub heading: Angle<f32>,
2022-08-11 17:14:42 +02:00
pub index: u64,
2022-08-19 19:08:02 +02:00
pub drawing: bool,
2022-08-08 14:23:56 +02:00
}
impl TurtleCommands {
fn new(commands: Vec<TurtleCommand>) -> Self {
Self {
commands,
lines: vec![],
2022-08-11 17:14:42 +02:00
state: TurtleState {
start: Vec2::ZERO,
2022-08-20 15:33:47 +02:00
heading: Angle::degrees(0.),
2022-08-11 17:14:42 +02:00
index: 0,
2022-08-19 19:08:02 +02:00
drawing: true,
2022-08-11 17:14:42 +02:00
},
2022-08-08 14:23:56 +02:00
}
}
}
2022-08-06 08:42:08 +02:00
impl TurtleCommands {
2022-08-19 19:08:02 +02:00
fn get_next(&mut self) -> Option<(Option<Tween<Transform>>, Option<TurtleGraphElement>)> {
2022-08-11 17:14:42 +02:00
let index = self.state.index;
let next_index = index + 1;
if let Some(command) = self.commands.get(self.state.index as usize) {
let res = match command {
2022-08-06 08:42:08 +02:00
TurtleCommand::Forward(Length(x)) => {
2022-08-11 17:14:42 +02:00
crate::turtle_movement::turtle_move(&mut self.state, *x as f32)
2022-08-08 14:23:56 +02:00
}
TurtleCommand::Backward(Length(x)) => {
2022-08-11 17:14:42 +02:00
crate::turtle_movement::turtle_move(&mut self.state, -*x as f32)
2022-08-06 08:42:08 +02:00
}
2022-08-20 15:33:47 +02:00
TurtleCommand::Left(angle) => {
crate::turtle_movement::turtle_turn(&mut self.state, *angle)
2022-08-08 14:23:56 +02:00
}
2022-08-20 15:33:47 +02:00
TurtleCommand::Right(angle) => {
crate::turtle_movement::turtle_turn(&mut self.state, -angle)
2022-08-06 08:42:08 +02:00
}
2022-08-19 19:08:02 +02:00
TurtleCommand::PenUp => {
self.state.drawing = false;
(None, None)
}
TurtleCommand::PenDown => {
self.state.drawing = true;
(None, None)
}
TurtleCommand::Circle { radius, angle } => {
crate::turtle_movement::turtle_circle(&mut self.state, radius.0 as f32, *angle)
}
2022-08-06 10:28:12 +02:00
TurtleCommand::Pause => todo!(),
2022-08-11 17:14:42 +02:00
};
self.state.index = next_index;
Some(res)
} else {
None
2022-08-06 08:42:08 +02:00
}
}
}
2022-08-08 14:23:56 +02:00
#[derive(Inspectable, Default)]
pub enum TurtleGraphElement {
TurtleLine {
start: Vec2,
end: Vec2,
},
2022-08-19 19:08:02 +02:00
TurtleCircle {
start: Vec2,
end: Vec2,
center: Vec2,
radii: Vec2,
2022-08-20 15:33:47 +02:00
angle: Angle<f32>,
2022-08-19 19:08:02 +02:00
},
2022-08-08 14:23:56 +02:00
#[default]
Noop,
}
2022-08-06 10:28:12 +02:00
#[derive(Clone, Component, Inspectable)]
2022-08-06 08:42:08 +02:00
pub struct TurtleShape;
2022-08-06 10:28:12 +02:00
#[derive(Clone, Component, Inspectable)]
2022-08-06 08:42:08 +02:00
pub struct Colors {
color: Color,
fill_color: Color,
}
2022-08-19 19:08:02 +02:00
#[derive(Inspectable, Default, Copy, Clone, Debug)]
pub struct Length(f32);
2022-08-06 08:42:08 +02:00
2022-08-06 10:28:12 +02:00
#[derive(Component, Inspectable, Default)]
2022-08-08 14:23:56 +02:00
pub enum TurtleCommand {
2022-08-06 08:42:08 +02:00
Forward(Length),
Backward(Length),
2022-08-20 15:33:47 +02:00
Left(Angle<f32>),
Right(Angle<f32>),
2022-08-06 08:42:08 +02:00
PenUp,
PenDown,
2022-08-06 10:28:12 +02:00
#[default]
Pause,
2022-08-19 19:08:02 +02:00
Circle {
radius: Length,
2022-08-20 15:33:47 +02:00
angle: Angle<f32>,
2022-08-19 19:08:02 +02:00
},
2022-08-06 08:42:08 +02:00
}
fn setup(mut commands: Commands) {
let animator = Animator::new(Tween::new(
EaseFunction::QuadraticInOut,
TweeningType::PingPong,
2022-08-11 17:14:42 +02:00
Duration::from_millis(500),
2022-08-19 19:08:02 +02:00
TransformScaleLens {
start: Vec3::new(1., 1., 0.),
end: Vec3::new(1.3, 1.3, 0.),
2022-08-06 08:42:08 +02:00
},
));
commands.spawn_bundle(Camera2dBundle::default());
commands
.spawn_bundle(Turtle::default())
.insert_bundle(GeometryBuilder::build_as(
&turtle_shapes::turtle(),
DrawMode::Outlined {
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
},
2022-08-06 10:28:12 +02:00
Transform::identity(),
2022-08-06 08:42:08 +02:00
))
2022-08-09 22:37:26 +02:00
.insert(animator)
.insert(TurtleShape);
2022-08-06 08:42:08 +02:00
}
2022-08-08 14:23:56 +02:00
fn draw_lines(
mut commands: Commands,
2022-08-11 17:14:42 +02:00
mut tcmd: Query<&mut TurtleCommands>,
mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>,
2022-08-09 22:37:26 +02:00
mut query_event: EventReader<TweenCompleted>, // TODO: howto attach only to the right event?
2022-08-08 14:23:56 +02:00
) {
2022-08-19 19:08:02 +02:00
for ev in query_event.iter() {
2022-08-11 17:14:42 +02:00
let mut tcmd = tcmd.single_mut();
2022-08-19 19:08:02 +02:00
loop {
match tcmd.get_next() {
Some((Some(turtle_animation), Some(graph_element_to_draw))) => {
let mut turtle = turtle.single_mut();
turtle.set_tweenable(turtle_animation);
match graph_element_to_draw {
TurtleGraphElement::TurtleLine { start, end } => {
let line_animator = Animator::new(Tween::new(
EaseFunction::QuadraticInOut,
TweeningType::Once,
Duration::from_millis(500),
LineAnimationLens::new(start, end),
));
commands
.spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index))
.insert(line_animator);
}
TurtleGraphElement::Noop => println!("No drawing!"),
TurtleGraphElement::TurtleCircle {
center,
radii,
angle,
start,
end,
} => {
2022-08-19 22:30:28 +02:00
let line_animator = Animator::new(Tween::new(
EaseFunction::QuadraticInOut,
TweeningType::Once,
Duration::from_millis(500),
CircleAnimationLens {
start_pos: start,
center,
radii,
2022-08-20 15:33:47 +02:00
start: Angle::degrees(0.),
2022-08-19 22:30:28 +02:00
end: angle,
},
2022-08-19 19:08:02 +02:00
));
2022-08-19 22:30:28 +02:00
commands
.spawn_bundle(TurtleDrawCircle::new(
center,
radii,
2022-08-20 15:33:47 +02:00
Angle::degrees(0.),
2022-08-19 22:30:28 +02:00
tcmd.state.index,
start,
end,
))
.insert(line_animator);
2022-08-19 19:08:02 +02:00
}
}
return;
2022-08-09 22:37:26 +02:00
}
2022-08-19 19:08:02 +02:00
Some((_, _)) => {
println!("without animation");
}
None => {
println!("nothing to draw");
return;
}
};
}
2022-08-08 14:23:56 +02:00
}
}
2022-08-06 08:42:08 +02:00
fn keypresses(
2022-08-11 17:14:42 +02:00
mut commands: Commands,
2022-08-06 08:42:08 +02:00
keys: Res<Input<KeyCode>>,
2022-08-09 22:37:26 +02:00
mut qry: Query<&mut Animator<Transform>, With<TurtleShape>>,
2022-08-08 14:23:56 +02:00
mut tcmd: Query<&mut TurtleCommands>,
2022-08-06 08:42:08 +02:00
) {
if keys.just_pressed(KeyCode::W) {
2022-08-08 14:23:56 +02:00
let mut tcmd = tcmd.single_mut();
2022-08-11 17:14:42 +02:00
tcmd.state = TurtleState {
start: Vec2::ZERO,
2022-08-20 15:33:47 +02:00
heading: Angle::degrees(0.),
2022-08-11 17:14:42 +02:00
index: 0,
2022-08-19 19:08:02 +02:00
drawing: true,
2022-08-11 17:14:42 +02:00
};
2022-08-19 19:08:02 +02:00
if let Some((Some(turtle_animation), Some(graph_element_to_draw))) = tcmd.get_next() {
2022-08-11 17:14:42 +02:00
let mut shap = qry.single_mut();
shap.set_tweenable(turtle_animation);
match graph_element_to_draw {
TurtleGraphElement::TurtleLine { start, end } => {
let line_animator = Animator::new(Tween::new(
EaseFunction::QuadraticInOut,
TweeningType::Once,
Duration::from_millis(500),
2022-08-19 19:08:02 +02:00
LineAnimationLens::new(start, end),
2022-08-11 17:14:42 +02:00
));
commands
.spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index))
.insert(line_animator);
}
TurtleGraphElement::Noop => (),
2022-08-19 19:08:02 +02:00
TurtleGraphElement::TurtleCircle {
center,
radii,
angle,
start,
end,
} => {
commands.spawn_bundle(TurtleDrawCircle::new(
center,
radii,
angle,
tcmd.state.index,
start,
end,
));
}
2022-08-11 17:14:42 +02:00
}
};
2022-08-06 08:42:08 +02:00
}
}