pub mod builders; pub mod state; pub mod turtle; use std::time::Duration; use bevy::prelude::*; use bevy_prototype_lyon::prelude::*; use bevy_tweening::{ component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, RepeatCount, Tween, TweenCompleted, TweeningPlugin, }; #[allow(unused_imports)] use crate::paths; use crate::{ animation_step::run_animation_step, datatypes::{angle::Angle, length::Length}, primitives::bundles::{Turtle, TurtleDrawCircle, TurtleDrawLine}, turtle_movement::TurtleStep, }; 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::<Path>) .register_type::<Colors>() .register_type::<TurtleCommands>(); } } pub type Precision = f32; #[derive(Component, Reflect)] pub struct TurtleCommands { commands: Vec<TurtleCommand>, lines: Vec<TurtleGraphElement>, state: TurtleState, } #[derive(Reflect)] pub struct TurtleState { pub start: Vec2, pub heading: Angle<f32>, pub speed: u64, pub index: u64, pub drawing: bool, } impl TurtleCommands { pub fn new(commands: Vec<TurtleCommand>) -> Self { Self { commands, lines: vec![], state: TurtleState { start: Vec2::ZERO, heading: Angle::degrees(0.), speed: 2000, index: 0, drawing: true, }, } } } impl TurtleCommands { pub(crate) fn get_next(&mut self) -> Option<TurtleStep> { 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; TurtleStep { turtle_animation: None, line_segment: None, line_animation: None, fill: None, stroke: None, } } TurtleCommand::PenDown => { self.state.drawing = true; TurtleStep { turtle_animation: None, line_segment: None, line_animation: None, fill: None, stroke: None, } } TurtleCommand::Circle { radius, angle } => { crate::turtle_movement::turtle_circle(&mut self.state, radius.0 as f32, *angle) } TurtleCommand::Pause => todo!(), TurtleCommand::BeginFill => todo!(), TurtleCommand::EndFill => todo!(), }; self.state.index = next_index; Some(res) } else { None } } } #[derive(Reflect, Default)] pub enum TurtleGraphElement { TurtleLine(TurtleDrawLine), TurtleCircle(TurtleDrawCircle), #[default] Noop, } #[derive(Clone, Component, Reflect)] pub struct TurtleShape; #[derive(Clone, Component, Reflect, Default)] pub struct Colors { color: Color, fill_color: Color, } #[derive(Component, Reflect, Default)] pub enum TurtleCommand { Forward(Length), Backward(Length), Left(Angle<f32>), Right(Angle<f32>), PenUp, PenDown, BeginFill, EndFill, #[default] Pause, Circle { radius: Length, angle: Angle<f32>, }, } fn setup(mut commands: Commands) { let animator = Animator::new( Tween::new( EaseFunction::QuadraticInOut, Duration::from_millis(500), TransformScaleLens { start: Vec3::new(1., 1., 0.), end: Vec3::new(1.3, 1.3, 0.), }, ) .with_repeat_strategy(bevy_tweening::RepeatStrategy::MirroredRepeat) .with_repeat_count(RepeatCount::Infinite), ); commands.spawn(Camera2dBundle::default()); let mut turtle_bundle = Turtle::default(); let mut tcommands = vec![]; //for _ in 0..100 { tcommands.append(&mut paths::geometry_task::geometry_task()); //tcommands.append(&mut paths::circle_star::circle_star()); //} turtle_bundle.set_commands(tcommands); commands.spawn(( turtle_bundle, animator, TurtleShape, Fill::color(Color::MIDNIGHT_BLUE), Stroke::color(Color::BLACK), )); } fn draw_lines( mut commands: Commands, mut tcmd: Query<&mut TurtleCommands>, mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>, mut query_event: EventReader<TweenCompleted>, // TODO: howto attach only to the right event? ) { for _ev in query_event.iter() { let mut tcmd = tcmd.single_mut(); run_animation_step(&mut commands, &mut tcmd, &mut turtle) } } fn keypresses( mut commands: Commands, keys: Res<Input<KeyCode>>, mut tcmd: Query<&mut TurtleCommands>, mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>, ) { if keys.just_pressed(KeyCode::W) { let mut tcmd = tcmd.single_mut(); tcmd.state = TurtleState { start: Vec2::ZERO, heading: Angle::degrees(0.), speed: 1, index: 0, drawing: true, }; run_animation_step(&mut commands, &mut tcmd, &mut turtle); } }