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-23 10:52:20 +02:00
|
|
|
#[allow(unused_imports)]
|
|
|
|
use crate::paths::{circle_star::circle_star, geometry_task::geometry_task};
|
2022-08-19 19:08:02 +02:00
|
|
|
use crate::{
|
2022-08-23 10:52:20 +02:00
|
|
|
datatypes::{angle::Angle, length::Length},
|
|
|
|
primitives::{
|
|
|
|
animation::{CircleAnimationLens, LineAnimationLens},
|
|
|
|
bundles::{Turtle, 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
|
|
|
}
|
|
|
|
}
|
2022-08-23 10:52:20 +02:00
|
|
|
pub type Precision = f32;
|
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-22 14:17:26 +02:00
|
|
|
pub speed: u64,
|
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 {
|
2022-08-23 10:52:20 +02:00
|
|
|
pub fn new(commands: Vec<TurtleCommand>) -> Self {
|
2022-08-08 14:23:56 +02:00
|
|
|
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-22 14:17:26 +02:00
|
|
|
speed: 2000,
|
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-23 10:52:20 +02:00
|
|
|
#[derive(Clone, Component, Inspectable, Default)]
|
2022-08-06 08:42:08 +02:00
|
|
|
pub struct Colors {
|
|
|
|
color: Color,
|
|
|
|
fill_color: Color,
|
|
|
|
}
|
|
|
|
|
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());
|
2022-08-23 10:52:20 +02:00
|
|
|
let mut turtle_bundle = Turtle::default();
|
|
|
|
turtle_bundle.set_commands(geometry_task());
|
2022-08-06 08:42:08 +02:00
|
|
|
commands
|
2022-08-23 10:52:20 +02:00
|
|
|
.spawn_bundle(turtle_bundle)
|
2022-08-06 08:42:08 +02:00
|
|
|
.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,
|
2022-08-22 14:17:26 +02:00
|
|
|
Duration::from_millis(tcmd.state.speed),
|
2022-08-19 19:08:02 +02:00
|
|
|
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,
|
2022-08-22 14:17:26 +02:00
|
|
|
Duration::from_millis(tcmd.state.speed),
|
2022-08-19 22:30:28 +02:00
|
|
|
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-22 14:17:26 +02:00
|
|
|
speed: 2000,
|
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,
|
2022-08-22 14:17:26 +02:00
|
|
|
Duration::from_millis(tcmd.state.speed),
|
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
|
|
|
}
|
|
|
|
}
|