diff --git a/TODO.md b/TODO.md index 127d397..40b2a6d 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1 @@ -*[x] Fix orientation after and during circles \ No newline at end of file +*[x] make the \ No newline at end of file diff --git a/src/datatypes.rs b/src/datatypes.rs index 8f932ea..054d211 100644 --- a/src/datatypes.rs +++ b/src/datatypes.rs @@ -1 +1,2 @@ pub mod angle; +pub mod length; diff --git a/src/datatypes/length.rs b/src/datatypes/length.rs index e69de29..cf4fb9a 100644 --- a/src/datatypes/length.rs +++ b/src/datatypes/length.rs @@ -0,0 +1,4 @@ +use bevy_inspector_egui::Inspectable; + +#[derive(Inspectable, Default, Copy, Clone, Debug)] +pub struct Length(pub f32); diff --git a/src/main.rs b/src/main.rs index 40d4773..05db5c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod datatypes; mod debug; +mod paths; mod primitives; mod turtle; mod turtle_movement; @@ -14,8 +15,8 @@ fn main() { .insert_resource(Msaa { samples: 4 }) .insert_resource(ClearColor(Color::BEIGE)) .insert_resource(WindowDescriptor { - width: 400.0, - height: 400.0, + width: 500.0, + height: 500.0, title: "Turtle Window".to_string(), present_mode: bevy::window::PresentMode::AutoVsync, ..default() diff --git a/src/paths/circle_star.rs b/src/paths/circle_star.rs new file mode 100644 index 0000000..a123173 --- /dev/null +++ b/src/paths/circle_star.rs @@ -0,0 +1,46 @@ +use crate::{ + datatypes::{angle::Angle, length::Length}, + turtle::TurtleCommand, +}; + +#[allow(dead_code)] +pub fn circle_star() -> Vec { + vec![ + TurtleCommand::Right(Angle::degrees(36.)), + TurtleCommand::Forward(Length(200.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Circle { + radius: Length(20.), + angle: Angle::degrees(324.), + }, + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(200.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Circle { + radius: Length(20.), + angle: Angle::degrees(324.), + }, + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(200.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Circle { + radius: Length(20.), + angle: Angle::degrees(324.), + }, + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(200.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Circle { + radius: Length(20.), + angle: Angle::degrees(324.), + }, + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(200.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Circle { + radius: Length(20.), + angle: Angle::degrees(324.), + }, + TurtleCommand::Right(Angle::degrees(90.)), + ] +} diff --git a/src/paths/geometry_task.rs b/src/paths/geometry_task.rs new file mode 100644 index 0000000..aab7fdf --- /dev/null +++ b/src/paths/geometry_task.rs @@ -0,0 +1,32 @@ +use crate::{ + datatypes::{angle::Angle, length::Length}, + turtle::TurtleCommand, +}; + +#[allow(dead_code)] +pub fn geometry_task() -> Vec { + vec![ + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Backward(Length(100.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Right(Angle::degrees(45.)), + //TurtleCommand::PenUp, + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(50.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Right(Angle::degrees(90.)), + TurtleCommand::Forward(Length(50.)), + TurtleCommand::Right(Angle::degrees(45.)), + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Left(Angle::degrees(120.)), + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Left(Angle::degrees(120.)), + TurtleCommand::Forward(Length(100.)), + TurtleCommand::Right(Angle::degrees(150.)), + TurtleCommand::Forward(Length(100.)), + ] +} diff --git a/src/paths/mod.rs b/src/paths/mod.rs new file mode 100644 index 0000000..a276a32 --- /dev/null +++ b/src/paths/mod.rs @@ -0,0 +1,2 @@ +pub mod circle_star; +pub mod geometry_task; diff --git a/src/primitives.rs b/src/primitives.rs index ae3a68c..c9fdf5a 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,156 +1,4 @@ -use bevy::prelude::{Bundle, Color, Component, Name, Quat, Transform, Vec2}; -use bevy_prototype_lyon::{ - entity::ShapeBundle, - prelude::{DrawMode, FillMode, GeometryBuilder, Path, PathBuilder, ShapePath, StrokeMode}, - shapes::{self, Line}, -}; -use bevy_tweening::Lens; - -use crate::datatypes::angle::Angle; - -pub(crate) struct LineAnimationLens { - start: Vec2, - end: Vec2, -} - -impl LineAnimationLens { - pub(crate) fn new(start: Vec2, end: Vec2) -> Self { - Self { start, end } - } -} - -impl Lens for LineAnimationLens { - fn lerp(&mut self, target: &mut Path, ratio: f32) { - let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio)); - *target = ShapePath::build_as(&line); - } -} - -#[derive(Bundle)] -pub(crate) struct TurtleDrawLine { - #[bundle] - line: ShapeBundle, - name: Name, - marker: LineMarker, -} - -#[derive(Component, Default)] -struct LineMarker; - -impl TurtleDrawLine { - pub(crate) fn new(start: Vec2, _end: Vec2, index: u64) -> Self { - Self { - line: GeometryBuilder::build_as( - &Line(start, start), - DrawMode::Outlined { - fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), - outline_mode: StrokeMode::new(Color::BLACK, 1.0), - }, - Transform::identity(), - ), - name: Name::new(format!("Line {}", index)), - marker: LineMarker, - } - } -} - -pub(crate) struct CircleAnimationLens { - pub start_pos: Vec2, - pub center: Vec2, - pub radii: Vec2, - pub start: Angle, - pub end: Angle, -} - -impl Lens for CircleAnimationLens { - fn lerp(&mut self, target: &mut Path, ratio: f32) { - let mut path_builder = PathBuilder::new(); - path_builder.move_to(self.start_pos); - // The center point of the radius, then the radii in x and y direction, then the angle that will be drawn, then the x_rotation ? - path_builder.arc( - self.center, - self.radii, - (self.start + ((self.end - self.start) * ratio)) - .to_radians() - .value(), - 0., - ); - let line = path_builder.build(); - *target = ShapePath::build_as(&line); - } -} - -pub(crate) struct CircleMovementLens { - pub center: Vec2, - pub start: Transform, - pub end: Angle, -} - -impl Lens for CircleMovementLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { - let angle = self.end * ratio; - let mut rotated = self.start; - - rotated.rotate_around( - self.center.extend(0.), - Quat::from_rotation_z(angle.to_radians().value()), - ); - - *target = rotated; - } -} - -#[derive(Bundle)] - -pub(crate) struct TurtleDrawCircle { - #[bundle] - line: ShapeBundle, - name: Name, - marker: CircleMarker, -} - -#[derive(Component, Default)] -struct CircleMarker; - -impl TurtleDrawCircle { - pub(crate) fn new( - center: Vec2, - radii: Vec2, - angle: Angle, - index: u64, - start: Vec2, - end: Vec2, - ) -> Self { - let mut path_builder = PathBuilder::new(); - path_builder.move_to(start); - // The center point of the radius, then the radii in x and y direction, then the angle that will be drawn, then the x_rotation ? - path_builder.arc(center, radii, angle.to_radians().value(), 0.); - - /* println!("The radiuses: {}", radii); - path_builder.move_to(Vec2::ZERO); - path_builder.line_to(center); - path_builder.line_to(Vec2::new(radii.x, 0.) + center); - path_builder.move_to(center); - path_builder.line_to(Vec2::new(0., radii.y) + center); - path_builder.move_to(center); - path_builder.line_to( - center + Vec2::new(radii.x.abs(), 0.).rotate(Vec2::from_angle(angle.0.to_radians())), - ); - path_builder.move_to(center); */ - let line = path_builder.build(); - println!("Draw Circle: {} {} {:?}", center, radii, angle); - - Self { - line: GeometryBuilder::build_as( - &line, - DrawMode::Outlined { - fill_mode: FillMode::color(Color::rgba(0., 0., 0., 0.)), - outline_mode: StrokeMode::new(Color::BLACK, 1.0), - }, - Transform::identity(), - ), - name: Name::new(format!("Circle {}", index)), - marker: CircleMarker, - } - } -} +pub mod animation; +pub mod bundles; +pub mod components; +pub mod turtle_primitives; diff --git a/src/primitives/animation.rs b/src/primitives/animation.rs new file mode 100644 index 0000000..e2fccdb --- /dev/null +++ b/src/primitives/animation.rs @@ -0,0 +1,72 @@ +use bevy::prelude::{Quat, Transform, Vec2}; +use bevy_prototype_lyon::{ + prelude::{Path, PathBuilder, ShapePath}, + shapes, +}; +use bevy_tweening::Lens; + +use crate::datatypes::angle::Angle; + +pub(crate) struct LineAnimationLens { + start: Vec2, + end: Vec2, +} + +impl LineAnimationLens { + pub(crate) fn new(start: Vec2, end: Vec2) -> Self { + Self { start, end } + } +} + +impl Lens for LineAnimationLens { + fn lerp(&mut self, target: &mut Path, ratio: f32) { + let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio)); + *target = ShapePath::build_as(&line); + } +} + +pub(crate) struct CircleAnimationLens { + pub start_pos: Vec2, + pub center: Vec2, + pub radii: Vec2, + pub start: Angle, + pub end: Angle, +} + +impl Lens for CircleAnimationLens { + fn lerp(&mut self, target: &mut Path, ratio: f32) { + let mut path_builder = PathBuilder::new(); + path_builder.move_to(self.start_pos); + // The center point of the radius, then the radii in x and y direction, then the angle that will be drawn, then the x_rotation ? + path_builder.arc( + self.center, + self.radii, + (self.start + ((self.end - self.start) * ratio)) + .to_radians() + .value(), + 0., + ); + let line = path_builder.build(); + *target = ShapePath::build_as(&line); + } +} + +pub(crate) struct CircleMovementLens { + pub center: Vec2, + pub start: Transform, + pub end: Angle, +} + +impl Lens for CircleMovementLens { + fn lerp(&mut self, target: &mut Transform, ratio: f32) { + let angle = self.end * ratio; + let mut rotated = self.start; + + rotated.rotate_around( + self.center.extend(0.), + Quat::from_rotation_z(angle.to_radians().value()), + ); + + *target = rotated; + } +} diff --git a/src/primitives/bundles.rs b/src/primitives/bundles.rs new file mode 100644 index 0000000..642e6dd --- /dev/null +++ b/src/primitives/bundles.rs @@ -0,0 +1,123 @@ +use bevy::prelude::{Bundle, Color, Component, Name, Transform, Vec2}; +use bevy_prototype_lyon::{ + entity::ShapeBundle, + prelude::{DrawMode, FillMode, GeometryBuilder, PathBuilder, StrokeMode}, + shapes::Line, +}; + +use crate::{ + datatypes::angle::Angle, + turtle::{Colors, TurtleCommand, TurtleCommands}, +}; + +#[derive(Bundle)] +pub(crate) struct TurtleDrawLine { + #[bundle] + line: ShapeBundle, + name: Name, + marker: LineMarker, +} + +#[derive(Component, Default)] +struct LineMarker; + +impl TurtleDrawLine { + pub(crate) fn new(start: Vec2, _end: Vec2, index: u64) -> Self { + Self { + line: GeometryBuilder::build_as( + &Line(start, start), + DrawMode::Outlined { + fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), + outline_mode: StrokeMode::new(Color::BLACK, 1.0), + }, + Transform::identity(), + ), + name: Name::new(format!("Line {}", index)), + marker: LineMarker, + } + } +} + +#[derive(Bundle)] + +pub(crate) struct TurtleDrawCircle { + #[bundle] + line: ShapeBundle, + name: Name, + marker: CircleMarker, +} + +#[derive(Component, Default)] +struct CircleMarker; + +impl TurtleDrawCircle { + pub(crate) fn new( + center: Vec2, + radii: Vec2, + angle: Angle, + index: u64, + start: Vec2, + end: Vec2, + ) -> Self { + let mut path_builder = PathBuilder::new(); + path_builder.move_to(start); + // The center point of the radius - this is responsible for the orientation of the ellipse, + // then the radii in x and y direction - this can be rotated using the x_rotation parameter, + // then the angle - the part of the circle that will be drawn like (PI/2.0) for a quarter circle, + // then the x_rotation (maybe the rotation of the radii?) + path_builder.arc(center, radii, angle.to_radians().value(), 0.); + let line = path_builder.build(); + println!("Draw Circle: {} {} {:?}", center, radii, angle); + + Self { + line: GeometryBuilder::build_as( + &line, + DrawMode::Outlined { + fill_mode: FillMode::color(Color::rgba(0., 0., 0., 0.)), + outline_mode: StrokeMode::new(Color::BLACK, 1.0), + }, + Transform::identity(), + ), + name: Name::new(format!("Circle {}", index)), + marker: CircleMarker, + } + } +} + +#[derive(Bundle)] +pub struct Turtle { + colors: Colors, + commands: TurtleCommands, + name: Name, +} + +impl Default for Turtle { + fn default() -> Self { + Self { + colors: Colors::default(), + commands: TurtleCommands::new(vec![]), + 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 + } */ + pub fn set_commands(&mut self, commands: Vec) { + self.commands = TurtleCommands::new(commands); + } +} diff --git a/src/primitives/components.rs b/src/primitives/components.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/primitives/turtle_primitives.rs b/src/primitives/turtle_primitives.rs new file mode 100644 index 0000000..1a4f692 --- /dev/null +++ b/src/primitives/turtle_primitives.rs @@ -0,0 +1,23 @@ +use bevy::prelude::Vec2; + +use crate::{datatypes::angle::Angle, turtle::Precision}; + +pub struct TurtleAction { + heading: Angle, + position: Vec2, + primitive: TurtleActionType, +} + +pub enum TurtleActionType { + Straight { + target: Vec2, + }, + Rotate { + angle: Angle, + }, + Circle { + center: Vec2, + radii: Vec2, + extent: Angle, + }, +} diff --git a/src/turtle.rs b/src/turtle.rs index cb13ff7..c6220f6 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -8,9 +8,14 @@ use bevy_tweening::{ TweenCompleted, TweeningPlugin, TweeningType, }; +#[allow(unused_imports)] +use crate::paths::{circle_star::circle_star, geometry_task::geometry_task}; use crate::{ - datatypes::angle::Angle, - primitives::{CircleAnimationLens, LineAnimationLens, TurtleDrawCircle, TurtleDrawLine}, + datatypes::{angle::Angle, length::Length}, + primitives::{ + animation::{CircleAnimationLens, LineAnimationLens}, + bundles::{Turtle, TurtleDrawCircle, TurtleDrawLine}, + }, turtle_shapes, }; @@ -27,116 +32,7 @@ impl Plugin for TurtlePlugin { .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 - } */ -} +pub type Precision = f32; #[derive(Component, Inspectable)] pub struct TurtleCommands { @@ -154,7 +50,7 @@ pub struct TurtleState { } impl TurtleCommands { - fn new(commands: Vec) -> Self { + pub fn new(commands: Vec) -> Self { Self { commands, lines: vec![], @@ -229,15 +125,12 @@ pub enum TurtleGraphElement { #[derive(Clone, Component, Inspectable)] pub struct TurtleShape; -#[derive(Clone, Component, Inspectable)] +#[derive(Clone, Component, Inspectable, Default)] 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), @@ -265,8 +158,10 @@ fn setup(mut commands: Commands) { }, )); commands.spawn_bundle(Camera2dBundle::default()); + let mut turtle_bundle = Turtle::default(); + turtle_bundle.set_commands(geometry_task()); commands - .spawn_bundle(Turtle::default()) + .spawn_bundle(turtle_bundle) .insert_bundle(GeometryBuilder::build_as( &turtle_shapes::turtle(), DrawMode::Outlined { diff --git a/src/turtle_movement.rs b/src/turtle_movement.rs index b9adaab..bbc2279 100644 --- a/src/turtle_movement.rs +++ b/src/turtle_movement.rs @@ -1,14 +1,14 @@ -use std::{f32::consts::PI, time::Duration}; +use std::time::Duration; use bevy::prelude::{Quat, Transform, Vec2, Vec3}; use bevy_tweening::{ - lens::{TransformPositionLens, TransformRotateAxisLens, TransformRotateZLens}, + lens::{TransformPositionLens, TransformRotateZLens}, EaseFunction, Tween, TweeningType, }; use crate::{ datatypes::angle::Angle, - primitives::{CircleAnimationLens, CircleMovementLens}, + primitives::animation::CircleMovementLens, turtle::{TurtleGraphElement, TurtleState}, };