Add circle drawing

This commit is contained in:
Dietrich 2022-12-21 11:07:02 +01:00
parent 043e44b7f2
commit 75771e8556
5 changed files with 183 additions and 81 deletions

View File

@ -1,5 +1,7 @@
use std::ops::Neg;
use crate::{ use crate::{
commands::{DrawElement, TurtleSegment}, commands::{DrawElement, MoveCommand, TurtleSegment},
general::{angle::Angle, length::Length, Precision}, general::{angle::Angle, length::Length, Precision},
}; };
@ -82,3 +84,38 @@ pub trait Turnable: WithCommands {
} }
impl Turnable for TurtlePlan {} 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 {}

View File

@ -2,11 +2,11 @@ use bevy::prelude::Component;
use bevy_inspector_egui::Inspectable; use bevy_inspector_egui::Inspectable;
use crate::{ use crate::{
builders::WithCommands,
drawing::{ drawing::{
self,
animation::{ animation::{
draw_straight_segment, move_straight_segment, turtle_turn, ToAnimationSegment, draw_circle_segment, draw_straight_segment, move_straight_segment, turtle_turn,
TurtleAnimationSegment, ToAnimationSegment, TurtleAnimationSegment,
}, },
TurtleGraphElement, TurtleGraphElement,
}, },
@ -80,7 +80,9 @@ impl ToAnimationSegment for DrawElement {
DrawElement::Draw(e) => match e { DrawElement::Draw(e) => match e {
MoveCommand::Forward(length) => draw_straight_segment(state, length.0), MoveCommand::Forward(length) => draw_straight_segment(state, length.0),
MoveCommand::Backward(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!(), MoveCommand::Goto(coord) => todo!(),
}, },
DrawElement::Move(e) => match e { 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<TurtleSegment> {
&mut self.commands
}
fn get_commands(self) -> Vec<TurtleSegment> {
self.commands
}
}

View File

@ -1,18 +1,15 @@
mod circle_lens; mod circle_lens;
mod line_lens; mod line_lens;
use bevy::{ use bevy::prelude::{Quat, Transform, Vec2, Vec3};
prelude::{Quat, Transform, Vec2, Vec3},
render::render_resource::encase::rts_array::Length,
};
use bevy_prototype_lyon::prelude::Path; use bevy_prototype_lyon::prelude::Path;
use bevy_tweening::{ use bevy_tweening::{
lens::{TransformPositionLens, TransformRotateZLens}, lens::{TransformPositionLens, TransformRotateZLens},
Animator, EaseFunction, RepeatCount, RepeatStrategy, Tween, Animator, EaseFunction, Tween,
}; };
use crate::{ use crate::{
general::{angle::Angle, Coordinate, Precision}, general::{angle::Angle, length::Length, Coordinate, Precision},
state::TurtleState, 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<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 { struct MoveStraightLineAnimation {
start: Coordinate, _start: Coordinate,
end: Coordinate, _end: Coordinate,
line: TurtleDrawLine, line: TurtleDrawLine,
animation: Tween<Path>, animation: Tween<Path>,
} }
@ -95,7 +108,7 @@ struct MoveStraightLineAnimation {
impl MoveStraightLineAnimation { impl MoveStraightLineAnimation {
fn new( fn new(
state: &TurtleState, state: &TurtleState,
length: Precision, _length: Precision,
turtle_animation: &MoveStraightTurtleAnimation, turtle_animation: &MoveStraightTurtleAnimation,
) -> Self { ) -> Self {
let line = TurtleDrawLine::new(turtle_animation.start, turtle_animation.end); let line = TurtleDrawLine::new(turtle_animation.start, turtle_animation.end);
@ -107,8 +120,8 @@ impl MoveStraightLineAnimation {
/* .with_repeat_strategy(RepeatStrategy::MirroredRepeat) /* .with_repeat_strategy(RepeatStrategy::MirroredRepeat)
.with_repeat_count(RepeatCount::Infinite)*/; .with_repeat_count(RepeatCount::Infinite)*/;
Self { Self {
start: turtle_animation.start, _start: turtle_animation.start,
end: turtle_animation.end, _end: turtle_animation.end,
line, line,
animation: line_animation, animation: line_animation,
} }
@ -142,18 +155,69 @@ impl MoveStraightTurtleAnimation {
} }
} }
pub fn turtle_circle( struct MoveCircleLineAnimation {
state: &mut TurtleState, _start: Coordinate,
radius: Precision, _end: Coordinate,
angle: Angle<Precision>, line: TurtleDrawCircle,
) -> TurtleAnimationSegment { animation: Tween<Path>,
let radii = Vec2::ONE * radius.abs(); }
let left_right = Angle::degrees(if radius >= 0. { 90. } else { -90. });
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() let center = state.position()
+ (Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle( + (Vec2::new(radius.0.abs(), 0.).rotate(Vec2::from_angle(
((state.heading() + left_right).to_radians()).value(), ((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( let turtle_movement_animation = Tween::new(
EaseFunction::QuadraticInOut, EaseFunction::QuadraticInOut,
state.animation_duration(), state.animation_duration(),
@ -163,42 +227,16 @@ pub fn turtle_circle(
rotation: Quat::from_rotation_z(state.heading().to_radians().value()), rotation: Quat::from_rotation_z(state.heading().to_radians().value()),
scale: Vec3::ONE, scale: Vec3::ONE,
}, },
end: angle, end: if radius.0 > 0. { angle } else { -angle },
center, center,
}, },
) )
.with_completed_event(state.segment_index()); .with_completed_event(state.segment_index() as u64);
let end_pos = center Self {
+ Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle( _start: start,
(state.heading() + angle - left_right).to_radians().value(), end: end_pos,
)); end_heading,
let line = /* if state.drawing { */ animation: turtle_movement_animation,
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),
} }
} }

View File

@ -10,3 +10,10 @@ impl From<i16> for Length {
Self(Precision::from(i)) Self(Precision::from(i))
} }
} }
impl From<f32> for Length {
fn from(i: f32) -> Self {
#[allow(clippy::useless_conversion)]
Self(Precision::from(i))
}
}

View File

@ -7,9 +7,9 @@ use bevy_prototype_lyon::{
}; };
use crate::{ use crate::{
builders::{TurtlePlan, WithCommands}, builders::{CurvedMovement, DirectionalMovement, Turnable, TurtlePlan, WithCommands},
commands::{DrawElement, MoveCommand, TurtleCommands, TurtleSegment}, commands::{TurtleCommands, TurtleSegment},
general::{length::Length, Speed}, general::Speed,
shapes::{self, TurtleColors}, shapes::{self, TurtleColors},
}; };
@ -52,12 +52,6 @@ impl TurtleBundle {
} }
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) { pub fn set_speed(&mut self, speed: Speed) {
self.commands.set_speed(speed); self.commands.set_speed(speed);
} }
@ -83,3 +77,17 @@ impl DerefMut for AnimatedTurtle {
&mut self.turtle_bundle &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 DirectionalMovement for TurtleBundle {}
impl Turnable for TurtleBundle {}
impl CurvedMovement for TurtleBundle {}