diff --git a/turtle-lib/examples/clock_threaded.rs b/turtle-lib/examples/clock_threaded.rs new file mode 100644 index 0000000..6dba085 --- /dev/null +++ b/turtle-lib/examples/clock_threaded.rs @@ -0,0 +1,114 @@ +//! Animated clock example using threading +//! +//! This example demonstrates how to use turtle command channels for animated updates. +//! A separate thread generates the clock drawing commands every second while the main +//! thread handles rendering via the Macroquad game loop. + +use chrono::{Local, Timelike}; +use macroquad::prelude::{clear_background, is_key_pressed, next_frame, KeyCode, WHITE}; +use turtle_lib::{create_turtle_plan, vec2, DirectionalMovement, Turnable, TurtleApp}; + +#[macroquad::main("Clock (Threaded)")] +async fn main() { + const HOURS: i32 = 12; + const MINUTES: f32 = 60.0; + const SECONDS: f32 = 60.0; + const FULL_CIRCLE: f32 = 360.0; + + let mut app = TurtleApp::new(); + let turtle_tx = app.create_turtle_channel(10); + + // Spawn a thread that generates clock commands every second + std::thread::spawn(move || { + let mut last_second = -1i32; + + loop { + let now = Local::now(); + let current_second = now.second() as i32; + + // Only generate commands when the time changes + if current_second != last_second { + let mut turtle = create_turtle_plan(); + turtle.reset().set_speed(1100).left(90.0); + + // Draw the clock circle and hour markers + for i in 1..=HOURS { + turtle + .pen_up() + .go_to(vec2(0.0, 0.0)) + .right(FULL_CIRCLE / HOURS as f32) + .forward(205.0); + + let pen_size = if (i) % 3 == 0 { 7.0 } else { 2.0 }; + turtle + .set_pen_width(pen_size) + .pen_down() + .forward(10.0) + .right(90.0) + .pen_up() + .backward(pen_size.max(4.0) + (i / 10) as f32 * 4.0) + .pen_down() + .write_text(format!("{i}"), 2 * pen_size as i32 + 14) + .pen_up() + .forward(pen_size.max(4.0) + (i / 10) as f32 * 4.0) + .pen_down() + .left(90.0); + } + + // Draw the hour hand + turtle + .pen_up() + .go_to(vec2(0.0, 0.0)) + .set_heading(90.0) + .right(FULL_CIRCLE / HOURS as f32 * (now.hour() % 12) as f32) + .set_pen_width(7.0) + .pen_down() + .forward(120.0); + + // Draw the minute hand + turtle + .pen_up() + .go_to(vec2(0.0, 0.0)) + .set_heading(90.0) + .right(FULL_CIRCLE / MINUTES * now.minute() as f32) + .set_pen_width(3.0) + .pen_down() + .forward(150.0); + + // Draw the second hand + turtle + .pen_up() + .go_to(vec2(0.0, 0.0)) + .set_heading(90.0) + .right(FULL_CIRCLE / SECONDS * now.second() as f32) + .set_pen_width(1.0) + .pen_down() + .forward(180.0); + + // Send the command queue to the main thread + let _ = turtle_tx.send(turtle.build()); + last_second = current_second; + } + + // Sleep briefly to avoid busy-waiting (update ~10 times per second) + std::thread::sleep(std::time::Duration::from_millis(100)); + } + }); + + // Main render loop + loop { + clear_background(WHITE); + + // Process any pending commands from the worker thread + app.process_commands(); + + app.update(); + app.render(); + + if is_key_pressed(KeyCode::Escape) || is_key_pressed(KeyCode::Q) { + break; + } + + next_frame().await; + } +}