turtlers/src/turtle.rs

250 lines
7.7 KiB
Rust
Raw Normal View History

2022-08-08 14:23:56 +02:00
use std::time::Duration;
2022-08-06 08:42:08 +02:00
use bevy::prelude::*;
2022-08-06 11:45:35 +02:00
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
2022-08-08 14:23:56 +02:00
use bevy_prototype_lyon::{prelude::*, shapes::Line};
2022-08-06 08:42:08 +02:00
use bevy_tweening::{
2022-08-08 14:23:56 +02:00
lens::TransformPositionLens, Animator, EaseFunction, Sequence, Tween, TweenCompleted,
TweeningPlugin, TweeningType,
2022-08-06 08:42:08 +02:00
};
2022-08-08 14:23:56 +02:00
use crate::{turtle_movement::turtle_move, turtle_shapes};
2022-08-06 08:42:08 +02:00
pub struct TurtlePlugin;
impl Plugin for TurtlePlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_plugin(TweeningPlugin)
.add_startup_system(setup)
2022-08-06 10:28:12 +02:00
.add_system(keypresses)
2022-08-08 14:23:56 +02:00
.add_system(draw_lines)
2022-08-06 10:28:12 +02:00
.register_inspectable::<Colors>()
.register_inspectable::<TurtleCommands>();
2022-08-06 08:42:08 +02:00
}
}
#[derive(Bundle)]
pub struct Turtle {
colors: Colors,
commands: TurtleCommands,
2022-08-09 22:37:26 +02:00
name: Name,
2022-08-06 08:42:08 +02:00
}
impl Default for Turtle {
fn default() -> Self {
Self {
colors: Colors {
color: Color::DARK_GRAY,
fill_color: Color::BLACK,
},
2022-08-08 14:23:56 +02:00
commands: TurtleCommands::new(vec![
2022-08-06 08:42:08 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(90.)),
TurtleCommand::Backward(Length(100.)),
TurtleCommand::Right(Angle(90.)),
2022-08-06 10:28:12 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(45.)),
2022-08-06 10:28:12 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(90.)),
TurtleCommand::Forward(Length(50.)),
TurtleCommand::Right(Angle(90.)),
2022-08-06 10:28:12 +02:00
TurtleCommand::Forward(Length(100.)),
2022-08-08 14:23:56 +02:00
TurtleCommand::Right(Angle(90.)),
TurtleCommand::Forward(Length(50.)),
TurtleCommand::Right(Angle(45.)),
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Left(Angle(120.)),
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Left(Angle(120.)),
TurtleCommand::Forward(Length(100.)),
TurtleCommand::Right(Angle(150.)),
TurtleCommand::Forward(Length(100.)),
]),
2022-08-09 22:37:26 +02:00
name: Name::new("Turtle"),
2022-08-06 08:42:08 +02:00
}
}
}
impl Turtle {
pub fn set_color(&mut self, color: Color) {
self.colors.color = color;
}
pub fn set_fill_color(&mut self, color: Color) {
self.colors.fill_color = color;
}
pub fn get_colors(&self) -> &Colors {
&self.colors
}
pub fn forward(&mut self) -> &mut Self {
2022-08-08 14:23:56 +02:00
self.commands
.commands
.push(TurtleCommand::Forward(Length(100.0)));
2022-08-06 08:42:08 +02:00
self
}
}
2022-08-06 10:28:12 +02:00
#[derive(Component, Inspectable)]
2022-08-08 14:23:56 +02:00
pub struct TurtleCommands {
commands: Vec<TurtleCommand>,
lines: Vec<TurtleGraphElement>,
}
impl TurtleCommands {
fn new(commands: Vec<TurtleCommand>) -> Self {
Self {
commands,
lines: vec![],
}
}
}
2022-08-06 08:42:08 +02:00
impl TurtleCommands {
2022-08-08 14:23:56 +02:00
fn generate_tweenable(&mut self) -> Sequence<Transform> {
self.lines.clear();
let mut seq = Sequence::with_capacity(self.commands.len());
2022-08-06 10:28:12 +02:00
let mut pos = Vec2::ZERO;
let mut ori: f32 = 0.;
2022-08-08 14:23:56 +02:00
for (index, op) in self.commands.iter().enumerate() {
2022-08-06 08:42:08 +02:00
match op {
TurtleCommand::Forward(Length(x)) => {
2022-08-08 14:23:56 +02:00
let (target, animation, line) = turtle_move(pos, ori, *x as f32, index as f32);
self.lines.push(line);
seq = seq.then(animation);
pos = target;
}
TurtleCommand::Backward(Length(x)) => {
let (target, animation, line) = turtle_move(pos, ori, -*x as f32, index as f32);
self.lines.push(line);
seq = seq.then(animation);
pos = target;
2022-08-06 08:42:08 +02:00
}
TurtleCommand::Left(Angle(x)) => {
2022-08-08 14:23:56 +02:00
let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, *x as f32);
ori = nori;
seq = seq.then(tween);
self.lines.push(line);
}
TurtleCommand::Right(Angle(x)) => {
let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, -*x as f32);
ori = nori;
seq = seq.then(tween);
self.lines.push(line);
2022-08-06 08:42:08 +02:00
}
TurtleCommand::PenUp => todo!(),
TurtleCommand::PenDown => todo!(),
TurtleCommand::Circle => todo!(),
2022-08-06 10:28:12 +02:00
TurtleCommand::Pause => todo!(),
2022-08-06 08:42:08 +02:00
}
}
seq
}
}
2022-08-08 14:23:56 +02:00
#[derive(Inspectable, Default)]
pub enum TurtleGraphElement {
TurtleLine {
start: Vec2,
end: Vec2,
},
#[default]
Noop,
}
2022-08-06 10:28:12 +02:00
#[derive(Clone, Component, Inspectable)]
2022-08-06 08:42:08 +02:00
pub struct TurtleShape;
2022-08-06 10:28:12 +02:00
#[derive(Clone, Component, Inspectable)]
2022-08-06 08:42:08 +02:00
pub struct Colors {
color: Color,
fill_color: Color,
}
2022-08-06 10:28:12 +02:00
#[derive(Inspectable, Default)]
2022-08-06 08:42:08 +02:00
pub struct Length(f64);
2022-08-06 10:28:12 +02:00
#[derive(Inspectable, Default)]
2022-08-06 08:42:08 +02:00
pub struct Angle(f64);
2022-08-06 10:28:12 +02:00
#[derive(Component, Inspectable, Default)]
2022-08-08 14:23:56 +02:00
pub enum TurtleCommand {
2022-08-06 08:42:08 +02:00
Forward(Length),
Backward(Length),
Left(Angle),
Right(Angle),
PenUp,
PenDown,
2022-08-06 10:28:12 +02:00
#[default]
Pause,
2022-08-06 08:42:08 +02:00
Circle,
}
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_secs(1),
// 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.),
},
));
commands.spawn_bundle(Camera2dBundle::default());
commands
.spawn_bundle(Turtle::default())
.insert_bundle(GeometryBuilder::build_as(
&turtle_shapes::turtle(),
DrawMode::Outlined {
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
},
2022-08-06 10:28:12 +02:00
Transform::identity(),
2022-08-06 08:42:08 +02:00
))
2022-08-09 22:37:26 +02:00
.insert(animator)
.insert(TurtleShape);
2022-08-06 08:42:08 +02:00
}
2022-08-08 14:23:56 +02:00
fn draw_lines(
mut commands: Commands,
tcmd: Query<&TurtleCommands>,
2022-08-09 22:37:26 +02:00
mut query_event: EventReader<TweenCompleted>, // TODO: howto attach only to the right event?
2022-08-08 14:23:56 +02:00
) {
for ev in query_event.iter() {
let index = ev.user_data;
2022-08-09 22:37:26 +02:00
for t in tcmd.iter() {
let t = t.lines.get(index as usize).unwrap();
match t {
TurtleGraphElement::TurtleLine { start, end } => {
commands.spawn_bundle(GeometryBuilder::build_as(
&Line(*start, *end),
DrawMode::Outlined {
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
},
Transform::identity(),
));
}
TurtleGraphElement::Noop => (),
2022-08-08 14:23:56 +02:00
}
}
}
}
2022-08-06 08:42:08 +02:00
fn keypresses(
keys: Res<Input<KeyCode>>,
2022-08-09 22:37:26 +02:00
mut qry: Query<&mut Animator<Transform>, With<TurtleShape>>,
2022-08-08 14:23:56 +02:00
mut tcmd: Query<&mut TurtleCommands>,
2022-08-06 08:42:08 +02:00
) {
if keys.just_pressed(KeyCode::W) {
2022-08-08 14:23:56 +02:00
let mut tcmd = tcmd.single_mut();
2022-08-06 08:42:08 +02:00
let c = tcmd.generate_tweenable();
let mut shap = qry.single_mut();
shap.set_tweenable(c);
}
}