daika7ana / smallest-box
Calculate the smallest rectangular box (W x L x H) that fits a set of rectangular items, using axis-aligned 3D bin packing with rotation support.
Requires
- php: >=7.4
Requires (Dev)
- laravel/pint: ^1.29
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2026-05-30 18:44:53 UTC
README
Calculate the smallest rectangular box that fits a set of rectangular items, using axis-aligned 3D bin packing with rotation support.
Zero dependencies, fully typed, three packing algorithms, and rotation-aware candidate generation.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- Packing Algorithms
- Custom Sort & Pack Orders
- Testing
- Documentation
Features
Core Strengths
- ✅ Smallest Box Finding — Greedy 3D bin packing with rotation support
- ✅ Six Axis-Aligned Rotations — Items tested in all orientations for optimal fit
- ✅ Three Packing Algorithms — Guillotine, MaxRects, and ExtremePoint
- ✅ Custom Sort & Pack Orders — Fine-tune item ordering via closures
- ✅ Fluent API — Build item lists incrementally with
add(),remove(),clear() - ✅ Immutable Value Objects —
ItemandBoxwith type-safe accessors - ✅ Static Analysis with PHPStan — Strict type checks across the codebase
- ✅ Zero Dependencies — Only requires PHP 7.4+
Perfect For
- 🎯 Logistics and shipping box optimisation
- 🎯 Warehouse packing calculations
- 🎯 3D layout and spatial planning
- 🎯 Any scenario needing the smallest bounding box for rectangular items
Requirements
- PHP 7.4+
Installation
Via Composer
composer require daika7ana/smallest-box
Quick Start
Pass items directly
use Daika7ana\SmallestBox\Item; use Daika7ana\SmallestBox\SmallestBoxFinder; $items = [ new Item(5.0, 3.0, 2.0), new Item(3.0, 3.0, 3.0), new Item(2.0, 2.0, 1.0), ]; $finder = new SmallestBoxFinder(); $box = $finder->find($items); echo $box; // "5.00 x 5.00 x 3.00" echo $box->volume(); // 75.0
Build item list incrementally
$finder = new SmallestBoxFinder(); $finder ->add(new Item(5.0, 3.0, 2.0)) ->add(new Item(3.0, 3.0, 3.0)) ->add(new Item(2.0, 2.0, 1.0)); $box = $finder->find();
Choose a packing algorithm
// Default: guillotine (fast, ~65% efficiency for diverse items) $finder = new SmallestBoxFinder(); // MaxRects (slower, ~100% efficiency for diverse items) $finder = new SmallestBoxFinder(SmallestBoxFinder::ALGO_MAXRECTS); // ExtremePoint (medium speed, ~75% efficiency for diverse items) $finder = new SmallestBoxFinder(SmallestBoxFinder::ALGO_EXTREMEPOINT);
That's it! You've got the smallest box that fits all your items.
Packing Algorithms
| Algorithm | Efficiency | Speed | Best For |
|---|---|---|---|
ALGO_GUILLOTINE (default) |
~65% | Fast | Uniform items, speed-critical |
ALGO_EXTREMEPOINT |
~75% | Medium | Balanced efficiency and speed |
ALGO_MAXRECTS |
~100% | Slower | Diverse items, best fit |
How It Works
- Sort items by multiple heuristics (volume, dimensions, footprint).
- Generate candidate box dimensions from item dimension permutations and sums.
- Test each candidate with the selected packing algorithm and all six rotations.
- Return the smallest box that successfully fits all items.
Custom Sort & Pack Orders
Fine-tune packing behaviour for your specific use case:
$finder = new SmallestBoxFinder(); // Sort items by height ascending (build layers from bottom up) $finder->addSortOrder(function (Item $a, Item $b): int { return $a->height() <=> $b->height(); }); // Pack widest items first $finder->addPackOrder(function (Item $a, Item $b): int { return $b->width() <=> $a->width(); }); $box = $finder->find($items);
Custom orders are tried after the built-in ones. Both methods support fluent chaining.
Testing
Run Static Analysis
composer stan
PHPStan is configured via phpstan.neon and analyses the src/ directory.
Run Code Style Checks
composer pint
Run Full Test Suite
composer test
Run The Same Checks As CI
composer ci
Available Composer scripts:
composer pint— Check code style (Laravel Pint)composer stan— Run static analysis (PHPStan)composer test— Run the test suite (PHPUnit)composer ci— Run all checks in sequence
Documentation
| Guide | Purpose |
|---|---|
| 📖 Usage & Examples | Working examples and patterns |
| 🔧 Installation | Detailed setup instructions |
| 📋 API Reference | Complete class and method reference |
| ⚙️ Algorithm Details | How the packing algorithms work |
Made with ❤️
Questions? Check the docs or open an issue