Compare commits
3 Commits
main
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
8fb0022280 | |||
9a551573cb | |||
03af02b4e6 |
2693
Cargo.lock
generated
2693
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@ -7,10 +7,12 @@ default-run = "turtlers"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.8", features = ["dynamic"] }
|
||||
bevy-inspector-egui = "0.12"
|
||||
bevy_prototype_lyon = "0.6"
|
||||
bevy_tweening = "0.5"
|
||||
bevy = { version = "0.11", features = ["dynamic_linking", "wayland"] }
|
||||
bevy-inspector-egui = "0.19"
|
||||
bevy_egui = "0.21"
|
||||
egui = "0.22"
|
||||
bevy_prototype_lyon = {version="0.9"}
|
||||
bevy_tweening = {version="0.8"}
|
||||
num-traits = "0.2"
|
||||
|
||||
# Enable a small amount of optimization in debug mode
|
||||
|
@ -1,4 +1,5 @@
|
||||
use bevy::prelude::{Commands, Query, Transform, With};
|
||||
use bevy::prelude::{Color, Commands, Query, Transform, With};
|
||||
use bevy_prototype_lyon::prelude::{Fill, Stroke};
|
||||
use bevy_tweening::Animator;
|
||||
|
||||
use crate::{
|
||||
@ -17,16 +18,20 @@ pub(crate) fn run_animation_step(
|
||||
turtle_animation: Some(turtle_animation),
|
||||
line_segment: Some(graph_element_to_draw),
|
||||
line_animation: Some(line_animation),
|
||||
fill,
|
||||
stroke,
|
||||
}) => {
|
||||
let mut turtle = turtle.single_mut();
|
||||
turtle.set_tweenable(turtle_animation);
|
||||
let fill = fill.unwrap_or(Fill::color(Color::MIDNIGHT_BLUE));
|
||||
let stroke = stroke.unwrap_or(Stroke::color(Color::BLACK));
|
||||
match graph_element_to_draw {
|
||||
TurtleGraphElement::TurtleLine(line) => {
|
||||
commands.spawn_bundle(line).insert(line_animation);
|
||||
commands.spawn((line, line_animation, fill, stroke));
|
||||
}
|
||||
TurtleGraphElement::Noop => (),
|
||||
TurtleGraphElement::TurtleCircle(circle) => {
|
||||
commands.spawn_bundle(circle).insert(line_animation);
|
||||
commands.spawn((circle, line_animation, fill, stroke));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -36,6 +41,7 @@ pub(crate) fn run_animation_step(
|
||||
turtle_animation: Some(turtle_animation),
|
||||
line_segment: Some(_),
|
||||
line_animation: None,
|
||||
..
|
||||
}) => {
|
||||
let mut turtle = turtle.single_mut();
|
||||
turtle.set_tweenable(turtle_animation);
|
||||
|
@ -1,143 +0,0 @@
|
||||
//! Create and play an animation defined by code that operates on the `Transform` component.
|
||||
|
||||
use std::f32::consts::{FRAC_PI_2, PI};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 1.0,
|
||||
})
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut animations: ResMut<Assets<AnimationClip>>,
|
||||
) {
|
||||
// Camera
|
||||
commands.spawn_bundle(Camera3dBundle {
|
||||
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
// The animation API uses the `Name` component to target entities
|
||||
let planet = Name::new("planet");
|
||||
let orbit_controller = Name::new("orbit_controller");
|
||||
let satellite = Name::new("satellite");
|
||||
|
||||
// Creating the animation
|
||||
let mut animation = AnimationClip::default();
|
||||
// A curve can modify a single part of a transform, here the translation
|
||||
animation.add_curve_to_path(
|
||||
EntityPath {
|
||||
parts: vec![planet.clone()],
|
||||
},
|
||||
VariableCurve {
|
||||
keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
|
||||
keyframes: Keyframes::Translation(vec![
|
||||
Vec3::new(1.0, 0.0, 1.0),
|
||||
Vec3::new(-1.0, 0.0, 1.0),
|
||||
Vec3::new(-1.0, 0.0, -1.0),
|
||||
Vec3::new(1.0, 0.0, -1.0),
|
||||
// in case seamless looping is wanted, the last keyframe should
|
||||
// be the same as the first one
|
||||
Vec3::new(1.0, 0.0, 1.0),
|
||||
]),
|
||||
},
|
||||
);
|
||||
// Or it can modify the rotation of the transform.
|
||||
// To find the entity to modify, the hierarchy will be traversed looking for
|
||||
// an entity with the right name at each level
|
||||
animation.add_curve_to_path(
|
||||
EntityPath {
|
||||
parts: vec![planet.clone(), orbit_controller.clone()],
|
||||
},
|
||||
VariableCurve {
|
||||
keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
|
||||
keyframes: Keyframes::Rotation(vec![
|
||||
Quat::from_axis_angle(Vec3::Y, 0.0),
|
||||
Quat::from_axis_angle(Vec3::Y, FRAC_PI_2),
|
||||
Quat::from_axis_angle(Vec3::Y, PI),
|
||||
Quat::from_axis_angle(Vec3::Y, 3.0 * FRAC_PI_2),
|
||||
Quat::from_axis_angle(Vec3::Y, 0.0),
|
||||
]),
|
||||
},
|
||||
);
|
||||
// If a curve in an animation is shorter than the other, it will not repeat
|
||||
// until all other curves are finished. In that case, another animation should
|
||||
// be created for each part that would have a different duration / period
|
||||
animation.add_curve_to_path(
|
||||
EntityPath {
|
||||
parts: vec![planet.clone(), orbit_controller.clone(), satellite.clone()],
|
||||
},
|
||||
VariableCurve {
|
||||
keyframe_timestamps: vec![0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0],
|
||||
keyframes: Keyframes::Scale(vec![
|
||||
Vec3::splat(0.8),
|
||||
Vec3::splat(1.2),
|
||||
Vec3::splat(0.8),
|
||||
Vec3::splat(1.2),
|
||||
Vec3::splat(0.8),
|
||||
Vec3::splat(1.2),
|
||||
Vec3::splat(0.8),
|
||||
Vec3::splat(1.2),
|
||||
Vec3::splat(0.8),
|
||||
]),
|
||||
},
|
||||
);
|
||||
// There can be more than one curve targeting the same entity path
|
||||
animation.add_curve_to_path(
|
||||
EntityPath {
|
||||
parts: vec![planet.clone(), orbit_controller.clone(), satellite.clone()],
|
||||
},
|
||||
VariableCurve {
|
||||
keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
|
||||
keyframes: Keyframes::Rotation(vec![
|
||||
Quat::from_axis_angle(Vec3::Y, 0.0),
|
||||
Quat::from_axis_angle(Vec3::Y, FRAC_PI_2),
|
||||
Quat::from_axis_angle(Vec3::Y, PI),
|
||||
Quat::from_axis_angle(Vec3::Y, 3.0 * FRAC_PI_2),
|
||||
Quat::from_axis_angle(Vec3::Y, 0.0),
|
||||
]),
|
||||
},
|
||||
);
|
||||
|
||||
// Create the animation player, and set it to repeat
|
||||
let mut player = AnimationPlayer::default();
|
||||
player.play(animations.add(animation)).repeat();
|
||||
|
||||
// Create the scene that will be animated
|
||||
// First entity is the planet
|
||||
commands
|
||||
.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Icosphere::default())),
|
||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||
..default()
|
||||
})
|
||||
// Add the Name component, and the animation player
|
||||
.insert_bundle((planet, player))
|
||||
.with_children(|p| {
|
||||
// This entity is just used for animation, but doesn't display anything
|
||||
p.spawn_bundle(SpatialBundle::default())
|
||||
// Add the Name component
|
||||
.insert(orbit_controller)
|
||||
.with_children(|p| {
|
||||
// The satellite, placed at a distance of the planet
|
||||
p.spawn_bundle(PbrBundle {
|
||||
transform: Transform::from_xyz(1.5, 0.0, 0.0),
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
|
||||
material: materials.add(Color::rgb(0.3, 0.9, 0.3).into()),
|
||||
..default()
|
||||
})
|
||||
// Add the Name component
|
||||
.insert(satellite);
|
||||
});
|
||||
});
|
||||
}
|
112
src/bin/bevy_egui_test.rs
Normal file
112
src/bin/bevy_egui_test.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use bevy::{app::AppExit, prelude::*};
|
||||
use bevy_egui::{egui, EguiContexts, EguiPlugin};
|
||||
use bevy_prototype_lyon::prelude::{
|
||||
Fill, GeometryBuilder, Path, PathBuilder, ShapeBundle, ShapePlugin, Stroke,
|
||||
};
|
||||
|
||||
#[derive(Default, Resource)]
|
||||
struct OccupiedScreenSpace {
|
||||
left: f32,
|
||||
top: f32,
|
||||
right: f32,
|
||||
bottom: f32,
|
||||
}
|
||||
|
||||
#[derive(Default, Resource)]
|
||||
struct Line {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
#[derive(Resource, Deref, DerefMut)]
|
||||
struct OriginalCameraTransform(Transform);
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
resolution: (800., 600.).into(),
|
||||
title: "Turtle Window".to_string(),
|
||||
present_mode: bevy::window::PresentMode::AutoVsync,
|
||||
//decorations: false,
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugins(EguiPlugin)
|
||||
.add_plugins(ShapePlugin)
|
||||
.init_resource::<OccupiedScreenSpace>()
|
||||
.add_systems(Startup, setup_system)
|
||||
.add_systems(Update, ui)
|
||||
.add_systems(Update, update_path)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn update_path(line: Res<Line>, mut path: Query<&mut Path>) {
|
||||
for mut path in path.iter_mut() {
|
||||
let mut path_builder = PathBuilder::new();
|
||||
path_builder.move_to(Vec2::new(line.x, line.y));
|
||||
path_builder.line_to(Vec2::new(100., 0.));
|
||||
*path = path_builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
fn ui(
|
||||
mut egui_contexts: EguiContexts,
|
||||
mut occupied_screen_space: ResMut<OccupiedScreenSpace>,
|
||||
mut line: ResMut<Line>,
|
||||
mut exit: EventWriter<AppExit>,
|
||||
) {
|
||||
let mut style = (*egui_contexts.ctx_mut().style()).clone();
|
||||
style.visuals.button_frame = false;
|
||||
egui_contexts.ctx_mut().set_style(style);
|
||||
occupied_screen_space.left = 0.0;
|
||||
occupied_screen_space.right = egui::SidePanel::right("right_panel")
|
||||
.resizable(false)
|
||||
.show(egui_contexts.ctx_mut(), |ui| {
|
||||
ui.add_space(7.);
|
||||
ui.menu_button("Menu", |ui| {
|
||||
if ui
|
||||
.add_sized([ui.available_width(), 30.], egui::Button::new("Exit"))
|
||||
.clicked()
|
||||
{
|
||||
exit.send(AppExit);
|
||||
}
|
||||
})
|
||||
})
|
||||
.response
|
||||
.rect
|
||||
.width();
|
||||
occupied_screen_space.top = 0.0;
|
||||
occupied_screen_space.bottom = egui::TopBottomPanel::bottom("bottom_panel")
|
||||
.resizable(false)
|
||||
.show(egui_contexts.ctx_mut(), |ui| {
|
||||
ui.add_sized(
|
||||
ui.available_size(),
|
||||
egui::Slider::new(&mut line.x, 0.0..=100.0),
|
||||
);
|
||||
})
|
||||
.response
|
||||
.rect
|
||||
.height();
|
||||
}
|
||||
|
||||
fn setup_system(mut commands: Commands) {
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
commands.insert_resource(Line { x: 100., y: 100. });
|
||||
let mut path_builder = PathBuilder::new();
|
||||
path_builder.move_to(Vec2::new(200., 200.));
|
||||
path_builder.line_to(Vec2::new(100., 0.));
|
||||
let line = path_builder.build();
|
||||
let fill = Fill::color(Color::MIDNIGHT_BLUE);
|
||||
let stroke = Stroke::color(Color::BLACK);
|
||||
|
||||
commands.spawn((
|
||||
ShapeBundle {
|
||||
path: GeometryBuilder::build_as(&line),
|
||||
..default()
|
||||
},
|
||||
fill,
|
||||
stroke,
|
||||
));
|
||||
}
|
@ -1,30 +1,142 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_prototype_lyon::prelude::*;
|
||||
use bevy_tweening::{
|
||||
component_animator_system, Animator, EaseFunction, Lens, Sequence, Tween, TweeningPlugin,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.insert_resource(Msaa::Sample4)
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(ShapePlugin)
|
||||
.add_startup_system(setup_system)
|
||||
.add_plugins(ShapePlugin)
|
||||
.add_plugins(TweeningPlugin)
|
||||
.add_systems(Startup, setup_system)
|
||||
.add_systems(Update, component_animator_system::<Path>)
|
||||
.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.,
|
||||
);
|
||||
path_builder.line_to(Vec2::new(100., 0.));
|
||||
let ops = vec![
|
||||
GElem::Circle {
|
||||
center: Vec2::ZERO,
|
||||
radii: Vec2::splat(100.),
|
||||
angle: 90.,
|
||||
},
|
||||
GElem::Line {
|
||||
start: Vec2::new(0., 100.),
|
||||
target: Vec2::splat(200.),
|
||||
},
|
||||
GElem::Line {
|
||||
start: Vec2::splat(200.),
|
||||
target: Vec2::new(100., 0.),
|
||||
},
|
||||
GElem::Circle {
|
||||
center: Vec2::ZERO,
|
||||
radii: Vec2::splat(100.),
|
||||
angle: -90.,
|
||||
},
|
||||
GElem::Line {
|
||||
start: Vec2::new(0., -100.),
|
||||
target: Vec2::new(200., -200.),
|
||||
},
|
||||
GElem::Line {
|
||||
start: Vec2::new(200., -200.),
|
||||
target: Vec2::new(100., 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(),
|
||||
));
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
let mut seq = Sequence::with_capacity(ops.len());
|
||||
for (step, op) in ops.clone().into_iter().enumerate() {
|
||||
let mut done = ops.clone();
|
||||
done.truncate(step);
|
||||
let next = Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
Duration::from_millis(1000),
|
||||
FilledAnimation { current: op, done },
|
||||
);
|
||||
seq = seq.then(next);
|
||||
}
|
||||
let animator = Animator::new(seq);
|
||||
let fill = Fill::color(Color::MIDNIGHT_BLUE);
|
||||
let stroke = Stroke::color(Color::BLACK);
|
||||
commands
|
||||
.spawn((
|
||||
ShapeBundle {
|
||||
path: GeometryBuilder::build_as(&line),
|
||||
..default()
|
||||
},
|
||||
fill,
|
||||
stroke,
|
||||
))
|
||||
.insert(animator);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum GElem {
|
||||
Circle {
|
||||
center: Vec2,
|
||||
radii: Vec2,
|
||||
angle: f32,
|
||||
},
|
||||
Line {
|
||||
start: Vec2,
|
||||
target: Vec2,
|
||||
},
|
||||
}
|
||||
|
||||
impl GElem {
|
||||
fn draw_to_builder(self, b: &mut PathBuilder) {
|
||||
match self {
|
||||
GElem::Circle {
|
||||
center,
|
||||
radii,
|
||||
angle,
|
||||
} => {
|
||||
b.arc(center, radii, angle.to_radians(), 0.);
|
||||
}
|
||||
GElem::Line { target, start: _ } => {
|
||||
b.line_to(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FilledAnimation {
|
||||
current: GElem,
|
||||
done: Vec<GElem>,
|
||||
}
|
||||
|
||||
impl Lens<Path> for FilledAnimation {
|
||||
fn lerp(&mut self, target: &mut Path, ratio: f32) {
|
||||
let mut path_builder = PathBuilder::new();
|
||||
path_builder.move_to(Vec2::new(100., 0.));
|
||||
for x in &self.done {
|
||||
x.draw_to_builder(&mut path_builder)
|
||||
}
|
||||
|
||||
let part = match self.current {
|
||||
GElem::Circle {
|
||||
center,
|
||||
radii,
|
||||
angle,
|
||||
} => GElem::Circle {
|
||||
center,
|
||||
radii,
|
||||
angle: angle * ratio,
|
||||
},
|
||||
GElem::Line { target, start } => GElem::Line {
|
||||
target: start + ((target - start) * ratio),
|
||||
start,
|
||||
},
|
||||
};
|
||||
part.draw_to_builder(&mut path_builder);
|
||||
*target = path_builder.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,8 @@
|
||||
use bevy::prelude::Vec2;
|
||||
|
||||
pub mod angle;
|
||||
pub mod length;
|
||||
|
||||
pub type Coordinate = Vec2;
|
||||
pub type Visibility = bool;
|
||||
pub type Speed = u32;
|
||||
|
@ -1,12 +1,14 @@
|
||||
use bevy_inspector_egui::Inspectable;
|
||||
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
ops::{Add, Div, Mul, Neg, Rem, Sub},
|
||||
};
|
||||
|
||||
use bevy::reflect::Reflect;
|
||||
|
||||
use crate::turtle::Precision;
|
||||
|
||||
#[derive(Inspectable, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Reflect, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum AngleUnit<T: Default> {
|
||||
Degrees(T),
|
||||
Radians(T),
|
||||
@ -18,7 +20,7 @@ impl<T: Default> Default for AngleUnit<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Inspectable, Copy, Default, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Reflect, Copy, Default, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Angle<T: Default> {
|
||||
value: AngleUnit<T>,
|
||||
}
|
||||
@ -177,3 +179,19 @@ fn convert_to_radians() {
|
||||
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);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use bevy_inspector_egui::Inspectable;
|
||||
use bevy::reflect::Reflect;
|
||||
|
||||
use crate::turtle::Precision;
|
||||
|
||||
#[derive(Inspectable, Default, Copy, Clone, Debug)]
|
||||
#[derive(Reflect, Default, Copy, Clone, Debug)]
|
||||
pub struct Length(pub Precision);
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bevy::prelude::Plugin;
|
||||
use bevy_inspector_egui::WorldInspectorPlugin;
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
|
||||
pub struct DebugPlugin;
|
||||
|
||||
|
17
src/main.rs
17
src/main.rs
@ -3,8 +3,10 @@ mod datatypes;
|
||||
mod debug;
|
||||
mod paths;
|
||||
mod primitives;
|
||||
mod structs;
|
||||
mod turtle;
|
||||
mod turtle_movement;
|
||||
mod turtle_state;
|
||||
use bevy::{prelude::*, window::close_on_esc};
|
||||
|
||||
use bevy_prototype_lyon::prelude::*;
|
||||
@ -12,16 +14,17 @@ use turtle::TurtlePlugin;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.insert_resource(Msaa::Sample4)
|
||||
.insert_resource(ClearColor(Color::BEIGE))
|
||||
.insert_resource(WindowDescriptor {
|
||||
width: 500.0,
|
||||
height: 500.0,
|
||||
title: "Turtle Window".to_string(),
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
resolution: (800.,600.).into(),
|
||||
//title: "Turtle Window".to_string(),
|
||||
present_mode: bevy::window::PresentMode::AutoVsync,
|
||||
..default()
|
||||
})
|
||||
.add_plugins(DefaultPlugins)
|
||||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(ShapePlugin)
|
||||
.add_plugin(debug::DebugPlugin)
|
||||
.add_plugin(TurtlePlugin)
|
||||
|
@ -1,8 +1,10 @@
|
||||
use bevy::prelude::{Bundle, Color, Component, Name, Transform, Vec2};
|
||||
use bevy_inspector_egui::Inspectable;
|
||||
use bevy::{
|
||||
prelude::{default, Bundle, Component, Name, Vec2},
|
||||
reflect::Reflect,
|
||||
};
|
||||
use bevy_prototype_lyon::{
|
||||
entity::ShapeBundle,
|
||||
prelude::{DrawMode, FillMode, GeometryBuilder, PathBuilder, StrokeMode},
|
||||
prelude::{Fill, GeometryBuilder, PathBuilder, Stroke},
|
||||
shapes::Line,
|
||||
};
|
||||
|
||||
@ -13,46 +15,41 @@ use crate::{
|
||||
|
||||
use super::turtle_shapes;
|
||||
|
||||
#[derive(Bundle, Inspectable, Default)]
|
||||
#[derive(Bundle, Reflect, Default)]
|
||||
pub struct TurtleDrawLine {
|
||||
#[bundle]
|
||||
#[inspectable(ignore)]
|
||||
#[reflect(ignore)]
|
||||
line: ShapeBundle,
|
||||
name: Name,
|
||||
marker: LineMarker,
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Inspectable)]
|
||||
#[derive(Component, Default, Reflect)]
|
||||
struct LineMarker;
|
||||
|
||||
impl TurtleDrawLine {
|
||||
pub(crate) fn new(start: Vec2, _end: Vec2, index: u64) -> Self {
|
||||
let bundle = ShapeBundle {
|
||||
path: GeometryBuilder::build_as(&Line(start, start)),
|
||||
..default()
|
||||
};
|
||||
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(),
|
||||
),
|
||||
line: bundle,
|
||||
name: Name::new(format!("Line {}", index)),
|
||||
marker: LineMarker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle, Inspectable, Default)]
|
||||
#[derive(Bundle, Reflect, Default)]
|
||||
|
||||
pub struct TurtleDrawCircle {
|
||||
#[bundle]
|
||||
#[inspectable(ignore)]
|
||||
#[reflect(ignore)]
|
||||
line: ShapeBundle,
|
||||
name: Name,
|
||||
marker: CircleMarker,
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Inspectable)]
|
||||
#[derive(Component, Default, Reflect)]
|
||||
struct CircleMarker;
|
||||
|
||||
impl TurtleDrawCircle {
|
||||
@ -74,15 +71,12 @@ impl TurtleDrawCircle {
|
||||
let line = path_builder.build();
|
||||
println!("Draw Circle: {} {} {:?}", center, radii, angle);
|
||||
|
||||
let bundle = ShapeBundle {
|
||||
path: GeometryBuilder::build_as(&line),
|
||||
..default()
|
||||
};
|
||||
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(),
|
||||
),
|
||||
line: bundle,
|
||||
name: Name::new(format!("Circle {}", index)),
|
||||
marker: CircleMarker,
|
||||
}
|
||||
@ -94,24 +88,20 @@ pub struct Turtle {
|
||||
colors: Colors,
|
||||
commands: TurtleCommands,
|
||||
name: Name,
|
||||
#[bundle]
|
||||
shape: ShapeBundle,
|
||||
}
|
||||
|
||||
impl Default for Turtle {
|
||||
fn default() -> Self {
|
||||
let bundle = ShapeBundle {
|
||||
path: GeometryBuilder::build_as(&turtle_shapes::turtle()),
|
||||
..default()
|
||||
};
|
||||
Self {
|
||||
colors: Colors::default(),
|
||||
commands: TurtleCommands::new(vec![]),
|
||||
name: Name::new("Turtle"),
|
||||
shape: GeometryBuilder::build_as(
|
||||
&turtle_shapes::turtle(),
|
||||
DrawMode::Outlined {
|
||||
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
|
||||
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
|
||||
},
|
||||
Transform::identity(),
|
||||
),
|
||||
shape: bundle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
69
src/structs.rs
Normal file
69
src/structs.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use bevy_prototype_lyon::prelude::PathBuilder;
|
||||
|
||||
use crate::{
|
||||
datatypes::{
|
||||
angle::Angle,
|
||||
length::Length,
|
||||
Coordinate,
|
||||
},
|
||||
turtle::Precision,
|
||||
};
|
||||
|
||||
/**
|
||||
* All the possibilities to draw something with turtle. All the commands can get the position, heading,
|
||||
* color and fill_color from the turtles state.
|
||||
*/
|
||||
pub enum MoveCommand {
|
||||
Forward(Length),
|
||||
Backward(Length),
|
||||
Circle { radius: Length, angle: Angle<f32> },
|
||||
Goto(Coordinate),
|
||||
Home,
|
||||
}
|
||||
|
||||
/// Different ways to drop breadcrumbs on the way like a dot or a stamp of the turtles shape.
|
||||
|
||||
pub enum Breadcrumb {
|
||||
Dot,
|
||||
Stamp,
|
||||
}
|
||||
|
||||
/// Different ways that change the orientation of the turtle.
|
||||
pub enum OrientationCommand {
|
||||
Left(Angle<Precision>),
|
||||
Right(Angle<Precision>),
|
||||
SetHeading,
|
||||
LookAt(Coordinate),
|
||||
}
|
||||
|
||||
/// A combination of all commands that can be used while drawing.
|
||||
pub enum DrawElement {
|
||||
Draw(MoveCommand),
|
||||
Move(MoveCommand),
|
||||
Orient(OrientationCommand),
|
||||
Drip(Breadcrumb),
|
||||
}
|
||||
|
||||
pub enum DrawingSegment {
|
||||
Single(DrawElement),
|
||||
Outline(Vec<DrawElement>),
|
||||
Filled(Vec<DrawElement>),
|
||||
}
|
||||
|
||||
impl DrawingSegment {
|
||||
pub fn draw(&self) {
|
||||
match self {
|
||||
DrawingSegment::Single(elem) => {
|
||||
let mut path_builder = PathBuilder::new();
|
||||
}
|
||||
DrawingSegment::Outline(_) => todo!(),
|
||||
DrawingSegment::Filled(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum DrawState {
|
||||
PenDown,
|
||||
PenUp,
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
pub mod builders;
|
||||
pub mod state;
|
||||
pub mod turtle;
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
|
||||
use bevy_prototype_lyon::prelude::*;
|
||||
use bevy_tweening::{
|
||||
component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, Tween,
|
||||
TweenCompleted, TweeningPlugin, TweeningType,
|
||||
component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, RepeatCount,
|
||||
Tween, TweenCompleted, TweeningPlugin,
|
||||
};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::paths::{circle_star::circle_star, geometry_task::geometry_task};
|
||||
use crate::paths;
|
||||
use crate::{
|
||||
animation_step::run_animation_step,
|
||||
datatypes::{angle::Angle, length::Length},
|
||||
@ -26,19 +28,19 @@ impl Plugin for TurtlePlugin {
|
||||
.add_system(keypresses)
|
||||
.add_system(draw_lines)
|
||||
.add_system(component_animator_system::<Path>)
|
||||
.register_inspectable::<Colors>()
|
||||
.register_inspectable::<TurtleCommands>();
|
||||
.register_type::<Colors>()
|
||||
.register_type::<TurtleCommands>();
|
||||
}
|
||||
}
|
||||
pub type Precision = f32;
|
||||
|
||||
#[derive(Component, Inspectable)]
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct TurtleCommands {
|
||||
commands: Vec<TurtleCommand>,
|
||||
lines: Vec<TurtleGraphElement>,
|
||||
state: TurtleState,
|
||||
}
|
||||
#[derive(Inspectable)]
|
||||
#[derive(Reflect)]
|
||||
pub struct TurtleState {
|
||||
pub start: Vec2,
|
||||
pub heading: Angle<f32>,
|
||||
@ -89,6 +91,8 @@ impl TurtleCommands {
|
||||
turtle_animation: None,
|
||||
line_segment: None,
|
||||
line_animation: None,
|
||||
fill: None,
|
||||
stroke: None,
|
||||
}
|
||||
}
|
||||
TurtleCommand::PenDown => {
|
||||
@ -98,12 +102,16 @@ impl TurtleCommands {
|
||||
turtle_animation: None,
|
||||
line_segment: None,
|
||||
line_animation: None,
|
||||
fill: None,
|
||||
stroke: None,
|
||||
}
|
||||
}
|
||||
TurtleCommand::Circle { radius, angle } => {
|
||||
crate::turtle_movement::turtle_circle(&mut self.state, radius.0 as f32, *angle)
|
||||
}
|
||||
TurtleCommand::Pause => todo!(),
|
||||
TurtleCommand::BeginFill => todo!(),
|
||||
TurtleCommand::EndFill => todo!(),
|
||||
};
|
||||
self.state.index = next_index;
|
||||
Some(res)
|
||||
@ -113,7 +121,7 @@ impl TurtleCommands {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Inspectable, Default)]
|
||||
#[derive(Reflect, Default)]
|
||||
pub enum TurtleGraphElement {
|
||||
TurtleLine(TurtleDrawLine),
|
||||
TurtleCircle(TurtleDrawCircle),
|
||||
@ -121,16 +129,16 @@ pub enum TurtleGraphElement {
|
||||
Noop,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component, Inspectable)]
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct TurtleShape;
|
||||
|
||||
#[derive(Clone, Component, Inspectable, Default)]
|
||||
#[derive(Clone, Component, Reflect, Default)]
|
||||
pub struct Colors {
|
||||
color: Color,
|
||||
fill_color: Color,
|
||||
}
|
||||
|
||||
#[derive(Component, Inspectable, Default)]
|
||||
#[derive(Component, Reflect, Default)]
|
||||
pub enum TurtleCommand {
|
||||
Forward(Length),
|
||||
Backward(Length),
|
||||
@ -138,6 +146,8 @@ pub enum TurtleCommand {
|
||||
Right(Angle<f32>),
|
||||
PenUp,
|
||||
PenDown,
|
||||
BeginFill,
|
||||
EndFill,
|
||||
#[default]
|
||||
Pause,
|
||||
Circle {
|
||||
@ -147,22 +157,33 @@ pub enum TurtleCommand {
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
let animator = Animator::new(Tween::new(
|
||||
let animator = Animator::new(
|
||||
Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::PingPong,
|
||||
Duration::from_millis(500),
|
||||
TransformScaleLens {
|
||||
start: Vec3::new(1., 1., 0.),
|
||||
end: Vec3::new(1.3, 1.3, 0.),
|
||||
},
|
||||
));
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
)
|
||||
.with_repeat_strategy(bevy_tweening::RepeatStrategy::MirroredRepeat)
|
||||
.with_repeat_count(RepeatCount::Infinite),
|
||||
);
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
let mut turtle_bundle = Turtle::default();
|
||||
turtle_bundle.set_commands(circle_star());
|
||||
commands
|
||||
.spawn_bundle(turtle_bundle)
|
||||
.insert(animator)
|
||||
.insert(TurtleShape);
|
||||
let mut tcommands = vec![];
|
||||
//for _ in 0..100 {
|
||||
tcommands.append(&mut paths::geometry_task::geometry_task());
|
||||
//tcommands.append(&mut paths::circle_star::circle_star());
|
||||
//}
|
||||
turtle_bundle.set_commands(tcommands);
|
||||
commands.spawn((
|
||||
turtle_bundle,
|
||||
animator,
|
||||
TurtleShape,
|
||||
Fill::color(Color::MIDNIGHT_BLUE),
|
||||
Stroke::color(Color::BLACK),
|
||||
));
|
||||
}
|
||||
|
||||
fn draw_lines(
|
||||
@ -171,7 +192,7 @@ fn draw_lines(
|
||||
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();
|
||||
run_animation_step(&mut commands, &mut tcmd, &mut turtle)
|
||||
}
|
||||
@ -188,7 +209,7 @@ fn keypresses(
|
||||
tcmd.state = TurtleState {
|
||||
start: Vec2::ZERO,
|
||||
heading: Angle::degrees(0.),
|
||||
speed: 500,
|
||||
speed: 1,
|
||||
index: 0,
|
||||
drawing: true,
|
||||
};
|
||||
|
15
src/turtle/builders.rs
Normal file
15
src/turtle/builders.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use bevy_prototype_lyon::prelude::PathBuilder;
|
||||
|
||||
use super::state::TurtleState;
|
||||
|
||||
/// A turtle that combines its commands to one closed shape with the `close()` command.
|
||||
pub struct ShapeBuilder {
|
||||
state: TurtleState,
|
||||
builder: PathBuilder,
|
||||
}
|
||||
|
||||
/// A turtle that draws a filled shape. End the filling process with the `end()` command.
|
||||
pub struct FilledBuilder {
|
||||
state: TurtleState,
|
||||
builder: PathBuilder,
|
||||
}
|
28
src/turtle/state.rs
Normal file
28
src/turtle/state.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use bevy::prelude::{Color, Transform};
|
||||
|
||||
use crate::{
|
||||
datatypes::{angle::Angle, Coordinate, Speed, Visibility},
|
||||
structs::{DrawState, DrawingSegment},
|
||||
turtle::Precision,
|
||||
};
|
||||
|
||||
/// Describing the full state of a turtle.
|
||||
pub struct TurtleState {
|
||||
drawing: Vec<DrawingSegment>,
|
||||
position: Coordinate,
|
||||
heading: Angle<Precision>,
|
||||
color: Color,
|
||||
draw_state: DrawState,
|
||||
visible: Visibility,
|
||||
shape_transform: Transform,
|
||||
speed: Speed,
|
||||
}
|
||||
|
||||
impl TurtleState {
|
||||
pub fn add_segment(&mut self, seg: DrawingSegment) {
|
||||
self.drawing.push(seg);
|
||||
}
|
||||
pub fn drawing(&self) -> bool {
|
||||
self.draw_state == DrawState::PenDown
|
||||
}
|
||||
}
|
72
src/turtle/turtle.rs
Normal file
72
src/turtle/turtle.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use bevy::prelude::{Color, Component};
|
||||
|
||||
use crate::{
|
||||
structs::{DrawElement, DrawingSegment, MoveCommand},
|
||||
turtle_state::TurtleDraw,
|
||||
};
|
||||
|
||||
use super::state::TurtleState;
|
||||
|
||||
/// A default turtle drawing lines each line is one Segment.
|
||||
#[derive(Component)]
|
||||
pub struct Turtle {
|
||||
state: TurtleState,
|
||||
}
|
||||
|
||||
pub struct FilledTurtle {
|
||||
state: TurtleState,
|
||||
drawing: Vec<DrawingSegment>,
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl Turtle {
|
||||
pub fn begin_fill(self, color: Color) -> FilledTurtle {
|
||||
FilledTurtle {
|
||||
state: self.state,
|
||||
drawing: vec![],
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TurtleDraw for Turtle {
|
||||
fn forward(&mut self, length: crate::datatypes::length::Length) -> &mut Self {
|
||||
let move_command = MoveCommand::Forward(length);
|
||||
self.state
|
||||
.add_segment(DrawingSegment::Single(if self.state.drawing() {
|
||||
DrawElement::Draw(move_command)
|
||||
} else {
|
||||
DrawElement::Move(move_command)
|
||||
}));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn backward(&mut self, length: crate::datatypes::length::Length) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn circle(
|
||||
&mut self,
|
||||
radius: crate::datatypes::length::Length,
|
||||
angle: crate::datatypes::angle::Angle<super::Precision>,
|
||||
) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn goto(&mut self, coordinate: crate::datatypes::Coordinate) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn home(&mut self) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn dot(&mut self, size: crate::datatypes::length::Length) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn stamp(&mut self, size: crate::datatypes::length::Length) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::{Quat, Transform, Vec2, Vec3};
|
||||
use bevy_prototype_lyon::prelude::Path;
|
||||
use bevy::prelude::{default, Color, Quat, Transform, Vec2, Vec3};
|
||||
use bevy_prototype_lyon::prelude::{Fill, Path, Stroke};
|
||||
use bevy_tweening::{
|
||||
lens::{TransformPositionLens, TransformRotateZLens},
|
||||
Animator, EaseFunction, Tween, TweeningType,
|
||||
Animator, EaseFunction, Tween,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -20,6 +20,8 @@ pub struct TurtleStep {
|
||||
pub turtle_animation: Option<Tween<Transform>>,
|
||||
pub line_segment: Option<TurtleGraphElement>,
|
||||
pub line_animation: Option<Animator<Path>>,
|
||||
pub fill: Option<Fill>,
|
||||
pub stroke: Option<Stroke>,
|
||||
}
|
||||
|
||||
pub fn turtle_turn(state: &mut TurtleState, angle_to_turn: Angle<Precision>) -> TurtleStep {
|
||||
@ -27,7 +29,6 @@ pub fn turtle_turn(state: &mut TurtleState, angle_to_turn: Angle<Precision>) ->
|
||||
let end = state.heading + angle_to_turn;
|
||||
let animation = Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
Duration::from_millis(state.speed),
|
||||
TransformRotateZLens {
|
||||
start: start.to_radians().value(),
|
||||
@ -43,6 +44,8 @@ pub fn turtle_turn(state: &mut TurtleState, angle_to_turn: Angle<Precision>) ->
|
||||
turtle_animation: Some(animation),
|
||||
line_segment: Some(line),
|
||||
line_animation: None,
|
||||
fill: None,
|
||||
stroke: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +54,6 @@ pub fn turtle_move(state: &mut TurtleState, length: Precision) -> TurtleStep {
|
||||
let end = state.start + (Vec2::from_angle(state.heading.to_radians().value()) * length);
|
||||
let turtle_movement_animation = Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
Duration::from_millis(state.speed),
|
||||
TransformPositionLens {
|
||||
start: start.extend(0.),
|
||||
@ -66,7 +68,6 @@ pub fn turtle_move(state: &mut TurtleState, length: Precision) -> TurtleStep {
|
||||
};
|
||||
let line_animator = Animator::new(Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
Duration::from_millis(state.speed),
|
||||
LineAnimationLens::new(start, end),
|
||||
));
|
||||
@ -75,6 +76,8 @@ pub fn turtle_move(state: &mut TurtleState, length: Precision) -> TurtleStep {
|
||||
turtle_animation: Some(turtle_movement_animation),
|
||||
line_segment: Some(line),
|
||||
line_animation: Some(line_animator),
|
||||
fill: Some(Fill::color(Color::MIDNIGHT_BLUE)),
|
||||
stroke: Some(Stroke::color(Color::BLACK)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +95,6 @@ pub fn turtle_circle(
|
||||
|
||||
let turtle_movement_animation = Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
Duration::from_millis(state.speed),
|
||||
CircleMovementLens {
|
||||
start: Transform {
|
||||
@ -123,7 +125,6 @@ pub fn turtle_circle(
|
||||
};
|
||||
let line_animator = Animator::new(Tween::new(
|
||||
EaseFunction::QuadraticInOut,
|
||||
TweeningType::Once,
|
||||
Duration::from_millis(state.speed),
|
||||
CircleAnimationLens {
|
||||
start_pos: state.start,
|
||||
@ -139,5 +140,7 @@ pub fn turtle_circle(
|
||||
turtle_animation: Some(turtle_movement_animation),
|
||||
line_segment: Some(line),
|
||||
line_animation: Some(line_animator),
|
||||
fill: Some(Fill::color(Color::MIDNIGHT_BLUE)),
|
||||
stroke: Some(Stroke::color(Color::BLACK)),
|
||||
}
|
||||
}
|
||||
|
61
src/turtle_state.rs
Normal file
61
src/turtle_state.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use bevy::prelude::{Color, Transform};
|
||||
use bevy_prototype_lyon::prelude::PathBuilder;
|
||||
|
||||
use crate::{
|
||||
datatypes::{angle::Angle, length::Length, Coordinate, Speed, Visibility},
|
||||
structs::DrawingSegment,
|
||||
turtle::Precision,
|
||||
};
|
||||
|
||||
/// Describing the full state of a turtle.
|
||||
pub struct TurtleState {
|
||||
drawing: Vec<DrawingSegment>,
|
||||
position: Coordinate,
|
||||
heading: Angle<Precision>,
|
||||
color: Color,
|
||||
fill_color: Color,
|
||||
visible: Visibility,
|
||||
shape_transform: Transform,
|
||||
speed: Speed,
|
||||
}
|
||||
|
||||
/// A default turtle drawing lines each line is one Segment.
|
||||
pub struct Turtle {
|
||||
state: TurtleState,
|
||||
}
|
||||
|
||||
/// A turtle that combines its commands to one closed shape with the `close()` command.
|
||||
pub struct ShapeBuilder {
|
||||
state: TurtleState,
|
||||
builder: PathBuilder,
|
||||
}
|
||||
|
||||
/// A turtle that draws a filled shape. End the filling process with the `end()` command.
|
||||
pub struct FilledBuilder {
|
||||
state: TurtleState,
|
||||
builder: PathBuilder,
|
||||
}
|
||||
|
||||
pub trait TurtleDraw {
|
||||
fn forward(&mut self, length: Length) -> &mut Self;
|
||||
fn backward(&mut self, length: Length) -> &mut Self;
|
||||
fn circle(&mut self, radius: Length, angle: Angle<Precision>) -> &mut Self;
|
||||
fn goto(&mut self, coordinate: Coordinate) -> &mut Self;
|
||||
fn home(&mut self) -> &mut Self;
|
||||
fn dot(&mut self, size: Length) -> &mut Self;
|
||||
fn stamp(&mut self, size: Length) -> &mut Self;
|
||||
}
|
||||
|
||||
pub trait TurtleTurn {
|
||||
fn left(&mut self, angle: Angle<Precision>) -> &mut Self;
|
||||
fn right(&mut self, angle: Angle<Precision>) -> &mut Self;
|
||||
fn look_at(&mut self, coordinate: Coordinate) -> &mut Self;
|
||||
}
|
||||
|
||||
pub trait TurtleHistory {
|
||||
fn reset(&mut self) -> &mut Self;
|
||||
fn clear(&mut self) -> &mut Self;
|
||||
fn undo(&mut self) -> &mut Self;
|
||||
|
||||
fn clear_stamps(&mut self) -> &mut Self;
|
||||
}
|
Loading…
Reference in New Issue
Block a user