From b31ac29deb505b80546640fec2e6686979099b10 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sat, 18 Oct 2025 22:19:02 +0200 Subject: [PATCH] add clock and bezier example --- turtle-lib/Cargo.toml | 1 + turtle-lib/examples/bezier.rs | 56 +++++++++++++++++++++ turtle-lib/examples/clock.rs | 91 +++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 turtle-lib/examples/bezier.rs create mode 100644 turtle-lib/examples/clock.rs diff --git a/turtle-lib/Cargo.toml b/turtle-lib/Cargo.toml index 89d51b3..4fe1171 100644 --- a/turtle-lib/Cargo.toml +++ b/turtle-lib/Cargo.toml @@ -19,3 +19,4 @@ crossbeam = "0.8" # For examples and testing tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } dialog = "*" +chrono = "0.4" diff --git a/turtle-lib/examples/bezier.rs b/turtle-lib/examples/bezier.rs new file mode 100644 index 0000000..34fd65e --- /dev/null +++ b/turtle-lib/examples/bezier.rs @@ -0,0 +1,56 @@ +//! Cubic Bézier curve example +//! + +use turtle_lib::{turtle_main, vec2}; + +struct CubicBezier { + point0: (f32, f32), + point1: (f32, f32), + point2: (f32, f32), + point3: (f32, f32), +} + +impl CubicBezier { + /// Returns the value of this curve at the given parameter t (0.0 to 1.0) + pub fn at(&self, t: f64) -> (f32, f32) { + let t = t as f32; + let mt = 1.0 - t; // (1 - t) + + // Cubic Bézier formula from Wikipedia + let p0_weight = mt.powi(3); + let p1_weight = 3.0 * mt.powi(2) * t; + let p2_weight = 3.0 * mt * t.powi(2); + let p3_weight = t.powi(3); + + ( + self.point0.0 * p0_weight + + self.point1.0 * p1_weight + + self.point2.0 * p2_weight + + self.point3.0 * p3_weight, + self.point0.1 * p0_weight + + self.point1.1 * p1_weight + + self.point2.1 * p2_weight + + self.point3.1 * p3_weight, + ) + } +} + +#[turtle_main("Bézier Curve")] +fn draw(turtle: &mut TurtlePlan) { + let curve = CubicBezier { + point0: (-200.0, -100.0), + point1: (-100.0, 400.0), + point2: (100.0, -500.0), + point3: (300.0, 200.0), + }; + + let start = curve.at(0.0); + turtle.pen_up().go_to(vec2(start.0, start.1)).pen_down(); + + let samples = 100; + for i in 0..samples { + let t = f64::from(i) / f64::from(samples); + let point = curve.at(t); + turtle.go_to(vec2(point.0, point.1)); + } +} diff --git a/turtle-lib/examples/clock.rs b/turtle-lib/examples/clock.rs new file mode 100644 index 0000000..9ca3198 --- /dev/null +++ b/turtle-lib/examples/clock.rs @@ -0,0 +1,91 @@ +//! Animated clock example +//! +//! This example draws an animated clock that shows the current time. +//! The clock updates every second to reflect the actual time. + +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")] +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 mut last_update = Local::now(); + + loop { + clear_background(WHITE); + + let now = Local::now(); + + // Only redraw when the time changes + if now.second() != last_update.second() { + let mut turtle = create_turtle_plan(); + turtle.reset().set_speed(1100).left(90.0); // Instant mode for smooth updates + + // 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) + .write_text(format!("{i}"), 2 * pen_size as i32 + 10) + .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(5.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); + + app = TurtleApp::new().with_commands(turtle.build()); + last_update = now; + } + + app.update(); + app.render(); + + if is_key_pressed(KeyCode::Escape) || is_key_pressed(KeyCode::Q) { + break; + } + + next_frame().await; + } +}