An intelligent 3D packing optimization service with interactive visualization.
- Heuristic packing algorithm considering:
- Weight limits and distribution
- Stability and support (60% minimum support ratio)
- Center of mass balance
- Layering (heavy objects at the bottom)
- Automatic multi-container management
- Optional object rotations (enabled via request flag or environment variable)
- Comprehensive unit tests
- REST API with JSON communication
- OpenAPI & Swagger UI with live documentation at
/docs - OOP principles with DRY architecture
- Fully documented code (Rust docstrings)
- Interactive 3D visualization
- OrbitControls for camera control
- Container navigation (Previous/Next buttons)
- Step-by-step animation of the packing process
- Live statistics:
- Object count
- Total weight
- Volume utilization
- Center of mass position
- Configuration modal with object rotation toggle
- Responsive design
- Rust (1.70+)
- Cargo
- Modern web browser
cargo runThe server runs on http://localhost:8080
π‘ Configuration note: Copy
.env.exampleto.envif needed to customize the API port, host, or update parameters. Unset values automatically fall back to their defaults.
The web client is automatically served by the Rust backend. After startup, simply open http://localhost:8080/ in your browser.
In the browser:
- Button "π Pack (Batch)" performs a one-time optimization and displays the result.
- Button "π‘ Pack (Live)" starts the live stream of optimization steps via SSE and renders them continuously.
A GitHub Actions workflow (.github/workflows/release.yml) exists for releases that generates platform packages when tags in the format v* are created (or manually via workflow_dispatch):
- Linux (x86_64):
sort-it-now-<version>-linux-x86_64.tar.gz - macOS (ARM64/Apple Silicon):
sort-it-now-<version>-macos-arm64.tar.gz - macOS (x86_64/Intel):
sort-it-now-<version>-macos-x86_64.tar.gz - Windows (x86_64):
sort-it-now-<version>-windows-x86_64.zip
Each package contains the pre-compiled binary, the current README.md, and an installation script.
The artifacts are uploaded both as workflow artifacts and automatically added to the GitHub release for the corresponding tag version.
- Linux/macOS: Run
./install.shin the extracted folder (optionally withsudo) to copysort_it_nowto/usr/local/bin. - Windows: Run
install.ps1(PowerShell). By default, it installs to%ProgramFiles%\sort-it-nowand adds the path to the user environment variable.
For each release, a Docker image is automatically published to Docker Hub. Images are provided for multiple architectures (linux/amd64, linux/arm64).
π Setup guide: See DOCKER_SETUP.md for a detailed guide on setting up the Docker Hub deployment pipeline.
Run Docker image:
Note: Replace
<username>withjosunlp(or the corresponding Docker Hub username of the project maintainer).
docker run -p 8080:8080 -e SORT_IT_NOW_SKIP_UPDATE_CHECK=1 <username>/sort-it-now:latestWith environment variables:
docker run -p 8080:8080 \
-e SORT_IT_NOW_API_HOST=0.0.0.0 \
-e SORT_IT_NOW_API_PORT=8080 \
-e SORT_IT_NOW_SKIP_UPDATE_CHECK=1 \
<username>/sort-it-now:latestBuild your own image:
docker build -t sort-it-now .
docker run -p 8080:8080 -e SORT_IT_NOW_SKIP_UPDATE_CHECK=1 sort-it-nowThe server is then available at http://localhost:8080.
On startup, the service checks for the latest GitHub releases (JosunLP/sort-it-now) in the background. If a newer version is found, the updater downloads the appropriate release package and runs the installation script for the current platform. This automatically applies the update where possible. On Windows, if sort_it_now.exe is locked, a sort_it_now.new.exe is placed instead.
- The check can be disabled via the environment variable
SORT_IT_NOW_SKIP_UPDATE_CHECK=1(e.g., for offline installations or CI). - GitHub limits unauthenticated API calls to 60 per hour. If the limit is reached, the check is skipped and info is displayed. Optionally set
SORT_IT_NOW_GITHUB_TOKEN(orGITHUB_TOKEN) to a Personal Access Token to get higher limits; the updater also uses the token when downloading release artifacts. - To avoid unexpectedly large downloads, the updater limits release artifacts to 200 MB by default. Adjust the limit via
SORT_IT_NOW_MAX_DOWNLOAD_MB(value0disables the limit). - Repo/owner and timeout can be configured via
SORT_IT_NOW_GITHUB_OWNER,SORT_IT_NOW_GITHUB_REPO, andSORT_IT_NOW_HTTP_TIMEOUT_SECSβ defaults apply automatically if no.envis present.
GET /docsdelivers an interactive Swagger UI with Subresource Integrity-protected assets.GET /docs/openapi.jsonprovides the OpenAPI schema (v3) and can be used for code generators.
Packs objects into containers.
Request:
{
"containers": [
{ "name": "Standard", "dims": [100.0, 100.0, 70.0], "max_weight": 500.0 },
{ "name": "Compact", "dims": [60.0, 80.0, 50.0], "max_weight": 320.0 }
],
"objects": [
{ "id": 1, "dims": [30.0, 30.0, 10.0], "weight": 50.0 },
{ "id": 2, "dims": [20.0, 50.0, 15.0], "weight": 30.0 }
],
"allow_rotations": true
}The optional field allow_rotations enables 90Β° rotations per request. If omitted, the default setting from the environment variable SORT_IT_NOW_PACKING_ALLOW_ROTATIONS (default: false) applies.
Response:
{
"results": [
{
"id": 1,
"template_id": 0,
"label": "Standard",
"dims": [100.0, 100.0, 70.0],
"max_weight": 500.0,
"total_weight": 80.0,
"placed": [
{
"id": 1,
"pos": [0.0, 0.0, 0.0],
"weight": 50.0,
"dims": [30.0, 30.0, 10.0]
}
]
}
]
}Streams progress events in real-time as text/event-stream. Each event is a JSON object with a type field:
ContainerStarted{ id, dims, max_weight, label, template_id }ObjectPlaced{ container_id, id, pos, weight, dims, total_weight }Finished
Note: In the frontend, you can start live mode with the "π‘ Pack (Live)" button.
cargo testAll tests should pass successfully:
- β heavy_boxes_stay_below_lighter
- β single_box_snaps_to_corner
- β creates_additional_containers_when_weight_exceeded
- β reject_heavier_on_light_support
- β sample_pack_respects_weight_order
- Application entry point
- Starts the Tokio runtime and API server
Box3D: Represents a 3D object with ID, dimensions, and weightPlacedBox: Object with position in the containerContainer: Packaging container with capacity limits- Methods:
volume(),base_area(),total_weight(),remaining_weight(),utilization_percent()
intersects(): AABB collision detection between two objectsoverlap_1d(): Calculates 1D overlapoverlap_area_xy(): Calculates XY overlap areapoint_inside(): Point-in-box test
PackingConfig: Configurable parameters (grid, support ratio, tolerances)pack_objects(): Main packing algorithmpack_objects_with_config(): Version with customizable parametersfind_stable_position(): Finds stable position for an objectsupports_weight_correctly(): Checks weight hierarchyhas_sufficient_support(): Checks minimum support ratiocalculate_balance_after(): Calculates center of mass deviation
- REST API with Axum framework
- CORS support for frontend communication
- JSON serialization/deserialization
- Three.js scene setup
- OrbitControls for camera
- Functions:
clearScene(): Clears scenedrawContainerFrame(): Draws container wireframedrawBox(): Renders individual objectvisualizeContainer(): Shows complete containeranimateContainer(): Step-by-step animationupdateStats(): Updates statistics panelfetchPacking(): API communication
PackingConfigstructure instead of scattered constants- Reusable functions for geometry calculations
- Centralized error handling
- Clear separation of data models and logic
- Encapsulation in modules
- Trait implementation for common behavior
- Rust docstrings for all public functions
- JSDoc comments in frontend
- Inline comments for complex algorithms
The application optionally loads a .env file on startup (using dotenvy). Unset variables retain their defaults, so the service runs normally even without .env. Relevant variables:
| Variable | Default | Description |
|---|---|---|
SORT_IT_NOW_API_HOST |
0.0.0.0 |
IP address the HTTP server binds to. Set e.g. 127.0.0.1 for local access. |
SORT_IT_NOW_API_PORT |
8080 |
API server port. Values of 0 are rejected. |
SORT_IT_NOW_GITHUB_OWNER |
JosunLP |
GitHub owner/organization whose releases are queried for updates. |
SORT_IT_NOW_GITHUB_REPO |
sort-it-now |
Repository name for the updater. |
SORT_IT_NOW_HTTP_TIMEOUT_SECS |
30 |
Timeout in seconds for GitHub HTTP requests by the updater. |
SORT_IT_NOW_MAX_DOWNLOAD_MB |
200 |
Maximum size of a release asset (0 = unlimited). |
SORT_IT_NOW_GITHUB_TOKEN / GITHUB_TOKEN |
β | Optional PAT for higher GitHub rate limits and private releases. |
SORT_IT_NOW_SKIP_UPDATE_CHECK |
β | If set (any value), disables automatic update check. |
SORT_IT_NOW_PACKING_GRID_STEP |
5.0 |
|
SORT_IT_NOW_PACKING_SUPPORT_RATIO |
0.6 |
|
SORT_IT_NOW_PACKING_HEIGHT_EPSILON |
1e-3 |
|
SORT_IT_NOW_PACKING_GENERAL_EPSILON |
1e-6 |
|
SORT_IT_NOW_PACKING_BALANCE_LIMIT_RATIO |
0.45 |
|
SORT_IT_NOW_PACKING_ALLOW_ROTATIONS |
false |
Enables all 90Β° object rotations. Can also be set per request via allow_rotations. |
An example file can be found in .env.example.
PackingConfig {
grid_step: 5.0, // Position grid in units
support_ratio: 0.6, // 60% minimum support
height_epsilon: 1e-3, // Height tolerance
general_epsilon: 1e-6, // General tolerance
balance_limit_ratio: 0.45, // Max center of mass deviation
allow_item_rotation: false, // Enable object rotations (disabled by default)
}const CONTAINER_SIZE = [100, 100, 70]; // Container dimensions
const COLOR_PALETTE = [...]; // Object colors- Throughput: ~100 objects/second
- Memory: O(n) for n objects
- Complexity: O(n Γ p Γ z) where:
- n = number of objects
- p = grid positions
- z = Z-levels
- Rotation: Only 90Β° rotations; complex freeform rotations are not covered
- Dynamic stability: No physical simulation
- Optimal packing: Heuristic, no guaranteed optimum
- Browser support: Requires WebGL support
Project-specific - See license file.
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Open a pull request
For questions or issues, please open an issue.
Developed with β€οΈ in Rust & Three.js