Skip to content

Repository files navigation

stlbench

PyPI version Python versions License: MIT

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.

Installation

pip install stlbench

Development install

git clone https://github.com/NikitaDmitryuk/stlbench.git
cd stlbench
poetry install --with dev

Quick Start

Run 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 --scale

No 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"

Commands

prepare — Full pipeline: scale → orient → layout

stlbench prepare -i ./parts -o ./plates -c my_printer.toml

Runs the three preparation steps in the optimal order for resin printing:

  1. 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.
  2. Orient — rotates each scaled part to minimise overhanging surface area, constrained so the part still fits the build volume.
  3. 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.

Orientation and packing principles

prepare, job, and autopack --orient use a resin-oriented multi-objective heuristic. The goals are ordered by practical printing risk:

  1. 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.
  2. 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.
  3. Source-up and surface protection — the original STL +Z direction 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.
  4. 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.
  5. 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.


job — Per-part configurable pipeline from a job file

stlbench job job.toml -o ./plates

Runs 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 only

Valid 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.


info — Inspect models (read-only)

stlbench info -i ./parts -c my_printer.toml

Displays 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.


scale — Uniform scaling

stlbench scale -i ./parts -o ./scaled -c my_printer.toml

Computes 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.95

Key options: --scale N, --any-rotation, --maximize, --no-upscale, --post-fit-scale, --method sorted|conservative, --dry-run, --suffix, --recursive.


orient — Minimise support structures

stlbench orient -i ./parts -o ./oriented -c my_printer.toml

For 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.


layout — Pack parts onto plates

stlbench layout -i ./scaled -o ./plates -c my_printer.toml

Arranges 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.


autopack — Scale + layout in one step

stlbench autopack -i ./parts -o ./packed -c my_printer.toml

Binary-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.


fill — Maximum copies of one part

stlbench fill -i ./part.stl -o ./filled -c my_printer.toml

Packs 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.


config init — Create a printer profile

stlbench config init -o my_printer.toml

Writes a printer profile TOML with [printer], [scaling], [packing], and [orientation] sections. Use --stdout to print without saving, or --force to overwrite.

config job — Create a job-file template

stlbench config job -o job.toml

Writes 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.

Configuration

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

Output Files

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.

Examples

See examples/README.md for a step-by-step walkthrough using the included Gandalf model (3 parts tracked via Git LFS).

Package Structure

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.)

Limitations

  • 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.

License

MIT

About

Prepare STL files for resin 3D printing: repair meshes, optimize orientation, scale to fit, pack parts onto build plates, and export 3MF.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages