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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								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(),
 | 
			
		||||
            present_mode: bevy::window::PresentMode::AutoVsync,
 | 
			
		||||
        .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()
 | 
			
		||||
            }),
 | 
			
		||||
            ..default()
 | 
			
		||||
        })
 | 
			
		||||
        .add_plugins(DefaultPlugins)
 | 
			
		||||
        }))
 | 
			
		||||
        .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(
 | 
			
		||||
        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,
 | 
			
		||||
        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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user