refactor for simpler circle command and removed duplicated code
This commit is contained in:
parent
25753b47ce
commit
7da0dcf141
@ -61,10 +61,11 @@ pub trait CurvedMovement: WithCommands {
|
|||||||
{
|
{
|
||||||
let r: Precision = radius.into();
|
let r: Precision = radius.into();
|
||||||
let a: Precision = angle.into();
|
let a: Precision = angle.into();
|
||||||
self.get_commands_mut().push(TurtleCommand::CircleLeft {
|
self.get_commands_mut().push(TurtleCommand::Circle {
|
||||||
radius: r,
|
radius: r,
|
||||||
angle: a,
|
angle: a,
|
||||||
steps,
|
steps,
|
||||||
|
direction: crate::circle_geometry::CircleDirection::Left,
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -76,10 +77,11 @@ pub trait CurvedMovement: WithCommands {
|
|||||||
{
|
{
|
||||||
let r: Precision = radius.into();
|
let r: Precision = radius.into();
|
||||||
let a: Precision = angle.into();
|
let a: Precision = angle.into();
|
||||||
self.get_commands_mut().push(TurtleCommand::CircleRight {
|
self.get_commands_mut().push(TurtleCommand::Circle {
|
||||||
radius: r,
|
radius: r,
|
||||||
angle: a,
|
angle: a,
|
||||||
steps,
|
steps,
|
||||||
|
direction: crate::circle_geometry::CircleDirection::Right,
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,15 +15,11 @@ pub enum TurtleCommand {
|
|||||||
Right(Precision), // degrees
|
Right(Precision), // degrees
|
||||||
|
|
||||||
// Circle drawing
|
// Circle drawing
|
||||||
CircleLeft {
|
Circle {
|
||||||
radius: Precision,
|
|
||||||
angle: Precision, // degrees
|
|
||||||
steps: usize,
|
|
||||||
},
|
|
||||||
CircleRight {
|
|
||||||
radius: Precision,
|
radius: Precision,
|
||||||
angle: Precision, // degrees
|
angle: Precision, // degrees
|
||||||
steps: usize,
|
steps: usize,
|
||||||
|
direction: crate::circle_geometry::CircleDirection,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Pen control
|
// Pen control
|
||||||
|
|||||||
@ -144,21 +144,14 @@ pub(crate) fn render_world_with_tween(
|
|||||||
if let Some(tween) = active_tween {
|
if let Some(tween) = active_tween {
|
||||||
if tween.start_state.pen_down {
|
if tween.start_state.pen_down {
|
||||||
match &tween.command {
|
match &tween.command {
|
||||||
crate::commands::TurtleCommand::CircleLeft {
|
crate::commands::TurtleCommand::Circle {
|
||||||
radius,
|
radius,
|
||||||
angle,
|
angle,
|
||||||
steps,
|
steps,
|
||||||
|
direction,
|
||||||
} => {
|
} => {
|
||||||
// Draw arc segments from start to current position
|
// Draw arc segments from start to current position
|
||||||
draw_tween_arc_left(tween, *radius, *angle, *steps);
|
draw_tween_arc(tween, *radius, *angle, *steps, *direction);
|
||||||
}
|
|
||||||
crate::commands::TurtleCommand::CircleRight {
|
|
||||||
radius,
|
|
||||||
angle,
|
|
||||||
steps,
|
|
||||||
} => {
|
|
||||||
// Draw arc segments from start to current position
|
|
||||||
draw_tween_arc_right(tween, *radius, *angle, *steps);
|
|
||||||
}
|
}
|
||||||
_ if should_draw_tween_line(&tween.command) => {
|
_ if should_draw_tween_line(&tween.command) => {
|
||||||
// Draw straight line for other movement commands
|
// 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
|
/// Draw arc segments for circle tween animation
|
||||||
fn draw_tween_arc_left(
|
fn draw_tween_arc(
|
||||||
tween: &crate::tweening::CommandTween,
|
tween: &crate::tweening::CommandTween,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
total_angle: f32,
|
total_angle: f32,
|
||||||
steps: usize,
|
steps: usize,
|
||||||
|
direction: CircleDirection,
|
||||||
) {
|
) {
|
||||||
let geom = CircleGeometry::new(
|
let geom = CircleGeometry::new(
|
||||||
tween.start_state.position,
|
tween.start_state.position,
|
||||||
tween.start_state.heading,
|
tween.start_state.heading,
|
||||||
radius,
|
radius,
|
||||||
CircleDirection::Left,
|
direction,
|
||||||
);
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Debug: draw center
|
// Debug: draw center
|
||||||
|
|||||||
@ -62,18 +62,14 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
|
|||||||
state.heading += degrees.to_radians();
|
state.heading += degrees.to_radians();
|
||||||
}
|
}
|
||||||
|
|
||||||
TurtleCommand::CircleLeft {
|
TurtleCommand::Circle {
|
||||||
radius,
|
radius,
|
||||||
angle,
|
angle,
|
||||||
steps,
|
steps,
|
||||||
|
direction,
|
||||||
} => {
|
} => {
|
||||||
let start_heading = state.heading;
|
let start_heading = state.heading;
|
||||||
let geom = CircleGeometry::new(
|
let geom = CircleGeometry::new(state.position, start_heading, *radius, *direction);
|
||||||
state.position,
|
|
||||||
start_heading,
|
|
||||||
*radius,
|
|
||||||
CircleDirection::Left,
|
|
||||||
);
|
|
||||||
|
|
||||||
if state.pen_down {
|
if state.pen_down {
|
||||||
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);
|
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
|
// Update turtle position and heading
|
||||||
state.position = geom.position_at_angle(angle.to_radians());
|
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::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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TurtleCommand::PenUp => {
|
TurtleCommand::PenUp => {
|
||||||
@ -225,56 +192,18 @@ pub fn add_draw_for_completed_tween(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TurtleCommand::CircleLeft {
|
TurtleCommand::Circle {
|
||||||
radius,
|
radius,
|
||||||
angle,
|
angle,
|
||||||
steps,
|
steps,
|
||||||
|
direction,
|
||||||
} => {
|
} => {
|
||||||
if start_state.pen_down {
|
if start_state.pen_down {
|
||||||
let geom = CircleGeometry::new(
|
let geom = CircleGeometry::new(
|
||||||
start_state.position,
|
start_state.position,
|
||||||
start_state.heading,
|
start_state.heading,
|
||||||
*radius,
|
*radius,
|
||||||
CircleDirection::Left,
|
*direction,
|
||||||
);
|
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);
|
let (rotation_degrees, arc_degrees) = geom.draw_arc_params(*angle);
|
||||||
|
|
||||||
|
|||||||
@ -84,22 +84,19 @@ impl TweenController {
|
|||||||
let progress = tween.heading_tweener.move_to(elapsed);
|
let progress = tween.heading_tweener.move_to(elapsed);
|
||||||
|
|
||||||
state.position = match &tween.command {
|
state.position = match &tween.command {
|
||||||
TurtleCommand::CircleLeft { radius, angle, .. } => {
|
TurtleCommand::Circle {
|
||||||
|
radius,
|
||||||
|
angle,
|
||||||
|
direction,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let angle_traveled = angle.to_radians() * progress;
|
let angle_traveled = angle.to_radians() * progress;
|
||||||
calculate_circle_left_position(
|
calculate_circle_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(
|
|
||||||
tween.start_state.position,
|
tween.start_state.position,
|
||||||
tween.start_state.heading,
|
tween.start_state.heading,
|
||||||
*radius,
|
*radius,
|
||||||
angle_traveled,
|
angle_traveled,
|
||||||
|
*direction,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -110,12 +107,16 @@ impl TweenController {
|
|||||||
|
|
||||||
// Heading changes proportionally with progress for all commands
|
// Heading changes proportionally with progress for all commands
|
||||||
state.heading = match &tween.command {
|
state.heading = match &tween.command {
|
||||||
TurtleCommand::CircleLeft { angle, .. } => {
|
TurtleCommand::Circle {
|
||||||
|
angle, direction, ..
|
||||||
|
} => match direction {
|
||||||
|
CircleDirection::Left => {
|
||||||
tween.start_state.heading - angle.to_radians() * progress
|
tween.start_state.heading - angle.to_radians() * progress
|
||||||
}
|
}
|
||||||
TurtleCommand::CircleRight { angle, .. } => {
|
CircleDirection::Right => {
|
||||||
tween.start_state.heading + angle.to_radians() * progress
|
tween.start_state.heading + angle.to_radians() * progress
|
||||||
}
|
}
|
||||||
|
},
|
||||||
TurtleCommand::Left(angle) => {
|
TurtleCommand::Left(angle) => {
|
||||||
tween.start_state.heading - angle.to_radians() * progress
|
tween.start_state.heading - angle.to_radians() * progress
|
||||||
}
|
}
|
||||||
@ -219,8 +220,7 @@ impl TweenController {
|
|||||||
command,
|
command,
|
||||||
TurtleCommand::Forward(_)
|
TurtleCommand::Forward(_)
|
||||||
| TurtleCommand::Backward(_)
|
| TurtleCommand::Backward(_)
|
||||||
| TurtleCommand::CircleLeft { .. }
|
| TurtleCommand::Circle { .. }
|
||||||
| TurtleCommand::CircleRight { .. }
|
|
||||||
| TurtleCommand::Goto(_)
|
| TurtleCommand::Goto(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -234,8 +234,7 @@ impl TweenController {
|
|||||||
// Rotation speed: assume 180 degrees per second at speed 100
|
// Rotation speed: assume 180 degrees per second at speed 100
|
||||||
angle.abs() / (speed * 1.8)
|
angle.abs() / (speed * 1.8)
|
||||||
}
|
}
|
||||||
TurtleCommand::CircleLeft { radius, angle, .. }
|
TurtleCommand::Circle { radius, angle, .. } => {
|
||||||
| TurtleCommand::CircleRight { radius, angle, .. } => {
|
|
||||||
let arc_length = radius * angle.to_radians().abs();
|
let arc_length = radius * angle.to_radians().abs();
|
||||||
arc_length / speed
|
arc_length / speed
|
||||||
}
|
}
|
||||||
@ -272,25 +271,24 @@ impl TweenController {
|
|||||||
TurtleCommand::Right(angle) => {
|
TurtleCommand::Right(angle) => {
|
||||||
target.heading += angle.to_radians();
|
target.heading += angle.to_radians();
|
||||||
}
|
}
|
||||||
TurtleCommand::CircleLeft { radius, angle, .. } => {
|
TurtleCommand::Circle {
|
||||||
|
radius,
|
||||||
|
angle,
|
||||||
|
direction,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Use helper function to calculate final position
|
// Use helper function to calculate final position
|
||||||
target.position = calculate_circle_left_position(
|
target.position = calculate_circle_position(
|
||||||
current.position,
|
current.position,
|
||||||
current.heading,
|
current.heading,
|
||||||
*radius,
|
*radius,
|
||||||
angle.to_radians(),
|
angle.to_radians(),
|
||||||
|
*direction,
|
||||||
);
|
);
|
||||||
target.heading = current.heading - angle.to_radians();
|
target.heading = match direction {
|
||||||
}
|
CircleDirection::Left => current.heading - angle.to_radians(),
|
||||||
TurtleCommand::CircleRight { radius, angle, .. } => {
|
CircleDirection::Right => current.heading + angle.to_radians(),
|
||||||
// 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();
|
|
||||||
}
|
}
|
||||||
TurtleCommand::Goto(coord) => {
|
TurtleCommand::Goto(coord) => {
|
||||||
target.position = *coord;
|
target.position = *coord;
|
||||||
@ -331,24 +329,14 @@ impl TweenController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate position on a circular arc for circle_left
|
/// Calculate position on a circular arc
|
||||||
fn calculate_circle_left_position(
|
fn calculate_circle_position(
|
||||||
start_pos: Vec2,
|
start_pos: Vec2,
|
||||||
start_heading: f32,
|
start_heading: f32,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
angle_traveled: f32, // How much of the total angle we've traveled (in radians)
|
angle_traveled: f32, // How much of the total angle we've traveled (in radians)
|
||||||
|
direction: CircleDirection,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let geom = CircleGeometry::new(start_pos, start_heading, radius, CircleDirection::Left);
|
let geom = CircleGeometry::new(start_pos, start_heading, radius, direction);
|
||||||
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);
|
|
||||||
geom.position_at_angle(angle_traveled)
|
geom.position_at_angle(angle_traveled)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user