Moved files
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
use bevy::{
|
||||
prelude::Component,
|
||||
reflect::{FromReflect, Reflect},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
builders::WithCommands,
|
||||
drawing::{
|
||||
animation::{
|
||||
draw_circle_segment, draw_straight_segment, move_straight_segment, turtle_turn,
|
||||
ToAnimationSegment, TurtleAnimationSegment,
|
||||
},
|
||||
TurtleGraphElement,
|
||||
},
|
||||
general::{angle::Angle, length::Length, Coordinate, Precision, Speed},
|
||||
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, Reflect, FromReflect, 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, FromReflect, Default, Debug, Clone)]
|
||||
pub enum Breadcrumb {
|
||||
Dot,
|
||||
#[default]
|
||||
Stamp,
|
||||
}
|
||||
|
||||
/// Different ways that change the orientation of the turtle.
|
||||
#[derive(Component, Reflect, FromReflect, 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, FromReflect, Debug, Clone)]
|
||||
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 } => {
|
||||
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, FromReflect, Debug, Clone)]
|
||||
pub enum TurtleSegment {
|
||||
Single(DrawElement),
|
||||
Outline(Vec<DrawElement>),
|
||||
Filled(Vec<DrawElement>),
|
||||
}
|
||||
|
||||
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, 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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use bevy::prelude::Plugin;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use bevy::reflect::{FromReflect, Reflect};
|
||||
|
||||
pub use self::line_segments::{TurtleDrawCircle, TurtleDrawLine};
|
||||
|
||||
pub mod animation;
|
||||
mod line_segments;
|
||||
pub(crate) mod run_step;
|
||||
|
||||
#[derive(Reflect, FromReflect, Default, Debug)]
|
||||
pub enum TurtleGraphElement {
|
||||
TurtleLine(TurtleDrawLine),
|
||||
TurtleCircle(TurtleDrawCircle),
|
||||
#[default]
|
||||
Noop,
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
mod circle_lens;
|
||||
mod line_lens;
|
||||
|
||||
use bevy::prelude::{Quat, Transform, Vec2, Vec3};
|
||||
use bevy_prototype_lyon::prelude::Path;
|
||||
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<Path>>,
|
||||
}
|
||||
|
||||
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<Path>,
|
||||
}
|
||||
|
||||
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<Path>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Precision>,
|
||||
pub end: Angle<Precision>,
|
||||
}
|
||||
|
||||
impl Lens<Path> 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<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;
|
||||
}
|
||||
}
|
||||
@@ -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<Path> 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use bevy::{
|
||||
prelude::{Bundle, Color, Component, Name, Transform, Vec2},
|
||||
reflect::{FromReflect, Reflect},
|
||||
};
|
||||
use bevy_prototype_lyon::{
|
||||
entity::ShapeBundle,
|
||||
prelude::{DrawMode, FillMode, GeometryBuilder, PathBuilder, StrokeMode},
|
||||
shapes::Line,
|
||||
};
|
||||
|
||||
use crate::general::{angle::Angle, Precision};
|
||||
|
||||
#[derive(Bundle, Reflect, FromReflect, Default)]
|
||||
pub struct TurtleDrawLine {
|
||||
#[reflect(ignore)]
|
||||
line: ShapeBundle,
|
||||
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, FromReflect, Debug, Clone, Copy)]
|
||||
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, Reflect, FromReflect, Default)]
|
||||
|
||||
pub struct TurtleDrawCircle {
|
||||
#[reflect(ignore)]
|
||||
line: ShapeBundle,
|
||||
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, FromReflect, Debug, Clone)]
|
||||
struct CircleMarker;
|
||||
|
||||
impl TurtleDrawCircle {
|
||||
pub(crate) fn new(
|
||||
center: Vec2,
|
||||
radii: Vec2,
|
||||
angle: Angle<Precision>,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
use bevy::prelude::Entity;
|
||||
|
||||
pub struct DrawingStartedEvent(pub Entity);
|
||||
@@ -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;
|
||||
@@ -0,0 +1,236 @@
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
ops::{Add, Div, Mul, Neg, Rem, Sub},
|
||||
};
|
||||
|
||||
use bevy::reflect::{FromReflect, Reflect};
|
||||
|
||||
use super::Precision;
|
||||
|
||||
#[derive(Reflect, FromReflect, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum AngleUnit<T: Default + Send + Sync + Reflect + Copy + FromReflect> {
|
||||
Degrees(T),
|
||||
Radians(T),
|
||||
}
|
||||
|
||||
impl<T: Default + Send + Sync + Reflect + Copy + FromReflect> Default for AngleUnit<T> {
|
||||
fn default() -> Self {
|
||||
Self::Degrees(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Reflect, FromReflect, Copy, Default, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Angle<T: Default + Send + Sync + Reflect + Copy + FromReflect> {
|
||||
value: AngleUnit<T>,
|
||||
}
|
||||
|
||||
impl<T: From<i16> + Default + Send + Sync + Reflect + Copy + FromReflect> 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 + FromReflect + 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 + FromReflect + 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 + FromReflect + 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 + FromReflect + 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 + FromReflect + 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 + FromReflect> 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 + FromReflect + 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
|
||||
+ FromReflect
|
||||
+ 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
|
||||
+ FromReflect
|
||||
+ 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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
use bevy::reflect::{FromReflect, Reflect};
|
||||
|
||||
use super::Precision;
|
||||
|
||||
#[derive(Reflect, FromReflect, 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))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*, window::close_on_esc};
|
||||
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;
|
||||
|
||||
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 {
|
||||
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::<DrawingStartedEvent>()
|
||||
.add_startup_system(setup)
|
||||
.add_system(keypresses)
|
||||
.add_system(component_animator_system::<Path>)
|
||||
.add_system(close_on_esc)
|
||||
.add_system(draw_lines)
|
||||
.register_type::<TurtleColors>()
|
||||
.register_type::<TurtleCommands>();
|
||||
}
|
||||
}
|
||||
|
||||
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<Input<KeyCode>>,
|
||||
mut tcmd: Query<(Entity, &mut TurtleCommands)>,
|
||||
mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>,
|
||||
mut ev_start: EventWriter<DrawingStartedEvent>,
|
||||
) {
|
||||
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<Transform>, With<TurtleShape>>,
|
||||
mut query_event: EventReader<TweenCompleted>, // 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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,
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
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::{
|
||||
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: 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 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 {
|
||||
pub animator: bevy_tweening::Animator<bevy::prelude::Transform>,
|
||||
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 {}
|
||||
Reference in New Issue
Block a user