Commit Graph
16 Commits
Author SHA1 Message Date
dietrich f4357cb73d When running:
```bash
cargo run --example yinyang --features svg -- --export-svg yinyang.svg
```
the application crashed with:
```text
thread 'main' panicked at macroquad-0.4.16/src/lib.rs:172:13:
assertion failed: THREAD_ID.is_some()
```
along with 10 compiler dead-code warnings in `turtle-lib`.

1. **Headless Execution Path**: When `--export-svg` is provided,
   `turtle_main` runs `run_headless_svg_export` headlessly without
   creating a graphics window (`macroquad::Window::new` is bypassed).
2. **Speed Overwrite in Headless Mode**: `run_headless_svg_export`
   originally called `app.set_all_turtles_speed(Instant(1000))` and
   stepped animations with `while !app.is_complete() {
   app.step_animations(); }`. However, `yinyang.rs` contains
   `turtle.set_speed(100)` in its plan. When `TweenController` processed
   `SetSpeed(100)`, it switched to animated mode.
3. **Macroquad Context Assertion**: In animated mode,
   `TweenController::update` creates a `CommandTween` and called
   `current_time()`. In `turtle-lib/src/tweening.rs`, `current_time()`
   called `macroquad::time::get_time()`, which queried Macroquad's
   context (`get_context()`). Because no window was created, Macroquad
   asserted `THREAD_ID.is_some()` and panicked.
4. **Dead Code Warnings**: Types previously made `pub(crate)` had dead
   fields and obsolete helper methods that were never called internally
   or were superseded by `execution.rs`.

---

-
  **[`turtle-lib/src/export.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/export.rs)**:
  - Updated `run_headless_svg_export` to use `app.execute_immediate(0,
    turtle)` instead of queuing commands and stepping animations in a
    loop.
  - Headless SVG export now executes all commands synchronously in under
    0.1s regardless of any `set_speed` in the drawing plan.
-
  **[`turtle-lib/src/lib.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/lib.rs)**:
  - In `TurtleApp::execute_immediate`: ensured the turtle exists in
    `self.world` before executing.
  - Removed unused `pub(crate) fn world` and `pub(crate) fn world_mut`.
