Skip to content

JosunLP/sort-it-now

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

72 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Sort-it-now - 3D Box Packing Optimizer

An intelligent 3D packing optimization service with interactive visualization.

🎯 Features

Backend (Rust)

  • Physics-aware 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)
  • Background GitHub release updater with checksum verification and configurable rate-limit handling
  • Native release installers for Linux (.deb), macOS (.pkg), and Windows (.msix)
  • 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)

Frontend (JavaScript/Three.js)

  • Interactive 3D visualization
  • OrbitControls for camera control
  • Container navigation (Previous/Next buttons)
  • Step-by-step animation of the packing process
  • Highlighted live/animation focus for the current placement step
  • Live statistics:
    • Object count
    • Total weight
    • Volume utilization
    • Center of mass position
  • Packing status panel with progress and configuration readiness
  • Unplaced object panel with rejection reasons
  • Configuration modal with object rotation toggle
  • Persistent configuration via browser local storage
  • Keyboard shortcuts for batch/live runs, animation, navigation, and configuration
  • Inline validation and toast notifications for faster feedback
  • Responsive design

πŸš€ Installation & Startup

Prerequisites

  • Rust (1.70+)
  • Cargo
  • Modern web browser
  • Python 3 (only needed for the Unix one-command installer)

Start the backend

cargo run

The server runs on http://localhost:8080

πŸ’‘ Configuration note: Copy .env.example to .env if needed to customize the API port, host, or update parameters. Unset values automatically fall back to their defaults.

Open the frontend

The web client is automatically served by the Rust backend. After startup, simply open http://localhost:8080/ in your browser.

πŸ”— Same-origin note: The frontend intentionally calls /pack and /pack_stream on the same origin that serves the UI. This matches the default local setup (cargo run) and the production deployment model where the Rust backend serves both API and web assets.

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.
  • Saved configurations are restored automatically after a page reload.
  • The status and unplaced-object panels provide immediate feedback without blocking dialogs.
  • Keyboard shortcuts:
    • B = batch packing
    • L = live packing
    • C = open configuration
    • ← / β†’ = switch containers
    • Space = start/stop animation

πŸ“¦ Pre-built Releases & Release Pipeline

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
  • Linux native installer: sort-it-now-<version>-linux-x86_64.deb
  • 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
  • macOS native installer: sort-it-now-<version>-macos-<arch>.pkg
  • Windows (x86_64): sort-it-now-<version>-windows-x86_64.zip
  • Windows native installer: sort-it-now-<version>-windows-x86_64.msix

Each archive package contains the pre-compiled binary, the current README.md, and installation/uninstallation scripts. The artifacts are uploaded both as workflow artifacts and automatically added to the GitHub release for the corresponding tag version.

Single-command Installation / Uninstallation

For reproducible installs, prefer a release tag (or commit SHA) instead of the mutable main branch. Replace every <version> placeholder below with the same release tag, including the v prefix (for example v1.3.0). The examples download the script first so you can review it before executing. You can additionally set SORT_IT_NOW_VERSION=<version> to instruct the install scripts to download that specific release.

  • Linux / macOS install:

    curl -fsSLo /tmp/sort-it-now-install-unix.sh \
      https://raw.githubusercontent.com/JosunLP/sort-it-now/<version>/scripts/install-unix.sh
    chmod +x /tmp/sort-it-now-install-unix.sh
    SORT_IT_NOW_VERSION=<version> /tmp/sort-it-now-install-unix.sh
  • Linux / macOS uninstall:

    curl -fsSLo /tmp/sort-it-now-uninstall-unix.sh \
      https://raw.githubusercontent.com/JosunLP/sort-it-now/<version>/scripts/uninstall-unix.sh
    chmod +x /tmp/sort-it-now-uninstall-unix.sh
    /tmp/sort-it-now-uninstall-unix.sh
  • Windows install (PowerShell):

    $version = "<version>"
    $script = Join-Path $env:TEMP "sort-it-now-install-windows.ps1"
    irm "https://raw.githubusercontent.com/JosunLP/sort-it-now/$version/scripts/install-windows.ps1" -OutFile $script
    $env:SORT_IT_NOW_VERSION = $version
    & $script
  • Windows uninstall (PowerShell):

    $version = "<version>"
    $script = Join-Path $env:TEMP "sort-it-now-uninstall-windows.ps1"
    irm "https://raw.githubusercontent.com/JosunLP/sort-it-now/$version/scripts/uninstall-windows.ps1" -OutFile $script
    & $script

Both installer scripts also continue to work locally from an extracted release bundle. Set INSTALL_DIR (Unix) or -Destination (PowerShell) to override the default target.

Archive Installation Scripts

  • Linux/macOS: Run ./install.sh in the extracted folder (optionally with sudo) to copy sort_it_now to /usr/local/bin.
  • Linux/macOS: Run ./uninstall.sh in the extracted folder to remove a prior archive-based installation again.
  • Windows: Run install.ps1 (PowerShell). By default, it installs to %ProgramFiles%\sort-it-now and adds the path to the user environment variable.
  • Windows: Run uninstall.ps1 to remove the installed binary and clean the user PATH entry again.

