From 75771e8556af85286b4e53b7faf1b01957753e58 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 21 Dec 2022 11:07:02 +0100 Subject: [PATCH] Add circle drawing --- src/builders.rs | 39 ++++++++- src/commands.rs | 20 ++++- src/drawing/animation.rs | 172 ++++++++++++++++++++++++--------------- src/general/length.rs | 7 ++ src/turtle_bundle.rs | 26 ++++-- 5 files changed, 183 insertions(+), 81 deletions(-) diff --git a/src/builders.rs b/src/builders.rs index dfa5d9b..ebaba36 100644 --- a/src/builders.rs +++ b/src/builders.rs @@ -1,5 +1,7 @@ +use std::ops::Neg; + use crate::{ - commands::{DrawElement, TurtleSegment}, + commands::{DrawElement, MoveCommand, TurtleSegment}, general::{angle::Angle, length::Length, Precision}, }; @@ -82,3 +84,38 @@ pub trait Turnable: WithCommands { } impl Turnable for TurtlePlan {} + +pub trait CurvedMovement: WithCommands { + fn circle( + &mut self, + radius: IntoDistance, + extend: IntoAngle, + ) -> &mut Self + where + Angle: From, + Length: From, + { + let angle: Angle = extend.into(); + let radius: Length = radius.into(); + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Draw( + MoveCommand::Circle { radius, angle }, + ))); + self + } + fn circle_right>( + &mut self, + radius: IntoDistance, + extend: IntoAngle, + ) -> &mut Self + where + Angle: From, + Length: From, + { + self.circle(-radius, extend); + println!("Warning: circle with right arc not working yet..."); + self + } +} + +impl CurvedMovement for TurtlePlan {} diff --git a/src/commands.rs b/src/commands.rs index ca55e91..b67e0d1 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -2,11 +2,11 @@ use bevy::prelude::Component; use bevy_inspector_egui::Inspectable; use crate::{ + builders::WithCommands, drawing::{ - self, animation::{ - draw_straight_segment, move_straight_segment, turtle_turn, ToAnimationSegment, - TurtleAnimationSegment, + draw_circle_segment, draw_straight_segment, move_straight_segment, turtle_turn, + ToAnimationSegment, TurtleAnimationSegment, }, TurtleGraphElement, }, @@ -80,7 +80,9 @@ impl ToAnimationSegment for DrawElement { DrawElement::Draw(e) => match e { MoveCommand::Forward(length) => draw_straight_segment(state, length.0), MoveCommand::Backward(length) => draw_straight_segment(state, -length.0), - MoveCommand::Circle { radius, angle } => todo!(), + MoveCommand::Circle { radius, angle } => { + draw_circle_segment(state, *radius, *angle) + } MoveCommand::Goto(coord) => todo!(), }, DrawElement::Move(e) => match e { @@ -170,3 +172,13 @@ impl Iterator for TurtleCommands { } } } + +impl WithCommands for TurtleCommands { + fn get_mut_commands(&mut self) -> &mut Vec { + &mut self.commands + } + + fn get_commands(self) -> Vec { + self.commands + } +} diff --git a/src/drawing/animation.rs b/src/drawing/animation.rs index 682f8dc..f27fe75 100644 --- a/src/drawing/animation.rs +++ b/src/drawing/animation.rs @@ -1,18 +1,15 @@ mod circle_lens; mod line_lens; -use bevy::{ - prelude::{Quat, Transform, Vec2, Vec3}, - render::render_resource::encase::rts_array::Length, -}; +use bevy::prelude::{Quat, Transform, Vec2, Vec3}; use bevy_prototype_lyon::prelude::Path; use bevy_tweening::{ lens::{TransformPositionLens, TransformRotateZLens}, - Animator, EaseFunction, RepeatCount, RepeatStrategy, Tween, + Animator, EaseFunction, Tween, }; use crate::{ - general::{angle::Angle, Coordinate, Precision}, + general::{angle::Angle, length::Length, Coordinate, Precision}, state::TurtleState, }; @@ -85,9 +82,25 @@ pub fn draw_straight_segment(state: &mut TurtleState, length: Precision) -> Turt } } +pub fn draw_circle_segment( + state: &mut TurtleState, + radius: Length, + angle: Angle, +) -> TurtleAnimationSegment { + let animation = MoveCircleTurtleAnimation::new(state, radius, angle); + let line_animation = MoveCircleLineAnimation::new(state, radius, angle); + state.set_position(animation.end); + state.set_heading(animation.end_heading); + TurtleAnimationSegment { + turtle_animation: Some(animation.animation), + line_segment: Some(TurtleGraphElement::TurtleCircle(line_animation.line)), + line_animation: Some(Animator::new(line_animation.animation)), + } +} + struct MoveStraightLineAnimation { - start: Coordinate, - end: Coordinate, + _start: Coordinate, + _end: Coordinate, line: TurtleDrawLine, animation: Tween, } @@ -95,7 +108,7 @@ struct MoveStraightLineAnimation { impl MoveStraightLineAnimation { fn new( state: &TurtleState, - length: Precision, + _length: Precision, turtle_animation: &MoveStraightTurtleAnimation, ) -> Self { let line = TurtleDrawLine::new(turtle_animation.start, turtle_animation.end); @@ -107,8 +120,8 @@ impl MoveStraightLineAnimation { /* .with_repeat_strategy(RepeatStrategy::MirroredRepeat) .with_repeat_count(RepeatCount::Infinite)*/; Self { - start: turtle_animation.start, - end: turtle_animation.end, + _start: turtle_animation.start, + _end: turtle_animation.end, line, animation: line_animation, } @@ -142,63 +155,88 @@ impl MoveStraightTurtleAnimation { } } -pub fn turtle_circle( - state: &mut TurtleState, - radius: Precision, - angle: Angle, -) -> TurtleAnimationSegment { - let radii = Vec2::ONE * radius.abs(); - let left_right = Angle::degrees(if radius >= 0. { 90. } else { -90. }); - let center = state.position() - + (Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle( - ((state.heading() + left_right).to_radians()).value(), - ))); +struct MoveCircleLineAnimation { + _start: Coordinate, + _end: Coordinate, + line: TurtleDrawCircle, + animation: Tween, +} - let turtle_movement_animation = Tween::new( - EaseFunction::QuadraticInOut, - state.animation_duration(), - CircleMovementLens { - start: Transform { - translation: state.position().extend(0.), - rotation: Quat::from_rotation_z(state.heading().to_radians().value()), - scale: Vec3::ONE, +impl MoveCircleLineAnimation { + fn new(state: &TurtleState, radius: Length, angle: Angle) -> Self { + let radii = Vec2::ONE * radius.0.abs(); + let start = state.position(); + let left_right = Angle::degrees(if radius.0 >= 0. { 90. } else { -90. }); + let center = state.position() + + (Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle( + ((state.heading() + left_right).to_radians()).value(), + ))); + let end_pos = center + + Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle( + (state.heading() + angle - left_right).to_radians().value(), + )); + + let line = + TurtleDrawCircle::new(center, radii, Angle::degrees(0.), state.position(), end_pos); + let line_animator = Tween::new( + EaseFunction::QuadraticInOut, + state.animation_duration(), + CircleAnimationLens { + start_pos: state.position(), + center, + radii, + start: Angle::degrees(0.), + end: if radius.0 > 0. { angle } else { -angle }, }, - end: angle, - center, - }, - ) - .with_completed_event(state.segment_index()); - let end_pos = center - + Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle( - (state.heading() + angle - left_right).to_radians().value(), - )); - let line = /* if state.drawing { */ - TurtleGraphElement::TurtleCircle(TurtleDrawCircle::new( - center, - radii, - Angle::degrees(0.), - state.position(), - end_pos, - )) - /* } else { - TurtleGraphElement::Noop - } */; - let line_animator = Animator::new(Tween::new( - EaseFunction::QuadraticInOut, - state.animation_duration(), - CircleAnimationLens { - start_pos: state.position(), - center, - radii, - start: Angle::degrees(0.), - end: angle, - }, - )); - state.set_position(end_pos); - state.set_heading(state.heading() + angle); - TurtleAnimationSegment { - turtle_animation: Some(turtle_movement_animation), - line_segment: Some(line), - line_animation: Some(line_animator), + ); + Self { + _start: start, + _end: end_pos, + line, + animation: line_animator, + } + } +} + +struct MoveCircleTurtleAnimation { + _start: Coordinate, + end: Coordinate, + end_heading: Angle, + animation: Tween, +} + +impl MoveCircleTurtleAnimation { + fn new(state: &TurtleState, radius: Length, angle: Angle) -> Self { + let start = state.position(); + let left_right = Angle::degrees(if radius.0 >= 0. { 90. } else { -90. }); + let center = state.position() + + (Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle( + ((state.heading() + left_right).to_radians()).value(), + ))); + let end_heading = state.heading() + if radius.0 > 0. { angle } else { -angle }; + let end_pos = center + + Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle( + (state.heading() + angle - left_right).to_radians().value(), + )); + let turtle_movement_animation = Tween::new( + EaseFunction::QuadraticInOut, + state.animation_duration(), + CircleMovementLens { + start: Transform { + translation: state.position().extend(0.), + rotation: Quat::from_rotation_z(state.heading().to_radians().value()), + scale: Vec3::ONE, + }, + end: if radius.0 > 0. { angle } else { -angle }, + center, + }, + ) + .with_completed_event(state.segment_index() as u64); + Self { + _start: start, + end: end_pos, + end_heading, + animation: turtle_movement_animation, + } } } diff --git a/src/general/length.rs b/src/general/length.rs index 8689773..351560a 100644 --- a/src/general/length.rs +++ b/src/general/length.rs @@ -10,3 +10,10 @@ impl From for Length { Self(Precision::from(i)) } } + +impl From for Length { + fn from(i: f32) -> Self { + #[allow(clippy::useless_conversion)] + Self(Precision::from(i)) + } +} diff --git a/src/turtle_bundle.rs b/src/turtle_bundle.rs index b8f864e..00e13c4 100644 --- a/src/turtle_bundle.rs +++ b/src/turtle_bundle.rs @@ -7,9 +7,9 @@ use bevy_prototype_lyon::{ }; use crate::{ - builders::{TurtlePlan, WithCommands}, - commands::{DrawElement, MoveCommand, TurtleCommands, TurtleSegment}, - general::{length::Length, Speed}, + builders::{CurvedMovement, DirectionalMovement, Turnable, TurtlePlan, WithCommands}, + commands::{TurtleCommands, TurtleSegment}, + general::Speed, shapes::{self, TurtleColors}, }; @@ -52,12 +52,6 @@ impl TurtleBundle { } impl TurtleBundle { - pub fn forward(&mut self, len: f32) -> &mut Self { - self.commands.push(TurtleSegment::Single(DrawElement::Draw( - MoveCommand::Forward(Length(len)), - ))); - self - } pub fn set_speed(&mut self, speed: Speed) { self.commands.set_speed(speed); } @@ -83,3 +77,17 @@ impl DerefMut for AnimatedTurtle { &mut self.turtle_bundle } } + +impl WithCommands for TurtleBundle { + fn get_mut_commands(&mut self) -> &mut Vec { + self.commands.get_mut_commands() + } + + fn get_commands(self) -> Vec { + self.commands.get_commands() + } +} + +impl DirectionalMovement for TurtleBundle {} +impl Turnable for TurtleBundle {} +impl CurvedMovement for TurtleBundle {}