-
  **[`turtle-lib/src/tweening.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/tweening.rs)**:
  - Updated `current_time()` to use monotonic `std::time::Instant` on
    non-WASM targets (`#[cfg(not(target_arch = "wasm32"))]`) and
    `macroquad::time::get_time()` on WASM (`#[cfg(target_arch =
    "wasm32")]`).
  - Removed unused `turtle_id` field on `CommandTween`.

-
  **[`turtle-lib/src/circle_geometry.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/circle_geometry.rs)**:
  - Removed obsolete unused methods: `position_at_progress`,
    `angle_to_position`, `draw_arc_params`, and
    `draw_arc_params_partial`.
-
  **[`turtle-lib/src/commands_channel.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/commands_channel.rs)**:
  - Removed unused `turtle_id` field from `TurtleCommandReceiver`.
  - Removed unused methods `turtle_id`, `try_recv`, `is_empty`, and
    `len` from `TurtleCommandReceiver`.
-
  **[`turtle-lib/src/general.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/general.rs)**:
  - Removed unused `Visibility` type alias.
-
  **[`turtle-lib/src/state.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/state.rs)**
  &
  **[`turtle-lib/src/execution.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/execution.rs)**:
  - Removed unused `start_position` field on `FillState`.
  - Removed unused superseded methods on `Turtle`: `heading_angle`,
    `reset`, `begin_fill`, `record_fill_vertex`, `close_fill_contour`,
    `start_fill_contour`, `record_fill_vertices_for_arc`, `reset_fill`.
  - Removed unused `background_color` field and unused methods
    `get_turtle`, `reset_turtle`, `clear` from `TurtleWorld`.
  - Added `#[allow(clippy::struct_field_names)]` on `Turtle::turtle_id`.
-
  **[`turtle-lib/src/export_svg.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/export_svg.rs)**:
  - Moved `update_bounds` outside `export`.
  - Replaced `if angle.value() > 180.0 { 1 } else { 0 }` with
    `i32::from(angle.value() > 180.0)`.
  - Replaced `d.push_str(&format!(...))` with `write!(d, ...)`.
  - Inlined format arguments in `color_to_svg`.

---

```bash
cargo run --example yinyang --features svg -- --export-svg yinyang.svg
```
Output:
```text
Finished `dev` profile [optimized + debuginfo] target(s) in 0.07s
Running `target/debug/examples/yinyang --export-svg yinyang.svg`
SVG exported successfully to: yinyang.svg
```
Completed cleanly in **0.07s** with **0 compiler warnings** and **0
errors**.

Inspected `yinyang.svg`: contains all expected paths, outer arcs, inner
S-curve, and EvenOdd fill contours.

```bash
cargo test --package turtle-lib --features svg
```
Output:
```text
test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered
out; finished in 0.00s
test result: ok. 34 passed; 0 failed; 1 ignored; 0 measured; 0 filtered
out; finished in 0.25s
```

```bash
cargo clippy --package turtle-lib --features svg -- -Wclippy::pedantic \
-Aclippy::cast_precision_loss -Aclippy::cast_sign_loss
-Aclippy::cast_possible_truncation
```
Output:
```text
Checking turtle-lib v0.2.0
(/home/dietrich/Projekte/Source/turtlers/turtle-lib)
Finished `dev` profile [optimized + debuginfo] target(s) in 0.51s
```
**0 warnings.**
2026-09-19 09:24:10 +02:00
dietrich 76d07ab009 Consistency Refactoring
All 6 consistency review items have been resolved, verified with new
unit tests, automated test suites, and clean example builds.

- **Fixed `Goto` Duration Bug**: In
  [`turtle-lib/src/command_behavior.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/command_behavior.rs),
  mapped Cartesian target coordinates to screen space (`vec2(target.x,
  -target.y)`) before calculating $\Delta x$ and $\Delta y$ in
  `animation_duration`.
- **Unit Test**: Added `test_goto_duration_cartesian_inversion` testing
  that moving from screen $(0, 100)$ (Cartesian $(0, -100)$) to
  Cartesian $(0, 100)$ computes duration based on the actual 200px
  Euclidean distance ($2.0\text{s}$ at $100\text{ px/s}$).

- **Stored `Degrees`**: Updated `TurtleCommand::SetHeading(Degrees)` in
  [`turtle-lib/src/commands.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/commands.rs)
  to store user degrees directly ($0^\circ = \text{East}$, $90^\circ =
  \text{North}$), matching `Turn(Degrees)` and `Circle { angle: Degrees
  }`.
- **Deferred Screen-Space Conversion**: Converted to internal screen
  radians (`normalize_angle(-heading.as_radians().value())`) inside
  `apply_to_params` in
  [`turtle-lib/src/command_behavior.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/command_behavior.rs).
- **Instant Duration**: Maintained instant transition ($0.01\text{s}$
  minimum) in `animation_duration`.
- **Unit Test**: Added `test_set_heading_degrees_and_instant_duration`
  verifying $90^\circ$ maps to North ($-\frac{\pi}{2}$ in screen space),
  $0^\circ$ to East ($0$), $270^\circ$ to South ($+\frac{\pi}{2}$), and
  duration is $0.01\text{s}$.

- **Enhanced `Length` Type**: Expanded
  [`turtle-lib/src/general/length.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/general/length.rs)
  with `new()`, `value()`, `Neg`, `PartialOrd`, and conversion
  implementations (`From<f32>`, `From<f64>`, `From<i32>`, `From<i16>`,
  `From<usize>`).
- **Adopted Across API**:
  - `TurtleCommand::Move(Length)` in `commands.rs`.
  - `TurtleCommand::Circle { radius: Length, ... }` in `commands.rs`.
  - `forward<T: Into<Length>>` and `backward<T: Into<Length>>` in
    [`turtle-lib/src/builders.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/builders.rs).
  - `circle_left`, `circle_right` accept `radius: impl Into<Length>`.
  - Kept stroke attribute `pen_width` as `Precision` (`f32`).
- **Unit Test**: Added `test_length_conversions_and_negation` in
  `general::length`.

- **Removed Duplicate Alias**: Removed `all_animations_complete(&self)`
  from `TurtleApp` in
  [`turtle-lib/src/lib.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/lib.rs);
  updated call in
  [`turtle-lib/src/export.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/export.rs)
  to use canonical `is_complete(&self)`.
- **Corrected
  [`README.md`](file:///home/dietrich/Projekte/Source/turtlers/README.md)**:
  - `plan.goto(...)` $\rightarrow$ `plan.go_to(...)`
  - `plan.set_color(...)` $\rightarrow$ `plan.set_pen_color(...)`
  - `create_turtle()` $\rightarrow$ `create_turtle_plan()`
- **Corrected
  [`AGENTS.md`](file:///home/dietrich/Projekte/Source/turtlers/AGENTS.md)**:
  - `create_turtle()` $\rightarrow$ `create_turtle_plan()` in Threading
    Pattern.

- **Unified on `1000.0`**:
  - Updated
    [`README.md`](file:///home/dietrich/Projekte/Source/turtlers/README.md)
    lines 9, 108, 109 to state `speed >= 1000` is Instant and `speed <
    1000` is Animated.
  - Updated
    [`turtle-lib/examples/circle_test.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/examples/circle_test.rs)
    to use `turtle.set_speed(1000)`.

- **Renamed Examples**:
  - `turtle-lib/examples/stern.rs` $\rightarrow$
    [`turtle-lib/examples/star.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/examples/star.rs)
  - `turtle-lib/examples/nikolaus.rs` $\rightarrow$
    [`turtle-lib/examples/house_of_nikolaus.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/examples/house_of_nikolaus.rs)
- **Translated Functions & Parameters**:
  - `house_of_nikolaus.rs`: Translated `nikolausquadrat` $\rightarrow$
    `house_square`, `nikolausdiag` $\rightarrow$ `house_diagonal`,
    `nikolausdach2` $\rightarrow$ `house_roof`, parameter `groesse`
    $\rightarrow$ `size`. Added doc comment explaining the Eulerian path
    puzzle.
  - `breadboard.rs`: Translated `pin_reihe` $\rightarrow$ `pin_row`,
    `pin_spalte` $\rightarrow$ `pin_column`, `pin_seite` $\rightarrow$
    `pin_side`, parameter `anzahl` $\rightarrow$ `count`,
    `anzahl_reihen` $\rightarrow$ `row_count`. Added `#[cfg(feature =
    "svg")]` to helper functions to eliminate dead-code warnings when
    SVG feature is not enabled.
- **Translated UI Text & Print Messages**:
  - `"Drücke E für SVG-Export"` $\rightarrow$ `"Press E for SVG export"`
  - `"SVG exportiert nach test.svg"` $\rightarrow$ `"SVG exported to
    test.svg"`
  - `"Fehler beim Export: {:?}"` $\rightarrow$ `"Export error: {:?}"`
  - `"SVG-Export ist nicht aktiviert..."` $\rightarrow$ `"SVG export is
    not enabled..."`
  - Translated module doc in `export_svg.rs`.
- **Updated
  [`README.md`](file:///home/dietrich/Projekte/Source/turtlers/README.md)**
  example lists and CLI commands to reference `star` and
  `house_of_nikolaus`.

---

- `cargo test --package turtle-lib`:
  - 13 unit tests passed (including 3 new targeted unit tests)
  - 34 doctests passed (1 ignored internal helper)
- `cargo check --workspace --all-targets --all-features`: Passed with 0
  errors.
- `cargo check --examples --package turtle-lib --all-features`: All 30
  examples compiled cleanly with 0 errors.
- `cargo clippy --package turtle-lib -- -Wclippy::pedantic
  -Aclippy::cast_precision_loss -Aclippy::cast_sign_loss
  -Aclippy::cast_possible_truncation`: Passed with 0 errors.
2026-09-19 07:51:55 +02:00
dietrich 1d0e149d22 Fix Unbounded Memory Growth (Memory Leak) in TweenController
In
[`TweenController`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/tweening.rs),
commands are currently accumulated in `queue: CommandQueue` (which wraps
a `Vec<TurtleCommand>`). When commands execute in `update()`, an
internal `cursor: usize` increments, but executed commands are never
removed. In streaming and threaded applications (such as
[`examples/clock_threaded.rs`](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/examples/clock_threaded.rs)),
new commands are appended continuously, causing `queue` to grow
unboundedly over time and exhaust memory.

We will replace `queue: CommandQueue` and `cursor: usize` with `queue:
std::collections::VecDeque<TurtleCommand>` inside `TweenController`.
Consumed commands will be removed immediately using `pop_front()`,
completely freeing memory as commands complete.

> [!NOTE]
> All design decisions were aligned during the `/grill-me` session:
  > - `TweenController` will internally store
  `std::collections::VecDeque<TurtleCommand>` and consume items via
  `pop_front()`.
  > - `self.cursor` is eliminated entirely.
  > - `CommandQueue` remains the public API container for passing
  batches of commands into `TweenController::new` and
  `TweenController::append_commands`.
  > - `TurtleCommand::Reset` retains its normal semantics (clearing
  drawings and parameters without clearing future queued commands), as
  continuous `pop_front()` already discards consumed commands.
  > - Standard `VecDeque` capacity behavior is maintained (no
  auto-shrinking) to prevent reallocation jitter in streaming scenarios.

[tweening.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/tweening.rs)
- Import `std::collections::VecDeque`.
- Update `TweenController` struct definition:
  - Replace `queue: CommandQueue` and `cursor: usize` with `queue:
    VecDeque<TurtleCommand>`.
- In `TweenController::new`:
  - Initialize `queue` using `queue.into_iter().collect()`.
  - Remove `cursor` initialization.
- In `TweenController::append_commands`:
  - Extend `self.queue` directly from `new_queue: CommandQueue`.
- In `TweenController::update`:
  - **Instant mode**: Replace `while let Some(command) =
    self.queue.get(self.cursor).cloned()` with `while let Some(command)
    = self.queue.pop_front()`, removing `self.cursor += 1` and
    unnecessary command cloning.
  - **Animated mode**: Replace `if let Some(command) =
    self.queue.get(self.cursor).cloned()` with `if let Some(command) =
    self.queue.pop_front()`, removing `self.cursor += 1` and unnecessary
    command cloning.
- In `TweenController::is_complete`:
  - Update check to `self.current_tween.is_none() &&
    self.queue.is_empty()`.
- Add unit tests in `#[cfg(test)] mod tests`:
  - Verify queue drains to 0 length in Instant mode.
  - Verify queue drains in Animated mode with multiple frames.
  - Verify streaming `append_commands` calls continuously drain and do
    not accumulate.
  - Verify `is_complete()` transitions correctly.

---

[commands.rs](file:///home/dietrich/Projekte/Source/turtlers/turtle-lib/src/commands.rs)
- Update doc comments referencing `TweenController`'s cursor to reflect
  the queue consumption model.

- Run `cargo test -p turtle-lib --lib tweening` to execute the new unit
  tests.
- Run `cargo test -p turtle-lib` to ensure all existing tests in
  `turtle-lib` continue to pass.
- Run `cargo check --examples` to ensure all examples (including
  `clock_threaded.rs`) compile cleanly.

- Review memory behavior or run `examples/clock_threaded.rs` in debug to
  confirm streaming updates run smoothly.
2026-09-18 21:13:46 +02:00
dietrich ece26bfe04 create a svg log instead of caching the commands where they do not
belong...
2026-05-21 21:28:14 +02:00
dietrich 6b558ca8a0 more consistent use of angle types 2026-05-21 15:02:57 +02:00
dietrich 3c076fdd03 improve command handling 2026-05-17 07:23:13 +02:00
dietrich 44046abe12 improve command handling 2026-05-16 16:03:09 +02:00
dietrich a7570911d8 reduce exposed internal functions and fields 2026-05-16 15:31:10 +02:00
dietrich 73d0ef4881 make speed increas exponentially 2025-10-24 16:31:43 +02:00
dietrich 88d188a794 small fixes for clippy compliance 2025-10-20 14:46:04 +02:00
dietrich 64bd8ee530 flip the direction of y now y increases upwards 2025-10-19 14:19:00 +02:00
dietrich 070b404bf4 add text capabilities 2025-10-18 19:50:55 +02:00
dietrich 16430f3958 add turtle.reset 2025-10-18 08:28:17 +02:00
dietrich 3509060390 add multi turtle support 2025-10-17 08:59:29 +02:00
dietrich bbb9348497 initial multi-turtle support 2025-10-13 09:42:34 +02:00
dietrich 08a1802bd2 remove the bevy based turtle and rename turtle-lib-macroquad to turtle-lib 2025-10-12 20:31:05 +02:00