Skip to content

ENH: Arrange - experimental packer (sparrow) uses up to 50% fewer plates - #12119

Open
timkarnold wants to merge 1 commit into
bambulab:masterfrom
timkarnold:feature/sparrow-arrange
Open

ENH: Arrange - experimental packer (sparrow) uses up to 50% fewer plates#12119
timkarnold wants to merge 1 commit into
bambulab:masterfrom
timkarnold:feature/sparrow-arrange

Conversation

@timkarnold

@timkarnold timkarnold commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an optional second backend for Arrange based on the sparrow nesting algorithm (MIT, Rust). It is off by default and selected with a new Use experimental packer checkbox in the Arrange settings popup. The stock libnest2d path is untouched and remains the default.

The stock arranger packs convex hulls at four fixed angles with a first-fit heuristic. The new backend packs the objects' real projected outlines with continuous rotation and an overlap-tolerant local search, which fills plates considerably tighter.

GIF Demo

18% increase in Benchys per plate
Benchy Arrange w Rotation
Experimental packer fit 33 benchys on single plate vs 28 with stock Arrange.

44% fewer plates on a 73-part project
Simmotion Arrange w Rotation 8s
Experimental packer fit 73 objects onto 10 plates vs 18 with stock Arrange.

Results

73-object project, CLI arrange from a clean layout, stock libnest2d versus sparrow at 8 s per plate, then a full re-slice of every output. All runs placed every object and every re-slice passed with no printable-area or exclusion-zone errors.

Machine Plates, libnest2d Plates, sparrow Time, libnest2d Time, sparrow
P1S 15 9 4.8 s 66 s
A1 18 9 6.3 s 65 s
H2C 10 6 4.1 s 45 s

Averaging 43% fewer plates with sparrow over stock libnest2d.

The set includes three 200 mm parts that each take a plate on their own on the 256 mm beds. Plate counts vary by one between sparrow runs because the search is time-bounded.

What changes for the user

  • Arrange settings gain Use experimental packer and a Search time per plate slider (2–60 s, default 8). The packer is a wall-clock local search, so more time means more attempts; total time scales with the number of plates used.
  • Progress is reported per plate in the existing toast: plate 2, 31/73 objects placed.
  • Both settings persist in the app config.
  • Applies wherever Arrange is invoked: the toolbar button, the A key, the Arrange menu, per-plate arrange and Arrange Selected.
  • Exclusion zones (e.g. the P1S front-left corner) and the wipe tower are respected. The "too close to exclusion area" checks now use the true outline too, so an object whose convex hull crosses a zone but whose real silhouette does not is no longer rejected.

Arrange menu including experimental packer options
Screenshot 2026-09-03 at 8 54 41 AM

What is not covered

The packer only handles the 2D problem. These cases automatically fall back to the stock arranger:

  • Sequential (by-object) printing, which needs height and print-order constraints.
  • "Allow multiple materials on same plate" disabled.
  • Fill Bed, which depends on the stock packer's callback protocol.

Known limits when the packer does run:

  • Filament temperature-group and TPU-per-plate rules are not enforced.
  • Objects are one solid outline each. Holes are ignored and a mesh with several disconnected islands is packed as the convex hull of all of them.
  • Results are not bit-for-bit reproducible across runs because the search is time-bounded.

Build

Requires a Rust toolchain (cargo on PATH, or in ~/.cargo/bin). CMake builds the crate as a static library and links it into libslic3r; the end user needs nothing extra. Configure with -DSLIC3R_SPARROW_ARRANGE=OFF to build without it, in which case the checkbox is absent and nothing else changes.

Tested on macOS arm64. The Windows and Linux link lines are written but have not been exercised.

