From cef63ca32ad31a62569780d352a11b5fa78b9e65 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sun, 12 Oct 2025 16:13:25 +0200 Subject: [PATCH] add a macro for simpler first examples. --- .copilot/instructions.md | 105 --------- .github/copilot-instructions.md | 200 ++++++++++++++++++ Cargo.toml | 7 +- turtle-lib-macroquad-macros/Cargo.toml | 13 ++ turtle-lib-macroquad-macros/README.md | 72 +++++++ turtle-lib-macroquad-macros/src/lib.rs | 175 +++++++++++++++ turtle-lib-macroquad/Cargo.toml | 1 + turtle-lib-macroquad/README.md | 67 +++++- turtle-lib-macroquad/examples/cheese_macro.rs | 66 ++++++ turtle-lib-macroquad/examples/circle_test.rs | 57 ++--- .../examples/direction_test_1.rs | 34 +-- .../examples/fill_advanced.rs | 2 + turtle-lib-macroquad/examples/fill_demo.rs | 52 ++--- .../examples/fill_requirements.rs | 33 +-- turtle-lib-macroquad/examples/hello_turtle.rs | 14 ++ turtle-lib-macroquad/examples/koch.rs | 51 ++--- .../examples/logging_example.rs | 15 +- turtle-lib-macroquad/examples/macro_demo.rs | 17 ++ .../examples/macro_demo_inline.rs | 17 ++ .../examples/macro_full_demo.rs | 57 +++++ turtle-lib-macroquad/examples/nikolaus.rs | 89 ++++---- turtle-lib-macroquad/examples/shapes.rs | 50 ++--- turtle-lib-macroquad/examples/square.rs | 27 +-- turtle-lib-macroquad/examples/stern.rs | 39 +--- turtle-lib-macroquad/examples/yinyang.rs | 63 ++---- turtle-lib-macroquad/src/lib.rs | 32 ++- 26 files changed, 902 insertions(+), 453 deletions(-) delete mode 100644 .copilot/instructions.md create mode 100644 .github/copilot-instructions.md create mode 100644 turtle-lib-macroquad-macros/Cargo.toml create mode 100644 turtle-lib-macroquad-macros/README.md create mode 100644 turtle-lib-macroquad-macros/src/lib.rs create mode 100644 turtle-lib-macroquad/examples/cheese_macro.rs create mode 100644 turtle-lib-macroquad/examples/hello_turtle.rs create mode 100644 turtle-lib-macroquad/examples/macro_demo.rs create mode 100644 turtle-lib-macroquad/examples/macro_demo_inline.rs create mode 100644 turtle-lib-macroquad/examples/macro_full_demo.rs diff --git a/.copilot/instructions.md b/.copilot/instructions.md deleted file mode 100644 index e739d1c..0000000 --- a/.copilot/instructions.md +++ /dev/null @@ -1,105 +0,0 @@ -## Project Context -- **turtle-lib**: Heavy Bevy-based turtle graphics (0.17.1) with ECS architecture -- **turtle-lib-macroquad**: Lightweight macroquad implementation with Lyon tessellation (current focus) -- **turtle-lyon-poc**: Proof of concept for Lyon (COMPLETED - integrated into main crate) -- **turtle-skia-poc**: Alternative tiny-skia rendering approach -- **Status**: Lyon migration complete, fill quality issues resolved - -## Architecture -``` -turtle-lib-macroquad/src/ -├── lib.rs - Public API & TurtleApp -├── state.rs - TurtleState & TurtleWorld -├── commands.rs - TurtleCommand & CommandQueue -├── builders.rs - Builder traits (DirectionalMovement, Turnable, CurvedMovement) -├── execution.rs - Command execution -├── tweening.rs - Animation controller -├── drawing.rs - Macroquad rendering -├── tessellation.rs - Lyon tessellation (355 lines - polygons, circles, arcs, strokes) -├── circle_geometry.rs - Circle/arc geometry calculations -├── shapes.rs - Turtle shapes -└── general/ - Type definitions (Angle, Length, Color, etc.) -``` - -## Current Status -1. ✅ **Lyon integration complete**: Using Lyon 1.0 for all tessellation -2. ✅ **Fill quality fixed**: EvenOdd fill rule handles complex fills and holes automatically -3. ✅ **Simplified codebase**: Replaced manual triangulation with Lyon's GPU-optimized tessellation -4. ✅ **Full feature set**: Polygons, circles, arcs, strokes all using Lyon - -## Key Features -- **Builder API**: Fluent interface for turtle commands -- **Animation system**: Tweening controller with configurable speeds (Instant/Animated) -- **Lyon tessellation**: Automatic hole detection, proper winding order, GPU-optimized -- **Fill support**: Multi-contour fills with automatic hole handling -- **Shapes**: Arrow, circle, square, triangle, classic turtle shapes - -## Response Style Rules -- NO emoji/smileys -- NO extensive summaries -- Use bullet points for lists -- Be concise and direct -- Focus on code solutions - -# Tools to use -- when in doubt you can always use #fetch to get additional docs and online information. -- when the userinput is incomplete generate a brief text and let the user confirm your understanding. - -## Code Patterns - -### Lyon Tessellation (Current) -```rust -// tessellation.rs - Lyon integration -pub fn tessellate_polygon(vertices: &[Vec2], color: Color) -> Result> -pub fn tessellate_multi_contour(contours: &[Vec], color: Color) -> Result> -pub fn tessellate_stroke(vertices: &[Vec2], color: Color, width: f32, closed: bool) -> Result> -pub fn tessellate_circle(center: Vec2, radius: f32, color: Color, filled: bool, stroke_width: f32) -> Result> -pub fn tessellate_arc(center: Vec2, radius: f32, start_angle: f32, arc_angle: f32, color: Color, stroke_width: f32, segments: usize) -> Result> -``` - -### Fill with Holes -```rust -// Multi-contour fills automatically detect holes using EvenOdd fill rule -let contours = vec![outer_boundary, hole1, hole2]; -let mesh = tessellate_multi_contour(&contours, color)?; -``` - -## Builder API -```rust -let mut t = create_turtle(); -t.forward(100).right(90) - .circle_left(50.0, 180.0, 36) - .begin_fill() - .set_fill_color(BLACK) - .circle_left(90.0, 180.0, 36) - .end_fill(); -let app = TurtleApp::new().with_commands(t.build()); -``` - -## File Links -- Main crate: [turtle-lib-macroquad/src/lib.rs](turtle-lib-macroquad/src/lib.rs) -- Tessellation: [turtle-lib-macroquad/src/tessellation.rs](turtle-lib-macroquad/src/tessellation.rs) -- Rendering: [turtle-lib-macroquad/src/drawing.rs](turtle-lib-macroquad/src/drawing.rs) -- Animation: [turtle-lib-macroquad/src/tweening.rs](turtle-lib-macroquad/src/tweening.rs) -- Examples: [turtle-lib-macroquad/examples/](turtle-lib-macroquad/examples/) - -## Testing -Run examples to verify Lyon integration: -```bash -cargo run --example yinyang -cargo run --example stern -cargo run --example nikolaus -``` - -## Code Quality -Run clippy with strict checks on turtle-lib-macroquad: -```bash -cargo clippy --package turtle-lib-macroquad -- -Wclippy::pedantic -Wclippy::cast_precision_loss -Wclippy::cast_sign_loss -Wclippy::cast_possible_truncation -``` -Note: Cast warnings are intentionally allowed for graphics code where precision loss is acceptable. - -## Dependencies -- macroquad 0.4 - Game framework and rendering -- lyon 1.0 - Tessellation (fills, strokes, circles, arcs) -- tween 2.1.0 - Animation easing -- tracing 0.1 - Logging (with log features) \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..948c1c0 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,200 @@ +# Turtle Graphics Library - AI Agent Instructions + +## Project Overview + +Rust workspace with turtle graphics implementations. **Primary focus: `turtle-lib-macroquad`** - lightweight library using Macroquad + Lyon for GPU-accelerated rendering. + +### Workspace Structure +``` +turtle/ +├── turtle-lib-macroquad/ # MAIN LIBRARY - Macroquad + Lyon (focus here) +├── turtle-lib-macroquad-macros/ # Proc macro for turtle_main +├── turtle-lib/ # Legacy Bevy 0.17.1 implementation (maintenance only) +├── turtle-example/ # Legacy examples +└── turtle-ui/ # UI components +``` + +## Architecture (`turtle-lib-macroquad`) + +### Core Design Pattern: Command Queue + Tweening +- **Builder API** (`TurtlePlan`) accumulates commands +- **Command Queue** stores execution plan +- **Tween Controller** interpolates between states for animation +- **Lyon Tessellation** converts all primitives to GPU meshes + +### Key Files +``` +src/ +├── lib.rs - Public API, TurtleApp (main loop), re-exports +├── builders.rs - Fluent API traits (forward/right/etc chain) +├── commands.rs - TurtleCommand enum (Move/Turn/Circle/etc) +├── execution.rs - Execute commands, update state +├── tweening.rs - Animation interpolation, speed control +├── drawing.rs - Render Lyon meshes with Macroquad +├── tessellation.rs - Lyon integration (polygons/strokes/fills/arcs) +├── state.rs - TurtleState, TurtleWorld, FillState +└── circle_geometry.rs - Arc/circle math +``` + +### Critical Concepts + +**1. Consolidated Commands** (reduces duplication): +- `Move(distance)` - negative = backward +- `Turn(angle)` - positive = right, negative = left (degrees) +- `Circle{radius, angle, steps, direction}` - unified left/right + +**2. Fill System** (multi-contour with holes): +- `FillState` tracks `Vec>` (multiple contours) +- `pen_up()` closes current contour, `pen_down()` opens new +- Lyon's EvenOdd fill rule auto-detects holes +- Example: Donut = outer circle + inner circle (2 contours) + +**3. Speed Modes**: +- `< 999`: Animated with tweening +- `>= 999`: Instant execution +- Controlled via `SetSpeed` commands (dynamic switching) + +**4. Lyon Tessellation Pipeline**: +All drawing → Lyon → GPU mesh → Macroquad rendering +- ~410 lines eliminated vs manual triangulation +- Functions: `tessellate_polygon/stroke/circle/arc/multi_contour` + +## Developer Workflows + +### Building & Testing +```bash +# Main library +cargo build --package turtle-lib-macroquad +cargo test --package turtle-lib-macroquad +cargo clippy --package turtle-lib-macroquad -- -Wclippy::pedantic \ + -Aclippy::cast_precision_loss -Aclippy::cast_sign_loss -Aclippy::cast_possible_truncation + +# Run examples (15+ examples available) +cargo run --package turtle-lib-macroquad --example hello_turtle +cargo run --package turtle-lib-macroquad --example yinyang +cargo run --package turtle-lib-macroquad --example cheese_macro +``` + +### Macro Crate +```bash +cargo build --package turtle-lib-macroquad-macros +``` + +### Code Quality Standards +- Clippy pedantic mode enabled +- Cast warnings allowed for graphics math +- All examples must build warning-free +- Use `#[must_use]` on builder methods + +## Project-Specific Patterns + +### 1. The `turtle_main` Macro (PREFERRED for examples) +Simplest way to create turtle programs: +```rust +use turtle_lib_macroquad::*; + +#[turtle_main("Window Title")] +fn draw(turtle: &mut TurtlePlan) { + turtle.forward(100.0).right(90.0); +} +``` + +Generates: window setup + render loop + quit handling (ESC/Q) + +### 2. Import Convention +Only need: `use turtle_lib_macroquad::*;` +- Re-exports: `vec2`, `RED/BLUE/GREEN/etc`, all turtle types +- No `use macroquad::prelude::*` needed (causes unused warnings) + +### 3. Builder Chain Pattern +```rust +let mut t = create_turtle(); +t.forward(100).right(90) + .set_pen_color(BLUE) + .circle_left(50.0, 360.0, 36) + .begin_fill() + .end_fill(); +let app = TurtleApp::new().with_commands(t.build()); +``` + +### 4. Multi-Contour Fill Example +```rust +turtle.begin_fill(); +turtle.circle_left(100.0, 360.0, 72); // Outer circle +turtle.pen_up(); // Closes contour +turtle.goto(vec2(0.0, -30.0)); +turtle.pen_down(); // Opens new contour +turtle.circle_left(30.0, 360.0, 36); // Inner (becomes hole) +turtle.end_fill(); // EvenOdd rule creates donut +``` + +### 5. Manual Setup (advanced control) +```rust +#[macroquad::main("Custom")] +async fn main() { + let mut turtle = create_turtle(); + // ... drawing code ... + let mut app = TurtleApp::new().with_commands(turtle.build()); + + loop { + clear_background(WHITE); + app.update(); + app.render(); + next_frame().await; + } +} +``` + +## Common Tasks + +### Adding New Turtle Command +1. Add variant to `TurtleCommand` enum in `commands.rs` +2. Implement builder method in `builders.rs` (chain with `self`) +3. Add execution logic in `execution.rs` +4. Update tessellation/rendering if needed + +### Adding Example +- Prefer `turtle_main` macro for simplicity +- Use only `use turtle_lib_macroquad::*;` +- Keep examples focused (one concept each) +- See `examples/hello_turtle.rs` for minimal template + +### Debugging Lyon Issues +- Enable tracing: `RUST_LOG=turtle_lib_macroquad=debug cargo run` +- Check `tessellation.rs` for Lyon API usage +- EvenOdd fill rule: holes must have opposite winding + +## Dependencies & Integration + +### Main Dependencies +- `macroquad = "0.4"` - Window/rendering framework +- `lyon = "1.0"` - Tessellation (fills, strokes, circles) +- `tween = "2.1.0"` - Animation easing +- `tracing = "0.1"` - Optional logging (zero overhead when unused) + +### Proc Macro Crate +- Separate crate required by Rust (proc-macro = true) +- Uses `syn`, `quote`, `proc-macro2` +- Generates full macroquad app boilerplate + +## What NOT to Do + +- Don't add `use macroquad::prelude::*` in examples when not required +- Don't manually triangulate - use Lyon functions +- Don't add commands for Forward/Backward separately (use Move) +- Don't modify `turtle-lib` (Bevy) unless specifically needed +- Don't create summary/comparison docs unless requested + +## Key Documentation Files + +- `README.md` - Main API docs +- `turtle-lib-macroquad/README.md` - Library-specific docs +- `turtle-lib-macroquad-macros/README.md` - Macro docs + +## Response Style + +- Be concise, no extensive summaries +- No emojis in technical responses +- Focus on code solutions over explanations +- Use bullet points for lists +- Reference specific files when helpful diff --git a/Cargo.toml b/Cargo.toml index d741902..3c0edc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,12 @@ [workspace] resolver = "2" -members = ["turtle-lib", "turtle-example", "turtle-lib-macroquad"] +members = [ + "turtle-lib", + "turtle-example", + "turtle-lib-macroquad", + "turtle-lib-macroquad-macros", +] [workspace.dependencies] # Pin Bevy across the workspace diff --git a/turtle-lib-macroquad-macros/Cargo.toml b/turtle-lib-macroquad-macros/Cargo.toml new file mode 100644 index 0000000..0bbd536 --- /dev/null +++ b/turtle-lib-macroquad-macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "turtle-lib-macroquad-macros" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full"] } diff --git a/turtle-lib-macroquad-macros/README.md b/turtle-lib-macroquad-macros/README.md new file mode 100644 index 0000000..1ccceb2 --- /dev/null +++ b/turtle-lib-macroquad-macros/README.md @@ -0,0 +1,72 @@ +# turtle-lib-macroquad-macros + +Procedural macros for `turtle-lib-macroquad`. + +## `turtle_main` Macro + +The `turtle_main` macro simplifies creating turtle graphics programs by automatically setting up: +- The Macroquad window +- Turtle initialization +- The main rendering loop +- Quit handling (ESC or Q keys) + +### Usage + +#### With a function parameter: + +```rust +use macroquad::prelude::*; +use turtle_lib_macroquad::*; + +#[turtle_main("My Drawing")] +fn my_drawing(turtle: &mut TurtlePlan) { + turtle.set_pen_color(RED); + turtle.forward(100.0); + turtle.right(90.0); + turtle.forward(100.0); +} +``` + +#### With inline code: + +```rust +use macroquad::prelude::*; +use turtle_lib_macroquad::*; + +#[turtle_main("My Drawing")] +fn my_drawing() { + turtle.set_pen_color(RED); + turtle.forward(100.0); + turtle.right(90.0); + turtle.forward(100.0); +} +``` + +### What it does + +The macro expands your code into a full Macroquad application with: +- `#[macroquad::main]` attribute for window creation +- Turtle instance creation +- TurtleApp initialization with your commands +- A main loop that: + - Clears the background to WHITE + - Updates the turtle app + - Renders the drawing + - Shows "Press ESC or Q to quit" message + - Handles quit keys + +### Benefits + +- **Less boilerplate**: No need to write the same loop structure in every example +- **Consistent UI**: All examples have the same quit behavior +- **Beginner-friendly**: Makes turtle graphics examples more approachable +- **Focus on drawing**: Your code focuses on the turtle commands, not the framework + +## License + +Licensed under either of: + +- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/turtle-lib-macroquad-macros/src/lib.rs b/turtle-lib-macroquad-macros/src/lib.rs new file mode 100644 index 0000000..8753b67 --- /dev/null +++ b/turtle-lib-macroquad-macros/src/lib.rs @@ -0,0 +1,175 @@ +//! Procedural macros for turtle-lib-macroquad +//! +//! This crate provides the `turtle_main` procedural macro that simplifies +//! creating turtle graphics programs by automatically setting up the +//! macroquad window, turtle initialization, and the main rendering loop. + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, ItemFn}; + +/// A convenience macro that wraps your turtle drawing code with the necessary +/// boilerplate for running a turtle graphics program. +/// +/// This macro: +/// - Wraps your code with `#[macroquad::main]` +/// - Creates a turtle instance (`turtle`) +/// - Sets up the `TurtleApp` with your drawing commands +/// - Provides a main loop with rendering and quit handling (ESC or Q) +/// +/// # Example +/// +/// ```no_run +/// use turtle_lib_macroquad::*; +/// +/// #[turtle_main("My Turtle Drawing")] +/// fn my_drawing(turtle: &mut TurtlePlan) { +/// // Use colors from turtle_lib_macroquad (re-exported from macroquad) +/// turtle.set_pen_color(RED); +/// turtle.forward(100.0); +/// turtle.right(90.0); +/// turtle.forward(100.0); +/// } +/// ``` +/// +/// If you need macroquad types not re-exported by turtle_lib_macroquad: +/// +/// ```no_run +/// use macroquad::prelude::SKYBLUE; // Import specific items +/// use turtle_lib_macroquad::*; +/// +/// #[turtle_main("My Drawing")] +/// fn my_drawing(turtle: &mut TurtlePlan) { +/// turtle.set_pen_color(SKYBLUE); +/// turtle.forward(100.0); +/// } +/// ``` +/// +/// This expands to approximately: +/// +/// ```no_run +/// use macroquad::prelude::*; +/// use turtle_lib_macroquad::*; +/// +/// #[macroquad::main("My Turtle Drawing")] +/// async fn main() { +/// let mut turtle = create_turtle(); +/// +/// // Your drawing code here +/// turtle.set_pen_color(RED); +/// turtle.forward(100.0); +/// turtle.right(90.0); +/// turtle.forward(100.0); +/// +/// let mut app = TurtleApp::new().with_commands(turtle.build()); +/// +/// loop { +/// clear_background(WHITE); +/// app.update(); +/// app.render(); +/// draw_text("Press ESC or Q to quit", 10.0, 40.0, 16.0, DARKGRAY); +/// +/// if is_key_pressed(KeyCode::Escape) || is_key_pressed(KeyCode::Q) { +/// break; +/// } +/// +/// next_frame().await; +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn turtle_main(args: TokenStream, input: TokenStream) -> TokenStream { + let input_fn = parse_macro_input!(input as ItemFn); + + // Parse the window title from args (default to "Turtle Graphics") + let window_title = if args.is_empty() { + quote! { "Turtle Graphics" } + } else { + let args_str = args.to_string(); + // Remove quotes if present + let title = args_str.trim().trim_matches('"'); + quote! { #title } + }; + + let fn_name = &input_fn.sig.ident; + let fn_block = &input_fn.block; + + // Check if the function has the expected signature + let has_turtle_param = input_fn.sig.inputs.len() == 1; + + let expanded = if has_turtle_param { + // Function takes a turtle parameter + quote! { + #[macroquad::main(#window_title)] + async fn main() { + let mut turtle = turtle_lib_macroquad::create_turtle(); + + // Call the user's function with the turtle + #fn_name(&mut turtle); + + let mut app = turtle_lib_macroquad::TurtleApp::new() + .with_commands(turtle.build()); + + loop { + macroquad::prelude::clear_background(macroquad::prelude::WHITE); + app.update(); + app.render(); + macroquad::prelude::draw_text( + "Press ESC or Q to quit", + 10.0, + 40.0, + 16.0, + macroquad::prelude::DARKGRAY + ); + + if macroquad::prelude::is_key_pressed(macroquad::prelude::KeyCode::Escape) + || macroquad::prelude::is_key_pressed(macroquad::prelude::KeyCode::Q) + { + break; + } + + macroquad::prelude::next_frame().await; + } + } + + fn #fn_name(turtle: &mut turtle_lib_macroquad::TurtlePlan) #fn_block + } + } else { + // Function takes no parameters - inline the code + quote! { + #[macroquad::main(#window_title)] + async fn main() { + let mut turtle = turtle_lib_macroquad::create_turtle(); + + // Inline the user's code + #fn_block + + let mut app = turtle_lib_macroquad::TurtleApp::new() + .with_commands(turtle.build()); + + loop { + macroquad::prelude::clear_background(macroquad::prelude::WHITE); + app.update(); + app.render(); + macroquad::prelude::draw_text( + "Press ESC or Q to quit", + 10.0, + 40.0, + 16.0, + macroquad::prelude::DARKGRAY + ); + + if macroquad::prelude::is_key_pressed(macroquad::prelude::KeyCode::Escape) + || macroquad::prelude::is_key_pressed(macroquad::prelude::KeyCode::Q) + { + break; + } + + macroquad::prelude::next_frame().await; + } + } + } + }; + + TokenStream::from(expanded) +} diff --git a/turtle-lib-macroquad/Cargo.toml b/turtle-lib-macroquad/Cargo.toml index 88dfbed..695024b 100644 --- a/turtle-lib-macroquad/Cargo.toml +++ b/turtle-lib-macroquad/Cargo.toml @@ -9,6 +9,7 @@ macroquad = "0.4" tween = "2.1.0" lyon = "1.0" tracing = { version = "0.1", features = ["log"], default-features = false } +turtle-lib-macroquad-macros = { path = "../turtle-lib-macroquad-macros" } [dev-dependencies] # For examples and testing diff --git a/turtle-lib-macroquad/README.md b/turtle-lib-macroquad/README.md index 81157ba..298959f 100644 --- a/turtle-lib-macroquad/README.md +++ b/turtle-lib-macroquad/README.md @@ -22,17 +22,74 @@ The main turtle graphics library built on Macroquad with Lyon tessellation. - Frame-rate independent animation - Live fill preview during circle/arc drawing +## Quick Start + +### Using the `turtle_main` Macro (Recommended for Beginners) + +The easiest way to create turtle programs is with the `turtle_main` macro: + +```rust +use macroquad::prelude::*; +use turtle_lib_macroquad::*; + +#[turtle_main("My First Drawing")] +fn my_drawing(turtle: &mut TurtlePlan) { + turtle.set_pen_color(RED); + turtle.forward(100.0); + turtle.right(90.0); + turtle.forward(100.0); +} +``` + +The macro automatically handles: +- Window creation and setup +- Turtle initialization +- Rendering loop +- Quit handling (ESC or Q keys) + +### Manual Setup (For Advanced Use) + +For more control over the application loop: + +```rust +use macroquad::prelude::*; +use turtle_lib_macroquad::*; + +#[macroquad::main("Turtle")] +async fn main() { + let mut turtle = create_turtle(); + turtle.forward(100.0).right(90.0); + + let mut app = TurtleApp::new().with_commands(turtle.build()); + + loop { + clear_background(WHITE); + app.update(); + app.render(); + next_frame().await; + } +} +``` + ## Quick Examples +All examples now use the `turtle_main` macro for simplicity: + ```bash # Run from this directory -cargo run --example square # Basic square drawing -cargo run --example yinyang # Multi-contour fills with holes -cargo run --example fill_advanced # Self-intersecting fills -cargo run --example koch # Recursive fractals -cargo run --example fill_demo # Multiple independent fills +cargo run --example hello_turtle # Minimal 10-line example +cargo run --example macro_demo # Simple square with macro +cargo run --example square # Basic square drawing +cargo run --example shapes # Different turtle shapes +cargo run --example yinyang # Multi-contour fills with holes +cargo run --example koch # Recursive fractals +cargo run --example fill_demo # Fill with holes (donut) +cargo run --example cheese_macro # Cheese example using macro +cargo run --example fill_advanced # Complex shapes (manual setup) ``` +Most examples use `turtle_main` for simplicity. A few keep manual setup for custom UI or logging. + ## Architecture Highlights ### Rendering Pipeline diff --git a/turtle-lib-macroquad/examples/cheese_macro.rs b/turtle-lib-macroquad/examples/cheese_macro.rs new file mode 100644 index 0000000..d82a617 --- /dev/null +++ b/turtle-lib-macroquad/examples/cheese_macro.rs @@ -0,0 +1,66 @@ +//! Cheese example using the turtle_main macro +//! +//! This is a simplified version of cheese.rs that demonstrates how the +//! turtle_main macro reduces boilerplate code. + +use turtle_lib_macroquad::*; + +#[turtle_main("Cheese with Holes - Using Macro")] +fn draw_cheese(turtle: &mut TurtlePlan) { + // Set fill color to yellow (cheese color!) + turtle.set_pen_color(ORANGE); + turtle.set_pen_width(3.0); + turtle.set_fill_color(YELLOW); + + println!("=== Starting cheese fill ==="); + turtle.begin_fill(); + + // Draw outer boundary (large square) + println!("Drawing outer square boundary..."); + for _ in 0..4 { + turtle.forward(400.0); + turtle.right(90.0); + } + + // Close outer contour and start drawing holes + println!("Closing outer contour with pen_up"); + turtle.pen_up(); + + // Draw triangular hole in the middle + println!("Drawing triangular hole..."); + turtle.go_to(vec2(200.0, 120.0)); + turtle.pen_down(); // Start new contour for hole + + for _ in 0..3 { + turtle.forward(160.0); + turtle.right(120.0); + } + + println!("Closing triangle contour with pen_up"); + turtle.pen_up(); // Close triangle hole contour + + // Draw circular hole (top-left) using circle_left + println!("Drawing circular hole (top-left) with circle_left..."); + turtle.go_to(vec2(100.0, 100.0)); + turtle.pen_down(); // Start new contour for hole + turtle.circle_left(30.0, 360.0, 36); // radius=30, full circle, 36 steps + println!("Closing circle contour with pen_up"); + turtle.pen_up(); // Close circle hole contour + + // Draw circular hole (bottom-right) using circle_right + println!("Drawing circular hole (bottom-right) with circle_right..."); + turtle.go_to(vec2(280.0, 280.0)); + turtle.pen_down(); // Start new contour for hole + turtle.circle_right(40.0, 360.0, 36); // radius=40, full circle, 36 steps + println!("Closing circle contour with pen_up"); + turtle.pen_up(); // Close circle hole contour + + // End fill - Lyon will automatically create holes! + println!("Calling end_fill - Lyon should create holes now!"); + turtle.end_fill(); + + // Set animation speed + turtle.set_speed(300); + + println!("Building and executing turtle plan..."); +} diff --git a/turtle-lib-macroquad/examples/circle_test.rs b/turtle-lib-macroquad/examples/circle_test.rs index e2832fd..cf82de5 100644 --- a/turtle-lib-macroquad/examples/circle_test.rs +++ b/turtle-lib-macroquad/examples/circle_test.rs @@ -1,46 +1,29 @@ //! Test circle_left and circle_right commands -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Circle Test")] -async fn main() { - // Create a turtle plan - let mut plan = create_turtle(); - plan.shape(ShapeType::Turtle); +#[turtle_main("Circle Test")] +fn draw(turtle: &mut TurtlePlan) { + turtle.shape(ShapeType::Turtle); // Draw some circles - plan.set_pen_color(RED); - plan.set_pen_width(0.5); - plan.left(90.0); - plan.set_speed(999); - plan.circle_left(100.0, 540.0, 72); // partial circle to the left + turtle.set_pen_color(RED); + turtle.set_pen_width(0.5); + turtle.left(90.0); + turtle.set_speed(999); + turtle.circle_left(100.0, 540.0, 72); // partial circle to the left - plan.forward(150.0); - plan.set_speed(100); - plan.set_pen_color(BLUE); - plan.circle_right(50.0, 270.0, 72); // partial circle to the right - // Set animation speed - plan.set_speed(20); - plan.forward(150.0); - plan.circle_left(50.0, 180.0, 12); - plan.circle_right(50.0, 180.0, 12); + turtle.forward(150.0); + turtle.set_speed(100); + turtle.set_pen_color(BLUE); + turtle.circle_right(50.0, 270.0, 72); // partial circle to the right + // Set animation speed + turtle.set_speed(20); + turtle.forward(150.0); + turtle.circle_left(50.0, 180.0, 12); + turtle.circle_right(50.0, 180.0, 12); - plan.set_speed(700); - plan.set_pen_color(GREEN); - plan.circle_left(50.0, 180.0, 36); // Half circle to the left - - // Create turtle app with animation (speed = 100 pixels/sec) - let mut app = TurtleApp::new().with_commands(plan.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + turtle.set_speed(700); + turtle.set_pen_color(GREEN); + turtle.circle_left(50.0, 180.0, 36); // Half circle to the left } diff --git a/turtle-lib-macroquad/examples/direction_test_1.rs b/turtle-lib-macroquad/examples/direction_test_1.rs index bc1756f..3d142f8 100644 --- a/turtle-lib-macroquad/examples/direction_test_1.rs +++ b/turtle-lib-macroquad/examples/direction_test_1.rs @@ -1,31 +1,13 @@ -//! Test circle_left and circle_right commands +//! Test direction and movement commands -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Circle Test")] -async fn main() { - // Create a turtle plan - let mut plan = create_turtle(); - +#[turtle_main("Direction Test")] +fn draw(turtle: &mut TurtlePlan) { // Set animation speed - plan.set_speed(50); - plan.right(45.0); - plan.forward(100.0); - plan.right(45.0); - plan.forward(100.0); - - // Create turtle app with animation (speed = 100 pixels/sec) - let mut app = TurtleApp::new().with_commands(plan.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + turtle.set_speed(50); + turtle.right(45.0); + turtle.forward(100.0); + turtle.right(45.0); + turtle.forward(100.0); } diff --git a/turtle-lib-macroquad/examples/fill_advanced.rs b/turtle-lib-macroquad/examples/fill_advanced.rs index 9a9d2b1..f5f656e 100644 --- a/turtle-lib-macroquad/examples/fill_advanced.rs +++ b/turtle-lib-macroquad/examples/fill_advanced.rs @@ -1,4 +1,6 @@ //! Advanced fill example with multiple holes and complex shapes +//! +//! This example uses manual setup to demonstrate custom window size and UI elements. use macroquad::{miniquad::window::set_window_size, prelude::*}; use turtle_lib_macroquad::*; diff --git a/turtle-lib-macroquad/examples/fill_demo.rs b/turtle-lib-macroquad/examples/fill_demo.rs index 2a80486..2f9261c 100644 --- a/turtle-lib-macroquad/examples/fill_demo.rs +++ b/turtle-lib-macroquad/examples/fill_demo.rs @@ -1,55 +1,43 @@ //! Fill demonstration with holes -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Fill Demo")] -async fn main() { - let mut t = create_turtle(); - +#[turtle_main("Fill Demo")] +fn draw(turtle: &mut TurtlePlan) { // Example from requirements: circle with hole (like a donut) - t.set_pen_color(BLUE); - t.set_pen_width(3.0); - t.right(90.0); + turtle.set_pen_color(BLUE); + turtle.set_pen_width(3.0); + turtle.right(90.0); // Set fill color and begin fill - t.set_fill_color(RED); - t.begin_fill(); + turtle.set_fill_color(RED); + turtle.begin_fill(); // Outer circle - t.circle_right(150.0, 360.0, 72); + turtle.circle_right(150.0, 360.0, 72); // Move to start of inner circle (hole) // pen_up doesn't matter for fill - vertices still recorded! - t.pen_up(); - t.forward(50.0); - t.pen_down(); + turtle.pen_up(); + turtle.forward(50.0); + turtle.pen_down(); // Inner circle (creates a hole) - t.circle_right(150.0, 360.0, 72); + turtle.circle_right(150.0, 360.0, 72); - t.end_fill(); + turtle.end_fill(); // Draw a square with no fill - t.pen_up(); - t.forward(100.0); - t.pen_down(); - t.set_pen_color(GREEN); + turtle.pen_up(); + turtle.forward(100.0); + turtle.pen_down(); + turtle.set_pen_color(GREEN); for _ in 0..4 { - t.forward(100.0); - t.right(90.0); + turtle.forward(100.0); + turtle.right(90.0); } // Set animation speed - t.set_speed(100); - - let mut app = TurtleApp::new().with_commands(t.build()); - - loop { - clear_background(WHITE); - app.update(); - app.render(); - next_frame().await - } + turtle.set_speed(100); } diff --git a/turtle-lib-macroquad/examples/fill_requirements.rs b/turtle-lib-macroquad/examples/fill_requirements.rs index 3bc7b7c..4ecaa55 100644 --- a/turtle-lib-macroquad/examples/fill_requirements.rs +++ b/turtle-lib-macroquad/examples/fill_requirements.rs @@ -1,12 +1,9 @@ //! Example matching the original requirements exactly -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Fill Example - Original Requirements")] -async fn main() { - let mut turtle = create_turtle(); - +#[turtle_main("Fill Example - Original Requirements")] +fn draw(turtle: &mut TurtlePlan) { turtle.right(90.0); turtle.set_pen_width(3.0); turtle.set_speed(900); @@ -37,30 +34,4 @@ async fn main() { // Set speed for animation turtle.set_speed(200); - - let mut app = TurtleApp::new().with_commands(turtle.build()); - - loop { - clear_background(WHITE); - app.update(); - app.render(); - - // Instructions - draw_text( - "Fill Example - Circle filled with red, square not filled", - 10.0, - 20.0, - 20.0, - BLACK, - ); - draw_text( - "Mouse: drag to pan, scroll to zoom", - 10.0, - 40.0, - 16.0, - DARKGRAY, - ); - - next_frame().await - } } diff --git a/turtle-lib-macroquad/examples/hello_turtle.rs b/turtle-lib-macroquad/examples/hello_turtle.rs new file mode 100644 index 0000000..e429b3a --- /dev/null +++ b/turtle-lib-macroquad/examples/hello_turtle.rs @@ -0,0 +1,14 @@ +//! Minimal turtle example - just 10 lines! +//! +//! This is the simplest possible turtle program using the macro. + +use turtle_lib_macroquad::*; + +#[turtle_main("Hello Turtle")] +fn hello() { + turtle.set_pen_color(BLUE); + for _ in 0..4 { + turtle.forward(100.0); + turtle.right(90.0); + } +} diff --git a/turtle-lib-macroquad/examples/koch.rs b/turtle-lib-macroquad/examples/koch.rs index bd47dbc..894c1ef 100644 --- a/turtle-lib-macroquad/examples/koch.rs +++ b/turtle-lib-macroquad/examples/koch.rs @@ -1,50 +1,37 @@ //! Koch snowflake fractal example -use macroquad::prelude::*; use turtle_lib_macroquad::*; -fn koch(depth: u32, plan: &mut TurtlePlan, distance: f32) { +fn koch(depth: u32, turtle: &mut TurtlePlan, distance: f32) { if depth == 0 { - plan.forward(distance); + turtle.forward(distance); } else { let new_distance = distance / 3.0; - koch(depth - 1, plan, new_distance); - plan.left(60.0); - koch(depth - 1, plan, new_distance); - plan.right(120.0); - koch(depth - 1, plan, new_distance); - plan.left(60.0); - koch(depth - 1, plan, new_distance); + koch(depth - 1, turtle, new_distance); + turtle.left(60.0); + koch(depth - 1, turtle, new_distance); + turtle.right(120.0); + koch(depth - 1, turtle, new_distance); + turtle.left(60.0); + koch(depth - 1, turtle, new_distance); } } -#[macroquad::main("Koch Snowflake")] -async fn main() { - let mut plan = create_turtle(); - +#[turtle_main("Koch Snowflake")] +fn draw(turtle: &mut TurtlePlan) { // Position turtle - plan.set_speed(1001); - plan.pen_up(); - plan.backward(150.0); + turtle.set_speed(1001); + turtle.pen_up(); + turtle.backward(150.0); - plan.pen_down(); + turtle.pen_down(); // Draw Koch snowflake (triangle of Koch curves) for _ in 0..3 { - koch(4, &mut plan, 300.0); - plan.right(120.0); - plan.set_speed(1200); + koch(4, turtle, 300.0); + turtle.right(120.0); + turtle.set_speed(1200); } - plan.hide(); // Hide turtle when done - - // Create app with animation - let mut app = TurtleApp::new().with_commands(plan.build()); - - loop { - clear_background(WHITE); - app.update(); - app.render(); - next_frame().await - } + turtle.hide(); // Hide turtle when done } diff --git a/turtle-lib-macroquad/examples/logging_example.rs b/turtle-lib-macroquad/examples/logging_example.rs index e8b602a..fa03847 100644 --- a/turtle-lib-macroquad/examples/logging_example.rs +++ b/turtle-lib-macroquad/examples/logging_example.rs @@ -17,6 +17,8 @@ //! RUST_LOG=turtle_lib_macroquad::tessellation=debug cargo run --example logging_example //! RUST_LOG=turtle_lib_macroquad::execution=debug cargo run --example logging_example //! ``` +//! +//! Note: This example uses manual setup to demonstrate custom initialization logic. use macroquad::prelude::*; use turtle_lib_macroquad::*; @@ -27,11 +29,10 @@ async fn main() { // This will respect the RUST_LOG environment variable tracing_subscriber::fmt() .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| { - // Default to showing info-level logs if RUST_LOG is not set - tracing_subscriber::EnvFilter::new("turtle_lib_macroquad=info") - }), + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { + // Default to showing info-level logs if RUST_LOG is not set + tracing_subscriber::EnvFilter::new("turtle_lib_macroquad=info") + }), ) .with_target(true) // Show which module the log came from .with_thread_ids(false) @@ -40,7 +41,9 @@ async fn main() { .init(); tracing::info!("Starting turtle graphics example with logging enabled"); - tracing::info!("Try running with: RUST_LOG=turtle_lib_macroquad=debug cargo run --example logging_example"); + tracing::info!( + "Try running with: RUST_LOG=turtle_lib_macroquad=debug cargo run --example logging_example" + ); // Create a turtle plan with fill operations to see detailed logging let mut t = create_turtle(); diff --git a/turtle-lib-macroquad/examples/macro_demo.rs b/turtle-lib-macroquad/examples/macro_demo.rs new file mode 100644 index 0000000..988a9e2 --- /dev/null +++ b/turtle-lib-macroquad/examples/macro_demo.rs @@ -0,0 +1,17 @@ +//! Simple demo of the turtle_main macro +//! +//! This example shows how the turtle_main macro simplifies turtle programs +//! by automatically handling window setup, turtle creation, and the render loop. + +use turtle_lib_macroquad::*; + +#[turtle_main("Macro Demo - Simple Square")] +fn draw_square(turtle: &mut TurtlePlan) { + turtle.set_pen_color(BLUE); + turtle.set_pen_width(3.0); + + for _ in 0..4 { + turtle.forward(150.0); + turtle.right(90.0); + } +} diff --git a/turtle-lib-macroquad/examples/macro_demo_inline.rs b/turtle-lib-macroquad/examples/macro_demo_inline.rs new file mode 100644 index 0000000..c16c113 --- /dev/null +++ b/turtle-lib-macroquad/examples/macro_demo_inline.rs @@ -0,0 +1,17 @@ +//! Demo of the turtle_main macro with inline code +//! +//! This example shows that you can write your turtle code directly +//! in the function body without taking a turtle parameter. + +use turtle_lib_macroquad::*; + +#[turtle_main("Macro Demo - Inline Spiral")] +fn draw_spiral() { + turtle.set_pen_color(RED); + turtle.set_pen_width(2.0); + + for i in 0..36 { + turtle.forward(i as f32 * 3.0); + turtle.right(25.0); + } +} diff --git a/turtle-lib-macroquad/examples/macro_full_demo.rs b/turtle-lib-macroquad/examples/macro_full_demo.rs new file mode 100644 index 0000000..2d7a791 --- /dev/null +++ b/turtle-lib-macroquad/examples/macro_full_demo.rs @@ -0,0 +1,57 @@ +//! Comprehensive macro example showing various turtle features +//! +//! This example demonstrates: +//! - Colors and pen settings +//! - Fills +//! - Circles +//! - Animation speed + +use turtle_lib_macroquad::*; + +#[turtle_main("Turtle Macro - Full Demo")] +fn full_demo(turtle: &mut TurtlePlan) { + // Draw a colorful flower + turtle.set_speed(200); + + // Center the drawing + turtle.pen_up(); + turtle.go_to(vec2(200.0, 300.0)); + turtle.pen_down(); + + // Draw petals + for i in 0..8 { + let hue = i as f32 / 8.0; + let color = Color::from_rgba( + ((hue * 360.0).to_radians().sin() * 127.0 + 128.0) as u8, + ((hue * 360.0 + 120.0).to_radians().sin() * 127.0 + 128.0) as u8, + ((hue * 360.0 + 240.0).to_radians().sin() * 127.0 + 128.0) as u8, + 255, + ); + + turtle.set_fill_color(color); + turtle.set_pen_color(color); + turtle.begin_fill(); + + // Draw a petal using circles + turtle.circle_left(50.0, 180.0, 20); + turtle.left(90.0); + turtle.circle_left(50.0, 180.0, 20); + turtle.left(90.0); + + turtle.end_fill(); + + // Move to next petal position + turtle.right(45.0); + } + + // Draw center circle + turtle.pen_up(); + turtle.go_to(vec2(200.0, 300.0)); + turtle.pen_down(); + turtle.set_fill_color(YELLOW); + turtle.set_pen_color(ORANGE); + turtle.set_pen_width(2.0); + turtle.begin_fill(); + turtle.circle_left(20.0, 360.0, 36); + turtle.end_fill(); +} diff --git a/turtle-lib-macroquad/examples/nikolaus.rs b/turtle-lib-macroquad/examples/nikolaus.rs index 44b9c95..a15cc28 100644 --- a/turtle-lib-macroquad/examples/nikolaus.rs +++ b/turtle-lib-macroquad/examples/nikolaus.rs @@ -1,74 +1,57 @@ //! Nikolaus example - draws a house-like figure -use macroquad::prelude::*; use turtle_lib_macroquad::*; -fn nikolausquadrat(plan: &mut TurtlePlan, groesse: f32) { - plan.forward(groesse); - plan.left(90.0); - plan.forward(groesse); - plan.left(90.0); - plan.forward(groesse); - plan.left(90.0); - plan.forward(groesse); - plan.left(90.0); +fn nikolausquadrat(turtle: &mut TurtlePlan, groesse: f32) { + turtle.forward(groesse); + turtle.left(90.0); + turtle.forward(groesse); + turtle.left(90.0); + turtle.forward(groesse); + turtle.left(90.0); + turtle.forward(groesse); + turtle.left(90.0); } -fn nikolausdiag(plan: &mut TurtlePlan, groesse: f32) { +fn nikolausdiag(turtle: &mut TurtlePlan, groesse: f32) { let quadrat = groesse * groesse; let diag = (quadrat + quadrat).sqrt(); - plan.left(45.0); - plan.forward(diag); - plan.left(45.0); - nikolausdach2(plan, groesse); - plan.left(45.0); - plan.forward(diag); - plan.left(45.0); + turtle.left(45.0); + turtle.forward(diag); + turtle.left(45.0); + nikolausdach2(turtle, groesse); + turtle.left(45.0); + turtle.forward(diag); + turtle.left(45.0); } -fn nikolausdach2(plan: &mut TurtlePlan, groesse: f32) { +fn nikolausdach2(turtle: &mut TurtlePlan, groesse: f32) { let quadrat = groesse * groesse; let diag = (quadrat + quadrat).sqrt(); - plan.left(45.0); - plan.forward(diag / 2.0); - plan.left(90.0); - plan.forward(diag / 2.0); - plan.left(45.0); + turtle.left(45.0); + turtle.forward(diag / 2.0); + turtle.left(90.0); + turtle.forward(diag / 2.0); + turtle.left(45.0); } -fn nikolaus(plan: &mut TurtlePlan, groesse: f32) { - nikolausquadrat(plan, groesse); - nikolausdiag(plan, groesse); +fn nikolaus(turtle: &mut TurtlePlan, groesse: f32) { + nikolausquadrat(turtle, groesse); + nikolausdiag(turtle, groesse); } -#[macroquad::main("Nikolaus")] -async fn main() { - // Create a turtle plan - let mut plan = create_turtle(); - plan.shape(ShapeType::Turtle); +#[turtle_main("Nikolaus")] +fn draw(turtle: &mut TurtlePlan) { + turtle.shape(ShapeType::Turtle); // Position the turtle (pen up, move, pen down) - plan.pen_up(); - plan.backward(80.0); - plan.left(90.0); - plan.forward(50.0); - plan.right(90.0); - plan.pen_down(); + turtle.pen_up(); + turtle.backward(80.0); + turtle.left(90.0); + turtle.forward(50.0); + turtle.right(90.0); + turtle.pen_down(); - nikolaus(&mut plan, 100.0); - - // Create turtle app with animation - let mut app = TurtleApp::new().with_commands(plan.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + nikolaus(turtle, 100.0); } diff --git a/turtle-lib-macroquad/examples/shapes.rs b/turtle-lib-macroquad/examples/shapes.rs index 401b972..830ab2e 100644 --- a/turtle-lib-macroquad/examples/shapes.rs +++ b/turtle-lib-macroquad/examples/shapes.rs @@ -1,50 +1,32 @@ //! Example demonstrating different turtle shapes -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Turtle Shapes")] -async fn main() { - // Create a turtle plan that demonstrates different shapes - let mut plan = create_turtle(); - +#[turtle_main("Turtle Shapes")] +fn draw(turtle: &mut TurtlePlan) { // Start with triangle (default) - plan.forward(100.0); - plan.right(90.0); + turtle.forward(100.0); + turtle.right(90.0); // Change to turtle shape - plan.shape(ShapeType::Turtle); - plan.forward(100.0); - plan.right(90.0); + turtle.shape(ShapeType::Turtle); + turtle.forward(100.0); + turtle.right(90.0); // Change to circle - plan.shape(ShapeType::Circle); - plan.forward(100.0); - plan.right(90.0); + turtle.shape(ShapeType::Circle); + turtle.forward(100.0); + turtle.right(90.0); // Change to square - plan.shape(ShapeType::Square); - plan.forward(100.0); - plan.right(90.0); + turtle.shape(ShapeType::Square); + turtle.forward(100.0); + turtle.right(90.0); // Change to arrow - plan.shape(ShapeType::Arrow); - plan.forward(100.0); + turtle.shape(ShapeType::Arrow); + turtle.forward(100.0); // Set animation speed - plan.set_speed(50); - - // Create turtle app with animation (speed = 100 pixels/sec for slower animation) - let mut app = TurtleApp::new().with_commands(plan.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + turtle.set_speed(50); } diff --git a/turtle-lib-macroquad/examples/square.rs b/turtle-lib-macroquad/examples/square.rs index c814144..42792fd 100644 --- a/turtle-lib-macroquad/examples/square.rs +++ b/turtle-lib-macroquad/examples/square.rs @@ -1,33 +1,16 @@ //! Simple square example demonstrating basic turtle graphics -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Turtle Square")] -async fn main() { - // Create a turtle plan - let mut plan = create_turtle(); - plan.shape(ShapeType::Turtle); +#[turtle_main("Turtle Square")] +fn draw(turtle: &mut TurtlePlan) { + turtle.shape(ShapeType::Turtle); // Draw a square for _ in 0..4 { - plan.forward(100.0).right(90.0); + turtle.forward(100.0).right(90.0); } // Set animation speed - plan.set_speed(50); - - // Create turtle app with animation - let mut app = TurtleApp::new().with_commands(plan.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + turtle.set_speed(50); } diff --git a/turtle-lib-macroquad/examples/stern.rs b/turtle-lib-macroquad/examples/stern.rs index 975f1e0..4d9f6f8 100644 --- a/turtle-lib-macroquad/examples/stern.rs +++ b/turtle-lib-macroquad/examples/stern.rs @@ -1,38 +1,21 @@ -//! Simple square example demonstrating basic turtle graphics +//! Star pattern example demonstrating complex turtle patterns -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Turtle Square")] -async fn main() { - // Create a turtle plan - let mut plan = create_turtle(); - plan.shape(ShapeType::Turtle); - plan.set_speed(1500); - plan.set_pen_width(0.5); +#[turtle_main("Star Pattern")] +fn draw(turtle: &mut TurtlePlan) { + turtle.shape(ShapeType::Turtle); + turtle.set_speed(1500); + turtle.set_pen_width(0.5); // Draw a 5-pointed star pattern repeatedly for _i in 0..50000 { - plan.forward(200.0); - plan.circle_left(10.0, 72.0, 1000); - plan.circle_right(5.0, 360.0, 1000); - plan.circle_left(10.0, 72.0, 1000); + turtle.forward(200.0); + turtle.circle_left(10.0, 72.0, 1000); + turtle.circle_right(5.0, 360.0, 1000); + turtle.circle_left(10.0, 72.0, 1000); } // Set animation speed - plan.set_speed(300); - - // Create turtle app with animation (speed = 100 pixels/sec) - let mut app = TurtleApp::new().with_commands(plan.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + turtle.set_speed(300); } diff --git a/turtle-lib-macroquad/examples/yinyang.rs b/turtle-lib-macroquad/examples/yinyang.rs index 8d637a9..a8a8f91 100644 --- a/turtle-lib-macroquad/examples/yinyang.rs +++ b/turtle-lib-macroquad/examples/yinyang.rs @@ -1,47 +1,30 @@ -//! Simple square example demonstrating basic turtle graphics +//! Yin-Yang symbol example demonstrating multi-contour fills -use macroquad::prelude::*; use turtle_lib_macroquad::*; -#[macroquad::main("Turtle Square")] -async fn main() { - // Create a turtle plan - let mut t = create_turtle(); - t.set_speed(900); +#[turtle_main("Yin-Yang")] +fn draw(turtle: &mut TurtlePlan) { + turtle.set_speed(900); - t.circle_left(90.0, 180.0, 36); - t.begin_fill(); - t.circle_left(90.0, 180.0, 36); - t.circle_left(45.0, 180.0, 26); - t.circle_right(45.0, 180.0, 26); - t.pen_up(); - t.right(90.0); - t.forward(37.0); - t.left(90.0); - t.pen_down(); - t.circle_right(8.0, 360.0, 12); - t.pen_up(); - t.right(90.0); - t.forward(90.0); - t.left(90.0); - t.pen_down(); - t.circle_right(8.0, 360.0, 12); - t.end_fill(); + turtle.circle_left(90.0, 180.0, 36); + turtle.begin_fill(); + turtle.circle_left(90.0, 180.0, 36); + turtle.circle_left(45.0, 180.0, 26); + turtle.circle_right(45.0, 180.0, 26); + turtle.pen_up(); + turtle.right(90.0); + turtle.forward(37.0); + turtle.left(90.0); + turtle.pen_down(); + turtle.circle_right(8.0, 360.0, 12); + turtle.pen_up(); + turtle.right(90.0); + turtle.forward(90.0); + turtle.left(90.0); + turtle.pen_down(); + turtle.circle_right(8.0, 360.0, 12); + turtle.end_fill(); // Set animation speed - t.set_speed(1000); - - // Create turtle app with animation (speed = 100 pixels/sec) - let mut app = TurtleApp::new().with_commands(t.build()); - - // Main loop - loop { - clear_background(WHITE); - - // Update and render - app.update(); - app.render(); - - next_frame().await - } + turtle.set_speed(1000); } diff --git a/turtle-lib-macroquad/src/lib.rs b/turtle-lib-macroquad/src/lib.rs index 7e638e9..093e1aa 100644 --- a/turtle-lib-macroquad/src/lib.rs +++ b/turtle-lib-macroquad/src/lib.rs @@ -3,7 +3,29 @@ //! This library provides a turtle graphics API for creating drawings and animations //! using the Macroquad game framework. //! -//! # Example +//! # Quick Start with `turtle_main` Macro +//! +//! The easiest way to create a turtle program is using the `turtle_main` macro: +//! +//! ```no_run +//! use macroquad::prelude::*; +//! use turtle_lib_macroquad::*; +//! +//! #[turtle_main("My Drawing")] +//! fn draw(turtle: &mut TurtlePlan) { +//! turtle.set_pen_color(RED); +//! turtle.forward(100.0); +//! turtle.right(90.0); +//! turtle.forward(100.0); +//! } +//! ``` +//! +//! The macro automatically handles window setup, rendering loop, and quit handling. +//! +//! # Manual Setup Example +//! +//! For more control, you can set up the application manually: +//! //! ```no_run //! use macroquad::prelude::*; //! use turtle_lib_macroquad::*; @@ -43,6 +65,14 @@ pub use shapes::{ShapeType, TurtleShape}; pub use state::{DrawCommand, TurtleState, TurtleWorld}; pub use tweening::TweenController; +// Re-export the turtle_main macro +pub use turtle_lib_macroquad_macros::turtle_main; + +// Re-export common macroquad types and colors for convenience +pub use macroquad::prelude::{ + vec2, BLACK, BLUE, DARKGRAY, GOLD, GREEN, ORANGE, PURPLE, RED, WHITE, YELLOW, +}; + use macroquad::prelude::*; /// Main turtle application struct