Cutting engine overview
Rubberfit’s cutting engine is the most differentiated piece of the platform. It is a real Rust workspace — not a Python notebook, not a wrapper around a CAD plugin — and it runs on every Pack request from the dashboard.
What the engine does
Given a roll and a list of parts (polygons + quantities), the engine returns:
- An optimal layout (positions and rotations for every placed part)
- A yield percentage (placed area / total roll area)
- A list of unplaced parts with the reason
- A travel-path estimate for the cutter head
- A full
layout_jsonblob suitable for archival tocut_history
The optimization target is maximize yield — minimize material waste — subject to a kerf constraint, an optional rotation constraint per part, and the roll boundary.
Two modes
What’s under the hood
The engine is a Rust workspace with four crates:
| Crate | Role |
|---|---|
rf-geometry | Polygon primitives, kerf offset, rotation, AABB intersection |
rf-layout | Bin-packing algorithms — wraps jagua-rs for polygon nesting and adds the sparrow-style Guided Local Search metaheuristic |
rf-types | Shared serde-derive structs for stdin/stdout JSON contracts |
pack-cli | The standalone binary the Next.js server shells out to |
The Next.js app does not link the Rust code directly. Instead, on every Pack request, the API route spawns a pack-cli subprocess, writes the request JSON to its stdin, and reads the response from stdout. This isolation means:
- The Rust binary can ship independently of the web app
- The same binary runs in CI for parity tests
- The same binary will run in a future native desktop build without changing the algorithm
The pipeline
For an Auto Nest request, pack-cli runs:
- Parse — deserialize the request, validate polygon winding and roll dimensions
- Pre-pass — apply kerf offset to every part polygon (Minkowski sum with a kerf-radius circle)
- Initial solution — greedy bottom-left-fill placement, sorted by area descending
- Sparrow Guided Local Search — iteratively perturb placements (swap, rotate, slide), accept improvements, escape local optima with a guidance penalty on frequently-perturbed regions
- Final pass — verify no overlaps, compute yield, serialize
layout_json - Return — write JSON to stdout, exit
Typical iteration count for a 6 m² roll with 24 parts: 2,000–4,000 iterations in 2.4 seconds median wall time on a c5.large-equivalent CPU.
Audit guarantees
Every committed pack writes to cut_history with:
layout_json— the full placement, replay-able into any future clientengine_version— thepack-clisemver from the running binarysolve_time_ms— wall time for the solveyield_pct— the headline numberoperator_id,roll_id,job_idcreated_at— UTC timestamp
This row cannot be deleted by manager or below. Corrections require an admin action, which itself writes to admin.admin_activity_log (see Admin activity log for the schema).
The engine is deterministic for a given input + engine_version. Replay a layout_json against the same engine version and you get pixel-identical placements. This is what makes audit defensible.