experiment WIP
This commit is contained in:
		
							parent
							
								
									5f4e47f540
								
							
						
					
					
						commit
						03af02b4e6
					
				
							
								
								
									
										1055
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1055
									
								
								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.9", features = ["dynamic", "wayland"] } | ||||
| bevy-inspector-egui = "0.14" | ||||
| bevy_egui = "0.17" | ||||
| egui = "0.19" | ||||
| bevy_prototype_lyon = {version="0.7"} | ||||
| bevy_tweening = {version="0.6"} | ||||
| num-traits = "0.2" | ||||
| 
 | ||||
| # Enable a small amount of optimization in debug mode | ||||
|  | ||||
| @ -22,11 +22,11 @@ pub(crate) fn run_animation_step( | ||||
|                 turtle.set_tweenable(turtle_animation); | ||||
|                 match graph_element_to_draw { | ||||
|                     TurtleGraphElement::TurtleLine(line) => { | ||||
|                         commands.spawn_bundle(line).insert(line_animation); | ||||
|                         commands.spawn((line, line_animation)); | ||||
|                     } | ||||
|                     TurtleGraphElement::Noop => (), | ||||
|                     TurtleGraphElement::TurtleCircle(circle) => { | ||||
|                         commands.spawn_bundle(circle).insert(line_animation); | ||||
|                         commands.spawn((circle, line_animation)); | ||||
|                     } | ||||
|                 } | ||||
|                 return; | ||||
|  | ||||
| @ -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); | ||||
|                 }); | ||||
|         }); | ||||
| } | ||||
							
								
								
									
										114
									
								
								src/bin/bevy_egui_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/bin/bevy_egui_test.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| use bevy::{app::AppExit, prelude::*}; | ||||
| use bevy_egui::{egui, EguiContext, EguiPlugin}; | ||||
| use bevy_prototype_lyon::prelude::{ | ||||
|     DrawMode, FillMode, FillOptions, GeometryBuilder, Path, PathBuilder, ShapePlugin, StrokeMode, | ||||
| }; | ||||
| 
 | ||||
| #[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 { | ||||
|             window: WindowDescriptor { | ||||
|                 width: 700.0, | ||||
|                 height: 500.0, | ||||
|                 title: "Turtle Window".to_string(), | ||||
|                 present_mode: bevy::window::PresentMode::AutoVsync, | ||||
|                 //decorations: false,
 | ||||
|                 ..default() | ||||
|             }, | ||||
|             ..default() | ||||
|         })) | ||||
|         .add_plugin(EguiPlugin) | ||||
|         .add_plugin(ShapePlugin) | ||||
|         .init_resource::<OccupiedScreenSpace>() | ||||
|         .add_startup_system(setup_system) | ||||
|         .add_system(ui) | ||||
|         .add_system(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_context: ResMut<EguiContext>, | ||||
|     mut occupied_screen_space: ResMut<OccupiedScreenSpace>, | ||||
|     mut line: ResMut<Line>, | ||||
|     mut exit: EventWriter<AppExit>, | ||||
| ) { | ||||
|     let mut style = (*egui_context.ctx_mut().style()).clone(); | ||||
|     style.visuals.button_frame = false; | ||||
|     egui_context.ctx_mut().set_style(style); | ||||
|     occupied_screen_space.left = 0.0; | ||||
|     occupied_screen_space.right = egui::SidePanel::right("right_panel") | ||||
|         .resizable(false) | ||||
|         .show(egui_context.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_context.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(); | ||||
| 
 | ||||
|     commands.spawn(GeometryBuilder::build_as( | ||||
|         &line, | ||||
|         DrawMode::Outlined { | ||||
|             fill_mode: FillMode { | ||||
|                 options: FillOptions::default(), | ||||
|                 color: Default::default(), | ||||
|             }, | ||||
|             outline_mode: StrokeMode::new(Color::BLACK, 6.), | ||||
|         }, | ||||
|         Transform::IDENTITY, | ||||
|     )); | ||||
| } | ||||
| @ -1,30 +1,143 @@ | ||||
| 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 }) | ||||
|         .add_plugins(DefaultPlugins) | ||||
|         .add_plugin(ShapePlugin) | ||||
|         .add_plugin(TweeningPlugin) | ||||
|         .add_startup_system(setup_system) | ||||
|         .add_system(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); | ||||
|     commands | ||||
|         .spawn(GeometryBuilder::build_as( | ||||
|             &line, | ||||
|             DrawMode::Outlined { | ||||
|                 fill_mode: FillMode { | ||||
|                     options: FillOptions::default(), | ||||
|                     color: Default::default(), | ||||
|                 }, | ||||
|                 outline_mode: StrokeMode::new(Color::BLACK, 3.), | ||||
|             }, | ||||
|             Transform::default(), | ||||
|         )) | ||||
|         .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; | ||||
|  | ||||
| @ -177,3 +177,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); | ||||
| } | ||||
|  | ||||
							
								
								
									
										18
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								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::*; | ||||
| @ -14,14 +16,16 @@ fn main() { | ||||
|     App::new() | ||||
|         .insert_resource(Msaa { samples: 4 }) | ||||
|         .insert_resource(ClearColor(Color::BEIGE)) | ||||
|         .insert_resource(WindowDescriptor { | ||||
|             width: 500.0, | ||||
|             height: 500.0, | ||||
|             title: "Turtle Window".to_string(), | ||||
|             present_mode: bevy::window::PresentMode::AutoVsync, | ||||
|         .add_plugins(DefaultPlugins.set(WindowPlugin { | ||||
|             window: WindowDescriptor { | ||||
|                 width: 500.0, | ||||
|                 height: 500.0, | ||||
|                 title: "Turtle Window".to_string(), | ||||
|                 present_mode: bevy::window::PresentMode::AutoVsync, | ||||
|                 ..default() | ||||
|             }, | ||||
|             ..default() | ||||
|         }) | ||||
|         .add_plugins(DefaultPlugins) | ||||
|         })) | ||||
|         .add_plugin(ShapePlugin) | ||||
|         .add_plugin(debug::DebugPlugin) | ||||
|         .add_plugin(TurtlePlugin) | ||||
|  | ||||
| @ -15,7 +15,6 @@ use super::turtle_shapes; | ||||
| 
 | ||||
| #[derive(Bundle, Inspectable, Default)] | ||||
| pub struct TurtleDrawLine { | ||||
|     #[bundle] | ||||
|     #[inspectable(ignore)] | ||||
|     line: ShapeBundle, | ||||
|     name: Name, | ||||
| @ -34,7 +33,7 @@ impl TurtleDrawLine { | ||||
|                     fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), | ||||
|                     outline_mode: StrokeMode::new(Color::BLACK, 1.0), | ||||
|                 }, | ||||
|                 Transform::identity(), | ||||
|                 Transform::IDENTITY, | ||||
|             ), | ||||
|             name: Name::new(format!("Line {}", index)), | ||||
|             marker: LineMarker, | ||||
| @ -45,7 +44,6 @@ impl TurtleDrawLine { | ||||
| #[derive(Bundle, Inspectable, Default)] | ||||
| 
 | ||||
| pub struct TurtleDrawCircle { | ||||
|     #[bundle] | ||||
|     #[inspectable(ignore)] | ||||
|     line: ShapeBundle, | ||||
|     name: Name, | ||||
| @ -81,7 +79,7 @@ impl TurtleDrawCircle { | ||||
|                     fill_mode: FillMode::color(Color::rgba(0., 0., 0., 0.)), | ||||
|                     outline_mode: StrokeMode::new(Color::BLACK, 1.0), | ||||
|                 }, | ||||
|                 Transform::identity(), | ||||
|                 Transform::IDENTITY, | ||||
|             ), | ||||
|             name: Name::new(format!("Circle {}", index)), | ||||
|             marker: CircleMarker, | ||||
| @ -94,7 +92,6 @@ pub struct Turtle { | ||||
|     colors: Colors, | ||||
|     commands: TurtleCommands, | ||||
|     name: Name, | ||||
|     #[bundle] | ||||
|     shape: ShapeBundle, | ||||
| } | ||||
| 
 | ||||
| @ -110,7 +107,7 @@ impl Default for Turtle { | ||||
|                     fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), | ||||
|                     outline_mode: StrokeMode::new(Color::BLACK, 1.0), | ||||
|                 }, | ||||
|                 Transform::identity(), | ||||
|                 Transform::IDENTITY, | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
|  | ||||
							
								
								
									
										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, AngleUnit}, | ||||
|         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,18 @@ | ||||
| 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}, | ||||
| @ -104,6 +107,8 @@ impl TurtleCommands { | ||||
|                     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) | ||||
| @ -138,6 +143,8 @@ pub enum TurtleCommand { | ||||
|     Right(Angle<f32>), | ||||
|     PenUp, | ||||
|     PenDown, | ||||
|     BeginFill, | ||||
|     EndFill, | ||||
|     #[default] | ||||
|     Pause, | ||||
|     Circle { | ||||
| @ -147,22 +154,27 @@ pub enum TurtleCommand { | ||||
| } | ||||
| 
 | ||||
| fn setup(mut commands: Commands) { | ||||
|     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()); | ||||
|     let animator = Animator::new( | ||||
|         Tween::new( | ||||
|             EaseFunction::QuadraticInOut, | ||||
|             Duration::from_millis(500), | ||||
|             TransformScaleLens { | ||||
|                 start: Vec3::new(1., 1., 0.), | ||||
|                 end: Vec3::new(1.3, 1.3, 0.), | ||||
|             }, | ||||
|         ) | ||||
|         .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)); | ||||
| } | ||||
| 
 | ||||
| fn draw_lines( | ||||
| @ -188,7 +200,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!() | ||||
|     } | ||||
| } | ||||
| @ -4,7 +4,7 @@ use bevy::prelude::{Quat, Transform, Vec2, Vec3}; | ||||
| use bevy_prototype_lyon::prelude::Path; | ||||
| use bevy_tweening::{ | ||||
|     lens::{TransformPositionLens, TransformRotateZLens}, | ||||
|     Animator, EaseFunction, Tween, TweeningType, | ||||
|     Animator, EaseFunction, Tween, | ||||
| }; | ||||
| 
 | ||||
| use crate::{ | ||||
| @ -27,7 +27,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(), | ||||
| @ -51,7 +50,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 +64,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), | ||||
|     )); | ||||
| @ -92,7 +89,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 +119,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, | ||||
|  | ||||
							
								
								
									
										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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user