STL preparation toolkit for resin 3D printing.
stlbench takes STL files and prepares them for SLA/DLP printers: automatic support-minimising orientation, uniform scaling to fit the build volume, packing parts onto rectangular print plates, filling the bed with copies, and a flexible job-file pipeline for mixing raw and pre-prepared models. Support generation and hollowing are not performed — use your slicer (Lychee, Chitubox, PrusaSlicer, Elegoo SatelLite, etc.) after export.
pip install stlbenchgit clone https://github.com/NikitaDmitryuk/stlbench.git
cd stlbench
poetry install --with devRun stlbench --help for the command cheatsheet. The most common workflows:
# 1a. Generate a printer profile (edit width_mm / depth_mm / height_mm for your machine)
stlbench config init -o my_printer.toml
# 1b. Or generate a job-file template (includes [pipeline] and [[parts]] sections)
stlbench config job -o job.toml
# 2a. Full pipeline in one command: scale → orient → layout (recommended)
stlbench prepare -i ./parts -o ./plates -c my_printer.toml
# 2b. Mix raw and pre-prepared parts using a job file
stlbench job job.toml -o ./plates
# 3. Or run individual steps manually:
stlbench info -i ./parts -c my_printer.toml # inspect dimensions
stlbench orient -i ./parts -o ./oriented -c my_printer.toml # minimise supports
stlbench scale -i ./oriented -o ./scaled -c my_printer.toml # fit to build volume
stlbench layout -i ./scaled -o ./plates -c my_printer.toml # pack onto plates
# Scale + pack all on one plate (no separate scale/layout steps)
stlbench autopack -i ./parts -o ./packed -c my_printer.toml
# Fill the bed with copies of a single part
stlbench fill -i ./part.stl -o ./filled -c my_printer.toml --scaleNo config file? Specify the build volume inline as three numbers (X, Y, Z) in mm:
stlbench prepare -i ./parts -o ./plates -p "153.36,77.76,165"stlbench prepare -i ./parts -o ./plates -c my_printer.tomlRuns the three preparation steps in the optimal order for resin printing:
- Scale — finds the largest scale factor that fits every part inside the build volume (Z-axis rotation search by default), then applies a uniform scale to all parts.
- Orient — rotates each scaled part to minimise overhanging surface area, constrained so the part still fits the build volume.
- Layout — packs the oriented parts onto the minimum number of plates, distributed as evenly as possible.
Exports one plate_NN.3mf + plate_NN.json manifest per plate.
Key options: --overhang-angle (default 45°), --orient-candidates (default 200),
--gap-mm, --edge-margin-mm, --resin-balance, --workers,
--post-fit-scale, --max-plates, --dry-run, --recursive, --resume,
--profile.
Use --max-plates N to have prepare find the largest packable layout scale
that fits in no more than N print areas. --post-fit-scale is still applied
after that search, so values like 0.9 or 0.95 continue to reserve room for
future supports.
prepare, job, and autopack --orient use a resin-oriented multi-objective
heuristic. The goals are ordered by practical printing risk:
- Hard safety constraints — every candidate orientation must fit the printer
volume, and final layout must keep strict
gap_mm,edge_margin_mm, and bed bounds. If a part cannot fit inside the effective bed, the run fails instead of silently writing an unsafe plate. - Support-aware orientation — candidates are scored by downward overhang area and support-contact proxy. Large flat low-detail areas are considered safer places for support scars than curved or high-relief surfaces.
- Source-up and surface protection — the original STL
+Zdirection is treated as the model's natural "up". The scorer penalises candidates that put the original top or high-saliency detail toward the build plate, so figurine heads, faces, and visible surfaces are less likely to receive supports when comparable alternatives exist. - Long thin parts — by default, only very thin line-like parts such as spears
are kept in a balanced
30–50°angle band. Broad elongated parts are allowed to use any angle chosen by the support/layout score. - Plate count and layout quality — after orientation, layout first minimises the number of plates, then tries to keep similar-height parts together, then spreads parts across the available area. The packer validates every accepted placement with real polygon clearances before export.
The main user-facing orientation knob is resin_balance:
| Value | Behaviour |
|---|---|
balanced |
Default. Keeps the trade-off between support quality, plate count, and cleanup effort. |
stability |
Gives more weight to support/surface safety, allowing somewhat larger footprints. |
compact |
Gives more weight to footprint and plate count, while still penalising high-damage surfaces. |
[orientation] long_part_angle_policy controls whether the long-part angle band is
used: thin_linear (default), linear (legacy broader matching), or disabled.
Profiling (--profile) writes profile.json, profile.txt, and profile.pstats.
The JSON includes orientation diagnostics such as source_up_dot_build_up,
surface_damage_proxy, support_contact_proxy, selected height, long-axis angle,
and the selection_reason used for each part.
stlbench job job.toml -o ./platesRuns a flexible pipeline where each part can have its own set of steps. Useful when some models are already oriented and supported (they only need packing) while others need the full scale → orient → layout treatment. All parts end up on the same plates.
Example job.toml:
[printer]
width_mm = 153.36
depth_mm = 77.76
height_mm = 165.0
[scaling]
post_fit_scale = 0.95
[packing]
gap_mm = 2.0
max_plates = 4 # optional for `prepare`: auto-scale to this many plates or fewer
[pipeline]
default_steps = ["scale", "orient", "layout"] # default for parts without explicit steps
[[parts]]
path = "models/gandalf.stl" # uses default_steps
[[parts]]
path = "models/staff.stl"
steps = ["scale", "layout"] # scale but skip orient
[[parts]]
path = "supported/sword.stl"
steps = ["layout"] # already prepared — pack onlyValid step sequences (layout must always be last):
steps |
What happens |
|---|---|
["scale", "orient", "layout"] |
Z-rotation search → global scale → Tweaker-3 orient → pack |
["orient", "scale", "layout"] |
Tweaker-3 orient → scale from oriented AABB → pack |
["scale", "layout"] |
Z-rotation search → global scale → pack |
["orient", "layout"] |
Tweaker-3 orient → pack |
["layout"] |
Pack only (model already prepared) |
Global scale is computed once across all parts that include the scale step, so
they all receive exactly the same scale factor.
Key options: --candidates, --overhang-angle, --rotation-samples,
--grid-step, --edge-margin-mm, --resin-balance, --dry-run, --verbose.
stlbench info -i ./parts -c my_printer.tomlDisplays a table with AABB dimensions, volume, vertex/face counts, whether each part
fits the bed, maximum scale factor, and how many copies would fit using fill.
No files are written.
stlbench scale -i ./parts -o ./scaled -c my_printer.tomlComputes a single scale factor so that every part fits inside the printer build volume. The largest part determines the factor; all parts share the same scale.
By default the model is rotated around the Z axis only (any angle, ~4 096 samples)
— the original face stays on the bed, but the model is turned to find the best fit in
the XY plane. Use --any-rotation to allow full 3D reorientation:
| Flags | Behaviour |
|---|---|
| (default) | Z-axis rotation search — keeps the model face-down, optimises XY footprint |
--any-rotation |
Try all 6 canonical axis permutations; model may be placed on any face |
--any-rotation --maximize |
Full SO(3) random search (4 096 samples × 6 permutations) to maximise scale factor |
--scale N |
Apply an explicit factor N instead of fitting to the printer; printer config becomes optional |
Examples:
# Default: Z-rotation search (model stays face-down)
stlbench scale -i ./parts -o ./scaled -c my_printer.toml
# Allow any face down — useful when models have no pre-placed supports
stlbench scale -i ./parts -o ./scaled -c my_printer.toml --any-rotation
# Full rotation search — maximise scale by trying arbitrary orientations (slow)
stlbench scale -i ./parts -o ./scaled -c my_printer.toml --any-rotation --maximize
# Explicit factor: double every part's size, no printer required
stlbench scale -i ./parts -o ./scaled --scale 2.0
# Shrink to 80% of current size
stlbench scale -i ./parts -o ./scaled --scale 0.8
# Fit to printer, but cap at 1.0 and add a 5% safety margin
stlbench scale -i ./parts -o ./scaled -c my_printer.toml --no-upscale --post-fit-scale 0.95Key options: --scale N, --any-rotation, --maximize, --no-upscale,
--post-fit-scale, --method sorted|conservative, --dry-run, --suffix, --recursive.
stlbench orient -i ./parts -o ./oriented -c my_printer.tomlFor each STL file, searches for the rotation that minimises overhanging surface area
(faces whose downward angle exceeds the threshold). Uses a two-phase search: discrete
evaluation of candidate orientations derived from the model's face normals, followed
by Nelder-Mead local refinement. The standalone orient command is intentionally
support-focused; the richer resin balance and surface-protection heuristics are used
by the full pipeline commands that know the scale/layout context. The result is
written with the bottom at z = 0.
When --config or --printer is supplied, the search is constrained to orientations
that fit inside the build volume.
Key options: --overhang-angle (default 45°), --candidates (default 200),
--dry-run, --suffix, --recursive.
stlbench layout -i ./scaled -o ./plates -c my_printer.tomlArranges already-scaled STL files onto rectangular print plates. Exports
plate_NN.3mf + plate_NN.json with part positions. Multiple plates are created
if all parts do not fit on one.
Key options: --dry-run, --gap-mm, --edge-margin-mm, --any-rotation.
stlbench autopack -i ./parts -o ./packed -c my_printer.tomlBinary-searches for the maximum scale factor at which all parts fit onto a single
plate simultaneously. Combines scale and layout into one step with the goal of
keeping everything on one plate.
Key options: --orient/--no-orient, --overhang-angle, --any-rotation, --dry-run,
--gap-mm, --edge-margin-mm, --resin-balance, --post-fit-scale.
stlbench fill -i ./part.stl -o ./filled -c my_printer.tomlPacks as many copies of a single STL as possible onto one plate. Add --scale to
fit the part to the bed first, --orient to minimise supports before filling.
Key options: --scale/--no-scale, --orient/--no-orient, --overhang-angle,
--any-rotation, --dry-run, --gap-mm.
stlbench config init -o my_printer.tomlWrites a printer profile TOML with [printer], [scaling], [packing], and
[orientation] sections. Use --stdout to print without saving, or --force to
overwrite.
stlbench config job -o job.tomlWrites a job-file template with all sections pre-filled: [printer], [scaling],
[packing], [orientation], [pipeline], and two commented-out [[parts]]
examples. Edit the file, fill in your STL paths, then run
stlbench job job.toml -o ./plates.
Printer profiles are TOML files. Generate a template with stlbench config init or
see configs/mars5_ultra.toml for a complete example
(ELEGOO Mars 5 Ultra).
| Section | Keys | Purpose |
|---|---|---|
[printer] |
name, width_mm, depth_mm, height_mm |
Build volume (required) |
[scaling] |
post_fit_scale (>0), any_rotation, maximize |
Scale behaviour (see scale command) |
[packing] |
gap_mm, edge_margin_mm, max_plates |
Part-to-part gap, bed inset, optional prepare plate cap |
[orientation] |
resin_balance, long_part_angle_policy, long-part angle thresholds |
Resin orientation trade-offs |
[pipeline] |
default_steps |
Default step list for job command |
[[parts]] |
path, steps |
Per-part entries for job command |
All commands that write files produce 3MF output (except scale and orient,
which write scaled/rotated STL files).
| Command | Output |
|---|---|
prepare |
plate_NN.3mf, plate_NN.json per plate |
job |
plate_NN.3mf, plate_NN.json per plate |
scale |
*.stl (one per input part, in-place scaled) |
orient |
*.stl (one per input part, rotated) |
layout |
plate_NN.3mf, plate_NN.json per plate |
autopack |
plate_NN.3mf, plate_NN.json per plate |
fill |
fill_plate.3mf, fill_plate.json |
info |
Console output only |
The 3MF files use only the core 3MF 2015/02 namespace and are compatible with Elegoo SatelLite, Chitubox, Lychee, and PrusaSlicer.
Commands that write geometry also create transforms.json in the output directory.
It records source files, bounds, per-part transforms, scale factors, and plate
placements so exported models can be audited or mapped back toward their source
size and position.
See examples/README.md for a step-by-step walkthrough
using the included Gandalf model (3 parts tracked via Git LFS).
| Module | Purpose |
|---|---|
stlbench.cli |
Typer CLI — all commands and argument parsing |
stlbench.config |
Pydantic schema (AppSettings, PartSpec) + TOML loader |
stlbench.core |
Scale factor computation, overhang analysis, orientation search |
stlbench.packing |
2D polygon packing onto plates (Shapely + custom grid) |
stlbench.export |
3MF and JSON manifest writers |
stlbench.pipeline |
Command runners (run_prepare, run_job, run_scale, etc.) |
- Non-manifold meshes may produce incorrect AABB or scale results. Repair first with a tool such as Meshmixer or Microsoft 3D Builder.
- Supports and hollowing are not generated — open the exported 3MF in your slicer.