refactor for simpler circle command and removed duplicated code

This commit is contained in:
Franz Dietrich 2025-10-09 13:23:52 +02:00
parent 25753b47ce
commit 7da0dcf141
5 changed files with 59 additions and 191 deletions

View File

@ -61,10 +61,11 @@ pub trait CurvedMovement: WithCommands {
{
let r: Precision = radius.into();
let a: Precision = angle.into();
self.get_commands_mut().push(TurtleCommand::CircleLeft {
self.get_commands_mut().push(TurtleCommand::Circle {
radius: r,
angle: a,
steps,
direction: crate::circle_geometry::CircleDirection::Left,
});
self
}
@ -76,10 +77,11 @@ pub trait CurvedMovement: WithCommands {
{
let r: Precision = radius.into();
let a: Precision = angle.into();
self.get_commands_mut().push(TurtleCommand::CircleRight {
self.get_commands_mut().push(TurtleCommand::Circle {
radius: r,
angle: a,
steps,
direction: crate::circle_geometry::CircleDirection::Right,
});
self
}

View File

@ -15,15 +15,11 @@ pub enum TurtleCommand {
Right(Precision), // degrees
// Circle drawing
CircleLeft {
radius: Precision,
angle: Precision, // degrees
steps: usize,
},
CircleRight {
Circle {
radius: Precision,
angle: Precision, // degrees
steps: usize,
direction: crate::circle_geometry::CircleDirection,
},
// Pen control

View File

@ -144,21 +144,14 @@ pub(crate) fn render_world_with_tween(
if let Some(tween) = active_tween {
if tween.start_state.pen_down {
match &tween.command {
crate::commands::TurtleCommand::CircleLeft {
crate::commands::TurtleCommand::Circle {
radius,
angle,
steps,
direction,
} => {
// Draw arc segments from start to current position
draw_tween_arc_left(tween, *radius, *angle, *steps);
}
crate::commands::TurtleCommand::CircleRight {
radius,
angle,
steps,
} => {
// Draw arc segments from start to current position
draw_tween_arc_right(tween, *radius, *angle, *steps);
draw_tween_arc(tween, *radius, *angle, *steps, *direction);
}
_ if should_draw_tween_line(&tween.command) => {
// Draw straight line for other movement commands
@ -200,59 +193,19 @@ fn should_draw_tween_line(command: &crate::commands::TurtleCommand) -> bool {
)
}
/// Draw arc segments for circle_left tween animation
fn draw_tween_arc_left(
/// Draw arc segments for circle tween animation
fn draw_tween_arc(
tween: &crate::tweening::CommandTween,
radius: f32,
total_angle: f32,
steps: usize,
direction: CircleDirection,
) {
let geom = CircleGeometry::new(
tween.start_state.position,
tween.start_state.heading,
radius,
CircleDirection::Left,
);
// Debug: draw center
draw_circle(geom.center.x, geom.center.y, 5.0, GRAY);
// Calculate how much of the arc we've traveled based on tween progress
// Use the same eased progress as the turtle position for synchronized animation
let elapsed = (get_time() - tween.start_time) as f32;
let t = (elapsed / tween.duration as f32).min(1.0);
let progress = CubicInOut.tween(1.0, t); // tween from 0 to 1
let angle_traveled = total_angle.to_radians() * progress;
let (rotation_degrees, arc_degrees) = geom.draw_arc_params_partial(angle_traveled);
// Adjust radius inward by half the line width so the line sits on the turtle's path
let draw_radius = radius - tween.start_state.pen_width / 2.0;
// Draw the partial arc
draw_arc(
geom.center.x,
geom.center.y,
steps as u8,
draw_radius,
rotation_degrees,
tween.start_state.pen_width,
arc_degrees,
tween.start_state.color,
);
}
/// Draw arc segments for circle_right tween animation
fn draw_tween_arc_right(
tween: &crate::tweening::CommandTween,
radius: f32,
total_angle: f32,
steps: usize,
) {
let geom = CircleGeometry::new(
tween.start_state.position,
tween.start_state.heading,
radius,
CircleDirection::Right,
direction,
);
// Debug: draw center

View File

@ -62,18 +62,14 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
state.heading += degrees.to_radians();
}
TurtleCommand::CircleLeft {
TurtleCommand::Circle {
radius,
angle,
steps,
direction,
} => {
let start_heading = state.heading;
let geom = CircleGeometry::new(
state.position,
start_heading,
*radius,
CircleDirection::Left,
);
let geom = CircleGeometry::new(state.position, start_heading, *radius, *direction);
if state.pen_down {
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);
@ -91,39 +87,10 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
// Update turtle position and heading
state.position = geom.position_at_angle(angle.to_radians());
state.heading = start_heading - angle.to_radians();
}
TurtleCommand::CircleRight {
radius,
angle,
steps,
} => {
let start_heading = state.heading;
let geom = CircleGeometry::new(
state.position,
start_heading,
*radius,
CircleDirection::Right,
);
if state.pen_down {
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);
world.add_command(DrawCommand::Arc {
center: geom.center,
radius: *radius - state.pen_width, // Adjust radius for pen width to keep arc inside
rotation: rotation_degrees,
arc: arc_degrees,
color: state.color,
width: state.pen_width,
sides: *steps as u8,
});
}
// Update turtle position and heading
state.position = geom.position_at_angle(angle.to_radians());
state.heading = start_heading + angle.to_radians();
state.heading = match direction {
CircleDirection::Left => start_heading - angle.to_radians(),
CircleDirection::Right => start_heading + angle.to_radians(),
};
}
TurtleCommand::PenUp => {
@ -225,56 +192,18 @@ pub fn add_draw_for_completed_tween(
});
}
}
TurtleCommand::CircleLeft {
TurtleCommand::Circle {
radius,
angle,
steps,
direction,
} => {
if start_state.pen_down {
let geom = CircleGeometry::new(
start_state.position,
start_state.heading,
*radius,
CircleDirection::Left,
);
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);
world.add_command(DrawCommand::Arc {
center: geom.center,
radius: *radius - start_state.pen_width / 2.0,
rotation: rotation_degrees,
arc: arc_degrees,
color: start_state.color,
width: start_state.pen_width,
sides: *steps as u8,
});
// Add endpoint circles for smooth joins
world.add_command(DrawCommand::Circle {
center: start_state.position,
radius: start_state.pen_width / 2.0,
color: start_state.color,
filled: true,
});
world.add_command(DrawCommand::Circle {
center: end_state.position,
radius: start_state.pen_width / 2.0,
color: start_state.color,
filled: true,
});
}
}
TurtleCommand::CircleRight {
radius,
angle,
steps,
} => {
if start_state.pen_down {
let geom = CircleGeometry::new(
start_state.position,
start_state.heading,
*radius,
CircleDirection::Right,
*direction,
);
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);

View File

@ -84,22 +84,19 @@ impl TweenController {
let progress = tween.heading_tweener.move_to(elapsed);
state.position = match &tween.command {
TurtleCommand::CircleLeft { radius, angle, .. } => {
TurtleCommand::Circle {
radius,
angle,
direction,
..
} => {
let angle_traveled = angle.to_radians() * progress;
calculate_circle_left_position(
tween.start_state.position,
tween.start_state.heading,
*radius,
angle_traveled,
)
}
TurtleCommand::CircleRight { radius, angle, .. } => {
let angle_traveled = angle.to_radians() * progress;
calculate_circle_right_position(
calculate_circle_position(
tween.start_state.position,
tween.start_state.heading,
*radius,
angle_traveled,
*direction,
)
}
_ => {
@ -110,12 +107,16 @@ impl TweenController {
// Heading changes proportionally with progress for all commands
state.heading = match &tween.command {
TurtleCommand::CircleLeft { angle, .. } => {
tween.start_state.heading - angle.to_radians() * progress
}
TurtleCommand::CircleRight { angle, .. } => {
tween.start_state.heading + angle.to_radians() * progress
}
TurtleCommand::Circle {
angle, direction, ..
} => match direction {
CircleDirection::Left => {
tween.start_state.heading - angle.to_radians() * progress
}
CircleDirection::Right => {
tween.start_state.heading + angle.to_radians() * progress
}
},
TurtleCommand::Left(angle) => {
tween.start_state.heading - angle.to_radians() * progress
}
@ -219,8 +220,7 @@ impl TweenController {
command,
TurtleCommand::Forward(_)
| TurtleCommand::Backward(_)
| TurtleCommand::CircleLeft { .. }
| TurtleCommand::CircleRight { .. }
| TurtleCommand::Circle { .. }
| TurtleCommand::Goto(_)
)
}
@ -234,8 +234,7 @@ impl TweenController {
// Rotation speed: assume 180 degrees per second at speed 100
angle.abs() / (speed * 1.8)
}
TurtleCommand::CircleLeft { radius, angle, .. }
| TurtleCommand::CircleRight { radius, angle, .. } => {
TurtleCommand::Circle { radius, angle, .. } => {
let arc_length = radius * angle.to_radians().abs();
arc_length / speed
}
@ -272,25 +271,24 @@ impl TweenController {
TurtleCommand::Right(angle) => {
target.heading += angle.to_radians();
}
TurtleCommand::CircleLeft { radius, angle, .. } => {
TurtleCommand::Circle {
radius,
angle,
direction,
..
} => {
// Use helper function to calculate final position
target.position = calculate_circle_left_position(
target.position = calculate_circle_position(
current.position,
current.heading,
*radius,
angle.to_radians(),
*direction,
);
target.heading = current.heading - angle.to_radians();
}
TurtleCommand::CircleRight { radius, angle, .. } => {
// Use helper function to calculate final position
target.position = calculate_circle_right_position(
current.position,
current.heading,
*radius,
angle.to_radians(),
);
target.heading = current.heading + angle.to_radians();
target.heading = match direction {
CircleDirection::Left => current.heading - angle.to_radians(),
CircleDirection::Right => current.heading + angle.to_radians(),
};
}
TurtleCommand::Goto(coord) => {
target.position = *coord;
@ -331,24 +329,14 @@ impl TweenController {
}
}
/// Calculate position on a circular arc for circle_left
fn calculate_circle_left_position(
/// Calculate position on a circular arc
fn calculate_circle_position(
start_pos: Vec2,
start_heading: f32,
radius: f32,
angle_traveled: f32, // How much of the total angle we've traveled (in radians)
direction: CircleDirection,
) -> Vec2 {
let geom = CircleGeometry::new(start_pos, start_heading, radius, CircleDirection::Left);
geom.position_at_angle(angle_traveled)
}
/// Calculate position on a circular arc for circle_right
fn calculate_circle_right_position(
start_pos: Vec2,
start_heading: f32,
radius: f32,
angle_traveled: f32, // How much of the total angle we've traveled (in radians)
) -> Vec2 {
let geom = CircleGeometry::new(start_pos, start_heading, radius, CircleDirection::Right);
let geom = CircleGeometry::new(start_pos, start_heading, radius, direction);
geom.position_at_angle(angle_traveled)
}