use std::time::Duration; use bevy::prelude::*; use bevy_inspector_egui::{Inspectable, RegisterInspectable}; use bevy_prototype_lyon::prelude::*; use bevy_tweening::{ component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween, TweenCompleted, TweeningPlugin, TweeningType, }; use crate::{ datatypes::angle::Angle, primitives::{CircleAnimationLens, LineAnimationLens, TurtleDrawCircle, TurtleDrawLine}, turtle_shapes, }; pub struct TurtlePlugin; impl Plugin for TurtlePlugin { fn build(&self, app: &mut bevy::prelude::App) { app.add_plugin(TweeningPlugin) .add_startup_system(setup) .add_system(keypresses) .add_system(draw_lines) .add_system(component_animator_system::) .register_inspectable::() .register_inspectable::(); } } #[derive(Bundle)] pub struct Turtle { colors: Colors, commands: TurtleCommands, name: Name, } impl Default for Turtle { fn default() -> Self { Self { colors: Colors { color: Color::DARK_GRAY, fill_color: Color::BLACK, }, commands: TurtleCommands::new(vec![ TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Circle { radius: Length(20.), angle: Angle::degrees(324.), }, TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Circle { radius: Length(20.), angle: Angle::degrees(324.), }, TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Circle { radius: Length(20.), angle: Angle::degrees(324.), }, TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Circle { radius: Length(20.), angle: Angle::degrees(324.), }, TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Circle { radius: Length(20.), angle: Angle::degrees(324.), }, TurtleCommand::Right(Angle::degrees(90.)), TurtleCommand::Forward(Length(100.)), /* TurtleCommand::Forward(Length(100.)), TurtleCommand::Circle { radius: Length(70.), angle: Angle::degrees(60.), }, TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle::degrees(70.)), //TurtleCommand::PenDown, TurtleCommand::Circle { radius: Length(30.), angle: Angle::degrees(360. - 46.), }, */ /* TurtleCommand::Backward(Length(100.)), TurtleCommand::Right(Angle(90.)), TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle(45.)), //TurtleCommand::PenUp, TurtleCommand::Forward(Length(100.)), TurtleCommand::Right(Angle(90.)), TurtleCommand::Forward(Length(50.)), TurtleCommand::Right(Angle(90.)), TurtleCommand::Forward(Length(100.)), 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.)), TurtleCommand::Forward(Length(100.)), */ ]), name: Name::new("Turtle"), } } } impl Turtle { /* pub fn set_color(&mut self, color: Color) { 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 { self.commands .commands .push(TurtleCommand::Forward(Length(100.0))); self } */ } #[derive(Component, Inspectable)] pub struct TurtleCommands { commands: Vec, lines: Vec, state: TurtleState, } #[derive(Inspectable)] pub struct TurtleState { pub start: Vec2, pub heading: Angle, pub speed: u64, pub index: u64, pub drawing: bool, } impl TurtleCommands { fn new(commands: Vec) -> Self { Self { commands, lines: vec![], state: TurtleState { start: Vec2::ZERO, heading: Angle::degrees(0.), speed: 2000, index: 0, drawing: true, }, } } } impl TurtleCommands { fn get_next(&mut self) -> Option<(Option>, Option)> { 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 { TurtleCommand::Forward(Length(x)) => { crate::turtle_movement::turtle_move(&mut self.state, *x as f32) } TurtleCommand::Backward(Length(x)) => { crate::turtle_movement::turtle_move(&mut self.state, -*x as f32) } TurtleCommand::Left(angle) => { crate::turtle_movement::turtle_turn(&mut self.state, *angle) } TurtleCommand::Right(angle) => { crate::turtle_movement::turtle_turn(&mut self.state, -angle) } 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) } TurtleCommand::Pause => todo!(), }; self.state.index = next_index; Some(res) } else { None } } } #[derive(Inspectable, Default)] pub enum TurtleGraphElement { TurtleLine { start: Vec2, end: Vec2, }, TurtleCircle { start: Vec2, end: Vec2, center: Vec2, radii: Vec2, angle: Angle, }, #[default] Noop, } #[derive(Clone, Component, Inspectable)] pub struct TurtleShape; #[derive(Clone, Component, Inspectable)] pub struct Colors { color: Color, fill_color: Color, } #[derive(Inspectable, Default, Copy, Clone, Debug)] pub struct Length(f32); #[derive(Component, Inspectable, Default)] pub enum TurtleCommand { Forward(Length), Backward(Length), Left(Angle), Right(Angle), PenUp, PenDown, #[default] Pause, Circle { radius: Length, angle: Angle, }, } fn setup(mut commands: Commands) { let animator = Animator::new(Tween::new( EaseFunction::QuadraticInOut, TweeningType::PingPong, Duration::from_millis(500), TransformScaleLens { start: Vec3::new(1., 1., 0.), end: Vec3::new(1.3, 1.3, 0.), }, )); 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), }, Transform::identity(), )) .insert(animator) .insert(TurtleShape); } fn draw_lines( mut commands: Commands, mut tcmd: Query<&mut TurtleCommands>, mut turtle: Query<&mut Animator, With>, mut query_event: EventReader, // TODO: howto attach only to the right event? ) { for ev in query_event.iter() { let mut tcmd = tcmd.single_mut(); 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(tcmd.state.speed), 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, } => { let line_animator = Animator::new(Tween::new( EaseFunction::QuadraticInOut, TweeningType::Once, Duration::from_millis(tcmd.state.speed), CircleAnimationLens { start_pos: start, center, radii, start: Angle::degrees(0.), end: angle, }, )); commands .spawn_bundle(TurtleDrawCircle::new( center, radii, Angle::degrees(0.), tcmd.state.index, start, end, )) .insert(line_animator); } } return; } Some((_, _)) => { println!("without animation"); } None => { println!("nothing to draw"); return; } }; } } } fn keypresses( mut commands: Commands, keys: Res>, mut qry: Query<&mut Animator, With>, mut tcmd: Query<&mut TurtleCommands>, ) { if keys.just_pressed(KeyCode::W) { let mut tcmd = tcmd.single_mut(); tcmd.state = TurtleState { start: Vec2::ZERO, heading: Angle::degrees(0.), speed: 2000, index: 0, drawing: true, }; if let Some((Some(turtle_animation), Some(graph_element_to_draw))) = tcmd.get_next() { 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(tcmd.state.speed), LineAnimationLens::new(start, end), )); commands .spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index)) .insert(line_animator); } TurtleGraphElement::Noop => (), TurtleGraphElement::TurtleCircle { center, radii, angle, start, end, } => { commands.spawn_bundle(TurtleDrawCircle::new( center, radii, angle, tcmd.state.index, start, end, )); } } }; } }