diff --git a/src/main.rs b/src/main.rs index 1f14dbd..51fd67d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod debug; mod turtle; +mod turtle_movement; mod turtle_shapes; use bevy::{prelude::*, window::close_on_esc}; diff --git a/src/turtle.rs b/src/turtle.rs index aec9fb6..cef2c6f 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -1,14 +1,14 @@ -use std::{f32::consts::PI, time::Duration}; +use std::time::Duration; use bevy::prelude::*; use bevy_inspector_egui::{Inspectable, RegisterInspectable}; -use bevy_prototype_lyon::prelude::*; +use bevy_prototype_lyon::{prelude::*, shapes::Line}; use bevy_tweening::{ - lens::{TransformPositionLens, TransformRotateZLens}, - Animator, EaseFunction, Sequence, Tween, TweeningPlugin, TweeningType, + lens::TransformPositionLens, Animator, EaseFunction, Sequence, Tween, TweenCompleted, + TweeningPlugin, TweeningType, }; -use crate::turtle_shapes; +use crate::{turtle_movement::turtle_move, turtle_shapes}; pub struct TurtlePlugin; @@ -17,6 +17,7 @@ impl Plugin for TurtlePlugin { app.add_plugin(TweeningPlugin) .add_startup_system(setup) .add_system(keypresses) + .add_system(draw_lines) .register_inspectable::() .register_inspectable::(); } @@ -34,24 +35,29 @@ impl Default for Turtle { color: Color::DARK_GRAY, fill_color: Color::BLACK, }, - commands: TurtleCommands(vec![ + commands: TurtleCommands::new(vec![ TurtleCommand::Forward(Length(100.)), - TurtleCommand::Left(Angle(90.)), + TurtleCommand::Right(Angle(90.)), + TurtleCommand::Backward(Length(100.)), + TurtleCommand::Right(Angle(90.)), TurtleCommand::Forward(Length(100.)), - TurtleCommand::Left(Angle(90.)), + TurtleCommand::Right(Angle(45.)), TurtleCommand::Forward(Length(100.)), - TurtleCommand::Left(Angle(90.)), + TurtleCommand::Right(Angle(90.)), + TurtleCommand::Forward(Length(50.)), + TurtleCommand::Right(Angle(90.)), TurtleCommand::Forward(Length(100.)), - TurtleCommand::Left(Angle(90.)), - ]), /* - shape: TurtleShape(GeometryBuilder::build_as( - &turtle_shapes::turtle(), - DrawMode::Outlined { - fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), - outline_mode: StrokeMode::new(Color::BLACK, 1.0), - }, - Default::default(), - )), */ + 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.)), + ]), } } } @@ -67,61 +73,60 @@ impl Turtle { &self.colors } pub fn forward(&mut self) -> &mut Self { - self.commands.0.push(TurtleCommand::Forward(Length(100.0))); + self.commands + .commands + .push(TurtleCommand::Forward(Length(100.0))); self } } #[derive(Component, Inspectable)] -pub struct TurtleCommands(Vec); +pub struct TurtleCommands { + commands: Vec, + lines: Vec, +} impl TurtleCommands { - fn generate_tweenable(&self) -> Sequence { - let mut seq = Sequence::with_capacity(self.0.len()); + fn new(commands: Vec) -> Self { + Self { + commands, + lines: vec![], + } + } +} + +impl TurtleCommands { + fn generate_tweenable(&mut self) -> Sequence { + self.lines.clear(); + let mut seq = Sequence::with_capacity(self.commands.len()); let mut pos = Vec2::ZERO; let mut ori: f32 = 0.; - for op in &self.0 { + for (index, op) in self.commands.iter().enumerate() { match op { TurtleCommand::Forward(Length(x)) => { - println!("Adding Forward"); - let start = pos; - let end = pos + (Vec2::from_angle(ori) * *x as f32); - seq = seq.then(Tween::new( - // accelerate and decelerate - EaseFunction::QuadraticInOut, - // Loop animation back and forth. - TweeningType::Once, - // later to be controlled by speed - Duration::from_secs(1), - // set the start and end of the animation - TransformPositionLens { - start: start.extend(0.), - end: end.extend(0.), - }, - )); - pos = end; + let (target, animation, line) = turtle_move(pos, ori, *x as f32, index as f32); + self.lines.push(line); + seq = seq.then(animation); + pos = target; + } + TurtleCommand::Backward(Length(x)) => { + let (target, animation, line) = turtle_move(pos, ori, -*x as f32, index as f32); + self.lines.push(line); + seq = seq.then(animation); + pos = target; } - TurtleCommand::Backward(_) => todo!(), TurtleCommand::Left(Angle(x)) => { - println!("Adding Left"); - let start = ori; - let end = ori + (*x as f32 * PI / 180.); - seq = seq.then(Tween::new( - // Use a quadratic easing on both endpoints. - EaseFunction::QuadraticInOut, - // Loop animation back and forth. - TweeningType::Once, - // Animation time (one way only; for ping-pong it takes 2 seconds - // to come back to start). - Duration::from_secs(1), - // The lens gives access to the Transform component of the Entity, - // for the Animator to animate it. It also contains the start and - // end values respectively associated with the progress ratios 0. and 1. - TransformRotateZLens { start, end }, - )); - ori = end % (2. * PI); + let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, *x as f32); + ori = nori; + seq = seq.then(tween); + self.lines.push(line); + } + TurtleCommand::Right(Angle(x)) => { + let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, -*x as f32); + ori = nori; + seq = seq.then(tween); + self.lines.push(line); } - TurtleCommand::Right(_) => todo!(), TurtleCommand::PenUp => todo!(), TurtleCommand::PenDown => todo!(), TurtleCommand::Circle => todo!(), @@ -132,6 +137,16 @@ impl TurtleCommands { } } +#[derive(Inspectable, Default)] +pub enum TurtleGraphElement { + TurtleLine { + start: Vec2, + end: Vec2, + }, + #[default] + Noop, +} + #[derive(Clone, Component, Inspectable)] pub struct TurtleShape; @@ -147,7 +162,7 @@ pub struct Length(f64); pub struct Angle(f64); #[derive(Component, Inspectable, Default)] -enum TurtleCommand { +pub enum TurtleCommand { Forward(Length), Backward(Length), Left(Angle), @@ -190,45 +205,43 @@ fn setup(mut commands: Commands) { .insert(animator); } +fn draw_lines( + mut commands: Commands, + tcmd: Query<&TurtleCommands>, + mut query_event: EventReader, +) { + for ev in query_event.iter() { + let index = ev.user_data; + let tcmd = tcmd.get_single().unwrap(); + let t = tcmd.lines.get(index as usize).unwrap(); + match t { + TurtleGraphElement::TurtleLine { start, end } => { + commands.spawn_bundle(GeometryBuilder::build_as( + &Line(*start, *end), + DrawMode::Outlined { + fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), + outline_mode: StrokeMode::new(Color::BLACK, 1.0), + }, + Transform::identity(), + )); + } + TurtleGraphElement::Noop => (), + } + } +} + /// The sprite is animated by changing its translation depending on the time that has passed since /// the last frame. fn keypresses( //time: Res