Native Installer Notes

  • Linux (.deb): Install with sudo dpkg -i sort-it-now-<version>-linux-x86_64.deb, uninstall with sudo dpkg -r sort-it-now.
  • macOS (.pkg): Install with sudo installer -pkg sort-it-now-<version>-macos-<arch>.pkg -target /. Use the uninstall shell script afterwards if you want to remove the binary from /usr/local/bin.
  • Windows (.msix): Each release workflow run produces a signed MSIX together with a matching .cer certificate for that specific release. Import the certificate for the version you want to install into the trusted people store, then install the package with Add-AppxPackage .\sort-it-now-<version>-windows-x86_64.msix. Because the workflow currently signs with a repository-generated self-signed certificate, you may need to repeat the import step for a different release, and you should only trust a certificate when the release came from the official repository and the published checksums were verified.

Docker

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> with josunlp (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:latest

With 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:latest

Build your own image:

docker build -t sort-it-now .
docker run -p 8080:8080 -e SORT_IT_NOW_SKIP_UPDATE_CHECK=1 sort-it-now

The server is then available at http://localhost:8080.

πŸ”” Automatic Updates on Startup

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 archive package matching the current platform and updates the installed binary in place. Native installers (.deb, .pkg, .msix) are published alongside the archive assets for manual installation flows. 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 (or GITHUB_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 (value 0 disables the limit).
  • Repo/owner and timeout can be configured via SORT_IT_NOW_GITHUB_OWNER, SORT_IT_NOW_GITHUB_REPO, and SORT_IT_NOW_HTTP_TIMEOUT_SECS – defaults apply automatically if no .env is present.

πŸ“Š API Endpoints

OpenAPI & Swagger UI

  • GET /docs delivers an interactive Swagger UI with Subresource Integrity-protected assets.
  • GET /docs/openapi.json provides the OpenAPI schema (v3) and can be used for code generators.

POST /pack

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]
        }
      ]
    }
  ]
}

POST /pack_stream (SSE)

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.

πŸ§ͺ Running Tests

cargo test

All 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

πŸ—οΈ Architecture

Rust Modules

main.rs

  • Application entry point
  • Starts the Tokio runtime and API server

model.rs

  • Box3D: Represents a 3D object with ID, dimensions, and weight
  • PlacedBox: Object with position in the container
  • Container: Packaging container with capacity limits
  • Methods: volume(), base_area(), total_weight(), remaining_weight(), utilization_percent()

geometry.rs

  • intersects(): AABB collision detection between two objects
  • overlap_1d(): Calculates 1D overlap
  • overlap_area_xy(): Calculates XY overlap area
  • point_inside(): Point-in-box test

optimizer.rs

  • PackingConfig: Configurable parameters (grid, support ratio, tolerances)
  • pack_objects(): Main packing algorithm
  • pack_objects_with_config(): Version with customizable parameters
  • find_stable_position(): Finds stable position for an object
  • supports_weight_correctly(): Checks weight hierarchy
  • has_sufficient_support(): Checks minimum support ratio
  • calculate_balance_after(): Calculates center of mass deviation

api.rs

  • REST API with Axum framework
  • CORS support for frontend communication
  • JSON serialization/deserialization

JavaScript Modules

script.js

  • Three.js scene setup
  • OrbitControls for camera
  • Functions:
    • clearScene(): Clears scene
    • drawContainerFrame(): Draws container wireframe
    • drawBox(): Renders individual object
    • visualizeContainer(): Shows complete container
    • animateContainer(): Step-by-step animation
    • updateStats(): Updates statistics panel
    • fetchPacking(): API communication

🎨 Optimizations

DRY Principle

  • PackingConfig structure instead of scattered constants
  • Reusable functions for geometry calculations
  • Centralized error handling

OOP Principles

  • Clear separation of data models and logic
  • Encapsulation in modules
  • Trait implementation for common behavior

Code Documentation

  • Rust docstrings for all public functions
  • JSDoc comments in frontend
  • Inline comments for complex algorithms

πŸ”§ Configuration

Backend Configuration (.env)

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 ⚠️ Position grid step size; smaller values give finer placement but slow down and may cause unstable arrangements.
SORT_IT_NOW_PACKING_SUPPORT_RATIO 0.6 ⚠️ Minimum support ratio for stable stacking; lower values increase tipping risk.
SORT_IT_NOW_PACKING_HEIGHT_EPSILON 1e-3 ⚠️ Tolerance for height comparisons; values too large or small affect stability checks.
SORT_IT_NOW_PACKING_GENERAL_EPSILON 1e-6 ⚠️ General numerical tolerance; extreme values may cause incorrect collision results.
SORT_IT_NOW_PACKING_BALANCE_LIMIT_RATIO 0.45 ⚠️ Center of mass deviation limit; higher values allow more tilting.
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.

Packing Parameters (optimizer.rs)

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

Frontend Configuration (script.js)

const CONTAINER_SIZE = [100, 100, 70];  // Container dimensions
const COLOR_PALETTE = [...];            // Object colors

πŸ“ˆ Performance

  • 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

πŸ› Known Limitations

  1. Rotation: Only 90Β° rotations; complex freeform rotations are not covered
  2. Dynamic stability: No physical simulation
  3. Optimal packing: Heuristic, no guaranteed optimum
  4. Browser support: Requires WebGL support

πŸ“ License

Project-specific - See license file.

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Push to the branch
  5. Open a pull request

πŸ“§ Contact

For questions or issues, please open an issue.


Developed with ❀️ in Rust & Three.js

About

An intelligent 3D packing optimization service with interactive visualization.

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

Contributors