diff --git a/turtle-lib-macroquad/src/builders.rs b/turtle-lib-macroquad/src/builders.rs index b62610b..ab4f298 100644 --- a/turtle-lib-macroquad/src/builders.rs +++ b/turtle-lib-macroquad/src/builders.rs @@ -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 } diff --git a/turtle-lib-macroquad/src/commands.rs b/turtle-lib-macroquad/src/commands.rs index 7f70e3d..ca94246 100644 --- a/turtle-lib-macroquad/src/commands.rs +++ b/turtle-lib-macroquad/src/commands.rs @@ -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 diff --git a/turtle-lib-macroquad/src/drawing.rs b/turtle-lib-macroquad/src/drawing.rs index 60d148c..c4520cf 100644 --- a/turtle-lib-macroquad/src/drawing.rs +++ b/turtle-lib-macroquad/src/drawing.rs @@ -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 diff --git a/turtle-lib-macroquad/src/execution.rs b/turtle-lib-macroquad/src/execution.rs index a32aa85..2e6d504 100644 --- a/turtle-lib-macroquad/src/execution.rs +++ b/turtle-lib-macroquad/src/execution.rs @@ -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); diff --git a/turtle-lib-macroquad/src/tweening.rs b/turtle-lib-macroquad/src/tweening.rs index 1b3a27a..d992ed7 100644 --- a/turtle-lib-macroquad/src/tweening.rs +++ b/turtle-lib-macroquad/src/tweening.rs @@ -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) }