unify Left and Right to turn and forward backward to move

This commit is contained in:
Franz Dietrich 2025-10-09 13:35:10 +02:00
parent 7da0dcf141
commit cebad0459c
5 changed files with 21 additions and 67 deletions

View File

@ -17,7 +17,7 @@ pub trait DirectionalMovement: WithCommands {
T: Into<Precision>,
{
let dist: Precision = distance.into();
self.get_commands_mut().push(TurtleCommand::Forward(dist));
self.get_commands_mut().push(TurtleCommand::Move(dist));
self
}
@ -26,7 +26,7 @@ pub trait DirectionalMovement: WithCommands {
T: Into<Precision>,
{
let dist: Precision = distance.into();
self.get_commands_mut().push(TurtleCommand::Backward(dist));
self.get_commands_mut().push(TurtleCommand::Move(-dist));
self
}
}
@ -38,7 +38,7 @@ pub trait Turnable: WithCommands {
T: Into<Precision>,
{
let degrees: Precision = angle.into();
self.get_commands_mut().push(TurtleCommand::Left(degrees));
self.get_commands_mut().push(TurtleCommand::Turn(-degrees));
self
}
@ -47,7 +47,7 @@ pub trait Turnable: WithCommands {
T: Into<Precision>,
{
let degrees: Precision = angle.into();
self.get_commands_mut().push(TurtleCommand::Right(degrees));
self.get_commands_mut().push(TurtleCommand::Turn(degrees));
self
}
}

View File

@ -6,13 +6,11 @@ use crate::shapes::TurtleShape;
/// Individual turtle commands
#[derive(Clone, Debug)]
pub enum TurtleCommand {
// Movement
Forward(Precision),
Backward(Precision),
// Movement (positive = forward, negative = backward)
Move(Precision),
// Rotation
Left(Precision), // degrees
Right(Precision), // degrees
// Rotation (positive = right/clockwise, negative = left/counter-clockwise in degrees)
Turn(Precision),
// Circle drawing
Circle {

View File

@ -187,10 +187,7 @@ pub(crate) fn render_world_with_tween(
fn should_draw_tween_line(command: &crate::commands::TurtleCommand) -> bool {
use crate::commands::TurtleCommand;
matches!(
command,
TurtleCommand::Forward(..) | TurtleCommand::Backward(..) | TurtleCommand::Goto(..)
)
matches!(command, TurtleCommand::Move(..) | TurtleCommand::Goto(..))
}
/// Draw arc segments for circle tween animation

View File

@ -8,7 +8,7 @@ use macroquad::prelude::*;
/// Execute a single turtle command, updating state and adding draw commands
pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world: &mut TurtleWorld) {
match command {
TurtleCommand::Forward(distance) => {
TurtleCommand::Move(distance) => {
let start = state.position;
let dx = distance * state.heading.cos();
let dy = distance * state.heading.sin();
@ -31,34 +31,7 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
}
}
TurtleCommand::Backward(distance) => {
let start = state.position;
let dx = -distance * state.heading.cos();
let dy = -distance * state.heading.sin();
state.position = vec2(state.position.x + dx, state.position.y + dy);
if state.pen_down {
world.add_command(DrawCommand::Line {
start,
end: state.position,
color: state.color,
width: state.pen_width,
});
// Add circle at end point for smooth line joins
world.add_command(DrawCommand::Circle {
center: state.position,
radius: state.pen_width / 2.0,
color: state.color,
filled: true,
});
}
}
TurtleCommand::Left(degrees) => {
state.heading -= degrees.to_radians();
}
TurtleCommand::Right(degrees) => {
TurtleCommand::Turn(degrees) => {
state.heading += degrees.to_radians();
}
@ -175,7 +148,7 @@ pub fn add_draw_for_completed_tween(
world: &mut TurtleWorld,
) {
match command {
TurtleCommand::Forward(_) | TurtleCommand::Backward(_) | TurtleCommand::Goto(_) => {
TurtleCommand::Move(_) | TurtleCommand::Goto(_) => {
if start_state.pen_down {
world.add_command(DrawCommand::Line {
start: start_state.position,
@ -281,7 +254,7 @@ mod tests {
assert_eq!(state.heading, 0.0);
// Forward 100 - should move to (100, 0)
execute_command(&TurtleCommand::Forward(100.0), &mut state, &mut world);
execute_command(&TurtleCommand::Move(100.0), &mut state, &mut world);
assert!(
(state.position.x - 100.0).abs() < 0.01,
"After forward(100): x = {}",
@ -296,7 +269,7 @@ mod tests {
// Left 90 degrees - should face north (heading decreases by 90°)
// In screen coords: north = -90° = -π/2
execute_command(&TurtleCommand::Left(90.0), &mut state, &mut world);
execute_command(&TurtleCommand::Turn(-90.0), &mut state, &mut world);
assert!(
(state.position.x - 100.0).abs() < 0.01,
"After left(90): x = {}",
@ -316,7 +289,7 @@ mod tests {
);
// Forward 50 - should move north (negative Y) to (100, -50)
execute_command(&TurtleCommand::Forward(50.0), &mut state, &mut world);
execute_command(&TurtleCommand::Move(50.0), &mut state, &mut world);
assert!(
(state.position.x - 100.0).abs() < 0.01,
"Final position: x = {} (expected 100.0)",

View File

@ -117,10 +117,7 @@ impl TweenController {
tween.start_state.heading + angle.to_radians() * progress
}
},
TurtleCommand::Left(angle) => {
tween.start_state.heading - angle.to_radians() * progress
}
TurtleCommand::Right(angle) => {
TurtleCommand::Turn(angle) => {
tween.start_state.heading + angle.to_radians() * progress
}
TurtleCommand::SetHeading(_) | _ => {
@ -218,10 +215,7 @@ impl TweenController {
fn command_creates_drawing(command: &TurtleCommand) -> bool {
matches!(
command,
TurtleCommand::Forward(_)
| TurtleCommand::Backward(_)
| TurtleCommand::Circle { .. }
| TurtleCommand::Goto(_)
TurtleCommand::Move(_) | TurtleCommand::Circle { .. } | TurtleCommand::Goto(_)
)
}
@ -229,8 +223,8 @@ impl TweenController {
let speed = speed.max(1) as f32;
let base_time = match command {
TurtleCommand::Forward(dist) | TurtleCommand::Backward(dist) => dist.abs() / speed,
TurtleCommand::Left(angle) | TurtleCommand::Right(angle) => {
TurtleCommand::Move(dist) => dist.abs() / speed,
TurtleCommand::Turn(angle) => {
// Rotation speed: assume 180 degrees per second at speed 100
angle.abs() / (speed * 1.8)
}
@ -255,20 +249,12 @@ impl TweenController {
let mut target = current.clone();
match command {
TurtleCommand::Forward(dist) => {
TurtleCommand::Move(dist) => {
let dx = dist * current.heading.cos();
let dy = dist * current.heading.sin();
target.position = vec2(current.position.x + dx, current.position.y + dy);
}
TurtleCommand::Backward(dist) => {
let dx = -dist * current.heading.cos();
let dy = -dist * current.heading.sin();
target.position = vec2(current.position.x + dx, current.position.y + dy);
}
TurtleCommand::Left(angle) => {
target.heading -= angle.to_radians();
}
TurtleCommand::Right(angle) => {
TurtleCommand::Turn(angle) => {
target.heading += angle.to_radians();
}
TurtleCommand::Circle {