From fac9d218ad3c955439c4781a57be7c5d6d9b9041 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 6 Dec 2022 14:04:44 +0100 Subject: [PATCH 01/11] Initial turtle-lib commit Working so far: * display a window * display a turtle * go forward --- .gitignore | 2 + Cargo.toml | 15 ++ src/commands.rs | 161 +++++++++++++++++++++ src/debug.rs | 12 ++ src/drawing.rs | 15 ++ src/drawing/animation.rs | 204 +++++++++++++++++++++++++++ src/drawing/animation/circle_lens.rs | 51 +++++++ src/drawing/animation/line_lens.rs | 24 ++++ src/drawing/line_segments.rs | 82 +++++++++++ src/drawing/run_step.rs | 58 ++++++++ src/events.rs | 3 + src/general.rs | 9 ++ src/general/angle.rs | 195 +++++++++++++++++++++++++ src/general/length.rs | 6 + src/lib.rs | 118 ++++++++++++++++ src/shapes.rs | 13 ++ src/shapes/turtle.rs | 46 ++++++ src/state.rs | 69 +++++++++ src/turtle_bundle.rs | 75 ++++++++++ 19 files changed, 1158 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/commands.rs create mode 100644 src/debug.rs create mode 100644 src/drawing.rs create mode 100644 src/drawing/animation.rs create mode 100644 src/drawing/animation/circle_lens.rs create mode 100644 src/drawing/animation/line_lens.rs create mode 100644 src/drawing/line_segments.rs create mode 100644 src/drawing/run_step.rs create mode 100644 src/events.rs create mode 100644 src/general.rs create mode 100644 src/general/angle.rs create mode 100644 src/general/length.rs create mode 100644 src/lib.rs create mode 100644 src/shapes.rs create mode 100644 src/shapes/turtle.rs create mode 100644 src/state.rs create mode 100644 src/turtle_bundle.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8d0ed16 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "turtle-lib" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +bevy = { version = "0.9" } +bevy_prototype_lyon = {version="0.7"} +bevy-inspector-egui = "0.14" +num-traits = "0.2" +bevy_tweening = {version="0.6"} \ No newline at end of file diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..ae5a45d --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,161 @@ +use bevy::prelude::Component; +use bevy_inspector_egui::Inspectable; + +use crate::{ + drawing::{ + self, + animation::{ + draw_straight_segment, move_straight_segment, ToAnimationSegment, + TurtleAnimationSegment, + }, + TurtleGraphElement, + }, + general::{angle::Angle, length::Length, Coordinate, Precision}, + state::TurtleState, +}; +/** + * All the possibilities to draw something with turtle. All the commands can get the position, heading, + * color and fill_color from the turtles state. + */ +#[derive(Component, Inspectable)] +pub enum MoveCommand { + Forward(Length), + Backward(Length), + Circle { + radius: Length, + angle: Angle, + }, + Goto(Coordinate), +} + +impl Default for MoveCommand { + fn default() -> Self { + Self::Forward(Length(100.)) + } +} +/// Different ways to drop breadcrumbs on the way like a dot or a stamp of the turtles shape. + +#[derive(Component, Inspectable, Default)] +pub enum Breadcrumb { + Dot, + #[default] + Stamp, +} + +/// Different ways that change the orientation of the turtle. +#[derive(Component, Inspectable)] +pub enum OrientationCommand { + Left(Angle), + Right(Angle), + SetHeading, + LookAt(Coordinate), +} + +impl Default for OrientationCommand { + fn default() -> Self { + Self::Right(Default::default()) + } +} + +/// A combination of all commands that can be used while drawing. +#[derive(Component, Inspectable)] +pub enum DrawElement { + Draw(MoveCommand), + Move(MoveCommand), + Orient(OrientationCommand), + Drip(Breadcrumb), +} + +impl Default for DrawElement { + fn default() -> Self { + Self::Draw(Default::default()) + } +} +impl ToAnimationSegment for DrawElement { + fn to_draw_segment( + &self, + state: &mut TurtleState, + ) -> crate::drawing::animation::TurtleAnimationSegment { + match self { + 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::Goto(coord) => todo!(), + }, + DrawElement::Move(e) => match e { + MoveCommand::Forward(length) => move_straight_segment(state, length.0), + MoveCommand::Backward(length) => move_straight_segment(state, -length.0), + MoveCommand::Circle { radius, angle } => todo!(), + MoveCommand::Goto(coord) => todo!(), + }, + DrawElement::Orient(_) => todo!(), + DrawElement::Drip(_) => todo!(), + } + } +} + +#[derive(Component, Inspectable)] +pub enum TurtleSegment { + Single(DrawElement), + Outline(Vec), + Filled(Vec), +} + +impl Default for TurtleSegment { + fn default() -> Self { + Self::Single(Default::default()) + } +} +impl ToAnimationSegment for TurtleSegment { + fn to_draw_segment( + &self, + state: &mut TurtleState, + ) -> crate::drawing::animation::TurtleAnimationSegment { + match self { + Self::Single(e) => e.to_draw_segment(state), + Self::Outline(_) => todo!(), + Self::Filled(_) => todo!(), + } + } +} +#[derive(Component, Inspectable)] +pub struct TurtleCommands { + animation_state: usize, + commands: Vec, + lines: Vec, + state: TurtleState, +} + +impl TurtleCommands { + pub fn new(commands: Vec) -> Self { + let mut state = TurtleState::default(); + state.set_speed(200); + Self { + animation_state: 0, + commands, + lines: vec![], + state, + } + } + pub fn push(&mut self, segment: TurtleSegment) { + self.commands.push(segment) + } +} + +impl Iterator for TurtleCommands { + type Item = TurtleAnimationSegment; + + fn next(&mut self) -> Option { + let index = self.animation_state; + let next_index = index + 1; + + if let Some(command) = self.commands.get(self.animation_state) { + let res = command.to_draw_segment(&mut self.state); + self.animation_state = next_index; + Some(res) + } else { + None + } + } +} diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 0000000..214360c --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,12 @@ +use bevy::prelude::Plugin; +use bevy_inspector_egui::WorldInspectorPlugin; + +pub struct DebugPlugin; + +impl Plugin for DebugPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + if cfg!(debug_assertions) { + app.add_plugin(WorldInspectorPlugin::new()); + } + } +} diff --git a/src/drawing.rs b/src/drawing.rs new file mode 100644 index 0000000..1eda672 --- /dev/null +++ b/src/drawing.rs @@ -0,0 +1,15 @@ +use bevy_inspector_egui::Inspectable; + +pub use self::line_segments::{TurtleDrawCircle, TurtleDrawLine}; + +pub mod animation; +mod line_segments; +pub(crate) mod run_step; + +#[derive(Inspectable, Default)] +pub enum TurtleGraphElement { + TurtleLine(TurtleDrawLine), + TurtleCircle(TurtleDrawCircle), + #[default] + Noop, +} diff --git a/src/drawing/animation.rs b/src/drawing/animation.rs new file mode 100644 index 0000000..682f8dc --- /dev/null +++ b/src/drawing/animation.rs @@ -0,0 +1,204 @@ +mod circle_lens; +mod line_lens; + +use bevy::{ + prelude::{Quat, Transform, Vec2, Vec3}, + render::render_resource::encase::rts_array::Length, +}; +use bevy_prototype_lyon::prelude::Path; +use bevy_tweening::{ + lens::{TransformPositionLens, TransformRotateZLens}, + Animator, EaseFunction, RepeatCount, RepeatStrategy, Tween, +}; + +use crate::{ + general::{angle::Angle, Coordinate, Precision}, + state::TurtleState, +}; + +use self::{ + circle_lens::{CircleAnimationLens, CircleMovementLens}, + line_lens::LineAnimationLens, +}; + +use super::{TurtleDrawCircle, TurtleDrawLine, TurtleGraphElement}; + +pub struct TurtleAnimationSegment { + pub turtle_animation: Option>, + pub line_segment: Option, + pub line_animation: Option>, +} + +pub trait ToAnimationSegment { + fn to_draw_segment( + &self, + state: &mut TurtleState, + ) -> crate::drawing::animation::TurtleAnimationSegment; +} + +pub fn turtle_turn( + state: &mut TurtleState, + angle_to_turn: Angle, +) -> TurtleAnimationSegment { + let start = state.heading(); + let end = state.heading() + angle_to_turn; + let animation = Tween::new( + EaseFunction::QuadraticInOut, + state.animation_duration(), + TransformRotateZLens { + start: start.to_radians().value(), + end: end.to_radians().value(), + }, + ) + .with_completed_event(state.segment_index() as u64); + // Don't draw as the position does not change + let line = TurtleGraphElement::Noop; + // Update the state + state.set_heading(end.limit_smaller_than_full_circle()); + TurtleAnimationSegment { + turtle_animation: Some(animation), + line_segment: Some(line), + line_animation: None, + } +} + +pub fn move_straight_segment(state: &mut TurtleState, length: Precision) -> TurtleAnimationSegment { + let animation = MoveStraightTurtleAnimation::new(state, length); + + state.set_position(animation.end); + TurtleAnimationSegment { + turtle_animation: Some(animation.animation), + line_segment: None, + line_animation: None, + } +} + +pub fn draw_straight_segment(state: &mut TurtleState, length: Precision) -> TurtleAnimationSegment { + let animation = MoveStraightTurtleAnimation::new(state, length); + let line_animation = MoveStraightLineAnimation::new(state, length, &animation); + + state.set_position(animation.end); + TurtleAnimationSegment { + turtle_animation: Some(animation.animation), + line_segment: Some(TurtleGraphElement::TurtleLine(line_animation.line)), + line_animation: Some(Animator::new(line_animation.animation)), + } +} + +struct MoveStraightLineAnimation { + start: Coordinate, + end: Coordinate, + line: TurtleDrawLine, + animation: Tween, +} + +impl MoveStraightLineAnimation { + fn new( + state: &TurtleState, + length: Precision, + turtle_animation: &MoveStraightTurtleAnimation, + ) -> Self { + let line = TurtleDrawLine::new(turtle_animation.start, turtle_animation.end); + let line_animation = Tween::new( + EaseFunction::QuadraticInOut, + state.animation_duration(), + LineAnimationLens::new(turtle_animation.start, turtle_animation.end), + ) + /* .with_repeat_strategy(RepeatStrategy::MirroredRepeat) + .with_repeat_count(RepeatCount::Infinite)*/; + Self { + start: turtle_animation.start, + end: turtle_animation.end, + line, + animation: line_animation, + } + } +} + +struct MoveStraightTurtleAnimation { + start: Coordinate, + end: Coordinate, + animation: Tween, +} +impl MoveStraightTurtleAnimation { + fn new(state: &TurtleState, length: Precision) -> Self { + let start = state.position(); + let end = + state.position() + (Vec2::from_angle(state.heading().to_radians().value()) * length); + let turtle_movement_animation = Tween::new( + EaseFunction::QuadraticInOut, + state.animation_duration(), + TransformPositionLens { + start: start.extend(0.), + end: end.extend(0.), + }, + ) + .with_completed_event(state.segment_index() as u64); + Self { + start, + end, + animation: turtle_movement_animation, + } + } +} + +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(), + ))); + + 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: 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), + } +} diff --git a/src/drawing/animation/circle_lens.rs b/src/drawing/animation/circle_lens.rs new file mode 100644 index 0000000..e38f23a --- /dev/null +++ b/src/drawing/animation/circle_lens.rs @@ -0,0 +1,51 @@ +use bevy::prelude::{Quat, Transform, Vec2}; +use bevy_prototype_lyon::prelude::{Path, PathBuilder, ShapePath}; +use bevy_tweening::Lens; + +use crate::general::{angle::Angle, Precision}; + +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/drawing/animation/line_lens.rs b/src/drawing/animation/line_lens.rs new file mode 100644 index 0000000..6bb8011 --- /dev/null +++ b/src/drawing/animation/line_lens.rs @@ -0,0 +1,24 @@ +use bevy::prelude::Vec2; +use bevy_prototype_lyon::{ + prelude::{Path, ShapePath}, + shapes, +}; +use bevy_tweening::Lens; + +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); + } +} diff --git a/src/drawing/line_segments.rs b/src/drawing/line_segments.rs new file mode 100644 index 0000000..ee76338 --- /dev/null +++ b/src/drawing/line_segments.rs @@ -0,0 +1,82 @@ +use bevy::prelude::{Bundle, Color, Component, Name, Transform, Vec2}; +use bevy_inspector_egui::Inspectable; +use bevy_prototype_lyon::{ + entity::ShapeBundle, + prelude::{DrawMode, FillMode, GeometryBuilder, PathBuilder, StrokeMode}, + shapes::Line, +}; + +use crate::general::{angle::Angle, Precision}; + +#[derive(Bundle, Inspectable, Default)] +pub struct TurtleDrawLine { + #[inspectable(ignore)] + line: ShapeBundle, + name: Name, + marker: LineMarker, +} + +#[derive(Component, Default, Inspectable)] +struct LineMarker; + +impl TurtleDrawLine { + pub(crate) fn new(start: Vec2, end: Vec2) -> 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 {}-{}", start, end)), + marker: LineMarker, + } + } +} + +#[derive(Bundle, Inspectable, Default)] + +pub struct TurtleDrawCircle { + #[inspectable(ignore)] + line: ShapeBundle, + name: Name, + marker: CircleMarker, +} + +#[derive(Component, Default, Inspectable)] +struct CircleMarker; + +impl TurtleDrawCircle { + pub(crate) fn new( + center: Vec2, + radii: Vec2, + angle: Angle, + 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 at {}, {}", center.x, center.y)), + marker: CircleMarker, + } + } +} diff --git a/src/drawing/run_step.rs b/src/drawing/run_step.rs new file mode 100644 index 0000000..b272b66 --- /dev/null +++ b/src/drawing/run_step.rs @@ -0,0 +1,58 @@ +use bevy::prelude::{Commands, Query, Transform, With}; +use bevy_tweening::Animator; + +use crate::{commands::TurtleCommands, shapes::TurtleShape}; + +use super::{animation::TurtleAnimationSegment, TurtleGraphElement}; + +pub fn run_animation_step( + commands: &mut Commands, + tcmd: &mut TurtleCommands, + turtle: &mut Query<&mut Animator, With>, +) { + loop { + match tcmd.next() { + Some(TurtleAnimationSegment { + turtle_animation: Some(turtle_animation), + line_segment: Some(graph_element_to_draw), + line_animation: Some(line_animation), + }) => { + let mut turtle = turtle.single_mut(); + turtle.set_tweenable(turtle_animation); + match graph_element_to_draw { + TurtleGraphElement::TurtleLine(line) => { + commands.spawn((line, line_animation)); + } + TurtleGraphElement::Noop => (), + TurtleGraphElement::TurtleCircle(circle) => { + commands.spawn((circle, line_animation)); + } + } + return; + } + // In case a rotation is performed the line drawing can be skipped + Some(TurtleAnimationSegment { + turtle_animation: Some(turtle_animation), + line_segment: Some(_), + line_animation: None, + })| + // In case a rotation is performed the line drawing can be skipped + Some(TurtleAnimationSegment { + turtle_animation: Some(turtle_animation), + line_segment: None, + line_animation: None, + }) => { + let mut turtle = turtle.single_mut(); + turtle.set_tweenable(turtle_animation); + return; + } + Some(_e) => { + println!("without animation"); + } + None => { + println!("nothing to draw"); + return; + } + }; + } +} diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..fb0bb9b --- /dev/null +++ b/src/events.rs @@ -0,0 +1,3 @@ +use bevy::prelude::Entity; + +pub struct DrawingStartedEvent(pub Entity); diff --git a/src/general.rs b/src/general.rs new file mode 100644 index 0000000..335289a --- /dev/null +++ b/src/general.rs @@ -0,0 +1,9 @@ +use bevy::prelude::Vec2; + +pub mod angle; +pub mod length; + +pub type Precision = f32; +pub type Coordinate = Vec2; +pub type Visibility = bool; +pub type Speed = u32; diff --git a/src/general/angle.rs b/src/general/angle.rs new file mode 100644 index 0000000..3d98c48 --- /dev/null +++ b/src/general/angle.rs @@ -0,0 +1,195 @@ +use bevy_inspector_egui::Inspectable; +use std::{ + f32::consts::PI, + ops::{Add, Div, Mul, Neg, Rem, Sub}, +}; + +use super::Precision; + +#[derive(Inspectable, Copy, Clone, Debug, PartialEq, Eq)] +pub enum AngleUnit { + Degrees(T), + Radians(T), +} + +impl Default for AngleUnit { + fn default() -> Self { + Self::Degrees(Default::default()) + } +} + +#[derive(Inspectable, Copy, Default, Clone, Debug, PartialEq, Eq)] +pub struct Angle { + value: AngleUnit, +} + +impl> Rem for Angle { + type Output = Self; + + fn rem(self, rhs: T) -> Self::Output { + match self.value { + AngleUnit::Degrees(v) => Self::Output::degrees(v % rhs), + AngleUnit::Radians(v) => Self::Output::radians(v % rhs), + } + } +} + +impl> Mul for Angle { + type Output = Self; + + fn mul(self, rhs: T) -> Self::Output { + match self.value { + AngleUnit::Degrees(v) => Self::Output::degrees(v * rhs), + AngleUnit::Radians(v) => Self::Output::radians(v * rhs), + } + } +} + +impl Angle { + pub fn limit_smaller_than_full_circle(self) -> Self { + match self.value { + AngleUnit::Degrees(v) => Self { + value: AngleUnit::Degrees(v % 360.), + }, + AngleUnit::Radians(v) => Self { + value: AngleUnit::Radians(v % (2. * PI)), + }, + } + } +} +impl> Div for Angle { + type Output = Self; + + fn div(self, rhs: T) -> Self::Output { + match self.value { + AngleUnit::Degrees(v) => Self::Output::degrees(v / rhs), + AngleUnit::Radians(v) => Self::Output::radians(v / rhs), + } + } +} + +impl> Neg for Angle { + type Output = Self; + + fn neg(self) -> Self::Output { + match self.value { + AngleUnit::Degrees(v) => Self::Output::degrees(-v), + AngleUnit::Radians(v) => Self::Output::radians(-v), + } + } +} + +impl> Neg for &Angle { + type Output = Angle; + + fn neg(self) -> Self::Output { + match self.value.clone() { + AngleUnit::Degrees(v) => Self::Output::degrees(-v), + AngleUnit::Radians(v) => Self::Output::radians(-v), + } + } +} + +impl Angle { + pub fn degrees(value: T) -> Angle { + Self { + value: AngleUnit::Degrees(value), + } + } + pub fn radians(value: T) -> Angle { + Self { + value: AngleUnit::Radians(value), + } + } + pub fn value(&self) -> T { + match self.value.clone() { + AngleUnit::Degrees(v) => v, + AngleUnit::Radians(v) => v, + } + } +} + +impl Angle { + pub fn to_radians(self) -> Self { + match self.value { + AngleUnit::Degrees(v) => Self { + value: AngleUnit::Radians(v.to_radians()), + }, + AngleUnit::Radians(_) => self, + } + } + pub fn to_degrees(self) -> Self { + match self.value { + AngleUnit::Degrees(_) => self, + AngleUnit::Radians(v) => Self { + value: AngleUnit::Degrees(v.to_degrees()), + }, + } + } +} + +impl + Default + num_traits::float::Float> Add for Angle { + type Output = Angle; + + fn add(self, rhs: Self) -> Self::Output { + match (self.value, rhs.value) { + (AngleUnit::Degrees(v), AngleUnit::Degrees(o)) => Self::Output { + value: AngleUnit::Degrees(v + o), + }, + (AngleUnit::Degrees(v), AngleUnit::Radians(o)) => Self::Output { + value: AngleUnit::Radians(v.to_radians() + o), + }, + (AngleUnit::Radians(v), AngleUnit::Degrees(o)) => Self::Output { + value: AngleUnit::Radians(v + o.to_radians()), + }, + (AngleUnit::Radians(v), AngleUnit::Radians(o)) => Self::Output { + value: AngleUnit::Radians(v + o), + }, + } + } +} + +impl + Default + num_traits::float::Float> Sub for Angle { + type Output = Angle; + + fn sub(self, rhs: Self) -> Self::Output { + match (self.value, rhs.value) { + (AngleUnit::Degrees(v), AngleUnit::Degrees(o)) => Self::Output { + value: AngleUnit::Degrees(v - o), + }, + (AngleUnit::Degrees(v), AngleUnit::Radians(o)) => Self::Output { + value: AngleUnit::Radians(v.to_radians() - o), + }, + (AngleUnit::Radians(v), AngleUnit::Degrees(o)) => Self::Output { + value: AngleUnit::Radians(v - o.to_radians()), + }, + (AngleUnit::Radians(v), AngleUnit::Radians(o)) => Self::Output { + value: AngleUnit::Radians(v - o), + }, + } + } +} + +#[test] +fn convert_to_radians() { + let radi = Angle::radians(30f32.to_radians()); + let degr = Angle::degrees(30f32); + let converted = degr.to_radians(); + assert_eq!(radi, converted) +} +#[test] +fn sum_degrees() { + let fst = Angle::degrees(30f32); + let snd = Angle::degrees(30f32); + let sum = fst + snd; + assert!((sum.value() - 60f32).abs() < 0.0001); + assert!((sum.to_radians().value() - 60f32.to_radians()).abs() < 0.0001); +} +#[test] +fn sum_mixed() { + let fst = Angle::degrees(30f32); + let snd = Angle::radians(30f32.to_radians()); + let sum = fst + snd; + assert!((sum.to_degrees().value() - 60f32).abs() < 0.0001); + assert!((sum.to_radians().value() - 60f32.to_radians()).abs() < 0.0001); +} diff --git a/src/general/length.rs b/src/general/length.rs new file mode 100644 index 0000000..8ac332a --- /dev/null +++ b/src/general/length.rs @@ -0,0 +1,6 @@ +use bevy_inspector_egui::Inspectable; + +use super::Precision; + +#[derive(Inspectable, Default, Copy, Clone, Debug)] +pub struct Length(pub Precision); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..11d9f11 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,118 @@ +use std::time::Duration; + +use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*, window::close_on_esc}; +use bevy_inspector_egui::RegisterInspectable; +use bevy_prototype_lyon::prelude::{Path, ShapePlugin}; +use bevy_tweening::{ + component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween, + TweenCompleted, TweeningPlugin, +}; +use events::DrawingStartedEvent; +use shapes::{TurtleColors, TurtleShape}; +use turtle_bundle::{AnimatedTurtle, TurtleBundle}; + +pub use commands::TurtleCommands; + +mod commands; +mod debug; +mod drawing; +pub mod events; +mod general; +pub mod shapes; +mod state; +mod turtle_bundle; + +/** +The turtle plugin is the core of this turtle module. + +In order to facilitate the setup this plugin also inserts the `DefaultPlugins` and many other things. + +Before using any of the functions add this plugin using: +```rust + +app::new().add_plugin(turtle_lib::TurtlePlugin) +``` +*/ +pub struct TurtlePlugin; + +impl Plugin for TurtlePlugin { + fn build(&self, app: &mut App) { + app.add_plugins(DefaultPlugins.set(WindowPlugin { + window: WindowDescriptor { + title: "Immigration Game".to_string(), + width: 1200., + height: 800., + present_mode: bevy::window::PresentMode::Fifo, // vsync + ..Default::default() + }, + ..default() + })) + .add_plugin(debug::DebugPlugin) + .add_plugin(ShapePlugin) + .add_plugin(TweeningPlugin) + .add_event::() + .add_startup_system(setup) + .add_system(keypresses) + .add_system(component_animator_system::) + .add_system(close_on_esc) + .add_system(draw_lines) + .register_inspectable::() + .register_inspectable::(); + } +} + +fn setup(mut commands: Commands) { + commands.spawn(Camera2dBundle { + camera_2d: Camera2d { + clear_color: ClearColorConfig::Custom(Color::BEIGE), + }, + ..default() + }); +} + +pub fn get_a_turtle() -> AnimatedTurtle { + let animator = Animator::new(Tween::new( + EaseFunction::QuadraticInOut, + Duration::from_millis(3000), + TransformScaleLens { + start: Vec3::ZERO, + end: Vec3::ONE * 1.3, + }, + )); + let turtle_bundle = TurtleBundle::default(); + AnimatedTurtle { + animator, + turtle_bundle, + turtle_shape: TurtleShape, + } +} + +fn keypresses( + mut commands: Commands, + keys: Res>, + mut tcmd: Query<(Entity, &mut TurtleCommands)>, + mut turtle: Query<&mut Animator, With>, + mut ev_start: EventWriter, +) { + if keys.just_pressed(KeyCode::W) { + for (entity, mut tcmd) in tcmd.iter_mut() { + crate::drawing::run_step::run_animation_step(&mut commands, &mut tcmd, &mut turtle); + ev_start.send(DrawingStartedEvent(entity)) + } + } +} + +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() { + if let Ok(mut tcmd) = tcmd.get_single_mut() { + crate::drawing::run_step::run_animation_step(&mut commands, &mut tcmd, &mut turtle) + } else { + println!("Failed to get the turtle") + } + } +} diff --git a/src/shapes.rs b/src/shapes.rs new file mode 100644 index 0000000..d82fdfd --- /dev/null +++ b/src/shapes.rs @@ -0,0 +1,13 @@ +mod turtle; +use bevy::prelude::{Color, Component}; +use bevy_inspector_egui::Inspectable; +pub use turtle::turtle; + +#[derive(Clone, Component, Inspectable)] +pub struct TurtleShape; + +#[derive(Clone, Component, Inspectable, Default)] +pub struct TurtleColors { + color: Color, + fill_color: Color, +} diff --git a/src/shapes/turtle.rs b/src/shapes/turtle.rs new file mode 100644 index 0000000..40fd988 --- /dev/null +++ b/src/shapes/turtle.rs @@ -0,0 +1,46 @@ +use std::f32::consts::PI; + +use bevy::prelude::Vec2; +use bevy_prototype_lyon::prelude::{Path, PathBuilder}; + +use crate::general::Precision; + +pub fn turtle() -> Path { + let polygon: &[[Precision; 2]; 23] = &[ + [-2.5, 14.0], + [-1.25, 10.0], + [-4.0, 7.0], + [-7.0, 9.0], + [-9.0, 8.0], + [-6.0, 5.0], + [-7.0, 1.0], + [-5.0, -3.0], + [-8.0, -6.0], + [-6.0, -8.0], + [-4.0, -5.0], + [0.0, -7.0], + [4.0, -5.0], + [6.0, -8.0], + [8.0, -6.0], + [5.0, -3.0], + [7.0, 1.0], + [6.0, 5.0], + [9.0, 8.0], + [7.0, 9.0], + [4.0, 7.0], + [1.25, 10.0], + [2.5, 14.0], + ]; + let mut turtle_path = PathBuilder::new(); + turtle_path.line_to(Vec2::new(1.0, 1.0)); + turtle_path.line_to(Vec2::new(-1.0, 1.0)); + turtle_path.line_to(Vec2::new(-1.0, -1.0)); + turtle_path.line_to(Vec2::new(1.0, -1.0)); + turtle_path.close(); + turtle_path.move_to(Vec2::new(0.0, 16.0).rotate(Vec2::from_angle(-PI / 2.))); + for coord in polygon { + turtle_path.line_to(Vec2::from_array(*coord).rotate(Vec2::from_angle(-PI / 2.))); + } + turtle_path.close(); + turtle_path.build() +} diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..29b715e --- /dev/null +++ b/src/state.rs @@ -0,0 +1,69 @@ +use std::{cmp::max, time::Duration}; + +use bevy::prelude::{Color, Component, Transform}; +use bevy_inspector_egui::Inspectable; + +use crate::{ + commands::TurtleSegment, + general::{angle::Angle, Coordinate, Precision, Speed, Visibility}, + shapes::TurtleColors, +}; + +/// Describing the full state of a turtle. +#[derive(Component, Inspectable, Default)] +pub struct TurtleState { + drawing: Vec, + position: Coordinate, + heading: Angle, + colors: TurtleColors, + visible: Visibility, + shape_transform: Transform, + speed: Speed, + segment_index: u64, +} + +impl TurtleState { + pub fn add_segment(&mut self, seg: TurtleSegment) { + self.drawing.push(seg); + } +} + +impl TurtleState { + pub fn segment_index(&self) -> u64 { + self.segment_index + } + pub fn heading(&self) -> Angle { + self.heading + } + pub fn position(&self) -> Coordinate { + self.position + } + pub fn speed(&self) -> Speed { + self.speed + } + /// The duration of animations calculated from the speed. + pub fn animation_duration(&self) -> Duration { + Duration::from_millis(self.speed() as u64) + } + pub fn shape_transform(&self) -> Transform { + self.shape_transform + } +} +impl TurtleState { + pub fn set_heading(&mut self, angle: Angle) -> &mut Self { + self.heading = angle; + self + } + pub fn set_position(&mut self, position: Coordinate) -> &mut Self { + self.position = position; + self + } + pub fn set_speed(&mut self, speed: Speed) -> &mut Self { + self.speed = max(speed, 1); + self + } + pub fn set_shape_transform(&mut self, transform: Transform) -> &mut Self { + self.shape_transform = transform; + self + } +} diff --git a/src/turtle_bundle.rs b/src/turtle_bundle.rs new file mode 100644 index 0000000..9f8ee99 --- /dev/null +++ b/src/turtle_bundle.rs @@ -0,0 +1,75 @@ +use std::ops::{Deref, DerefMut}; + +use bevy::prelude::{Bundle, Color, Name, Transform}; +use bevy_prototype_lyon::{ + entity::ShapeBundle, + prelude::{DrawMode, FillMode, GeometryBuilder, StrokeMode}, +}; + +use crate::{ + commands::{DrawElement, MoveCommand, TurtleCommands, TurtleSegment}, + general::length::Length, + shapes::{self, TurtleColors}, +}; + +#[derive(Bundle)] +pub struct TurtleBundle { + colors: TurtleColors, + pub commands: TurtleCommands, + name: Name, + shape: ShapeBundle, +} + +impl Default for TurtleBundle { + fn default() -> Self { + Self { + colors: TurtleColors::default(), + commands: TurtleCommands::new(vec![]), + name: Name::new("Turtle"), + shape: GeometryBuilder::build_as( + &shapes::turtle(), + DrawMode::Outlined { + fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), + outline_mode: StrokeMode::new(Color::BLACK, 1.0), + }, + Transform::IDENTITY, + ), + } + } +} + +impl TurtleBundle { + pub fn set_commands(&mut self, commands: Vec) { + self.commands = TurtleCommands::new(commands); + } +} + +impl TurtleBundle { + pub fn forward(&mut self, len: f32) -> &mut Self { + self.commands.push(TurtleSegment::Single(DrawElement::Draw( + MoveCommand::Forward(Length(len)), + ))); + self + } +} + +#[derive(Bundle)] +pub struct AnimatedTurtle { + pub animator: bevy_tweening::Animator, + pub turtle_bundle: TurtleBundle, + pub turtle_shape: shapes::TurtleShape, +} + +impl Deref for AnimatedTurtle { + type Target = TurtleBundle; + + fn deref(&self) -> &Self::Target { + &self.turtle_bundle + } +} + +impl DerefMut for AnimatedTurtle { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.turtle_bundle + } +} From 08d4e6732858217a321986a91a32f3dee9b762f8 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 6 Dec 2022 14:23:08 +0100 Subject: [PATCH 02/11] Add a license file --- LICENSE | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2071b23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From e0926a00f7a62c6ab19119f24a20d16332631166 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 7 Dec 2022 13:50:21 +0100 Subject: [PATCH 03/11] Add Debug to a lot of structs --- src/commands.rs | 12 ++++++------ src/drawing.rs | 2 +- src/drawing/line_segments.rs | 22 ++++++++++++++++++++-- src/shapes.rs | 2 +- src/state.rs | 2 +- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index ae5a45d..0c3e630 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -17,7 +17,7 @@ use crate::{ * All the possibilities to draw something with turtle. All the commands can get the position, heading, * color and fill_color from the turtles state. */ -#[derive(Component, Inspectable)] +#[derive(Component, Inspectable, Debug)] pub enum MoveCommand { Forward(Length), Backward(Length), @@ -35,7 +35,7 @@ impl Default for MoveCommand { } /// Different ways to drop breadcrumbs on the way like a dot or a stamp of the turtles shape. -#[derive(Component, Inspectable, Default)] +#[derive(Component, Inspectable, Default, Debug)] pub enum Breadcrumb { Dot, #[default] @@ -43,7 +43,7 @@ pub enum Breadcrumb { } /// Different ways that change the orientation of the turtle. -#[derive(Component, Inspectable)] +#[derive(Component, Inspectable, Debug)] pub enum OrientationCommand { Left(Angle), Right(Angle), @@ -58,7 +58,7 @@ impl Default for OrientationCommand { } /// A combination of all commands that can be used while drawing. -#[derive(Component, Inspectable)] +#[derive(Component, Inspectable, Debug)] pub enum DrawElement { Draw(MoveCommand), Move(MoveCommand), @@ -95,7 +95,7 @@ impl ToAnimationSegment for DrawElement { } } -#[derive(Component, Inspectable)] +#[derive(Component, Inspectable, Debug)] pub enum TurtleSegment { Single(DrawElement), Outline(Vec), @@ -119,7 +119,7 @@ impl ToAnimationSegment for TurtleSegment { } } } -#[derive(Component, Inspectable)] +#[derive(Component, Inspectable, Debug)] pub struct TurtleCommands { animation_state: usize, commands: Vec, diff --git a/src/drawing.rs b/src/drawing.rs index 1eda672..fa6c2a4 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -6,7 +6,7 @@ pub mod animation; mod line_segments; pub(crate) mod run_step; -#[derive(Inspectable, Default)] +#[derive(Inspectable, Default, Debug)] pub enum TurtleGraphElement { TurtleLine(TurtleDrawLine), TurtleCircle(TurtleDrawCircle), diff --git a/src/drawing/line_segments.rs b/src/drawing/line_segments.rs index ee76338..9447207 100644 --- a/src/drawing/line_segments.rs +++ b/src/drawing/line_segments.rs @@ -16,7 +16,16 @@ pub struct TurtleDrawLine { marker: LineMarker, } -#[derive(Component, Default, Inspectable)] +impl std::fmt::Debug for TurtleDrawLine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TurtleDrawLine") + .field("name", &self.name) + .field("marker", &self.marker) + .finish() + } +} + +#[derive(Component, Default, Inspectable, Debug)] struct LineMarker; impl TurtleDrawLine { @@ -45,7 +54,16 @@ pub struct TurtleDrawCircle { marker: CircleMarker, } -#[derive(Component, Default, Inspectable)] +impl std::fmt::Debug for TurtleDrawCircle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TurtleDrawCircle") + .field("name", &self.name) + .field("marker", &self.marker) + .finish() + } +} + +#[derive(Component, Default, Inspectable, Debug)] struct CircleMarker; impl TurtleDrawCircle { diff --git a/src/shapes.rs b/src/shapes.rs index d82fdfd..6474ddb 100644 --- a/src/shapes.rs +++ b/src/shapes.rs @@ -6,7 +6,7 @@ pub use turtle::turtle; #[derive(Clone, Component, Inspectable)] pub struct TurtleShape; -#[derive(Clone, Component, Inspectable, Default)] +#[derive(Clone, Component, Inspectable, Default, Debug)] pub struct TurtleColors { color: Color, fill_color: Color, diff --git a/src/state.rs b/src/state.rs index 29b715e..542c2a0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -10,7 +10,7 @@ use crate::{ }; /// Describing the full state of a turtle. -#[derive(Component, Inspectable, Default)] +#[derive(Component, Inspectable, Default, Debug)] pub struct TurtleState { drawing: Vec, position: Coordinate, From 009f896e080788a25d7b479b73dbbb44988f9871 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 7 Dec 2022 13:51:08 +0100 Subject: [PATCH 04/11] Add impl From for angle and length --- src/general/angle.rs | 8 ++++++++ src/general/length.rs | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/general/angle.rs b/src/general/angle.rs index 3d98c48..18a933d 100644 --- a/src/general/angle.rs +++ b/src/general/angle.rs @@ -23,6 +23,14 @@ pub struct Angle { value: AngleUnit, } +impl + Default> From for Angle { + fn from(i: i16) -> Self { + Self { + value: AngleUnit::Degrees(T::from(i)), + } + } +} + impl> Rem for Angle { type Output = Self; diff --git a/src/general/length.rs b/src/general/length.rs index 8ac332a..8689773 100644 --- a/src/general/length.rs +++ b/src/general/length.rs @@ -4,3 +4,9 @@ use super::Precision; #[derive(Inspectable, Default, Copy, Clone, Debug)] pub struct Length(pub Precision); + +impl From for Length { + fn from(i: i16) -> Self { + Self(Precision::from(i)) + } +} From 11b9539800215db46cd610e8d6e8e05158683420 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 7 Dec 2022 13:51:46 +0100 Subject: [PATCH 05/11] enable basic left and right rotation --- src/commands.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 0c3e630..160acdb 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,7 +5,7 @@ use crate::{ drawing::{ self, animation::{ - draw_straight_segment, move_straight_segment, ToAnimationSegment, + draw_straight_segment, move_straight_segment, turtle_turn, ToAnimationSegment, TurtleAnimationSegment, }, TurtleGraphElement, @@ -89,7 +89,12 @@ impl ToAnimationSegment for DrawElement { MoveCommand::Circle { radius, angle } => todo!(), MoveCommand::Goto(coord) => todo!(), }, - DrawElement::Orient(_) => todo!(), + DrawElement::Orient(e) => match e { + OrientationCommand::Left(angle_to_turn) => turtle_turn(state, -*angle_to_turn), + OrientationCommand::Right(angle_to_turn) => turtle_turn(state, *angle_to_turn), + OrientationCommand::SetHeading => todo!(), + OrientationCommand::LookAt(_) => todo!(), + }, DrawElement::Drip(_) => todo!(), } } From f5aae7efe16a136406bef18cf5d419ac0f555ac6 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 7 Dec 2022 13:54:15 +0100 Subject: [PATCH 06/11] Add a builder for the turtle plans --- src/builders.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/turtle_bundle.rs | 8 ++++-- 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/builders.rs diff --git a/src/builders.rs b/src/builders.rs new file mode 100644 index 0000000..b7cc503 --- /dev/null +++ b/src/builders.rs @@ -0,0 +1,61 @@ +use crate::{ + commands::{DrawElement, TurtleSegment}, + general::{angle::Angle, length::Length, Precision}, +}; + +#[derive(Default, Debug)] +pub struct TurtlePlan { + commands: Vec, +} + +pub trait WithCommands { + fn get_mut_commands(&mut self) -> &mut Vec; + fn get_commands(self) -> Vec; +} + +impl WithCommands for TurtlePlan { + fn get_mut_commands(&mut self) -> &mut Vec { + &mut self.commands + } + + fn get_commands(self) -> Vec { + self.commands + } +} + +impl TurtlePlan { + pub fn new() -> TurtlePlan { + TurtlePlan { commands: vec![] } + } + pub fn forward(&mut self, length: Length) { + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Draw( + crate::commands::MoveCommand::Forward(length), + ))) + } + pub fn backward(&mut self, length: Precision) { + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Draw( + crate::commands::MoveCommand::Backward(Length(length)), + ))) + } +} + +pub trait Turnable: WithCommands { + fn right(&mut self, angle: Angle) -> &mut Self { + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Orient( + crate::commands::OrientationCommand::Right(angle), + ))); + self + } + fn left(&mut self, angle: Angle) -> &mut Self { + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Orient( + crate::commands::OrientationCommand::Left(angle), + ))); + self + } +} + +impl Turnable for TurtlePlan {} diff --git a/src/lib.rs b/src/lib.rs index 11d9f11..61d0ea0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ use turtle_bundle::{AnimatedTurtle, TurtleBundle}; pub use commands::TurtleCommands; +pub mod builders; mod commands; mod debug; mod drawing; diff --git a/src/turtle_bundle.rs b/src/turtle_bundle.rs index 9f8ee99..b5bea0f 100644 --- a/src/turtle_bundle.rs +++ b/src/turtle_bundle.rs @@ -7,6 +7,7 @@ use bevy_prototype_lyon::{ }; use crate::{ + builders::{TurtlePlan, WithCommands}, commands::{DrawElement, MoveCommand, TurtleCommands, TurtleSegment}, general::length::Length, shapes::{self, TurtleColors}, @@ -39,8 +40,11 @@ impl Default for TurtleBundle { } impl TurtleBundle { - pub fn set_commands(&mut self, commands: Vec) { - self.commands = TurtleCommands::new(commands); + pub fn apply_plan(&mut self, plan: TurtlePlan) { + self.commands = TurtleCommands::new(plan.get_commands()); + } + pub fn create_plan(&self) -> TurtlePlan { + TurtlePlan::new() } } From 7bdc83c760f85bb114eb01efd5a84ebbd88b9945 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 13 Dec 2022 11:31:29 +0100 Subject: [PATCH 07/11] adding extend_plan and set_speed --- src/commands.rs | 8 +++++++- src/lib.rs | 2 +- src/turtle_bundle.rs | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 160acdb..ca55e91 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -10,7 +10,7 @@ use crate::{ }, TurtleGraphElement, }, - general::{angle::Angle, length::Length, Coordinate, Precision}, + general::{angle::Angle, length::Length, Coordinate, Precision, Speed}, state::TurtleState, }; /** @@ -146,6 +146,12 @@ impl TurtleCommands { pub fn push(&mut self, segment: TurtleSegment) { self.commands.push(segment) } + pub fn extend(&mut self, segments: Vec) { + self.commands.extend(segments.into_iter()) + } + pub fn set_speed(&mut self, speed: Speed) { + self.state.set_speed(speed); + } } impl Iterator for TurtleCommands { diff --git a/src/lib.rs b/src/lib.rs index 61d0ea0..96a932e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub mod events; mod general; pub mod shapes; mod state; -mod turtle_bundle; +pub mod turtle_bundle; /** The turtle plugin is the core of this turtle module. diff --git a/src/turtle_bundle.rs b/src/turtle_bundle.rs index b5bea0f..b8f864e 100644 --- a/src/turtle_bundle.rs +++ b/src/turtle_bundle.rs @@ -9,7 +9,7 @@ use bevy_prototype_lyon::{ use crate::{ builders::{TurtlePlan, WithCommands}, commands::{DrawElement, MoveCommand, TurtleCommands, TurtleSegment}, - general::length::Length, + general::{length::Length, Speed}, shapes::{self, TurtleColors}, }; @@ -43,6 +43,9 @@ impl TurtleBundle { pub fn apply_plan(&mut self, plan: TurtlePlan) { self.commands = TurtleCommands::new(plan.get_commands()); } + pub fn extend_plan(&mut self, plan: TurtlePlan) { + self.commands.extend(plan.get_commands()) + } pub fn create_plan(&self) -> TurtlePlan { TurtlePlan::new() } @@ -55,6 +58,9 @@ impl TurtleBundle { ))); self } + pub fn set_speed(&mut self, speed: Speed) { + self.commands.set_speed(speed); + } } #[derive(Bundle)] From 043e44b7f2b9d336fabe7f1f337b0798e05133b9 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Mon, 19 Dec 2022 09:43:38 +0100 Subject: [PATCH 08/11] make the movement commands accept anything into the unit --- src/builders.rs | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/builders.rs b/src/builders.rs index b7cc503..dfa5d9b 100644 --- a/src/builders.rs +++ b/src/builders.rs @@ -27,29 +27,52 @@ impl TurtlePlan { pub fn new() -> TurtlePlan { TurtlePlan { commands: vec![] } } - pub fn forward(&mut self, length: Length) { +} + +pub trait DirectionalMovement: WithCommands { + fn forward(&mut self, length: IntoDistance) -> &mut Self + where + Length: From, + { + let length: Length = length.into(); self.get_mut_commands() .push(TurtleSegment::Single(DrawElement::Draw( crate::commands::MoveCommand::Forward(length), - ))) + ))); + self } - pub fn backward(&mut self, length: Precision) { + fn backward(&mut self, length: IntoDistance) -> &mut Self + where + Length: From, + { + let length: Length = length.into(); self.get_mut_commands() .push(TurtleSegment::Single(DrawElement::Draw( - crate::commands::MoveCommand::Backward(Length(length)), - ))) + crate::commands::MoveCommand::Backward(length), + ))); + self } } +impl DirectionalMovement for TurtlePlan {} + pub trait Turnable: WithCommands { - fn right(&mut self, angle: Angle) -> &mut Self { + fn right(&mut self, angle: IntoAngle) -> &mut Self + where + Angle: From, + { + let angle: Angle = angle.into(); self.get_mut_commands() .push(TurtleSegment::Single(DrawElement::Orient( crate::commands::OrientationCommand::Right(angle), ))); self } - fn left(&mut self, angle: Angle) -> &mut Self { + fn left(&mut self, angle: IntoAngle) -> &mut Self + where + Angle: From, + { + let angle: Angle = angle.into(); self.get_mut_commands() .push(TurtleSegment::Single(DrawElement::Orient( crate::commands::OrientationCommand::Left(angle), From 75771e8556af85286b4e53b7faf1b01957753e58 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 21 Dec 2022 11:07:02 +0100 Subject: [PATCH 09/11] 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 {} From 8c5c9b4ec625c8bcae52c6fa0018fe9b781f1b79 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Wed, 11 Jan 2023 10:20:15 +0100 Subject: [PATCH 10/11] Upgrade `inspector-egui` and add first `pen_up` Unfortunately `pen_up` does not seem to work even though it compiles --- Cargo.toml | 2 +- src/builders.rs | 134 +++++++++++++++++++++++++++++++++++ src/commands.rs | 18 ++--- src/debug.rs | 4 +- src/drawing.rs | 4 +- src/drawing/line_segments.rs | 18 ++--- src/general/angle.rs | 65 ++++++++++++----- src/general/length.rs | 4 +- src/lib.rs | 5 +- src/shapes.rs | 10 +-- src/state.rs | 8 ++- src/turtle_bundle.rs | 12 +++- 12 files changed, 234 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8d0ed16..cbe57cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,6 @@ license = "MIT OR Apache-2.0" bevy = { version = "0.9" } bevy_prototype_lyon = {version="0.7"} -bevy-inspector-egui = "0.14" +bevy-inspector-egui = "0.16" num-traits = "0.2" bevy_tweening = {version="0.6"} \ No newline at end of file diff --git a/src/builders.rs b/src/builders.rs index ebaba36..de71746 100644 --- a/src/builders.rs +++ b/src/builders.rs @@ -7,6 +7,10 @@ use crate::{ #[derive(Default, Debug)] pub struct TurtlePlan { + /** + * A turtle Plan contains the segments of a turtle drawing. + * The segments in turn contain the commands to draw the graph. + */ commands: Vec, } @@ -119,3 +123,133 @@ pub trait CurvedMovement: WithCommands { } impl CurvedMovement for TurtlePlan {} + +pub trait StopLine +where + T: WithCommands, +{ + fn pen_up(self) -> InvisibleLinesPlan; +} + +impl StopLine for TurtlePlan { + fn pen_up(self) -> InvisibleLinesPlan { + { + InvisibleLinesPlan { + before: self, + commands: vec![], + } + } + } +} + +pub trait StartLine { + fn pen_down(self) -> T; +} + +impl StartLine for InvisibleLinesPlan +where + T: WithCommands, +{ + fn pen_down(mut self) -> T { + self.before.get_mut_commands().append(&mut self.commands); + self.before + } +} + +pub struct InvisibleLinesPlan { + before: T, + commands: Vec, +} + +impl WithCommands for InvisibleLinesPlan +where + T: WithCommands, +{ + fn get_mut_commands(&mut self) -> &mut Vec { + &mut self.commands + } + + fn get_commands(self) -> Vec { + self.commands + } +} + +impl InvisibleLinesPlan +where + T: WithCommands, +{ + pub fn new(before: T) -> Self { + InvisibleLinesPlan { + before, + commands: vec![], + } + } +} + +impl Turnable for InvisibleLinesPlan {} + +impl DirectionalMovement for InvisibleLinesPlan +where + T: WithCommands, +{ + fn forward(&mut self, length: IntoDistance) -> &mut Self + where + Length: From, + { + let length: Length = length.into(); + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Move( + crate::commands::MoveCommand::Forward(length), + ))); + self + } + + fn backward(&mut self, length: IntoDistance) -> &mut Self + where + Length: From, + { + let length: Length = length.into(); + self.get_mut_commands() + .push(TurtleSegment::Single(DrawElement::Move( + crate::commands::MoveCommand::Backward(length), + ))); + self + } +} + +impl CurvedMovement for InvisibleLinesPlan +where + T: 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::Move( + 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 + } +} diff --git a/src/commands.rs b/src/commands.rs index b67e0d1..dc355b6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,5 +1,7 @@ -use bevy::prelude::Component; -use bevy_inspector_egui::Inspectable; +use bevy::{ + prelude::Component, + reflect::{FromReflect, Reflect}, +}; use crate::{ builders::WithCommands, @@ -17,7 +19,7 @@ use crate::{ * All the possibilities to draw something with turtle. All the commands can get the position, heading, * color and fill_color from the turtles state. */ -#[derive(Component, Inspectable, Debug)] +#[derive(Component, Reflect, FromReflect, Debug, Clone)] pub enum MoveCommand { Forward(Length), Backward(Length), @@ -35,7 +37,7 @@ impl Default for MoveCommand { } /// Different ways to drop breadcrumbs on the way like a dot or a stamp of the turtles shape. -#[derive(Component, Inspectable, Default, Debug)] +#[derive(Component, Reflect, FromReflect, Default, Debug, Clone)] pub enum Breadcrumb { Dot, #[default] @@ -43,7 +45,7 @@ pub enum Breadcrumb { } /// Different ways that change the orientation of the turtle. -#[derive(Component, Inspectable, Debug)] +#[derive(Component, Reflect, FromReflect, Debug, Clone)] pub enum OrientationCommand { Left(Angle), Right(Angle), @@ -58,7 +60,7 @@ impl Default for OrientationCommand { } /// A combination of all commands that can be used while drawing. -#[derive(Component, Inspectable, Debug)] +#[derive(Component, Reflect, FromReflect, Debug, Clone)] pub enum DrawElement { Draw(MoveCommand), Move(MoveCommand), @@ -102,7 +104,7 @@ impl ToAnimationSegment for DrawElement { } } -#[derive(Component, Inspectable, Debug)] +#[derive(Component, Reflect, FromReflect, Debug, Clone)] pub enum TurtleSegment { Single(DrawElement), Outline(Vec), @@ -126,7 +128,7 @@ impl ToAnimationSegment for TurtleSegment { } } } -#[derive(Component, Inspectable, Debug)] +#[derive(Component, Reflect, Debug)] pub struct TurtleCommands { animation_state: usize, commands: Vec, diff --git a/src/debug.rs b/src/debug.rs index 214360c..ae56520 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,12 +1,12 @@ use bevy::prelude::Plugin; -use bevy_inspector_egui::WorldInspectorPlugin; +use bevy_inspector_egui::quick::WorldInspectorPlugin; pub struct DebugPlugin; impl Plugin for DebugPlugin { fn build(&self, app: &mut bevy::prelude::App) { if cfg!(debug_assertions) { - app.add_plugin(WorldInspectorPlugin::new()); + app.add_plugin(WorldInspectorPlugin); } } } diff --git a/src/drawing.rs b/src/drawing.rs index fa6c2a4..f3819b0 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -1,4 +1,4 @@ -use bevy_inspector_egui::Inspectable; +use bevy::reflect::{FromReflect, Reflect}; pub use self::line_segments::{TurtleDrawCircle, TurtleDrawLine}; @@ -6,7 +6,7 @@ pub mod animation; mod line_segments; pub(crate) mod run_step; -#[derive(Inspectable, Default, Debug)] +#[derive(Reflect, FromReflect, Default, Debug)] pub enum TurtleGraphElement { TurtleLine(TurtleDrawLine), TurtleCircle(TurtleDrawCircle), diff --git a/src/drawing/line_segments.rs b/src/drawing/line_segments.rs index 9447207..0300b73 100644 --- a/src/drawing/line_segments.rs +++ b/src/drawing/line_segments.rs @@ -1,5 +1,7 @@ -use bevy::prelude::{Bundle, Color, Component, Name, Transform, Vec2}; -use bevy_inspector_egui::Inspectable; +use bevy::{ + prelude::{Bundle, Color, Component, Name, Transform, Vec2}, + reflect::{FromReflect, Reflect}, +}; use bevy_prototype_lyon::{ entity::ShapeBundle, prelude::{DrawMode, FillMode, GeometryBuilder, PathBuilder, StrokeMode}, @@ -8,9 +10,9 @@ use bevy_prototype_lyon::{ use crate::general::{angle::Angle, Precision}; -#[derive(Bundle, Inspectable, Default)] +#[derive(Bundle, Reflect, FromReflect, Default)] pub struct TurtleDrawLine { - #[inspectable(ignore)] + #[reflect(ignore)] line: ShapeBundle, name: Name, marker: LineMarker, @@ -25,7 +27,7 @@ impl std::fmt::Debug for TurtleDrawLine { } } -#[derive(Component, Default, Inspectable, Debug)] +#[derive(Component, Default, Reflect, FromReflect, Debug, Clone, Copy)] struct LineMarker; impl TurtleDrawLine { @@ -45,10 +47,10 @@ impl TurtleDrawLine { } } -#[derive(Bundle, Inspectable, Default)] +#[derive(Bundle, Reflect, FromReflect, Default)] pub struct TurtleDrawCircle { - #[inspectable(ignore)] + #[reflect(ignore)] line: ShapeBundle, name: Name, marker: CircleMarker, @@ -63,7 +65,7 @@ impl std::fmt::Debug for TurtleDrawCircle { } } -#[derive(Component, Default, Inspectable, Debug)] +#[derive(Component, Default, Reflect, FromReflect, Debug, Clone)] struct CircleMarker; impl TurtleDrawCircle { diff --git a/src/general/angle.rs b/src/general/angle.rs index 18a933d..e2e3dfc 100644 --- a/src/general/angle.rs +++ b/src/general/angle.rs @@ -1,29 +1,30 @@ -use bevy_inspector_egui::Inspectable; use std::{ f32::consts::PI, ops::{Add, Div, Mul, Neg, Rem, Sub}, }; +use bevy::reflect::{FromReflect, Reflect}; + use super::Precision; -#[derive(Inspectable, Copy, Clone, Debug, PartialEq, Eq)] -pub enum AngleUnit { +#[derive(Reflect, FromReflect, Copy, Clone, Debug, PartialEq, Eq)] +pub enum AngleUnit { Degrees(T), Radians(T), } -impl Default for AngleUnit { +impl Default for AngleUnit { fn default() -> Self { Self::Degrees(Default::default()) } } -#[derive(Inspectable, Copy, Default, Clone, Debug, PartialEq, Eq)] -pub struct Angle { +#[derive(Reflect, FromReflect, Copy, Default, Clone, Debug, PartialEq, Eq)] +pub struct Angle { value: AngleUnit, } -impl + Default> From for Angle { +impl + Default + Send + Sync + Reflect + Copy + FromReflect> From for Angle { fn from(i: i16) -> Self { Self { value: AngleUnit::Degrees(T::from(i)), @@ -31,7 +32,9 @@ impl + Default> From for Angle { } } -impl> Rem for Angle { +impl> Rem + for Angle +{ type Output = Self; fn rem(self, rhs: T) -> Self::Output { @@ -42,7 +45,9 @@ impl> Rem for Angle { } } -impl> Mul for Angle { +impl> Mul + for Angle +{ type Output = Self; fn mul(self, rhs: T) -> Self::Output { @@ -65,7 +70,9 @@ impl Angle { } } } -impl> Div for Angle { +impl> Div + for Angle +{ type Output = Self; fn div(self, rhs: T) -> Self::Output { @@ -76,7 +83,10 @@ impl> Div for Angle { } } -impl> Neg for Angle { +impl< + T: Default + Clone + Send + Sync + Reflect + Copy + FromReflect + std::ops::Neg, + > Neg for Angle +{ type Output = Self; fn neg(self) -> Self::Output { @@ -87,7 +97,10 @@ impl> Neg for Angle { } } -impl> Neg for &Angle { +impl< + T: Default + Clone + Send + Sync + Reflect + Copy + FromReflect + std::ops::Neg, + > Neg for &Angle +{ type Output = Angle; fn neg(self) -> Self::Output { @@ -98,7 +111,7 @@ impl> Neg for &Angle { } } -impl Angle { +impl Angle { pub fn degrees(value: T) -> Angle { Self { value: AngleUnit::Degrees(value), @@ -117,7 +130,7 @@ impl Angle { } } -impl Angle { +impl Angle { pub fn to_radians(self) -> Self { match self.value { AngleUnit::Degrees(v) => Self { @@ -136,7 +149,17 @@ impl Angle { } } -impl + Default + num_traits::float::Float> Add for Angle { +impl< + T: Add + + Send + + Sync + + Reflect + + Copy + + FromReflect + + Default + + num_traits::float::Float, + > Add for Angle +{ type Output = Angle; fn add(self, rhs: Self) -> Self::Output { @@ -157,7 +180,17 @@ impl + Default + num_traits::float::Float> Add for Angle { } } -impl + Default + num_traits::float::Float> Sub for Angle { +impl< + T: Sub + + Default + + Send + + Sync + + Reflect + + Copy + + FromReflect + + num_traits::float::Float, + > Sub for Angle +{ type Output = Angle; fn sub(self, rhs: Self) -> Self::Output { diff --git a/src/general/length.rs b/src/general/length.rs index 351560a..8c3f62f 100644 --- a/src/general/length.rs +++ b/src/general/length.rs @@ -1,8 +1,8 @@ -use bevy_inspector_egui::Inspectable; +use bevy::reflect::{FromReflect, Reflect}; use super::Precision; -#[derive(Inspectable, Default, Copy, Clone, Debug)] +#[derive(Reflect, FromReflect, Default, Copy, Clone, Debug)] pub struct Length(pub Precision); impl From for Length { diff --git a/src/lib.rs b/src/lib.rs index 96a932e..5efdaa5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ use std::time::Duration; use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*, window::close_on_esc}; -use bevy_inspector_egui::RegisterInspectable; use bevy_prototype_lyon::prelude::{Path, ShapePlugin}; use bevy_tweening::{ component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween, @@ -57,8 +56,8 @@ impl Plugin for TurtlePlugin { .add_system(component_animator_system::) .add_system(close_on_esc) .add_system(draw_lines) - .register_inspectable::() - .register_inspectable::(); + .register_type::() + .register_type::(); } } diff --git a/src/shapes.rs b/src/shapes.rs index 6474ddb..d08f3f2 100644 --- a/src/shapes.rs +++ b/src/shapes.rs @@ -1,12 +1,14 @@ mod turtle; -use bevy::prelude::{Color, Component}; -use bevy_inspector_egui::Inspectable; +use bevy::{ + prelude::{Color, Component}, + reflect::Reflect, +}; pub use turtle::turtle; -#[derive(Clone, Component, Inspectable)] +#[derive(Clone, Component, Reflect)] pub struct TurtleShape; -#[derive(Clone, Component, Inspectable, Default, Debug)] +#[derive(Clone, Component, Reflect, Default, Debug)] pub struct TurtleColors { color: Color, fill_color: Color, diff --git a/src/state.rs b/src/state.rs index 542c2a0..8707e80 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,9 @@ use std::{cmp::max, time::Duration}; -use bevy::prelude::{Color, Component, Transform}; -use bevy_inspector_egui::Inspectable; +use bevy::{ + prelude::{Component, Transform}, + reflect::Reflect, +}; use crate::{ commands::TurtleSegment, @@ -10,7 +12,7 @@ use crate::{ }; /// Describing the full state of a turtle. -#[derive(Component, Inspectable, Default, Debug)] +#[derive(Component, Reflect, Default, Debug, Clone)] pub struct TurtleState { drawing: Vec, position: Coordinate, diff --git a/src/turtle_bundle.rs b/src/turtle_bundle.rs index 00e13c4..b7b069f 100644 --- a/src/turtle_bundle.rs +++ b/src/turtle_bundle.rs @@ -7,7 +7,10 @@ use bevy_prototype_lyon::{ }; use crate::{ - builders::{CurvedMovement, DirectionalMovement, Turnable, TurtlePlan, WithCommands}, + builders::{ + CurvedMovement, DirectionalMovement, InvisibleLinesPlan, StopLine, Turnable, TurtlePlan, + WithCommands, + }, commands::{TurtleCommands, TurtleSegment}, general::Speed, shapes::{self, TurtleColors}, @@ -87,6 +90,13 @@ impl WithCommands for TurtleBundle { self.commands.get_commands() } } +impl StopLine for TurtleBundle { + fn pen_up(self) -> crate::builders::InvisibleLinesPlan { + { + InvisibleLinesPlan::new(self) + } + } +} impl DirectionalMovement for TurtleBundle {} impl Turnable for TurtleBundle {} From f1591bc9f1d86bcead1fa967f4c463de0d3d2150 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Wed, 25 Jan 2023 12:03:07 +0100 Subject: [PATCH 11/11] Moved files --- .gitignore | 2 -- Cargo.toml => turtle-lib/Cargo.toml | 0 LICENSE => turtle-lib/LICENSE | 0 {src => turtle-lib/src}/builders.rs | 0 {src => turtle-lib/src}/commands.rs | 0 {src => turtle-lib/src}/debug.rs | 0 {src => turtle-lib/src}/drawing.rs | 0 {src => turtle-lib/src}/drawing/animation.rs | 0 {src => turtle-lib/src}/drawing/animation/circle_lens.rs | 0 {src => turtle-lib/src}/drawing/animation/line_lens.rs | 0 {src => turtle-lib/src}/drawing/line_segments.rs | 0 {src => turtle-lib/src}/drawing/run_step.rs | 0 {src => turtle-lib/src}/events.rs | 0 {src => turtle-lib/src}/general.rs | 0 {src => turtle-lib/src}/general/angle.rs | 0 {src => turtle-lib/src}/general/length.rs | 0 {src => turtle-lib/src}/lib.rs | 0 {src => turtle-lib/src}/shapes.rs | 0 {src => turtle-lib/src}/shapes/turtle.rs | 0 {src => turtle-lib/src}/state.rs | 0 {src => turtle-lib/src}/turtle_bundle.rs | 0 21 files changed, 2 deletions(-) delete mode 100644 .gitignore rename Cargo.toml => turtle-lib/Cargo.toml (100%) rename LICENSE => turtle-lib/LICENSE (100%) rename {src => turtle-lib/src}/builders.rs (100%) rename {src => turtle-lib/src}/commands.rs (100%) rename {src => turtle-lib/src}/debug.rs (100%) rename {src => turtle-lib/src}/drawing.rs (100%) rename {src => turtle-lib/src}/drawing/animation.rs (100%) rename {src => turtle-lib/src}/drawing/animation/circle_lens.rs (100%) rename {src => turtle-lib/src}/drawing/animation/line_lens.rs (100%) rename {src => turtle-lib/src}/drawing/line_segments.rs (100%) rename {src => turtle-lib/src}/drawing/run_step.rs (100%) rename {src => turtle-lib/src}/events.rs (100%) rename {src => turtle-lib/src}/general.rs (100%) rename {src => turtle-lib/src}/general/angle.rs (100%) rename {src => turtle-lib/src}/general/length.rs (100%) rename {src => turtle-lib/src}/lib.rs (100%) rename {src => turtle-lib/src}/shapes.rs (100%) rename {src => turtle-lib/src}/shapes/turtle.rs (100%) rename {src => turtle-lib/src}/state.rs (100%) rename {src => turtle-lib/src}/turtle_bundle.rs (100%) diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 4fffb2f..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -/Cargo.lock diff --git a/Cargo.toml b/turtle-lib/Cargo.toml similarity index 100% rename from Cargo.toml rename to turtle-lib/Cargo.toml diff --git a/LICENSE b/turtle-lib/LICENSE similarity index 100% rename from LICENSE rename to turtle-lib/LICENSE diff --git a/src/builders.rs b/turtle-lib/src/builders.rs similarity index 100% rename from src/builders.rs rename to turtle-lib/src/builders.rs diff --git a/src/commands.rs b/turtle-lib/src/commands.rs similarity index 100% rename from src/commands.rs rename to turtle-lib/src/commands.rs diff --git a/src/debug.rs b/turtle-lib/src/debug.rs similarity index 100% rename from src/debug.rs rename to turtle-lib/src/debug.rs diff --git a/src/drawing.rs b/turtle-lib/src/drawing.rs similarity index 100% rename from src/drawing.rs rename to turtle-lib/src/drawing.rs diff --git a/src/drawing/animation.rs b/turtle-lib/src/drawing/animation.rs similarity index 100% rename from src/drawing/animation.rs rename to turtle-lib/src/drawing/animation.rs diff --git a/src/drawing/animation/circle_lens.rs b/turtle-lib/src/drawing/animation/circle_lens.rs similarity index 100% rename from src/drawing/animation/circle_lens.rs rename to turtle-lib/src/drawing/animation/circle_lens.rs diff --git a/src/drawing/animation/line_lens.rs b/turtle-lib/src/drawing/animation/line_lens.rs similarity index 100% rename from src/drawing/animation/line_lens.rs rename to turtle-lib/src/drawing/animation/line_lens.rs diff --git a/src/drawing/line_segments.rs b/turtle-lib/src/drawing/line_segments.rs similarity index 100% rename from src/drawing/line_segments.rs rename to turtle-lib/src/drawing/line_segments.rs diff --git a/src/drawing/run_step.rs b/turtle-lib/src/drawing/run_step.rs similarity index 100% rename from src/drawing/run_step.rs rename to turtle-lib/src/drawing/run_step.rs diff --git a/src/events.rs b/turtle-lib/src/events.rs similarity index 100% rename from src/events.rs rename to turtle-lib/src/events.rs diff --git a/src/general.rs b/turtle-lib/src/general.rs similarity index 100% rename from src/general.rs rename to turtle-lib/src/general.rs diff --git a/src/general/angle.rs b/turtle-lib/src/general/angle.rs similarity index 100% rename from src/general/angle.rs rename to turtle-lib/src/general/angle.rs diff --git a/src/general/length.rs b/turtle-lib/src/general/length.rs similarity index 100% rename from src/general/length.rs rename to turtle-lib/src/general/length.rs diff --git a/src/lib.rs b/turtle-lib/src/lib.rs similarity index 100% rename from src/lib.rs rename to turtle-lib/src/lib.rs diff --git a/src/shapes.rs b/turtle-lib/src/shapes.rs similarity index 100% rename from src/shapes.rs rename to turtle-lib/src/shapes.rs diff --git a/src/shapes/turtle.rs b/turtle-lib/src/shapes/turtle.rs similarity index 100% rename from src/shapes/turtle.rs rename to turtle-lib/src/shapes/turtle.rs diff --git a/src/state.rs b/turtle-lib/src/state.rs similarity index 100% rename from src/state.rs rename to turtle-lib/src/state.rs diff --git a/src/turtle_bundle.rs b/turtle-lib/src/turtle_bundle.rs similarity index 100% rename from src/turtle_bundle.rs rename to turtle-lib/src/turtle_bundle.rs