remove the bevy turtle

This commit is contained in:
2025-10-12 19:39:09 +02:00
parent 630b4fdc44
commit fe2beb01ed
23 changed files with 0 additions and 2032 deletions
-255
View File
@@ -1,255 +0,0 @@
use std::ops::Neg;
use crate::{
commands::{DrawElement, MoveCommand, TurtleSegment},
general::{angle::Angle, length::Length, Precision},
};
#[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<TurtleSegment>,
}
pub trait WithCommands {
fn get_mut_commands(&mut self) -> &mut Vec<TurtleSegment>;
fn get_commands(self) -> Vec<TurtleSegment>;
}
impl WithCommands for TurtlePlan {
fn get_mut_commands(&mut self) -> &mut Vec<TurtleSegment> {
&mut self.commands
}
fn get_commands(self) -> Vec<TurtleSegment> {
self.commands
}
}
impl TurtlePlan {
pub fn new() -> TurtlePlan {
TurtlePlan { commands: vec![] }
}
}
pub trait DirectionalMovement: WithCommands {
fn forward<IntoDistance>(&mut self, length: IntoDistance) -> &mut Self
where
Length: From<IntoDistance>,
{
let length: Length = length.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Draw(
crate::commands::MoveCommand::Forward(length),
)));
self
}
fn backward<IntoDistance>(&mut self, length: IntoDistance) -> &mut Self
where
Length: From<IntoDistance>,
{
let length: Length = length.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Draw(
crate::commands::MoveCommand::Backward(length),
)));
self
}
}
impl DirectionalMovement for TurtlePlan {}
pub trait Turnable: WithCommands {
fn right<IntoAngle>(&mut self, angle: IntoAngle) -> &mut Self
where
Angle<Precision>: From<IntoAngle>,
{
let angle: Angle<Precision> = angle.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Orient(
crate::commands::OrientationCommand::Right(angle),
)));
self
}
fn left<IntoAngle>(&mut self, angle: IntoAngle) -> &mut Self
where
Angle<Precision>: From<IntoAngle>,
{
let angle: Angle<Precision> = angle.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Orient(
crate::commands::OrientationCommand::Left(angle),
)));
self
}
}
impl Turnable for TurtlePlan {}
pub trait CurvedMovement: WithCommands {
fn circle<IntoAngle, IntoDistance>(
&mut self,
radius: IntoDistance,
extend: IntoAngle,
) -> &mut Self
where
Angle<Precision>: From<IntoAngle>,
Length: From<IntoDistance>,
{
let angle: Angle<Precision> = extend.into();
let radius: Length = radius.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Draw(
MoveCommand::Circle { radius, angle },
)));
self
}
fn circle_right<IntoAngle, IntoDistance: Neg + Neg<Output = IntoDistance>>(
&mut self,
radius: IntoDistance,
extend: IntoAngle,
) -> &mut Self
where
Angle<Precision>: From<IntoAngle>,
Length: From<IntoDistance>,
{
self.circle(-radius, extend);
println!("Warning: circle with right arc not working yet...");
self
}
}
impl CurvedMovement for TurtlePlan {}
pub trait StopLine<T>
where
T: WithCommands,
{
fn pen_up(self) -> InvisibleLinesPlan<T>;
}
impl StopLine<TurtlePlan> for TurtlePlan {
fn pen_up(self) -> InvisibleLinesPlan<TurtlePlan> {
{
InvisibleLinesPlan {
before: self,
commands: vec![],
}
}
}
}
pub trait StartLine<T> {
fn pen_down(self) -> T;
}
impl<T> StartLine<T> for InvisibleLinesPlan<T>
where
T: WithCommands,
{
fn pen_down(mut self) -> T {
self.before.get_mut_commands().append(&mut self.commands);
self.before
}
}
pub struct InvisibleLinesPlan<T: WithCommands> {
before: T,
commands: Vec<TurtleSegment>,
}
impl<T> WithCommands for InvisibleLinesPlan<T>
where
T: WithCommands,
{
fn get_mut_commands(&mut self) -> &mut Vec<TurtleSegment> {
&mut self.commands
}
fn get_commands(self) -> Vec<TurtleSegment> {
self.commands
}
}
impl<T> InvisibleLinesPlan<T>
where
T: WithCommands,
{
pub fn new(before: T) -> Self {
InvisibleLinesPlan {
before,
commands: vec![],
}
}
}
impl Turnable for InvisibleLinesPlan<TurtlePlan> {}
impl<T> DirectionalMovement for InvisibleLinesPlan<T>
where
T: WithCommands,
{
fn forward<IntoDistance>(&mut self, length: IntoDistance) -> &mut Self
where
Length: From<IntoDistance>,
{
let length: Length = length.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Move(
crate::commands::MoveCommand::Forward(length),
)));
self
}
fn backward<IntoDistance>(&mut self, length: IntoDistance) -> &mut Self
where
Length: From<IntoDistance>,
{
let length: Length = length.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Move(
crate::commands::MoveCommand::Backward(length),
)));
self
}
}
impl<T> CurvedMovement for InvisibleLinesPlan<T>
where
T: WithCommands,
{
fn circle<IntoAngle, IntoDistance>(
&mut self,
radius: IntoDistance,
extend: IntoAngle,
) -> &mut Self
where
Angle<Precision>: From<IntoAngle>,
Length: From<IntoDistance>,
{
let angle: Angle<Precision> = extend.into();
let radius: Length = radius.into();
self.get_mut_commands()
.push(TurtleSegment::Single(DrawElement::Move(
MoveCommand::Circle { radius, angle },
)));
self
}
fn circle_right<IntoAngle, IntoDistance: Neg + Neg<Output = IntoDistance>>(
&mut self,
radius: IntoDistance,
extend: IntoAngle,
) -> &mut Self
where
Angle<Precision>: From<IntoAngle>,
Length: From<IntoDistance>,
{
self.circle(-radius, extend);
println!("Warning: circle with right arc not working yet...");
self
}
}
-202
View File
@@ -1,202 +0,0 @@
use bevy::{prelude::Component, reflect::Reflect};
use crate::{
builders::WithCommands,
drawing::TurtleGraphElement,
general::{angle::Angle, length::Length, Coordinate, Precision, Speed},
state::TurtleState,
};
#[cfg(feature = "tweening")]
use crate::drawing::animation::{
draw_circle_segment, draw_straight_segment, move_straight_segment, turtle_turn,
ToAnimationSegment, TurtleAnimationSegment,
};
/**
* 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, Reflect, Debug, Clone)]
pub enum MoveCommand {
Forward(Length),
Backward(Length),
Circle {
radius: Length,
angle: Angle<Precision>,
},
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, Reflect, Default, Debug, Clone)]
pub enum Breadcrumb {
Dot,
#[default]
Stamp,
}
/// Different ways that change the orientation of the turtle.
#[derive(Component, Reflect, Debug, Clone)]
pub enum OrientationCommand {
Left(Angle<Precision>),
Right(Angle<Precision>),
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, Reflect, Debug, Clone)]
pub enum DrawElement {
Draw(MoveCommand),
Move(MoveCommand),
Orient(OrientationCommand),
Drip(Breadcrumb),
}
impl Default for DrawElement {
fn default() -> Self {
Self::Draw(Default::default())
}
}
#[cfg(feature = "tweening")]
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 } => {
draw_circle_segment(state, *radius, *angle)
}
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(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!(),
}
}
}
#[derive(Component, Reflect, Debug, Clone)]
pub enum TurtleSegment {
Single(DrawElement),
Outline(Vec<DrawElement>),
Filled(Vec<DrawElement>),
}
impl Default for TurtleSegment {
fn default() -> Self {
Self::Single(Default::default())
}
}
#[cfg(feature = "tweening")]
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, Reflect, Debug)]
pub struct TurtleCommands {
animation_state: usize,
commands: Vec<TurtleSegment>,
lines: Vec<TurtleGraphElement>,
state: TurtleState,
}
impl TurtleCommands {
pub fn new(commands: Vec<TurtleSegment>) -> 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)
}
pub fn extend(&mut self, segments: Vec<TurtleSegment>) {
self.commands.extend(segments.into_iter())
}
pub fn set_speed(&mut self, speed: Speed) {
self.state.set_speed(speed);
}
// Public accessors for immediate drawing
pub(crate) fn animation_state(&self) -> usize {
self.animation_state
}
pub(crate) fn animation_state_mut(&mut self) -> &mut usize {
&mut self.animation_state
}
pub(crate) fn commands(&self) -> &[TurtleSegment] {
&self.commands
}
pub(crate) fn state_mut(&mut self) -> &mut TurtleState {
&mut self.state
}
}
#[cfg(feature = "tweening")]
impl Iterator for TurtleCommands {
type Item = TurtleAnimationSegment;
fn next(&mut self) -> Option<Self::Item> {
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
}
}
}
impl WithCommands for TurtleCommands {
fn get_mut_commands(&mut self) -> &mut Vec<TurtleSegment> {
&mut self.commands
}
fn get_commands(self) -> Vec<TurtleSegment> {
self.commands
}
}
-14
View File
@@ -1,14 +0,0 @@
use bevy::prelude::Plugin;
#[cfg(feature = "inspector")]
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) {
#[cfg(feature = "inspector")]
app.add_plugins(WorldInspectorPlugin::default());
}
}
}
-19
View File
@@ -1,19 +0,0 @@
use bevy::reflect::Reflect;
pub use self::line_segments::{TurtleDrawCircle, TurtleDrawLine};
#[cfg(feature = "tweening")]
pub mod animation;
#[cfg(not(feature = "tweening"))]
pub(crate) mod immediate;
mod line_segments;
#[cfg(feature = "tweening")]
pub(crate) mod run_step;
#[derive(Reflect, Default, Debug)]
pub enum TurtleGraphElement {
TurtleLine(TurtleDrawLine),
TurtleCircle(TurtleDrawCircle),
#[default]
Noop,
}
-242
View File
@@ -1,242 +0,0 @@
mod circle_lens;
mod line_lens;
use bevy::prelude::{Quat, Transform, Vec2, Vec3};
use bevy_prototype_lyon::prelude::Shape;
use bevy_tweening::{
lens::{TransformPositionLens, TransformRotateZLens},
Animator, EaseFunction, Tween,
};
use crate::{
general::{angle::Angle, length::Length, 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<Tween<Transform>>,
pub line_segment: Option<TurtleGraphElement>,
pub line_animation: Option<Animator<Shape>>,
}
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<Precision>,
) -> 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)),
}
}
pub fn draw_circle_segment(
state: &mut TurtleState,
radius: Length,
angle: Angle<Precision>,
) -> 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,
line: TurtleDrawLine,
animation: Tween<Shape>,
}
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<Transform>,
}
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,
}
}
}
struct MoveCircleLineAnimation {
_start: Coordinate,
_end: Coordinate,
line: TurtleDrawCircle,
animation: Tween<Shape>,
}
impl MoveCircleLineAnimation {
fn new(state: &TurtleState, radius: Length, angle: Angle<Precision>) -> 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 },
},
);
Self {
_start: start,
_end: end_pos,
line,
animation: line_animator,
}
}
}
struct MoveCircleTurtleAnimation {
_start: Coordinate,
end: Coordinate,
end_heading: Angle<Precision>,
animation: Tween<Transform>,
}
impl MoveCircleTurtleAnimation {
fn new(state: &TurtleState, radius: Length, angle: Angle<Precision>) -> 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,
}
}
}
@@ -1,51 +0,0 @@
use bevy::prelude::{Quat, Transform, Vec2};
use bevy_prototype_lyon::{path::ShapePath, prelude::*, self as lyon_crate};
use lyon_crate::entity::Shape;
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<Precision>,
pub end: Angle<Precision>,
}
impl Lens<Shape> for CircleAnimationLens {
fn lerp(&mut self, target: &mut Shape, ratio: f32) {
let mut path = ShapePath::new();
path = path.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 = path.arc(
self.center,
self.radii,
(self.start + ((self.end - self.start) * ratio))
.to_radians()
.value(),
0.,
);
*target = Shape::from(path);
}
}
pub(crate) struct CircleMovementLens {
pub center: Vec2,
pub start: Transform,
pub end: Angle<Precision>,
}
impl Lens<Transform> 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;
}
}
@@ -1,21 +0,0 @@
use bevy::prelude::Vec2;
use bevy_prototype_lyon::{prelude::{Shape, 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<Shape> for LineAnimationLens {
fn lerp(&mut self, target: &mut Shape, ratio: f32) {
let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio));
*target = Shape::from(ShapePath::build_as(&line));
}
}
-159
View File
@@ -1,159 +0,0 @@
use bevy::prelude::{Commands, Query, Quat, Transform, Vec2, With};
use crate::{
commands::{DrawElement, MoveCommand, OrientationCommand, TurtleCommands, TurtleSegment},
general::angle::Angle,
shapes::TurtleShape,
};
use super::line_segments::{TurtleDrawCircle, TurtleDrawLine};
/// Executes all turtle commands immediately without animation
pub fn run_all_commands_immediately(
commands: &mut Commands,
tcmd: &mut TurtleCommands,
turtle: &mut Query<&mut Transform, With<TurtleShape>>,
) {
if let Ok(mut turtle_transform) = turtle.single_mut() {
// Execute all commands
loop {
let idx = tcmd.animation_state();
if idx >= tcmd.commands().len() {
break;
}
let segment = tcmd.commands()[idx].clone();
match segment {
TurtleSegment::Single(element) => {
execute_draw_element(commands, &element, tcmd, &mut turtle_transform);
}
TurtleSegment::Outline(elements) => {
for element in elements {
execute_draw_element(commands, &element, tcmd, &mut turtle_transform);
}
}
TurtleSegment::Filled(elements) => {
for element in elements {
execute_draw_element(commands, &element, tcmd, &mut turtle_transform);
}
}
}
*tcmd.animation_state_mut() += 1;
}
}
}
fn execute_draw_element(
commands: &mut Commands,
element: &DrawElement,
tcmd: &mut TurtleCommands,
turtle_transform: &mut Transform,
) {
match element {
DrawElement::Draw(move_cmd) => {
match move_cmd {
MoveCommand::Forward(length) => {
let start = tcmd.state_mut().position();
let end = start + (Vec2::from_angle(tcmd.state_mut().heading().to_radians().value()) * length.0);
tcmd.state_mut().set_position(end);
// Update turtle position
turtle_transform.translation.x = end.x;
turtle_transform.translation.y = end.y;
// Draw line
let line = TurtleDrawLine::new(start, end);
commands.spawn(line);
}
MoveCommand::Backward(length) => {
let start = tcmd.state_mut().position();
let end = start + (Vec2::from_angle(tcmd.state_mut().heading().to_radians().value()) * -length.0);
tcmd.state_mut().set_position(end);
// Update turtle position
turtle_transform.translation.x = end.x;
turtle_transform.translation.y = end.y;
// Draw line
let line = TurtleDrawLine::new(start, end);
commands.spawn(line);
}
MoveCommand::Circle { radius, angle } => {
let start = tcmd.state_mut().position();
let radii = Vec2::ONE * radius.0.abs();
let left_right = Angle::degrees(if radius.0 >= 0. { 90. } else { -90. });
let heading = tcmd.state_mut().heading();
let center = start + (Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle(
((heading + left_right).to_radians()).value(),
)));
let end_heading = heading + if radius.0 > 0. { *angle } else { -*angle };
let end = center + Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle(
(heading + *angle - left_right).to_radians().value(),
));
tcmd.state_mut().set_position(end);
tcmd.state_mut().set_heading(end_heading);
// Update turtle position and rotation
turtle_transform.translation.x = end.x;
turtle_transform.translation.y = end.y;
turtle_transform.rotation = Quat::from_rotation_z(end_heading.to_radians().value());
// Draw circle arc
let circle = TurtleDrawCircle::new(center, radii, *angle, start, end);
commands.spawn(circle);
}
MoveCommand::Goto(_coord) => {
// TODO: implement goto
}
}
}
DrawElement::Move(move_cmd) => {
match move_cmd {
MoveCommand::Forward(length) => {
let new_pos = tcmd.state_mut().position() + (Vec2::from_angle(tcmd.state_mut().heading().to_radians().value()) * length.0);
tcmd.state_mut().set_position(new_pos);
turtle_transform.translation.x = new_pos.x;
turtle_transform.translation.y = new_pos.y;
}
MoveCommand::Backward(length) => {
let new_pos = tcmd.state_mut().position() + (Vec2::from_angle(tcmd.state_mut().heading().to_radians().value()) * -length.0);
tcmd.state_mut().set_position(new_pos);
turtle_transform.translation.x = new_pos.x;
turtle_transform.translation.y = new_pos.y;
}
MoveCommand::Circle { .. } => {
// TODO: implement move circle
}
MoveCommand::Goto(_coord) => {
// TODO: implement goto
}
}
}
DrawElement::Orient(orient_cmd) => {
match orient_cmd {
OrientationCommand::Left(angle) => {
let new_heading = tcmd.state_mut().heading() - *angle;
tcmd.state_mut().set_heading(new_heading);
turtle_transform.rotation = Quat::from_rotation_z(new_heading.to_radians().value());
}
OrientationCommand::Right(angle) => {
let new_heading = tcmd.state_mut().heading() + *angle;
tcmd.state_mut().set_heading(new_heading);
turtle_transform.rotation = Quat::from_rotation_z(new_heading.to_radians().value());
}
OrientationCommand::SetHeading => {
// TODO: implement set_heading
}
OrientationCommand::LookAt(_coord) => {
// TODO: implement look_at
}
}
}
DrawElement::Drip(_breadcrumb) => {
// TODO: implement breadcrumbs
}
}
}
-99
View File
@@ -1,99 +0,0 @@
use bevy::{
prelude::{Bundle, Color, Component, Name, Vec2},
reflect::Reflect,
};
use bevy_prototype_lyon::{
draw::{Fill, Stroke},
entity::Shape,
geometry::ShapeBuilder,
path::ShapePath,
prelude::ShapeBuilderBase as _,
shapes::Line,
};
use crate::general::{angle::Angle, Precision};
#[derive(Bundle, Reflect, Default)]
pub struct TurtleDrawLine {
#[reflect(ignore)]
line: Shape,
name: Name,
marker: LineMarker,
}
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, Reflect, Debug, Clone, Copy)]
struct LineMarker;
impl TurtleDrawLine {
pub(crate) fn new(start: Vec2, end: Vec2) -> Self {
let line = Line(start, end);
Self {
line: ShapeBuilder::with(&line)
.fill(Fill::color(Color::NONE))
.stroke(Stroke::new(Color::srgb(0.0, 0.0, 0.0), 1.0))
.build(),
name: Name::new(format!("Line {}-{}", start, end)),
marker: LineMarker,
}
}
}
#[derive(Bundle, Reflect, Default)]
pub struct TurtleDrawCircle {
#[reflect(ignore)]
line: Shape,
name: Name,
marker: CircleMarker,
}
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, Reflect, Debug, Clone)]
struct CircleMarker;
impl TurtleDrawCircle {
pub(crate) fn new(
center: Vec2,
radii: Vec2,
angle: Angle<Precision>,
start: Vec2,
_end: Vec2,
) -> Self {
// 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?)
let path =
ShapePath::new()
.move_to(start)
.arc(center, radii, angle.to_radians().value(), 0.);
println!("Draw Circle: {} {} {:?}", center, radii, angle);
Self {
line: ShapeBuilder::with(&path)
.fill(Fill::color(Color::NONE))
.stroke(Stroke::new(Color::srgb(0.0, 0.0, 0.0), 1.0))
.build(),
name: Name::new(format!("Circle at {}, {}", center.x, center.y)),
marker: CircleMarker,
}
}
}
-58
View File
@@ -1,58 +0,0 @@
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<Transform>, With<TurtleShape>>,
) {
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;
}
};
}
}
-4
View File
@@ -1,4 +0,0 @@
use bevy::prelude::{Entity, Message};
#[derive(Message)]
pub struct DrawingStartedEvent(pub Entity);
-9
View File
@@ -1,9 +0,0 @@
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;
-212
View File
@@ -1,212 +0,0 @@
use std::{
f32::consts::PI,
ops::{Add, Div, Mul, Neg, Rem, Sub},
};
use bevy::reflect::Reflect;
use super::Precision;
#[derive(Reflect, Copy, Clone, Debug, PartialEq, Eq)]
pub enum AngleUnit<T: Default + Send + Sync + Reflect + Copy> {
Degrees(T),
Radians(T),
}
impl<T: Default + Send + Sync + Reflect + Copy> Default for AngleUnit<T> {
fn default() -> Self {
Self::Degrees(Default::default())
}
}
#[derive(Reflect, Copy, Default, Clone, Debug, PartialEq, Eq)]
pub struct Angle<T: Default + Send + Sync + Reflect + Copy> {
value: AngleUnit<T>,
}
impl<T: From<i16> + Default + Send + Sync + Reflect + Copy> From<i16> for Angle<T> {
fn from(i: i16) -> Self {
Self {
value: AngleUnit::Degrees(T::from(i)),
}
}
}
impl<T: Default + Send + Sync + Reflect + Clone + Copy + Rem<T, Output = T>> Rem<T> for Angle<T> {
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<T: Default + Clone + Send + Sync + Reflect + Copy + Mul<T, Output = T>> Mul<T> for Angle<T> {
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<Precision> {
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<T: Default + Clone + Send + Sync + Reflect + Copy + Div<T, Output = T>> Div<T> for Angle<T> {
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<T: Default + Clone + Send + Sync + Reflect + Copy + std::ops::Neg<Output = T>> Neg
for Angle<T>
{
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<T: Default + Clone + Send + Sync + Reflect + Copy + std::ops::Neg<Output = T>> Neg
for &Angle<T>
{
type Output = Angle<T>;
fn neg(self) -> Self::Output {
match self.value.clone() {
AngleUnit::Degrees(v) => Self::Output::degrees(-v),
AngleUnit::Radians(v) => Self::Output::radians(-v),
}
}
}
impl<T: Default + Clone + Send + Sync + Reflect + Copy> Angle<T> {
pub fn degrees(value: T) -> Angle<T> {
Self {
value: AngleUnit::Degrees(value),
}
}
pub fn radians(value: T) -> Angle<T> {
Self {
value: AngleUnit::Radians(value),
}
}
pub fn value(&self) -> T {
match self.value.clone() {
AngleUnit::Degrees(v) => v,
AngleUnit::Radians(v) => v,
}
}
}
impl<T: Default + Send + Sync + Reflect + Copy + num_traits::float::Float> Angle<T> {
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<T: Add<Output = T> + Send + Sync + Reflect + Copy + Default + num_traits::float::Float> Add
for Angle<T>
{
type Output = Angle<T>;
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<T: Sub<Output = T> + Default + Send + Sync + Reflect + Copy + num_traits::float::Float> Sub
for Angle<T>
{
type Output = Angle<T>;
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);
}
-19
View File
@@ -1,19 +0,0 @@
use bevy::reflect::Reflect;
use super::Precision;
#[derive(Reflect, Default, Copy, Clone, Debug)]
pub struct Length(pub Precision);
impl From<i16> for Length {
fn from(i: i16) -> Self {
Self(Precision::from(i))
}
}
impl From<f32> for Length {
fn from(i: f32) -> Self {
#[allow(clippy::useless_conversion)]
Self(Precision::from(i))
}
}
-145
View File
@@ -1,145 +0,0 @@
use bevy::{prelude::*, window::WindowResolution};
#[cfg(feature = "tweening")]
use bevy_prototype_lyon::entity::Shape;
use bevy_prototype_lyon::prelude::ShapePlugin;
#[cfg(feature = "tweening")]
use bevy_tweening::{
component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween,
TweenCompleted,
};
use events::DrawingStartedEvent;
use shapes::{TurtleColors, TurtleShape};
use turtle_bundle::{AnimatedTurtle, TurtleBundle};
pub use commands::TurtleCommands;
pub mod builders;
mod commands;
mod debug;
mod drawing;
pub mod events;
mod general;
pub mod shapes;
mod state;
pub 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 {
primary_window: Some(Window {
title: "Immigration Game".to_string(),
resolution: WindowResolution::new(1200, 800),
..default()
}),
..default()
}))
.add_plugins(debug::DebugPlugin)
.add_plugins(ShapePlugin)
.add_message::<DrawingStartedEvent>()
.add_systems(Startup, setup)
.add_systems(
Update,
(
keypresses,
#[cfg(feature = "tweening")]
component_animator_system::<Transform>,
#[cfg(feature = "tweening")]
component_animator_system::<Shape>,
#[cfg(feature = "tweening")]
draw_lines,
#[cfg(not(feature = "tweening"))]
draw_immediate,
),
)
.register_type::<TurtleColors>()
.register_type::<TurtleCommands>();
}
}
fn setup(mut commands: Commands) {
commands.spawn((Camera2d,));
}
pub fn get_a_turtle() -> AnimatedTurtle {
#[cfg(feature = "tweening")]
let animator = Animator::new(Tween::new(
EaseFunction::QuadraticInOut,
Duration::from_millis(3000),
TransformScaleLens {
start: Vec3::ZERO,
end: Vec3::ONE * 1.3,
},
));
#[cfg(not(feature = "tweening"))]
let animator = ();
let turtle_bundle = TurtleBundle::default();
AnimatedTurtle {
animator,
turtle_bundle,
turtle_shape: TurtleShape,
}
}
fn keypresses(
mut commands: Commands,
_keys: Res<ButtonInput<KeyCode>>,
mut tcmd: Query<(Entity, &mut TurtleCommands)>,
#[cfg(feature = "tweening")] mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>,
#[cfg(not(feature = "tweening"))] mut turtle: Query<&mut Transform, With<TurtleShape>>,
mut _ev_start: MessageWriter<DrawingStartedEvent>,
) {
if _keys.just_pressed(KeyCode::KeyW) {
for (entity, mut tcmd) in tcmd.iter_mut() {
#[cfg(feature = "tweening")]
crate::drawing::run_step::run_animation_step(&mut commands, &mut tcmd, &mut turtle);
#[cfg(not(feature = "tweening"))]
crate::drawing::immediate::run_all_commands_immediately(&mut commands, &mut tcmd, &mut turtle);
_ev_start.write(DrawingStartedEvent(entity));
}
}
}
#[cfg(feature = "tweening")]
fn draw_lines(
mut commands: Commands,
mut tcmd: Query<&mut TurtleCommands>,
mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>,
mut query_event: EventReader<TweenCompleted>, // TODO: howto attach only to the right event?
) {
for _ev in query_event.read() {
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")
}
}
}
#[cfg(not(feature = "tweening"))]
fn draw_immediate(
mut commands: Commands,
mut tcmd: Query<&mut TurtleCommands>,
mut turtle: Query<&mut Transform, With<TurtleShape>>,
) {
for mut tcmd in tcmd.iter_mut() {
// Only draw if there are commands to execute
if tcmd.animation_state() < tcmd.commands().len() {
crate::drawing::immediate::run_all_commands_immediately(&mut commands, &mut tcmd, &mut turtle);
}
}
}
-15
View File
@@ -1,15 +0,0 @@
mod turtle;
use bevy::{
prelude::{Color, Component},
reflect::Reflect,
};
pub use turtle::turtle;
#[derive(Clone, Component, Reflect)]
pub struct TurtleShape;
#[derive(Clone, Component, Reflect, Default, Debug)]
pub struct TurtleColors {
color: Color,
fill_color: Color,
}
-46
View File
@@ -1,46 +0,0 @@
use std::f32::consts::PI;
use bevy::prelude::Vec2;
use bevy_prototype_lyon::prelude::ShapePath;
use crate::general::Precision;
pub fn turtle() -> ShapePath {
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 = ShapePath::new()
.line_to(Vec2::new(1.0, 1.0))
.line_to(Vec2::new(-1.0, 1.0))
.line_to(Vec2::new(-1.0, -1.0))
.line_to(Vec2::new(1.0, -1.0))
.close()
.move_to(Vec2::new(0.0, 16.0).rotate(Vec2::from_angle(-PI / 2.)));
for coord in polygon {
turtle_path = turtle_path.line_to(Vec2::from_array(*coord).rotate(Vec2::from_angle(-PI / 2.)));
}
turtle_path.close()
}
-71
View File
@@ -1,71 +0,0 @@
use std::{cmp::max, time::Duration};
use bevy::{
prelude::{Component, Transform},
reflect::Reflect,
};
use crate::{
commands::TurtleSegment,
general::{angle::Angle, Coordinate, Precision, Speed, Visibility},
shapes::TurtleColors,
};
/// Describing the full state of a turtle.
#[derive(Component, Reflect, Default, Debug, Clone)]
pub struct TurtleState {
drawing: Vec<TurtleSegment>,
position: Coordinate,
heading: Angle<Precision>,
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<Precision> {
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<Precision>) -> &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
}
}
-104
View File
@@ -1,104 +0,0 @@
use std::ops::{Deref, DerefMut};
use bevy::prelude::{Bundle, Color, Name};
use bevy_prototype_lyon::{
draw::{Fill, Stroke},
entity::Shape,
geometry::ShapeBuilder,
prelude::ShapeBuilderBase as _,
};
use crate::{
builders::{
CurvedMovement, DirectionalMovement, InvisibleLinesPlan, StopLine, Turnable, TurtlePlan,
WithCommands,
},
commands::{TurtleCommands, TurtleSegment},
general::Speed,
shapes::{self, TurtleColors},
};
#[derive(Bundle)]
pub struct TurtleBundle {
colors: TurtleColors,
pub commands: TurtleCommands,
name: Name,
shape: Shape,
}
impl Default for TurtleBundle {
fn default() -> Self {
Self {
colors: TurtleColors::default(),
commands: TurtleCommands::new(vec![]),
name: Name::new("Turtle"),
shape: ShapeBuilder::with(&shapes::turtle())
.fill(Fill::color(Color::srgb(0.098, 0.098, 0.439)))
.stroke(Stroke::new(Color::srgb(0.0, 0.0, 0.0), 1.0))
.build(),
}
}
}
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()
}
}
impl TurtleBundle {
pub fn set_speed(&mut self, speed: Speed) {
self.commands.set_speed(speed);
}
}
#[derive(Bundle)]
pub struct AnimatedTurtle {
#[cfg(feature = "tweening")]
pub animator: bevy_tweening::Animator<bevy::prelude::Transform>,
#[cfg(not(feature = "tweening"))]
pub 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
}
}
impl WithCommands for TurtleBundle {
fn get_mut_commands(&mut self) -> &mut Vec<TurtleSegment> {
self.commands.get_mut_commands()
}
fn get_commands(self) -> Vec<TurtleSegment> {
self.commands.get_commands()
}
}
impl StopLine<TurtleBundle> for TurtleBundle {
fn pen_up(self) -> crate::builders::InvisibleLinesPlan<TurtleBundle> {
{
InvisibleLinesPlan::new(self)
}
}
}
impl DirectionalMovement for TurtleBundle {}
impl Turnable for TurtleBundle {}
impl CurvedMovement for TurtleBundle {}