first circle try
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_prototype_lyon::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(ShapePlugin)
|
||||
.add_startup_system(setup_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup_system(mut commands: Commands) {
|
||||
let mut path_builder = PathBuilder::new();
|
||||
path_builder.move_to(Vec2::new(100., 0.));
|
||||
path_builder.arc(
|
||||
100.0 * Vec2::ZERO,
|
||||
100.0 * Vec2::ONE,
|
||||
90f32.to_radians(),
|
||||
0.,
|
||||
);
|
||||
let line = path_builder.build();
|
||||
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
commands.spawn_bundle(GeometryBuilder::build_as(
|
||||
&line,
|
||||
DrawMode::Stroke(StrokeMode::new(Color::BLACK, 10.0)),
|
||||
Transform::default(),
|
||||
));
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
mod debug;
|
||||
mod primitives;
|
||||
mod turtle;
|
||||
mod turtle_movement;
|
||||
mod turtle_shapes;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
use bevy::prelude::{Bundle, Color, Component, Name, Transform, Vec2};
|
||||
use bevy_prototype_lyon::{
|
||||
entity::ShapeBundle,
|
||||
prelude::{DrawMode, FillMode, GeometryBuilder, Path, PathBuilder, ShapePath, StrokeMode},
|
||||
shapes::{self, Line},
|
||||
};
|
||||
use bevy_tweening::Lens;
|
||||
|
||||
use crate::turtle::Angle;
|
||||
|
||||
pub(crate) struct LineAnimationLens {
|
||||
start: Vec2,
|
||||
end: Vec2,
|
||||
}
|
||||
|
||||
impl LineAnimationLens {
|
||||
pub(crate) fn new(start: Vec2, end: Vec2) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
}
|
||||
|
||||
impl Lens<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);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
pub(crate) struct TurtleDrawLine {
|
||||
#[bundle]
|
||||
line: ShapeBundle,
|
||||
name: Name,
|
||||
marker: LineMarker,
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
struct LineMarker;
|
||||
|
||||
impl TurtleDrawLine {
|
||||
pub(crate) fn new(start: Vec2, _end: Vec2, index: u64) -> Self {
|
||||
Self {
|
||||
line: GeometryBuilder::build_as(
|
||||
&Line(start, start),
|
||||
DrawMode::Outlined {
|
||||
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
|
||||
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
|
||||
},
|
||||
Transform::identity(),
|
||||
),
|
||||
name: Name::new(format!("Line {}", index)),
|
||||
marker: LineMarker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CircleAnimationLens {
|
||||
start: Vec2,
|
||||
end: Vec2,
|
||||
}
|
||||
|
||||
impl Lens<Path> for CircleAnimationLens {
|
||||
fn lerp(&mut self, target: &mut Path, ratio: f32) {
|
||||
let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio));
|
||||
*target = ShapePath::build_as(&line);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
|
||||
pub(crate) struct TurtleDrawCircle {
|
||||
#[bundle]
|
||||
line: ShapeBundle,
|
||||
name: Name,
|
||||
marker: CircleMarker,
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
struct CircleMarker;
|
||||
|
||||
impl TurtleDrawCircle {
|
||||
pub(crate) fn new(
|
||||
center: Vec2,
|
||||
radii: Vec2,
|
||||
angle: Angle,
|
||||
index: u64,
|
||||
start: Vec2,
|
||||
end: Vec2,
|
||||
) -> Self {
|
||||
let mut path_builder = PathBuilder::new();
|
||||
path_builder.move_to(start);
|
||||
// The center point of the radius, then the radii in x and y direction, then the angle that will be drawn, then the x_rotation ?
|
||||
path_builder.arc(center, radii, angle.0.to_radians(), 0.);
|
||||
let line = path_builder.build();
|
||||
println!("Draw Circle: {} {} {:?}", center, radii, angle);
|
||||
|
||||
Self {
|
||||
line: GeometryBuilder::build_as(
|
||||
&line,
|
||||
DrawMode::Outlined {
|
||||
fill_mode: FillMode::color(Color::rgba(0., 0., 0., 0.)),
|
||||
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
|
||||
},
|
||||
Transform::identity(),
|
||||
),
|
||||
name: Name::new(format!("Circle {}", index)),
|
||||
marker: CircleMarker,
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
-98
@@ -2,13 +2,16 @@ use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
|
||||
use bevy_prototype_lyon::{entity::ShapeBundle, prelude::*, shapes::Line};
|
||||
use bevy_prototype_lyon::prelude::*;
|
||||
use bevy_tweening::{
|
||||
component_animator_system, lens::TransformPositionLens, Animator, EaseFunction, Lens, Tween,
|
||||
component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween,
|
||||
TweenCompleted, TweeningPlugin, TweeningType,
|
||||
};
|
||||
|
||||
use crate::turtle_shapes;
|
||||
use crate::{
|
||||
primitives::{LineAnimationLens, TurtleDrawCircle, TurtleDrawLine},
|
||||
turtle_shapes,
|
||||
};
|
||||
|
||||
pub struct TurtlePlugin;
|
||||
|
||||
@@ -39,12 +42,26 @@ impl Default for Turtle {
|
||||
},
|
||||
commands: TurtleCommands::new(vec![
|
||||
TurtleCommand::Forward(Length(100.)),
|
||||
TurtleCommand::Circle {
|
||||
radius: Length(150.),
|
||||
angle: Angle(180.),
|
||||
},
|
||||
TurtleCommand::Right(Angle(90.)),
|
||||
TurtleCommand::Circle {
|
||||
radius: Length(15.),
|
||||
angle: Angle(180.),
|
||||
},
|
||||
TurtleCommand::Backward(Length(100.)),
|
||||
TurtleCommand::Right(Angle(90.)),
|
||||
TurtleCommand::Forward(Length(100.)),
|
||||
TurtleCommand::Right(Angle(45.)),
|
||||
//TurtleCommand::PenUp,
|
||||
TurtleCommand::Forward(Length(100.)),
|
||||
//TurtleCommand::PenDown,
|
||||
TurtleCommand::Circle {
|
||||
radius: Length(-30.),
|
||||
angle: Angle(90.),
|
||||
},
|
||||
TurtleCommand::Right(Angle(90.)),
|
||||
TurtleCommand::Forward(Length(50.)),
|
||||
TurtleCommand::Right(Angle(90.)),
|
||||
@@ -94,6 +111,7 @@ pub struct TurtleState {
|
||||
pub start: Vec2,
|
||||
pub heading: f32,
|
||||
pub index: u64,
|
||||
pub drawing: bool,
|
||||
}
|
||||
|
||||
impl TurtleCommands {
|
||||
@@ -105,13 +123,14 @@ impl TurtleCommands {
|
||||
start: Vec2::ZERO,
|
||||
heading: 0f32.to_radians(),
|
||||
index: 0,
|
||||
drawing: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TurtleCommands {
|
||||
fn get_next(&mut self) -> Option<(Tween<Transform>, TurtleGraphElement)> {
|
||||
fn get_next(&mut self) -> Option<(Option<Tween<Transform>>, Option<TurtleGraphElement>)> {
|
||||
let index = self.state.index;
|
||||
let next_index = index + 1;
|
||||
|
||||
@@ -129,9 +148,17 @@ impl TurtleCommands {
|
||||
TurtleCommand::Right(Angle(x)) => {
|
||||
crate::turtle_movement::turtle_turn(&mut self.state, -*x as f32)
|
||||
}
|
||||
TurtleCommand::PenUp => todo!(),
|
||||
TurtleCommand::PenDown => todo!(),
|
||||
TurtleCommand::Circle => todo!(),
|
||||
TurtleCommand::PenUp => {
|
||||
self.state.drawing = false;
|
||||
(None, None)
|
||||
}
|
||||
TurtleCommand::PenDown => {
|
||||
self.state.drawing = true;
|
||||
(None, None)
|
||||
}
|
||||
TurtleCommand::Circle { radius, angle } => {
|
||||
crate::turtle_movement::turtle_circle(&mut self.state, radius.0 as f32, *angle)
|
||||
}
|
||||
TurtleCommand::Pause => todo!(),
|
||||
};
|
||||
self.state.index = next_index;
|
||||
@@ -148,6 +175,13 @@ pub enum TurtleGraphElement {
|
||||
start: Vec2,
|
||||
end: Vec2,
|
||||
},
|
||||
TurtleCircle {
|
||||
start: Vec2,
|
||||
end: Vec2,
|
||||
center: Vec2,
|
||||
radii: Vec2,
|
||||
angle: Angle,
|
||||
},
|
||||
#[default]
|
||||
Noop,
|
||||
}
|
||||
@@ -161,10 +195,10 @@ pub struct Colors {
|
||||
fill_color: Color,
|
||||
}
|
||||
|
||||
#[derive(Inspectable, Default)]
|
||||
pub struct Length(f64);
|
||||
#[derive(Inspectable, Default)]
|
||||
pub struct Angle(f64);
|
||||
#[derive(Inspectable, Default, Copy, Clone, Debug)]
|
||||
pub struct Length(f32);
|
||||
#[derive(Inspectable, Default, Copy, Clone, Debug)]
|
||||
pub struct Angle(pub f32);
|
||||
|
||||
#[derive(Component, Inspectable, Default)]
|
||||
pub enum TurtleCommand {
|
||||
@@ -176,24 +210,20 @@ pub enum TurtleCommand {
|
||||
PenDown,
|
||||
#[default]
|
||||
Pause,
|
||||
Circle,
|
||||
Circle {
|
||||
radius: Length,
|
||||
angle: Angle,
|
||||
},
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
let animator = Animator::new(Tween::new(
|
||||
// Use a quadratic easing on both endpoints.
|
||||
EaseFunction::QuadraticInOut,
|
||||
// Loop animation back and forth.
|
||||
TweeningType::PingPong,
|
||||
// Animation time (one way only; for ping-pong it takes 2 seconds
|
||||
// to come back to start).
|
||||
Duration::from_millis(500),
|
||||
// The lens gives access to the Transform component of the Entity,
|
||||
// for the Animator to animate it. It also contains the start and
|
||||
// end values respectively associated with the progress ratios 0. and 1.
|
||||
TransformPositionLens {
|
||||
start: Vec3::ZERO,
|
||||
end: Vec3::new(40., 40., 0.),
|
||||
TransformScaleLens {
|
||||
start: Vec3::new(1., 1., 0.),
|
||||
end: Vec3::new(1.3, 1.3, 0.),
|
||||
},
|
||||
));
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
@@ -211,81 +241,61 @@ fn setup(mut commands: Commands) {
|
||||
.insert(TurtleShape);
|
||||
}
|
||||
|
||||
struct MyCustomLens {
|
||||
start: Vec2,
|
||||
end: Vec2,
|
||||
}
|
||||
|
||||
impl Lens<Path> for MyCustomLens {
|
||||
fn lerp(&mut self, target: &mut Path, ratio: f32) {
|
||||
let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio));
|
||||
*target = ShapePath::build_as(&line);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
struct TurtleDrawLine {
|
||||
#[bundle]
|
||||
line: ShapeBundle,
|
||||
name: Name,
|
||||
marker: LineMarker,
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
struct LineMarker;
|
||||
|
||||
impl TurtleDrawLine {
|
||||
fn new(start: Vec2, _end: Vec2, index: u64) -> Self {
|
||||
Self {
|
||||
line: GeometryBuilder::build_as(
|
||||
&Line(start, start),
|
||||
DrawMode::Outlined {
|
||||
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
|
||||
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
|
||||
},
|
||||
Transform::identity(),
|
||||
),
|
||||
name: Name::new(format!("Line {}", index)),
|
||||
marker: LineMarker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
for ev in query_event.iter() {
|
||||
let mut tcmd = tcmd.single_mut();
|
||||
if let Some((turtle_animation, graph_element_to_draw)) = tcmd.get_next() {
|
||||
let mut turtle = turtle.single_mut();
|
||||
turtle.set_tweenable(turtle_animation);
|
||||
match graph_element_to_draw {
|
||||
TurtleGraphElement::TurtleLine { start, end } => {
|
||||
let line_animator = Animator::new(Tween::new(
|
||||
// Use a quadratic easing on both endpoints.
|
||||
EaseFunction::QuadraticInOut,
|
||||
// Loop animation back and forth.
|
||||
TweeningType::Once,
|
||||
// Animation time (one way only; for ping-pong it takes 2 seconds
|
||||
// to come back to start).
|
||||
Duration::from_millis(500),
|
||||
// The lens gives access to the Transform component of the Entity,
|
||||
// for the Animator to animate it. It also contains the start and
|
||||
// end values respectively associated with the progress ratios 0. and 1.
|
||||
MyCustomLens { start, end },
|
||||
));
|
||||
commands
|
||||
.spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index))
|
||||
.insert(line_animator);
|
||||
loop {
|
||||
match tcmd.get_next() {
|
||||
Some((Some(turtle_animation), Some(graph_element_to_draw))) => {
|
||||
let mut turtle = turtle.single_mut();
|
||||
turtle.set_tweenable(turtle_animation);
|
||||
match graph_element_to_draw {
|
||||
TurtleGraphElement::TurtleLine { start, end } => {
|
||||
let line_animator = Animator::new(Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
Duration::from_millis(500),
|
||||
LineAnimationLens::new(start, end),
|
||||
));
|
||||
commands
|
||||
.spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index))
|
||||
.insert(line_animator);
|
||||
}
|
||||
TurtleGraphElement::Noop => println!("No drawing!"),
|
||||
TurtleGraphElement::TurtleCircle {
|
||||
center,
|
||||
radii,
|
||||
angle,
|
||||
start,
|
||||
end,
|
||||
} => {
|
||||
// let circle_animator = todo!();
|
||||
commands.spawn_bundle(TurtleDrawCircle::new(
|
||||
center,
|
||||
radii,
|
||||
angle,
|
||||
tcmd.state.index,
|
||||
start,
|
||||
end,
|
||||
));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
TurtleGraphElement::Noop => println!("No drawing!"),
|
||||
}
|
||||
} else {
|
||||
println!("nothing to draw")
|
||||
};
|
||||
Some((_, _)) => {
|
||||
println!("without animation");
|
||||
}
|
||||
None => {
|
||||
println!("nothing to draw");
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,30 +311,40 @@ fn keypresses(
|
||||
start: Vec2::ZERO,
|
||||
heading: 0.,
|
||||
index: 0,
|
||||
drawing: true,
|
||||
};
|
||||
if let Some((turtle_animation, graph_element_to_draw)) = tcmd.get_next() {
|
||||
if let Some((Some(turtle_animation), Some(graph_element_to_draw))) = tcmd.get_next() {
|
||||
let mut shap = qry.single_mut();
|
||||
shap.set_tweenable(turtle_animation);
|
||||
match graph_element_to_draw {
|
||||
TurtleGraphElement::TurtleLine { start, end } => {
|
||||
let line_animator = Animator::new(Tween::new(
|
||||
// Use a quadratic easing on both endpoints.
|
||||
EaseFunction::QuadraticInOut,
|
||||
// Loop animation back and forth.
|
||||
TweeningType::Once,
|
||||
// Animation time (one way only; for ping-pong it takes 2 seconds
|
||||
// to come back to start).
|
||||
Duration::from_millis(500),
|
||||
// The lens gives access to the Transform component of the Entity,
|
||||
// for the Animator to animate it. It also contains the start and
|
||||
// end values respectively associated with the progress ratios 0. and 1.
|
||||
MyCustomLens { start, end },
|
||||
LineAnimationLens::new(start, end),
|
||||
));
|
||||
commands
|
||||
.spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index))
|
||||
.insert(line_animator);
|
||||
}
|
||||
TurtleGraphElement::Noop => (),
|
||||
TurtleGraphElement::TurtleCircle {
|
||||
center,
|
||||
radii,
|
||||
angle,
|
||||
start,
|
||||
end,
|
||||
} => {
|
||||
commands.spawn_bundle(TurtleDrawCircle::new(
|
||||
center,
|
||||
radii,
|
||||
angle,
|
||||
tcmd.state.index,
|
||||
start,
|
||||
end,
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+58
-14
@@ -6,40 +6,40 @@ use bevy_tweening::{
|
||||
EaseFunction, Tween, TweeningType,
|
||||
};
|
||||
|
||||
use crate::turtle::{TurtleGraphElement, TurtleState};
|
||||
use crate::turtle::{Angle, TurtleGraphElement, TurtleState};
|
||||
|
||||
pub fn turtle_turn(
|
||||
state: &mut TurtleState,
|
||||
angle_to_turn: f32,
|
||||
) -> (Tween<Transform>, TurtleGraphElement) {
|
||||
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
|
||||
let start = state.heading;
|
||||
let end = state.heading + (angle_to_turn * PI / 180.);
|
||||
let animation = Tween::new(
|
||||
// Use a quadratic easing on both endpoints.
|
||||
// Use a quadratic easing on both endpoints
|
||||
EaseFunction::QuadraticInOut,
|
||||
// Loop animation back and forth.
|
||||
TweeningType::Once,
|
||||
// Animation time (one way only; for ping-pong it takes 2 seconds
|
||||
// to come back to start).
|
||||
// Animation time
|
||||
Duration::from_millis(500),
|
||||
// The lens gives access to the Transform component of the Entity,
|
||||
// for the Animator to animate it. It also contains the start and
|
||||
// end values respectively associated with the progress ratios 0. and 1.
|
||||
// Rotate the turtle
|
||||
TransformRotateZLens { start, end },
|
||||
)
|
||||
.with_completed_event(state.index as u64);
|
||||
// Dont move and draw
|
||||
let line = TurtleGraphElement::Noop;
|
||||
// Update the state
|
||||
state.heading = end % (2. * PI);
|
||||
(animation, line)
|
||||
(Some(animation), Some(line))
|
||||
}
|
||||
|
||||
pub fn turtle_move(state: &mut TurtleState, length: f32) -> (Tween<Transform>, TurtleGraphElement) {
|
||||
pub fn turtle_move(
|
||||
state: &mut TurtleState,
|
||||
length: f32,
|
||||
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
|
||||
let start = state.start;
|
||||
let end = state.start + (Vec2::from_angle(state.heading) * length);
|
||||
let turtle_movement_animation = Tween::new(
|
||||
// accelerate and decelerate
|
||||
EaseFunction::QuadraticInOut,
|
||||
// Loop animation back and forth.
|
||||
TweeningType::Once,
|
||||
// later to be controlled by speed
|
||||
Duration::from_millis(500),
|
||||
@@ -50,7 +50,51 @@ pub fn turtle_move(state: &mut TurtleState, length: f32) -> (Tween<Transform>, T
|
||||
},
|
||||
)
|
||||
.with_completed_event(state.index as u64);
|
||||
let line = TurtleGraphElement::TurtleLine { start, end };
|
||||
// The line for animating and drawing
|
||||
let line = if state.drawing {
|
||||
TurtleGraphElement::TurtleLine { start, end }
|
||||
} else {
|
||||
TurtleGraphElement::Noop
|
||||
};
|
||||
state.start = end;
|
||||
(turtle_movement_animation, line)
|
||||
(Some(turtle_movement_animation), Some(line))
|
||||
}
|
||||
|
||||
pub fn turtle_circle(
|
||||
state: &mut TurtleState,
|
||||
radius: f32,
|
||||
angle: Angle,
|
||||
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
|
||||
let radius_tuple = Vec2::ONE * radius.abs();
|
||||
dbg!(state.start, radius_tuple);
|
||||
let center = state.start
|
||||
+ (Vec2::new(radius, 0.).rotate(Vec2::from_angle((state.heading + 90.).to_radians())));
|
||||
dbg!(center);
|
||||
let turtle_movement_animation = Tween::new(
|
||||
// accelerate and decelerate
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
// later to be controlled by speed
|
||||
Duration::from_millis(500),
|
||||
// set the start and end of the animation
|
||||
TransformPositionLens {
|
||||
start: center.extend(0.),
|
||||
end: center.extend(0.),
|
||||
},
|
||||
)
|
||||
.with_completed_event(state.index as u64);
|
||||
// The line for animating and drawing
|
||||
let line = if state.drawing {
|
||||
TurtleGraphElement::TurtleCircle {
|
||||
center,
|
||||
radii: radius_tuple,
|
||||
angle,
|
||||
start: state.start,
|
||||
end: state.start + 200.,
|
||||
}
|
||||
} else {
|
||||
TurtleGraphElement::Noop
|
||||
};
|
||||
// TODO update end_position : state.start = end;
|
||||
(Some(turtle_movement_animation), Some(line))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user