12 KiB
Turtle Graphics Library
A modern turtle graphics library for Rust built on Macroquad with Lyon for high-quality GPU-accelerated rendering.
Project Status
✅ Stable - Complete Lyon integration with multi-contour fill system and live animation preview.
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 ≥ 999)
- 🎯 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
tracingintegration 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:
//! 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:
use macroquad::prelude::*;
use turtle_lib::*;
#[macroquad::main("Turtle")]
async fn main() {
// Create a turtle plan
let mut plan = create_turtle();
// 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
let mut plan = create_turtle();
// 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_color(RED);
plan.set_pen_width(5.0);
plan.hide();
plan.show();
// Speed control (dynamic)
plan.set_speed(100); // Animated mode (< 999)
plan.set_speed(1000); // Instant mode (>= 999)
// 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:
let mut plan = create_turtle();
// Fast initial positioning (instant mode)
plan.set_speed(1000);
plan.pen_up();
plan.goto(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 < 999: Animated mode with smooth tweening
- Speed >= 999: 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 for structured diagnostic logging. This is completely optional - if you don't set up a subscriber, there's zero overhead.
Enable Logging
// 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:
# 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 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:
cargo run --example export_svg --features svg
Usage
use turtle_lib::*;
// Create your drawing
let mut plan = create_turtle();
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 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:
cargo run --example square
cargo run --example koch
cargo run --example shapes
cargo run --example yinyang
cargo run --example stern
cargo run --example nikolaus
# SVG export example (requires --features svg)
cargo run --example export_svg --features 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
- stern.rs: Star pattern drawing
- nikolaus.rs: Nikolaus (Santa) drawing
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
Why Lyon?
- Automatic hole detection via EvenOdd fill rule
- Handles any self-intersecting path
- GPU-accelerated rendering
- Standards-compliant (matches SVG, HTML Canvas)
Basic Fill
let mut plan = create_turtle();
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)
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.goto(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
Fill Features
- ✅ Live Preview - See fills progressively during animation
- ✅ Auto-Close - Automatically connects end point to start on
end_fill() - ✅ Multi-Contour -
pen_up()closes contour,pen_down()opens next one - ✅ Automatic Hole Detection - EvenOdd fill rule handles any complexity
- ✅ Self-Intersecting Paths - Stars and complex shapes work perfectly
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
# 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
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 ≥ 999) and animated mode (speed < 999)
- 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
- Command consolidation
- SVG Export - Export drawings to SVG with viewBox and padding (feature-gated)
What's New
Complete Lyon Migration ✨
All drawing operations now use GPU-accelerated Lyon tessellation:
- Unified pipeline: Lines, arcs, circles, and fills - all use the same high-quality rendering
- Simplified codebase: ~410 lines of code eliminated
- Better performance: GPU tessellation is faster than CPU-based primitives
- Consistent quality: No more mixed rendering approaches
Multi-Contour Fill System 🕳️
Advanced fill capabilities with automatic hole detection:
- EvenOdd fill rule: Draw shapes with holes - works like SVG and HTML Canvas
- Pen state management:
pen_up()closes contour,pen_down()opens next - Live preview: See fills progressively during animations
- Self-intersecting paths: Stars, complex shapes - all handled correctly
Architectural Improvements 🏗️
- Command consolidation: Unified Move/Turn/Circle commands (~250 lines eliminated)
- Dynamic speed control: Change speed during execution via commands
- Live animation preview: Progressive fill rendering during circle/arc drawing
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.