All issues identified across Sections 3.1, 3.2, and 3.3 have been
resolved, verified with unit tests, automated compilation checks under
`-D float_literal_f32_fallback`, and headless SVG export runs.
-
**[angle.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/general/angle.rs)**:
- Implemented `From<f64>` and `From<usize>` for `Degrees`.
- Added unit tests `from_integer` and `from_f64` to verify conversion
accuracy.
-
**[fontsize.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/general/fontsize.rs)**:
- Implemented `From<f64>` for `FontSize`.
- Refactored `FontSize::value(self)` to pass Copy type by value.
- Added unit test `font_size_conversions`.
-
**[general.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/general.rs)**:
- Implemented `From<f64>`, `From<i32>`, and `From<usize>` for
`AnimationSpeed`.
- Added unit test `animation_speed_conversions`.
- Re-exported `macroquad` crate (`pub use macroquad;`) so downstream
code and macro expansions have reliable direct access.
-
**[export.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/export.rs)**:
- Made `parse_svg_export_arg()` public.
- Implemented `run_headless_svg_export<F>(mut build_commands: F,
filename: &str) -> Result<(), ExportError>` that executes commands
using `app.step_animations()`, avoiding all window/GUI dependencies
and never calling `std::process::exit`.
- Updated `handle_svg_export` to delegate to
`run_headless_svg_export`.
-
**[lib.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/lib.rs)**:
- Extracted `pub fn step_animations(&mut self)` from `update(&mut
self)`, allowing command queue draining and tween stepping
headlessly without querying window mouse position or events.
-
**[state.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/state.rs)**:
- Changed `TurtleWorld::new()` to initialize camera with
`Camera2D::default()` instead of querying `screen_width()` /
`screen_height()`, eliminating panics when running without a
Macroquad window.
-
**[turtle-lib-macros/src/lib.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib-macros/src/lib.rs)**:
- Added `validate_input` helper providing clean compile diagnostics
with spans for:
- Multiple arguments: `#[turtle_main] functions must take either 0
arguments or a single &mut TurtlePlan`
- Non-unit return types: `#[turtle_main] functions cannot have a
return type`
- Async functions: `#[turtle_main] functions cannot be async`
- Replaced `#[macroquad::main]` wrapper expansion with a native `fn
main()` that inspects CLI arguments first. If `--export-svg` is
present, it runs `run_headless_svg_export` directly and returns
cleanly without opening a window. Otherwise, it launches
`macroquad::Window::new(#window_title, async { ... })`.
- Added 5 unit tests in `turtle-lib-macros` testing signature
validation.
-
**[.vscode/launch.json](file:///home/dietrich/Projekte/Source/turtlers/.vscode/launch.json)**:
- Removed stale references to nonexistent `turtle-example` and
`turtle-ui`.
- Added debug configurations for `turtle-lib` tests,
`turtle-lib-macros` tests, `hello_turtle`, and `breadboard`.
-
**[breadboard.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/examples/breadboard.rs)**:
- Refactored example to use `#[turtle_main("Breadboard")]`.
- Removed `#[cfg(feature = "svg")]` and the early-exit message; the
example now renders directly on screen by default and supports
`--features svg -- --export-svg breadboard.svg`.
-
**[README.md](file:///home/dietrich/Projekte/Source/turtlers/README.md)**:
- Documented optional user-level `~/.cargo/config.toml` mold/lld
fast-linking configuration under "Building and Running".
---
```bash
cargo test --workspace
```
- **Result**: 21 passed (16 in `turtle-lib`, 5 in `turtle-lib-macros`),
34 doctests passed, 0 failed.
```bash
RUSTFLAGS="-D float_literal_f32_fallback" cargo check --workspace
--all-targets --all-features
RUSTFLAGS="-D float_literal_f32_fallback" cargo check --package
turtle-lib --examples --all-features
```
- **Result**: Passed with 0 errors and 0 fallback warnings across all
workspace crates and all 30 examples.
```bash
cargo run --package turtle-lib --example hello_turtle --features svg --
--export-svg hello.svg
cargo run --package turtle-lib --example breadboard --features svg --
--export-svg breadboard.svg
```
- **Result**: Both exported SVG files successfully and exited with code
0 without creating or flashing a graphical window.
```bash
cargo check --package turtle-lib --example breadboard
```
- **Result**: Compiled cleanly with 0 errors when SVG feature is
disabled.
417 lines
11 KiB
Markdown
417 lines
11 KiB
Markdown
# Turtle Graphics Library
|
|
|
|
A modern turtle graphics library for Rust built on [Macroquad](https://macroquad.rs/) with [Lyon](https://github.com/nical/lyon) for high-quality GPU-accelerated rendering.
|
|
|
|
## Features
|
|
|
|
- 🎨 **Simple Builder API**: Chain commands like `forward(100).right(90)`
|
|
- ⚡ **Smooth Animations**: Tweening support with easing functions and live fill preview
|
|
- 🚀 **Instant Mode**: Execute commands immediately without animation (speed ≥ 1000)
|
|
- 🎯 **High-Quality Rendering**: Complete Lyon tessellation pipeline with GPU acceleration
|
|
- **Multi-Contour Fills**: Automatic hole detection with EvenOdd fill rule - draw cheese with holes!
|
|
- 📐 **Self-Intersecting Paths**: Stars, complex shapes - all handled correctly
|
|
- 🐢 **Multiple Turtle Shapes**: Triangle, classic turtle, circle, square, arrow, and custom shapes
|
|
- 🔍 **Structured Logging**: Optional `tracing` integration for debugging (zero overhead when disabled)
|
|
- 💨 **Lightweight**: Fast compilation and runtime
|
|
- 📤 **SVG Export**: Export drawings to SVG format with viewBox and padding (feature-gated)
|
|
|
|
## Quick Start
|
|
|
|
The simplest example to draw a square:
|
|
|
|
```rust
|
|
//! Minimal turtle example - just 10 lines!
|
|
//!
|
|
//! This is the simplest possible turtle program using the macro.
|
|
|
|
use turtle_lib::*;
|
|
|
|
#[turtle_main]
|
|
fn hello() {
|
|
turtle.set_pen_color(BLUE);
|
|
for _ in 0..4 {
|
|
turtle.forward(100.0);
|
|
turtle.right(90.0);
|
|
}
|
|
}
|
|
```
|
|
|
|
The turtle starts at the center of the window, facing right (0 degrees). The above code draws a blue square.
|
|
|
|
The `turtle_main` macro sets up the Macroquad window, turtle initialization, and main loop for you. It expands to code similar to this:
|
|
|
|
```rust
|
|
use macroquad::prelude::*;
|
|
use turtle_lib::*;
|
|
|
|
#[macroquad::main("Turtle")]
|
|
async fn main() {
|
|
// Create a turtle plan
|
|
let mut plan = create_turtle_plan();
|
|
|
|
// Set speed (part of the plan)
|
|
plan.set_speed(100);
|
|
|
|
// Draw a square
|
|
for _ in 0..4 {
|
|
plan.forward(100).right(90);
|
|
}
|
|
|
|
// Create app (speed is managed by commands)
|
|
let mut app = TurtleApp::new().with_commands(plan.build());
|
|
|
|
loop {
|
|
clear_background(WHITE);
|
|
app.update();
|
|
app.render();
|
|
next_frame().await
|
|
}
|
|
}
|
|
```
|
|
|
|
## API Overview
|
|
|
|
### Creating Plans
|
|
|
|
```rust
|
|
let mut plan = create_turtle_plan();
|
|
|
|
// Movement
|
|
plan.forward(100);
|
|
plan.backward(50);
|
|
|
|
// Rotation
|
|
plan.left(90); // degrees
|
|
plan.right(45);
|
|
|
|
// Circular arcs
|
|
plan.circle_left(50.0, 180.0, 36); // radius, angle (degrees), segments
|
|
plan.circle_right(50.0, 180.0, 36); // draws arc to the right
|
|
|
|
// Pen control
|
|
plan.pen_up();
|
|
plan.pen_down();
|
|
|
|
// Filling (with automatic hole detection)
|
|
plan.set_fill_color(BLUE);
|
|
plan.begin_fill();
|
|
// ... draw shape ...
|
|
plan.end_fill(); // Auto-closes and applies fill
|
|
|
|
// Appearance
|
|
plan.set_pen_color(RED);
|
|
plan.set_pen_width(5.0);
|
|
plan.hide();
|
|
plan.show();
|
|
|
|
// Speed control (dynamic)
|
|
plan.set_speed(100); // Animated mode (< 1000)
|
|
plan.set_speed(1000); // Instant mode (>= 1000)
|
|
|
|
// Turtle shapes
|
|
plan.shape(ShapeType::Triangle);
|
|
plan.shape(ShapeType::Turtle); // Classic turtle shape
|
|
plan.shape(ShapeType::Circle);
|
|
plan.shape(ShapeType::Square);
|
|
plan.shape(ShapeType::Arrow);
|
|
|
|
// Custom shapes
|
|
let custom = TurtleShape::new(
|
|
vec![vec2(10.0, 0.0), vec2(-5.0, 5.0), vec2(-5.0, -5.0)],
|
|
true // filled
|
|
);
|
|
plan.set_shape(custom);
|
|
|
|
// Method chaining
|
|
plan.forward(100).right(90).forward(50);
|
|
```
|
|
|
|
### Execution Modes
|
|
|
|
Speed controlled via commands, allowing dynamic switching during execution:
|
|
|
|
```rust
|
|
let mut plan = create_turtle_plan();
|
|
|
|
// Fast initial positioning (instant mode)
|
|
plan.set_speed(1000);
|
|
plan.pen_up();
|
|
plan.go_to(vec2(-100.0, -100.0));
|
|
|
|
// Slow animated drawing
|
|
plan.set_speed(50);
|
|
plan.pen_down();
|
|
plan.forward(200);
|
|
plan.right(90);
|
|
|
|
// Create app
|
|
let app = TurtleApp::new().with_commands(plan.build());
|
|
```
|
|
|
|
**Speed Modes:**
|
|
|
|
- **Speed < 1000**: Animated mode with smooth tweening
|
|
- **Speed >= 1000**: Instant mode (no animation) the bigger the number the more segments will be added per frame.
|
|
- Default speed is 100.0 if not specified
|
|
|
|
## Debugging and Logging
|
|
|
|
The library uses [`tracing`](https://docs.rs/tracing) for structured diagnostic logging. This is completely optional - if you don't set up a subscriber, there's zero overhead.
|
|
|
|
### Enable Logging
|
|
|
|
```rust
|
|
// Add to your Cargo.toml:
|
|
// tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
|
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
|
.init();
|
|
```
|
|
|
|
Control verbosity with the `RUST_LOG` environment variable:
|
|
|
|
```bash
|
|
# Show debug output
|
|
RUST_LOG=turtle_lib=debug cargo run
|
|
|
|
# Very verbose trace output
|
|
RUST_LOG=turtle_lib=trace cargo run
|
|
```
|
|
|
|
**See the complete example**: [`examples/logging_example.rs`](turtle-lib/examples/logging_example.rs) demonstrates initialization, log levels, filtering, and example output.
|
|
|
|
## SVG Export
|
|
|
|
Export your turtle drawings to SVG format for use in web applications, vector graphics editors, or further processing.
|
|
|
|
### Enabling SVG Export
|
|
|
|
Add the `svg` feature to enable SVG export functionality:
|
|
|
|
```bash
|
|
cargo run --example export_svg --features svg
|
|
```
|
|
|
|
### Command-Line SVG Export
|
|
|
|
When using the `turtle_main` macro with the `svg` feature enabled, you can export drawings directly to SVG files using the `--export-svg` command-line parameter:
|
|
|
|
```bash
|
|
# Export any example to SVG without showing the window
|
|
cargo run --example macro_demo --features svg -- --export-svg output.svg
|
|
|
|
# Works with all turtle_main-based examples
|
|
cargo run --example hello_turtle --features svg -- --export-svg square.svg
|
|
```
|
|
|
|
This will:
|
|
|
|
- Execute all drawing commands instantly (no animation)
|
|
- Export the result to an SVG file
|
|
- Exit immediately without opening a window
|
|
|
|
### Programmatic SVG Export
|
|
|
|
You can also export SVG programmatically from your code:
|
|
|
|
```rust
|
|
use turtle_lib::*;
|
|
|
|
// Create your drawing
|
|
let mut plan = create_turtle_plan();
|
|
plan.forward(100).right(90).forward(100);
|
|
|
|
// Create app
|
|
let mut app = TurtleApp::new().with_commands(plan.build());
|
|
|
|
// Export to SVG
|
|
app.export_drawing("drawing.svg", export::DrawingFormat::Svg)?;
|
|
```
|
|
|
|
### Features
|
|
|
|
- **Complete Primitive Support**: Lines, circles, arcs, polygons, and fills
|
|
- **Automatic viewBox**: Includes 20px padding around the entire drawing
|
|
- **Color and Styling**: Preserves colors, pen width, and fill colors
|
|
- **Multi-Contour Fills**: Exports complex fills with holes using SVG paths
|
|
- **Text Support**: Exports text elements with positioning
|
|
|
|
### Example
|
|
|
|
See [`examples/export_svg.rs`](turtle-lib/examples/export_svg.rs) for a complete example that draws various shapes and exports them to SVG.
|
|
|
|
The exported SVG can be opened in any web browser or vector graphics application.
|
|
|
|
## Examples
|
|
|
|
Run examples with:
|
|
|
|
```bash
|
|
cargo run --example square
|
|
cargo run --example koch
|
|
cargo run --example shapes
|
|
cargo run --example yinyang
|
|
cargo run --example star
|
|
cargo run --example house_of_nikolaus
|
|
|
|
# SVG export example (requires --features svg)
|
|
cargo run --example export_svg --features svg
|
|
|
|
# Export any example to SVG using CLI parameter (requires --features svg)
|
|
cargo run --example macro_demo --features svg -- --export-svg output.svg
|
|
cargo run --example hello_turtle --features svg -- --export-svg square.svg
|
|
|
|
# Logging example - shows how to enable debug output
|
|
cargo run --example logging_example
|
|
RUST_LOG=turtle_lib=debug cargo run --example logging_example
|
|
```
|
|
|
|
### Available Examples
|
|
|
|
#### Basic Drawing
|
|
|
|
- **square.rs**: Basic square drawing
|
|
- **koch.rs**: Koch snowflake fractal
|
|
- **shapes.rs**: Demonstrates different turtle shapes
|
|
- **star.rs**: Star pattern drawing
|
|
- **house_of_nikolaus.rs**: House of Nikolaus (Eulerian path puzzle)
|
|
|
|
#### Fill Examples
|
|
|
|
- **yinyang.rs**: Yin-yang symbol with automatic hole detection
|
|
- **fill_demo.rs**: Donut shape with hole
|
|
- **fill_requirements.rs**: Circle with red fill
|
|
- **fill_advanced.rs**: Complex shapes (star, swiss cheese, multiple holes)
|
|
- **fill_circle_test.rs**: Circle fills with different angles
|
|
- **fill_instant_test.rs**: Quick fill test in instant mode
|
|
|
|
#### Export
|
|
|
|
- **export_svg.rs**: Demonstrates SVG export functionality (requires `--features svg`)
|
|
|
|
#### Debugging
|
|
|
|
- **logging_example.rs**: Demonstrates how to enable and use tracing/logging output
|
|
|
|
### Basic Fill
|
|
|
|
```rust
|
|
let mut plan = create_turtle_plan();
|
|
plan.set_fill_color(RED);
|
|
plan.begin_fill();
|
|
|
|
// Draw shape
|
|
for _ in 0..4 {
|
|
plan.forward(100);
|
|
plan.right(90);
|
|
}
|
|
|
|
plan.end_fill(); // Auto-closes and fills
|
|
```
|
|
|
|
### Fill with Holes (Multi-Contour)
|
|
|
|
```rust
|
|
plan.set_fill_color(BLUE);
|
|
plan.begin_fill();
|
|
|
|
// Outer circle (first contour)
|
|
plan.circle_left(90.0, 360.0, 72);
|
|
|
|
// pen_up() closes current contour
|
|
plan.pen_up();
|
|
plan.go_to(vec2(0.0, -30.0));
|
|
|
|
// pen_down() starts new contour
|
|
plan.pen_down();
|
|
|
|
// Inner circle (becomes a hole automatically with EvenOdd rule!)
|
|
plan.circle_left(30.0, 360.0, 36);
|
|
|
|
plan.end_fill(); // Auto-detects holes and fills correctly
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Module Structure
|
|
|
|
```
|
|
turtle-lib/src/
|
|
├── lib.rs - Public API and TurtleApp
|
|
├── state.rs - TurtleState and TurtleWorld
|
|
├── commands.rs - TurtleCommand enum (consolidated commands)
|
|
├── builders.rs - Builder traits (DirectionalMovement, Turnable, etc.)
|
|
├── execution.rs - Command execution with fill support
|
|
├── tweening.rs - Animation/tweening controller with dynamic speed
|
|
├── drawing.rs - Rendering with Lyon tessellation
|
|
├── shapes.rs - Turtle shape definitions
|
|
├── tessellation.rs - Lyon tessellation utilities
|
|
├── circle_geometry.rs - Circle arc calculations
|
|
└── general/ - Type definitions (Angle, Length, etc.)
|
|
```
|
|
|
|
## Workspace Structure
|
|
|
|
```
|
|
turtlers/
|
|
├── turtle-lib/ - Main library (Macroquad + Lyon)
|
|
└── turtle-lib-macros/ - Procedural macros (turtle_main)
|
|
```
|
|
|
|
## Building and Running
|
|
|
|
```bash
|
|
# Check all packages
|
|
cargo check
|
|
|
|
# Run specific example
|
|
cargo run --example yinyang
|
|
|
|
# Run SVG export example (requires svg feature)
|
|
cargo run --example export_svg --features svg
|
|
|
|
# Build release version
|
|
cargo build --release
|
|
|
|
# Build with SVG support
|
|
cargo build --features svg
|
|
```
|
|
|
|
### Optional: Faster Linker Setup
|
|
|
|
For significantly faster incremental build and linking times during development on Linux, you can optionally configure `mold` or `lld` in your user-level Cargo configuration (`~/.cargo/config.toml`):
|
|
|
|
```toml
|
|
[target.x86_64-unknown-linux-gnu]
|
|
linker = "clang"
|
|
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
|
```
|
|
|
|
## Development Status
|
|
|
|
### ✅ Completed
|
|
|
|
- **Turtle movement** and rotation (consolidated Move/Turn commands)
|
|
- **Pen control** (up/down) with contour management
|
|
- **Color** and **pen width**
|
|
- **Circle arcs** (left/right with unified Circle command)
|
|
- **Dynamic speed control** via SetSpeed commands
|
|
- **Instant mode** (speed ≥ 1000) and **animated mode** (speed < 1000)
|
|
- **Multi-contour fill** system with automatic hole detection
|
|
- **Lyon integration** for all drawing primitives
|
|
- **Multiple turtle shapes** with custom shape support
|
|
- **Tweening** system with easing functions
|
|
- **EvenOdd fill rule** for complex self-intersecting paths
|
|
- **Live fill preview** during animation with progressive rendering
|
|
- **Multi-contour support** - pen_up/pen_down manage contours
|
|
- **SVG Export** - Export drawings to SVG with viewBox and padding (feature-gated)
|
|
|
|
## License
|
|
|
|
MIT OR Apache-2.0
|
|
|
|
## Contributing
|
|
|
|
Contributions are welcome! The library now has a stable foundation with complete Lyon integration and multi-contour fill support.
|