add tracing logging
This commit is contained in:
parent
62e87edd4d
commit
c96d66247e
37
README.md
37
README.md
@ -12,9 +12,10 @@ A modern turtle graphics library for Rust built on [Macroquad](https://macroquad
|
||||
- ⚡ **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!
|
||||
- **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
|
||||
|
||||
## Quick Start
|
||||
@ -149,6 +150,33 @@ loop {
|
||||
}
|
||||
```
|
||||
|
||||
## 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_macroquad=debug cargo run
|
||||
|
||||
# Very verbose trace output
|
||||
RUST_LOG=turtle_lib_macroquad=trace cargo run
|
||||
```
|
||||
|
||||
**See the complete example**: [`examples/logging_example.rs`](turtle-lib-macroquad/examples/logging_example.rs) demonstrates initialization, log levels, filtering, and example output.
|
||||
|
||||
## Examples
|
||||
|
||||
Run examples with:
|
||||
@ -161,6 +189,10 @@ cargo run --example yinyang
|
||||
cargo run --example stern
|
||||
cargo run --example nikolaus
|
||||
|
||||
# Logging example - shows how to enable debug output
|
||||
cargo run --example logging_example
|
||||
RUST_LOG=turtle_lib_macroquad=debug cargo run --example logging_example
|
||||
|
||||
# Lyon proof-of-concept examples
|
||||
cargo run --package turtle-lyon-poc --example yinyang --release
|
||||
cargo run --package turtle-lyon-poc --example basic_shapes --release
|
||||
@ -184,6 +216,9 @@ cargo run --package turtle-lyon-poc --example fill_comparison --release
|
||||
- **fill_circle_test.rs**: Circle fills with different angles
|
||||
- **fill_instant_test.rs**: Quick fill test in instant mode
|
||||
|
||||
#### Debugging
|
||||
- **logging_example.rs**: Demonstrates how to enable and use tracing/logging output
|
||||
|
||||
## Why Lyon?
|
||||
|
||||
- Automatic hole detection via EvenOdd fill rule
|
||||
|
||||
@ -8,6 +8,8 @@ license = "MIT OR Apache-2.0"
|
||||
macroquad = "0.4"
|
||||
tween = "2.1.0"
|
||||
lyon = "1.0"
|
||||
tracing = { version = "0.1", features = ["log"], default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
# For examples and testing
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
|
||||
97
turtle-lib-macroquad/examples/logging_example.rs
Normal file
97
turtle-lib-macroquad/examples/logging_example.rs
Normal file
@ -0,0 +1,97 @@
|
||||
//! Example demonstrating how to enable logging/tracing output from the turtle library
|
||||
//!
|
||||
//! This example shows how to use `tracing-subscriber` to see debug output from the library.
|
||||
//! You can control the log level using the `RUST_LOG` environment variable:
|
||||
//!
|
||||
//! ```bash
|
||||
//! # Show all debug output from turtle-lib-macroquad
|
||||
//! RUST_LOG=turtle_lib_macroquad=debug cargo run --example logging_example
|
||||
//!
|
||||
//! # Show only warnings and errors
|
||||
//! RUST_LOG=turtle_lib_macroquad=warn cargo run --example logging_example
|
||||
//!
|
||||
//! # Show trace-level output (very verbose, includes all vertices)
|
||||
//! RUST_LOG=turtle_lib_macroquad=trace cargo run --example logging_example
|
||||
//!
|
||||
//! # Show debug output from specific modules
|
||||
//! RUST_LOG=turtle_lib_macroquad::tessellation=debug cargo run --example logging_example
|
||||
//! RUST_LOG=turtle_lib_macroquad::execution=debug cargo run --example logging_example
|
||||
//! ```
|
||||
|
||||
use macroquad::prelude::*;
|
||||
use turtle_lib_macroquad::*;
|
||||
|
||||
#[macroquad::main("Turtle Logging Example")]
|
||||
async fn main() {
|
||||
// Initialize tracing subscriber to see debug output
|
||||
// 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")
|
||||
}),
|
||||
)
|
||||
.with_target(true) // Show which module the log came from
|
||||
.with_thread_ids(false)
|
||||
.with_line_number(true) // Show line numbers
|
||||
.with_file(false)
|
||||
.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");
|
||||
|
||||
// Create a turtle plan with fill operations to see detailed logging
|
||||
let mut t = create_turtle();
|
||||
t.set_speed(900);
|
||||
|
||||
// Draw a yin-yang symbol with fills (generates lots of debug output)
|
||||
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();
|
||||
|
||||
tracing::info!("Turtle plan created, starting animation");
|
||||
|
||||
// Set animation speed
|
||||
t.set_speed(100); // Slow animation to see the logs in real-time
|
||||
|
||||
// Create turtle app
|
||||
let mut app = TurtleApp::new().with_commands(t.build());
|
||||
|
||||
// Main loop
|
||||
loop {
|
||||
clear_background(WHITE);
|
||||
|
||||
// Update and render - this is where you'll see debug logs
|
||||
app.update();
|
||||
app.render();
|
||||
|
||||
// Exit when animation is complete
|
||||
if app.is_complete() {
|
||||
tracing::info!("Animation complete, press any key to exit");
|
||||
if is_key_pressed(KeyCode::Space) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
next_frame().await
|
||||
}
|
||||
|
||||
tracing::info!("Example finished");
|
||||
}
|
||||
@ -229,9 +229,9 @@ pub(crate) fn render_world_with_tween(
|
||||
draw_mesh(&mesh_data.to_mesh());
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"#### Lyon multi-contour tessellation error for fill preview: {:?}",
|
||||
e
|
||||
tracing::error!(
|
||||
error = ?e,
|
||||
"Lyon multi-contour tessellation error for fill preview"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
|
||||
state.pen_down = false;
|
||||
// Close current contour if filling
|
||||
if state.filling.is_some() {
|
||||
eprintln!("PenUp: Closing current contour");
|
||||
tracing::debug!("PenUp: Closing current contour");
|
||||
}
|
||||
state.close_fill_contour();
|
||||
}
|
||||
@ -95,9 +95,10 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
|
||||
state.pen_down = true;
|
||||
// Start new contour if filling
|
||||
if state.filling.is_some() {
|
||||
eprintln!(
|
||||
"PenDown: Starting new contour at position ({}, {})",
|
||||
state.position.x, state.position.y
|
||||
tracing::debug!(
|
||||
x = state.position.x,
|
||||
y = state.position.y,
|
||||
"PenDown: Starting new contour"
|
||||
);
|
||||
}
|
||||
state.start_fill_contour();
|
||||
@ -157,11 +158,11 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
|
||||
|
||||
TurtleCommand::BeginFill => {
|
||||
if state.filling.is_some() {
|
||||
eprintln!("Warning: begin_fill() called while already filling");
|
||||
tracing::warn!("begin_fill() called while already filling");
|
||||
}
|
||||
|
||||
let fill_color = state.fill_color.unwrap_or_else(|| {
|
||||
eprintln!("Warning: No fill_color set, using black");
|
||||
tracing::warn!("No fill_color set, using black");
|
||||
BLACK
|
||||
});
|
||||
|
||||
@ -176,10 +177,11 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
|
||||
}
|
||||
|
||||
// Debug output
|
||||
eprintln!("=== EndFill Debug ===");
|
||||
eprintln!("Total contours: {}", fill_state.contours.len());
|
||||
let span = tracing::debug_span!("end_fill", contours = fill_state.contours.len());
|
||||
let _enter = span.enter();
|
||||
|
||||
for (i, contour) in fill_state.contours.iter().enumerate() {
|
||||
eprintln!(" Contour {}: {} vertices", i, contour.len());
|
||||
tracing::debug!(contour_idx = i, vertices = contour.len(), "Contour info");
|
||||
}
|
||||
|
||||
// Create fill command - Lyon will handle EvenOdd automatically with multiple contours
|
||||
@ -188,17 +190,17 @@ pub fn execute_command(command: &TurtleCommand, state: &mut TurtleState, world:
|
||||
&fill_state.contours,
|
||||
fill_state.fill_color,
|
||||
) {
|
||||
eprintln!(
|
||||
"Successfully tessellated {} contours",
|
||||
fill_state.contours.len()
|
||||
tracing::debug!(
|
||||
contours = fill_state.contours.len(),
|
||||
"Successfully tessellated contours"
|
||||
);
|
||||
world.add_command(DrawCommand::Mesh(mesh_data));
|
||||
} else {
|
||||
eprintln!("ERROR: Failed to tessellate contours!");
|
||||
tracing::error!("Failed to tessellate contours");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Warning: end_fill() called without begin_fill()");
|
||||
tracing::warn!("end_fill() called without begin_fill()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,15 +78,15 @@ impl TurtleState {
|
||||
pub fn record_fill_vertex(&mut self) {
|
||||
if let Some(ref mut fill_state) = self.filling {
|
||||
if self.pen_down {
|
||||
eprintln!(
|
||||
" [FILL] Adding vertex ({:.2}, {:.2}) to current contour (now {} vertices)",
|
||||
self.position.x,
|
||||
self.position.y,
|
||||
fill_state.current_contour.len() + 1
|
||||
tracing::trace!(
|
||||
x = self.position.x,
|
||||
y = self.position.y,
|
||||
vertices = fill_state.current_contour.len() + 1,
|
||||
"Adding vertex to current contour"
|
||||
);
|
||||
fill_state.current_contour.push(self.position);
|
||||
} else {
|
||||
eprintln!(" [FILL] Skipping vertex (pen is up)");
|
||||
tracing::trace!("Skipping vertex (pen is up)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,42 +94,37 @@ impl TurtleState {
|
||||
/// Close the current contour and prepare for a new one (called on pen_up)
|
||||
pub fn close_fill_contour(&mut self) {
|
||||
if let Some(ref mut fill_state) = self.filling {
|
||||
eprintln!(
|
||||
" close_fill_contour called: current_contour has {} vertices",
|
||||
fill_state.current_contour.len()
|
||||
tracing::debug!(
|
||||
vertices = fill_state.current_contour.len(),
|
||||
"close_fill_contour called"
|
||||
);
|
||||
// Only close if we have vertices in current contour
|
||||
if fill_state.current_contour.len() >= 2 {
|
||||
eprintln!(
|
||||
" Closing contour with {} vertices",
|
||||
fill_state.current_contour.len()
|
||||
);
|
||||
eprintln!(
|
||||
" First: ({:.2}, {:.2})",
|
||||
fill_state.current_contour[0].x, fill_state.current_contour[0].y
|
||||
);
|
||||
eprintln!(
|
||||
" Last: ({:.2}, {:.2})",
|
||||
fill_state.current_contour[fill_state.current_contour.len() - 1].x,
|
||||
fill_state.current_contour[fill_state.current_contour.len() - 1].y
|
||||
tracing::debug!(
|
||||
vertices = fill_state.current_contour.len(),
|
||||
first_x = fill_state.current_contour[0].x,
|
||||
first_y = fill_state.current_contour[0].y,
|
||||
last_x = fill_state.current_contour[fill_state.current_contour.len() - 1].x,
|
||||
last_y = fill_state.current_contour[fill_state.current_contour.len() - 1].y,
|
||||
"Closing contour"
|
||||
);
|
||||
// Move current contour to completed contours
|
||||
let contour = std::mem::take(&mut fill_state.current_contour);
|
||||
fill_state.contours.push(contour);
|
||||
eprintln!(
|
||||
" Contour moved to completed list. Total completed contours: {}",
|
||||
fill_state.contours.len()
|
||||
tracing::debug!(
|
||||
completed_contours = fill_state.contours.len(),
|
||||
"Contour moved to completed list"
|
||||
);
|
||||
} else if !fill_state.current_contour.is_empty() {
|
||||
eprintln!(
|
||||
" WARNING: Current contour only has {} vertex/vertices, not closing",
|
||||
fill_state.current_contour.len()
|
||||
tracing::warn!(
|
||||
vertices = fill_state.current_contour.len(),
|
||||
"Current contour has insufficient vertices, not closing"
|
||||
);
|
||||
} else {
|
||||
eprintln!(" WARNING: Current contour is EMPTY, nothing to close");
|
||||
tracing::warn!("Current contour is empty, nothing to close");
|
||||
}
|
||||
} else {
|
||||
eprintln!(" close_fill_contour called but NO active fill state!");
|
||||
tracing::warn!("close_fill_contour called but no active fill state");
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,13 +132,11 @@ impl TurtleState {
|
||||
pub fn start_fill_contour(&mut self) {
|
||||
if let Some(ref mut fill_state) = self.filling {
|
||||
// Start new contour at current position
|
||||
eprintln!(
|
||||
" Starting NEW contour at ({:.2}, {:.2})",
|
||||
self.position.x, self.position.y
|
||||
);
|
||||
eprintln!(
|
||||
" Previous contour had {} completed contours",
|
||||
fill_state.contours.len()
|
||||
tracing::debug!(
|
||||
x = self.position.x,
|
||||
y = self.position.y,
|
||||
completed_contours = fill_state.contours.len(),
|
||||
"Starting new contour"
|
||||
);
|
||||
fill_state.current_contour = vec![self.position];
|
||||
}
|
||||
@ -165,8 +158,14 @@ impl TurtleState {
|
||||
// Sample points along the arc based on steps
|
||||
let num_samples = steps as usize;
|
||||
|
||||
eprintln!(" [FILL ARC] Recording arc vertices: center=({:.2}, {:.2}), radius={:.2}, steps={}, num_samples={}",
|
||||
center.x, center.y, radius, steps, num_samples);
|
||||
tracing::trace!(
|
||||
center_x = center.x,
|
||||
center_y = center.y,
|
||||
radius = radius,
|
||||
steps = steps,
|
||||
num_samples = num_samples,
|
||||
"Recording arc vertices"
|
||||
);
|
||||
|
||||
for i in 1..=num_samples {
|
||||
let progress = i as f32 / num_samples as f32;
|
||||
@ -183,12 +182,12 @@ impl TurtleState {
|
||||
center.x + radius * current_angle.cos(),
|
||||
center.y + radius * current_angle.sin(),
|
||||
);
|
||||
eprintln!(
|
||||
" [FILL ARC] Vertex {}: ({:.2}, {:.2}) at angle {:.2}°",
|
||||
i,
|
||||
vertex.x,
|
||||
vertex.y,
|
||||
current_angle.to_degrees()
|
||||
tracing::trace!(
|
||||
vertex_idx = i,
|
||||
x = vertex.x,
|
||||
y = vertex.y,
|
||||
angle_degrees = current_angle.to_degrees(),
|
||||
"Arc vertex"
|
||||
);
|
||||
fill_state.current_contour.push(vertex);
|
||||
}
|
||||
|
||||
@ -101,25 +101,32 @@ pub fn tessellate_multi_contour(
|
||||
return Err("No contours provided".into());
|
||||
}
|
||||
|
||||
eprintln!("\n=== tessellate_multi_contour Debug ===");
|
||||
eprintln!("Total contours to tessellate: {}", contours.len());
|
||||
let span = tracing::debug_span!("tessellate_multi_contour", contours = contours.len());
|
||||
let _enter = span.enter();
|
||||
|
||||
tracing::debug!("Starting multi-contour tessellation");
|
||||
|
||||
// Build path with multiple sub-paths (contours)
|
||||
let mut builder = Path::builder();
|
||||
|
||||
for (idx, contour) in contours.iter().enumerate() {
|
||||
if contour.is_empty() {
|
||||
eprintln!("WARNING: Contour {} is empty, skipping", idx);
|
||||
tracing::warn!(contour_idx = idx, "Contour is empty, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
eprintln!("\nContour {}: {} vertices", idx, contour.len());
|
||||
eprintln!(" First vertex: ({:.2}, {:.2})", contour[0].x, contour[0].y);
|
||||
tracing::trace!(
|
||||
contour_idx = idx,
|
||||
vertices = contour.len(),
|
||||
first_x = contour[0].x,
|
||||
first_y = contour[0].y,
|
||||
"Processing contour"
|
||||
);
|
||||
if contour.len() > 1 {
|
||||
eprintln!(
|
||||
" Last vertex: ({:.2}, {:.2})",
|
||||
contour[contour.len() - 1].x,
|
||||
contour[contour.len() - 1].y
|
||||
tracing::trace!(
|
||||
last_x = contour[contour.len() - 1].x,
|
||||
last_y = contour[contour.len() - 1].y,
|
||||
"Contour end vertex"
|
||||
);
|
||||
}
|
||||
|
||||
@ -128,24 +135,27 @@ pub fn tessellate_multi_contour(
|
||||
for (i, v) in contour[1..].iter().enumerate() {
|
||||
builder.line_to(to_lyon_point(*v));
|
||||
if i < 3 || i >= contour.len() - 4 {
|
||||
eprintln!(" Vertex {}: ({:.2}, {:.2})", i + 1, v.x, v.y);
|
||||
tracing::trace!(vertex_idx = i + 1, x = v.x, y = v.y, "Contour vertex");
|
||||
} else if i == 3 {
|
||||
eprintln!(" ... ({} more vertices)", contour.len() - 7);
|
||||
tracing::trace!(
|
||||
omitted = contour.len() - 7,
|
||||
"Additional vertices omitted from trace"
|
||||
);
|
||||
}
|
||||
}
|
||||
builder.end(true); // Close this contour
|
||||
eprintln!(" Contour closed");
|
||||
tracing::trace!(contour_idx = idx, "Contour closed");
|
||||
}
|
||||
|
||||
eprintln!("\nBuilding Lyon path...");
|
||||
tracing::debug!("Building Lyon path");
|
||||
let path = builder.build();
|
||||
eprintln!("Path built successfully");
|
||||
tracing::debug!("Path built successfully");
|
||||
|
||||
// Tessellate with EvenOdd fill rule - overlapping areas become holes
|
||||
let mut geometry: VertexBuffers<SimpleVertex, u16> = VertexBuffers::new();
|
||||
let mut tessellator = FillTessellator::new();
|
||||
|
||||
eprintln!("Starting tessellation with EvenOdd fill rule...");
|
||||
tracing::debug!("Starting tessellation with EvenOdd fill rule");
|
||||
match tessellator.tessellate_path(
|
||||
&path,
|
||||
&FillOptions::default().with_fill_rule(FillRule::EvenOdd),
|
||||
@ -154,16 +164,15 @@ pub fn tessellate_multi_contour(
|
||||
}),
|
||||
) {
|
||||
Ok(_) => {
|
||||
eprintln!("Tessellation successful!");
|
||||
eprintln!(
|
||||
" Generated {} vertices, {} indices",
|
||||
geometry.vertices.len(),
|
||||
geometry.indices.len()
|
||||
tracing::debug!(
|
||||
vertices = geometry.vertices.len(),
|
||||
indices = geometry.indices.len(),
|
||||
triangles = geometry.indices.len() / 3,
|
||||
"Tessellation successful"
|
||||
);
|
||||
eprintln!(" Triangles: {}", geometry.indices.len() / 3);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("ERROR: Tessellation failed: {}", e);
|
||||
tracing::error!(error = %e, "Tessellation failed");
|
||||
return Err(Box::new(e));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user