remove the bevy based turtle and rename turtle-lib-macroquad to turtle-lib
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "turtle-lib-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"] }
|
||||
@@ -0,0 +1,72 @@
|
||||
# turtle-lib-macros
|
||||
|
||||
Procedural macros for `turtle-lib`.
|
||||
|
||||
## `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::*;
|
||||
|
||||
#[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::*;
|
||||
|
||||
#[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.
|
||||
@@ -0,0 +1,175 @@
|
||||
//! Procedural macros for turtle-lib
|
||||
//!
|
||||
//! 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::*;
|
||||
///
|
||||
/// #[turtle_main("My Turtle Drawing")]
|
||||
/// fn my_drawing(turtle: &mut TurtlePlan) {
|
||||
/// // Use colors from turtle_lib (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:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use macroquad::prelude::SKYBLUE; // Import specific items
|
||||
/// use turtle_lib::*;
|
||||
///
|
||||
/// #[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::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::create_turtle();
|
||||
|
||||
// Call the user's function with the turtle
|
||||
#fn_name(&mut turtle);
|
||||
|
||||
let mut app = turtle_lib::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::TurtlePlan) #fn_block
|
||||
}
|
||||
} else {
|
||||
// Function takes no parameters - inline the code
|
||||
quote! {
|
||||
#[macroquad::main(#window_title)]
|
||||
async fn main() {
|
||||
let mut turtle = turtle_lib::create_turtle();
|
||||
|
||||
// Inline the user's code
|
||||
#fn_block
|
||||
|
||||
let mut app = turtle_lib::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)
|
||||
}
|
||||
Reference in New Issue
Block a user