A Python tool that generates fully customizable 3D-printable storage boxes with intelligent compartment layout optimization.
This software is developed incrementally. That means I have no clue how it works (though it mostly does).
If you have questions about this software, it will probably take you just as long to figure things out as it would take me. So I’d prefer that you investigate it yourself.
Having said that Don’t Even Think About Using It
Seriously. Don’t.
Using this software may injure or kill you during construction, burn your house down while in use, and then—just to be thorough—explode afterward.
This is not a joke. This project may use lethal voltages. If you are not a qualified software engineer, close this repository, step away from the terminal, and make yourself a cup of tea.
If you decide to ignore all of the above and use it anyway, you do so entirely at your own risk. You are fully responsible for taking proper safety precautions. I take zero responsibility for anything that happens—electrically, mechanically, chemically, spiritually, or otherwise.
Also, full disclosure: I am not a qualified software engineer. I provide no guarantees, no warranties, and absolutely no assurance that this software is correct, safe, or suitable for any purpose whatsoever.
This repository contains two related Python programs that generate compartmented storage boxes:
boxGenerator.pygridLayoutGenerator.py
They share the same overall goal:
- ask for box and compartment requirements,
- search for a usable layout,
- generate
.scad,.stl, and project JSON files, - keep project defaults so a later run can reuse them.
They differ mainly in how the layout is defined and how much freedom the packing algorithm has.
| Topic | boxGenerator.py |
gridLayoutGenerator.py |
|---|---|---|
| Input model | Physical sizes in mm | Fixed grid, compartment sizes in grid cells |
| Main use case | Flexible packing with more automatic optimization | Fast deterministic layouts on a discrete grid |
| Search strategy | Multi-attempt random packing of cluster groups | Multi-attempt grid packing with rotation and bounded backtracking |
| Remaining free space | Optional leftover compartment size in mm | Automatic 1x1 grid leftovers when everything fits |
| Compartment grouping | Supports cell counts and cluster sizes | Directly requests repeated NxM grid compartments |
| Placement preferences | Random/front/back plus lateral preference | No directional preference prompts; solver decides placement |
| Geometry freedom | May shrink/grow requested cavities in some cases | No size distortion; only rotate NxM to MxN |
| Output files | {project}.scad, {project}.stl, .boxGenerator.{project}.json |
{project}.scad, {project}.stl, .gridLayout.{project}.json |
Both programs:
- are interactive CLI tools,
- store project defaults in hidden JSON files,
- write OpenSCAD and STL output,
- generate internal labels in each compartment,
- support reproducibility through a random seed,
- show packing progress over multiple attempts,
- allow rerunning the same project by pressing Enter on default prompts.
- Python 3.8+
- No external Python packages
- Optional: OpenSCAD for viewing or re-exporting
.scad - Optional: slicer software for printing
Run either tool directly:
./boxGenerator.py
./gridLayoutGenerator.pyOr via Python:
python3 boxGenerator.py
python3 gridLayoutGenerator.pyIncluded example profile for boxGenerator.py:
.boxGenerator._boxGeneratorTest.json
Choose project _boxGeneratorTest in the menu.
boxGenerator.py is the more flexible generator. It starts from physical compartment cell sizes in mm and tries many layout attempts to fit grouped compartments inside the available box floor.
The program asks for:
- project selection,
- outer box size in mm,
- outer wall thickness,
- inner wall thickness,
- bottom thickness,
- inner wall height,
- random seed,
- number of layout attempts,
- number of attempts per cluster group,
- optional leftover compartment size,
- compartment specifications.
Each compartment specification contains:
- cell width and height in mm,
- number of cells,
- cluster size,
- placement mode,
- lateral mode.
The program:
- converts requested compartments into cluster groups,
- sorts larger groups before smaller ones,
- tries many complete layout attempts,
- tries many candidate positions/orientations per cluster group,
- chooses lower-fragmentation candidates,
- keeps the best result over all attempts.
Important behaviors:
- compartments may rotate when useful,
- compartments may shrink up to a limited amount to improve fit,
- tiny unusable strips may be absorbed by neighboring compartments,
- leftover free space may optionally be divided into extra compartments.
During search it prints lines like:
Packing progress: attempt 40/120, best placed compartments 17/18, current missing compartments C04, C11
That line reports:
- current global attempt,
- best placement count found so far,
- what is still missing in the current attempt.
boxGenerator.py writes:
{project}.scad{project}.stl.boxGenerator.{project}.json
The JSON stores the project defaults and enough information to rerun the same configuration.
gridLayoutGenerator.py is the grid-first generator. The user works in whole grid cells such as 2x3, 4x2, 1x1, while the script converts those cell counts to mm only after the grid layout is solved.
The program asks for:
- project selection,
- layout mode,
- random seed,
- number of layout attempts,
- number of attempts per cluster group,
- grid parameters,
- box size parameters,
- wall parameters,
- compartment specifications.
Layout mode can be:
- fixed grid,
- fixed box length with suggested grid sizes and valid widths,
- fixed box width with suggested grid sizes and valid lengths.
Compartment input is a repeated NxM plus count loop.
The program:
- expands requested compartment counts into a queue of grid rectangles,
- sorts and shuffles that queue across multiple attempts,
- allows each requested size to be placed in both orientations (
NxMandMxN), - scores candidate placements by how much they fragment the remaining free grid,
- performs bounded backtracking inside each attempt instead of only greedy first-fit,
- keeps the best full-attempt result.
Important behaviors:
- grid cells never shrink or grow,
- rotation is allowed,
- if all requested compartments fit, remaining cells are filled as
1x1leftovers, - labels show the compartment identifier plus the originally requested grid size such as
2 x 3.
During search it prints lines like:
Packing progress: attempt 24/100, best placed compartments 14/15, current missing compartments spec 04 x1
That line reports:
- current global attempt,
- best placed count found so far,
- which requested spec counts are still missing in the current attempt.
gridLayoutGenerator.py writes:
{project}.scad{project}.stl.gridLayout.{project}.json
The JSON stores:
- project mode,
- grid size,
- random seed,
- layout-attempt settings,
- box dimensions,
- wall dimensions,
- requested compartment specs,
- missing counts,
- final placements.
Typical fields:
{
"outer_length": 300.0,
"outer_width": 200.0,
"outer_height": 80.0,
"outer_wall_thickness": 2.0,
"inner_wall_thickness": 2.0,
"bottom_thickness": 2.0,
"outer_corner_radius": 10.0,
"rng_seed": 12345,
"layout_attempts": 120,
"per_item_attempts": 50,
"free_cell_w": 20.0,
"free_cell_h": 20.0,
"compartments": []
}Typical fields:
{
"project": "example",
"mode": 3,
"grid_size": 17.9,
"rng_seed": 12345,
"layout_attempts": 100,
"per_item_attempts": 50,
"outer_length": 250.7,
"outer_width": 197.0,
"outer_height": 50.0,
"outer_wall": 1.0,
"inner_wall": 1.0,
"bottom_thickness": 1.2,
"inner_wall_height": 40.0,
"grid_cols": 14,
"grid_rows": 11,
"compartments": [],
"missing_by_spec": {},
"placements": []
}Each placement stores both the requested grid size and the actual placed orientation.
Example:
{
"label": "C10",
"is_leftover": false,
"requested_w_cells": 2,
"requested_h_cells": 3,
"x_cell": 4,
"y_cell": 9,
"w_cells": 3,
"h_cells": 2
}That means the user requested 2x3, but the solver placed it rotated as 3x2.
- Language: Python 3 with standard library only.
- Interface: interactive CLI.
- Project persistence: hidden JSON file per project.
- Output: one
.scadfile and one.stlfile per project. - Geometry: rectangular outer shell, inner cavity, divider walls, simple label geometry.
- Determinism: same seed and same inputs must reproduce the same search behavior.
The rewritten script must:
- manage projects using
.boxGenerator.{project}.json, - ask for physical box dimensions and wall parameters,
- ask for compartment groups using mm-based cell sizes and cluster sizes,
- support random seed, layout attempts, attempts per cluster group,
- support optional leftover compartment size,
- perform multi-attempt randomized packing of cluster groups,
- support orientation changes,
- allow controlled size adjustments when needed,
- prefer layouts with fewer missing groups and less fragmentation,
- print periodic packing progress,
- export
{project}.scadand{project}.stl, - generate labels that include compartment number and size/adjustment information.
The rewritten script must:
- manage projects using
.gridLayout.{project}.json, - support three input modes: fixed grid, fixed box length, fixed box width,
- support suggestion lists for valid dimensions when box length or width is fixed,
- remember defaults for mode, seed, attempts, walls, box size, and compartment list,
- ask for grid-based compartment specs as repeated
NxMplus count, - accept count
0to skip a remembered spec, - allow placement in both orientations,
- run multiple packing attempts,
- use candidate scoring plus bounded backtracking within an attempt,
- print periodic packing progress,
- fill leftover cells with
1x1only when all requested compartments fit, - export
{project}.scad,{project}.stl, and.gridLayout.{project}.json, - label compartments with the compartment identifier and the originally requested grid size written as
N x M, - show the rounded grid size in the largest compartment label.
A rewrite does not need to preserve:
- exact internal function names,
- exact prompt wording,
- exact triangle order in STL,
- exact OpenSCAD formatting.
A rewrite does need to preserve:
- the interactive workflow,
- the saved project data model,
- deterministic search from seed,
- the practical layout behavior,
- the emitted file types and overall geometry meaning.
Rewrite two Python CLI programs named boxGenerator.py and gridLayoutGenerator.py.
General constraints:
- Python 3 standard library only
- Interactive CLI
- Save per-project defaults in hidden JSON files
- Generate {project}.scad and {project}.stl
- Deterministic behavior for the same random seed and inputs
Program 1: boxGenerator.py
- Input model is physical sizes in mm
- Ask for project, box dimensions, wall dimensions, random seed, layout attempts, attempts per cluster group, optional leftover compartment size, and compartment group definitions
- Compartment group definitions must include cell width, cell height, count, cluster size, placement mode, and lateral mode
- Use a multi-attempt randomized packing algorithm
- Allow orientation changes
- Allow limited size adjustment when needed
- Prefer layouts with fewer missing groups and less fragmentation
- Print periodic packing progress during attempts
- Save defaults to .boxGenerator.{project}.json
Program 2: gridLayoutGenerator.py
- Input model is grid-based
- Support three layout modes: fixed grid, fixed box length, fixed box width
- Ask for project, mode, random seed, layout attempts, attempts per cluster group, box/grid values, wall values, and repeated compartment specs in NxM plus count form
- Accept count 0 to skip a remembered compartment spec
- Allow both orientations NxM and MxN
- Use multi-attempt search with candidate scoring and bounded backtracking
- Print periodic packing progress during attempts
- Fill remaining cells with 1x1 leftovers only if all requested compartments fit
- Save defaults and placements to .gridLayout.{project}.json
- Labels must show the compartment identifier and the originally requested grid size formatted like N x M
- The largest compartment must also show the rounded grid size
Outputs for both:
- OpenSCAD geometry for outer shell, cavity, walls, and labels
- STL export generated directly by Python
- Project JSON file with enough information to rerun the same project
Try one or more of these:
- increase box size,
- reduce counts,
- increase
layout_attempts, - increase
per_item_attempts, - change
rng_seed.
Check:
- project JSON defaults,
- current random seed,
- attempt counts,
- whether a compartment was rotated,
- whether
boxGenerator.pygrew or shrank a compartment.
The .scad file is the more editable source. If visual quality matters, open the .scad in OpenSCAD and export STL from there.
This project uses only standard Python libraries. No external dependencies.