Implementation

  • src/sparrow_arrange/: Rust crate exposing one C function, sparrow_arrange(), behind a frozen C header. It runs the vendored sparrow separator bed by bed: fill largest-first, separate, grow or evict, and move on when the plate is full. Before moving on, every remaining part gets a search-free collision check against the plate's gaps, largest first. Exclusion zones and fixed items are modeled as pinned obstacles. 12 tests, cargo test runs in a few seconds.
  • src/sparrow_arrange/vendor/sparrow/: sparrow at 50690c4 trimmed to the separator, samplers and collision tracker. Local patches are marked SPARROW_ARRANGE PATCH and listed in VENDORED.md.
  • src/libslic3r/ArrangeSparrow.cpp: converts ArrangePolygons to the C ABI, including the same inflation and bed margin the stock path applies, and dispatches from arrangement::arrange() when the flag is set. Any failure falls back to libnest2d.
  • src/libslic3r/Model.cpp: ModelObject::true_outline_2d() projects the model-part meshes to a 2D silhouette, cached per object and keyed on the volumes' meshes and transforms.
  • ArrangeJob.cpp, GLCanvas3D.cpp: settings plumbing, checkbox, slider, progress.
  • CLI: BBS_ARRANGE_SPARROW=1 and BBS_ARRANGE_SPARROW_TIME=<s> environment variables select the packer for --arrange.

Diff is 49 files, about 4700 lines, of which 1850 are the vendored sparrow sources and 790 are Cargo.lock. The new code is about 2100 lines.

Testing

  • cargo test in src/sparrow_arrange (12 tests: geometry contract, holes, fixed items, multi-bed spill, cancellation, determinism, FFI edge cases).
  • CLI arrange of the 73-part project on all three machines, then a full re-slice of each output to confirm every plate passes Print::validate().
  • GUI: toolbar, A key, menu and per-plate arrange on A1, P1S, and H2C profiles with their respective exclusion zones; drag-time exclusion checks; settings persistence across restart; progress toast.

@timkarnold
timkarnold marked this pull request as draft September 3, 2026 14:17
@timkarnold
timkarnold force-pushed the feature/sparrow-arrange branch from 6815e6e to 804d48e Compare September 3, 2026 14:29
@timkarnold
timkarnold marked this pull request as ready for review September 3, 2026 14:43
@timkarnold timkarnold changed the title ENH: Experimental packer for Arrange (denser layouts, any-angle rotation, true outlines) ENH: Arrange - experimental packer (sparrow) uses up to 57% fewer plates Sep 3, 2026
@timkarnold timkarnold changed the title ENH: Arrange - experimental packer (sparrow) uses up to 57% fewer plates ENH: Arrange - experimental packer (sparrow) uses up to 44% fewer plates Sep 3, 2026
@timkarnold timkarnold changed the title ENH: Arrange - experimental packer (sparrow) uses up to 44% fewer plates ENH: Arrange - experimental packer (sparrow) uses up to 50% fewer plates Sep 3, 2026
@timkarnold
timkarnold force-pushed the feature/sparrow-arrange branch from 804d48e to 5358676 Compare September 3, 2026 15:17
@timkarnold
timkarnold force-pushed the feature/sparrow-arrange branch from 5358676 to 068adf5 Compare September 3, 2026 15:21
@timkarnold
timkarnold marked this pull request as draft September 3, 2026 15:23
@timkarnold
timkarnold force-pushed the feature/sparrow-arrange branch 2 times, most recently from 5440e9e to e514986 Compare September 3, 2026 17:15
@timkarnold
timkarnold marked this pull request as ready for review September 3, 2026 17:16
…ion, true outlines)

## Summary

Adds an optional second backend for Arrange based on the [sparrow](https://github.com/JeroenGar/sparrow) nesting algorithm (MIT, Rust). It is off by default and selected with a new **Use experimental packer** checkbox in the *Arrange* settings popup. The stock `libnest2d` path is untouched and remains the default.

The stock arranger packs convex hulls at four fixed angles with a first-fit heuristic. The new backend packs the objects' real projected outlines with continuous rotation and an overlap-tolerant local search, which fills plates considerably tighter.

## GIF Example

## Results

73-object project, CLI arrange from a clean layout, stock libnest2d versus sparrow at 8 s per plate, then a full re-slice of every output. All runs placed every object and every re-slice passed with no printable-area or exclusion-zone errors.

| Machine | Plates, libnest2d | Plates, sparrow | Time, libnest2d | Time, sparrow |
|---|---|---|---|---|
| P1S | 15 | 9 | 4.8 s | 66 s |
| A1 | 18 | 9 | 6.3 s | 65 s |
| H2C | 10 | 6 | 4.1 s | 45 s |

**Averaging 40% fewer plates with sparrow over stock libnest2d.**

The set includes three 200 mm parts that each take a plate on their own on the 256 mm beds. Plate counts vary by one between sparrow runs because the search is time-bounded.

## What changes for the user

- *Arrange* settings gain **Use experimental packer** and a **Search time per plate** slider (2–60 s, default 8). The packer is a wall-clock local search, so more time means more attempts; total time scales with the number of plates used.
- Progress is reported per plate in the existing toast: `plate 2, 31/73 objects placed`.
- Both settings persist in the app config.
- Applies wherever Arrange is invoked: the toolbar button, the `A` key, the Arrange menu, per-plate arrange and Arrange Selected.
- Exclusion zones (e.g. the P1S front-left corner) and the wipe tower are respected. The "too close to exclusion area" checks now use the true outline too, so an object whose convex hull crosses a zone but whose real silhouette does not is no longer rejected.

## What is not covered

The packer only handles the 2D problem. These cases automatically fall back to the stock arranger:

- Sequential (by-object) printing, which needs height and print-order constraints.
- "Allow multiple materials on same plate" disabled.
- Fill Bed, which depends on the stock packer's callback protocol.

Known limits when the packer does run:

- Filament temperature-group and TPU-per-plate rules are not enforced.
- Objects are one solid outline each. Holes are ignored and a mesh with several disconnected islands is packed as the convex hull of all of them.
- Results are not bit-for-bit reproducible across runs because the search is time-bounded.

## Build

Requires a Rust toolchain (`cargo` on PATH, or in `~/.cargo/bin`). CMake builds the crate as a static library and links it into libslic3r; the end user needs nothing extra. Configure with `-DSLIC3R_SPARROW_ARRANGE=OFF` to build without it, in which case the checkbox is absent and nothing else changes.

Tested on macOS arm64. The Windows and Linux link lines are written but have not been exercised.

## Implementation

- `src/sparrow_arrange/`: Rust crate exposing one C function, `sparrow_arrange()`, behind a frozen C header. It runs the vendored sparrow separator bed by bed: fill largest-first, separate, grow or evict, and move on when the plate is full. Exclusion zones and fixed items are modeled as pinned obstacles. 12 tests, `cargo test` runs in about a second.
- `src/sparrow_arrange/vendor/sparrow/`: sparrow at `50690c4` trimmed to the separator, samplers and collision tracker. Local patches are marked `SPARROW_ARRANGE PATCH` and listed in `VENDORED.md`.
- `src/libslic3r/ArrangeSparrow.cpp`: converts `ArrangePolygon`s to the C ABI, including the same inflation and bed margin the stock path applies, and dispatches from `arrangement::arrange()` when the flag is set. Any failure falls back to libnest2d.
- `src/libslic3r/Model.cpp`: `ModelObject::true_outline_2d()` projects the model-part meshes to a 2D silhouette, cached per object and keyed on the volumes' meshes and transforms.
- `ArrangeJob.cpp`, `GLCanvas3D.cpp`: settings plumbing, checkbox, slider, progress.
- CLI: `BBS_ARRANGE_SPARROW=1` and `BBS_ARRANGE_SPARROW_TIME=<s>` environment variables select the packer for `--arrange`.

Diff is 49 files, about 4700 lines, of which 1850 are the vendored sparrow sources and 790 are `Cargo.lock`. The new code is about 2100 lines.

## Testing

- `cargo test` in `src/sparrow_arrange` (12 tests: geometry contract, holes, fixed items, multi-bed spill, cancellation, determinism, FFI edge cases).
- CLI arrange of both projects above, then a full re-slice of the output to confirm every plate passes `Print::validate()`.
- GUI: toolbar, `A` key, menu and per-plate arrange on A1, P1S, and H2C profiles with their respective exclusion zones; drag-time exclusion checks; settings persistence across restart; progress toast.
- An independent adversarial code review; all confirmed findings are fixed in the branch history.
@timkarnold
timkarnold force-pushed the feature/sparrow-arrange branch from e514986 to 02609d6 Compare September 3, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant