add a macro for simpler first examples.
This commit is contained in:
parent
453e8e39bd
commit
cef63ca32a
@ -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<MeshData, Box<dyn std::error::Error>>
|
||||
pub fn tessellate_multi_contour(contours: &[Vec<Vec2>], color: Color) -> Result<MeshData, Box<dyn std::error::Error>>
|
||||
pub fn tessellate_stroke(vertices: &[Vec2], color: Color, width: f32, closed: bool) -> Result<MeshData, Box<dyn std::error::Error>>
|
||||
pub fn tessellate_circle(center: Vec2, radius: f32, color: Color, filled: bool, stroke_width: f32) -> Result<MeshData, Box<dyn std::error::Error>>
|
||||
pub fn tessellate_arc(center: Vec2, radius: f32, start_angle: f32, arc_angle: f32, color: Color, stroke_width: f32, segments: usize) -> Result<MeshData, Box<dyn std::error::Error>>
|
||||
```
|
||||
|
||||
### 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)
|
||||
200
.github/copilot-instructions.md
vendored
Normal file
200
.github/copilot-instructions.md
vendored
Normal file
@ -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<Vec<Vec2>>` (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
|
||||
@ -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
|
||||
|
||||
13
turtle-lib-macroquad-macros/Cargo.toml
Normal file
13
turtle-lib-macroquad-macros/Cargo.toml
Normal file
@ -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"] }
|
||||
72
turtle-lib-macroquad-macros/README.md
Normal file
72
turtle-lib-macroquad-macros/README.md
Normal file
@ -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.
|
||||
175
turtle-lib-macroquad-macros/src/lib.rs
Normal file
175
turtle-lib-macroquad-macros/src/lib.rs
Normal file
@ -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)
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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 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 fill_advanced # Self-intersecting fills
|
||||
cargo run --example koch # Recursive fractals
|
||||
cargo run --example fill_demo # Multiple independent fills
|
||||
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
|
||||
|
||||
66
turtle-lib-macroquad/examples/cheese_macro.rs
Normal file
66
turtle-lib-macroquad/examples/cheese_macro.rs
Normal file
@ -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...");
|
||||
}
|
||||
@ -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
|
||||
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
|
||||
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.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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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::*;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
14
turtle-lib-macroquad/examples/hello_turtle.rs
Normal file
14
turtle-lib-macroquad/examples/hello_turtle.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,8 +29,7 @@ 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(|_| {
|
||||
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")
|
||||
}),
|
||||
@ -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();
|
||||
|
||||
17
turtle-lib-macroquad/examples/macro_demo.rs
Normal file
17
turtle-lib-macroquad/examples/macro_demo.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
17
turtle-lib-macroquad/examples/macro_demo_inline.rs
Normal file
17
turtle-lib-macroquad/examples/macro_demo_inline.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
57
turtle-lib-macroquad/examples/macro_full_demo.rs
Normal file
57
turtle-lib-macroquad/examples/macro_full_demo.rs
Normal file
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user