Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heuristics rewrite #5

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
composer.phar
/vendor/
composer.lock
.phpunit.result.cache
docker-compose.yml
Dockerfile
1 change: 0 additions & 1 deletion .phpunit.result.cache

This file was deleted.

83 changes: 47 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -5,18 +5,19 @@
### Basic

```php
$bin = new Bin(1000, 1000);
$blocks = [
new Block(100, 100),
new Block(300, 100),
new Block(175, 125),
new Block(200, 75),
new Block(200, 75),
];

$packer = new BinPacker();

$blocks = $packer->pack($bin, $blocks);
$fitHeuristic = new BestShortSideFit();
$splitHeuristic = new MaximizeAreaSplit();

$binPacker = new BinPacker($fitHeuristic, $splitHeuristic);

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$state = $binPacker->pack($bin, $blocks);
```

#### Determining the result (was a block packed?)
@@ -29,9 +30,20 @@ foreach ($blocks as $block) {
}
```

Or the other way around:

```php
foreach ($state->getUsedNodes() as $node) {
if ($node->isUsed()) {
$node->getBlock(); // packed block
}
}
```


### Rotation

By default, all blocks are allowed to rotate. Rotation occures only if a fit is not found with the initial orientation.
By default, all blocks are allowed to rotate. Rotation occurs only if a fit is not found with the initial orientation.

You can disable rotation by passing `false` as the 3rd parameter to the block's constructor.
```php
@@ -60,45 +72,44 @@ $bin = new Bin(1000, 1000, true);
You can use the visualizer to create pictures of the packed bin.

```php
$bin = new Bin(1000, 1000);
$blocks = [
new Block(100, 100),
new Block(300, 100),
new Block(175, 125),
new Block(200, 75),
new Block(200, 75),
];

$packer = new BinPacker();
$state = //...;

$blocks = $packer->pack($bin, $blocks);
$visualizer = new Visualizer();

$image = $visualizer->visualize($bin, $blocks);
$image = $visualizer->visualize($bin, $state);
```

This feature uses the Imagick extension, and returns an \Imagick class. You can use the result to save, or display the image.
This feature uses the GD extension, and returns a `\GdImage` class. You can use the result to save, or display the image.

```php
$image->setFormat('jpg');
$image->writeImage('bin.jpg');
ob_start();

imagejpeg($image);
$content = ob_get_contents();

ob_end_clean();


file_put_contents($filename, $content);
```

![visualizer](docs/bin.jpg)
![visualizer](docs/A5_B1/BSSF_MAXAS.jpg)

## GIF creator

**WARNING**
The GIF creators performance is very slow. I would suggest only using it for debug purposes, or non real-time scenarios
The GIF creators performance is relatively slow. I would suggest only using it for debug purposes, or non real-time scenarios.

```php
$packer = new BinPacker();
$gifMaker = new GifMaker(new Visualizer());

$blocks = $packer->pack($bin, $blocks, $gifMaker);
$state = $binPacker->pack($bin, $blocks, $gifMaker);

$gif = $gifMaker->create();

$gif->writeImages('bin.gif', true);
$gif->writeImages($filename, true);
```

![visualizer](docs/bin.gif)
![visualizer](docs/demo.gif)

## Growth

![visualizer](docs/growth.gif)
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -3,14 +3,16 @@
"description": "2D bin packing for PHP",
"type": "library",
"require": {
"php": "^7.2 || ^8.0"
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^8.1",
"ext-gd": "*",
"ext-imagick": "*"
},
"suggest": {
"ext-imagick": "To use the visualizer and GIF maker"
"ext-gd": "To use the visualizer.",
"ext-imagick": "To use the GIF maker."
},
"autoload": {
"psr-4": {
Binary file added docs/A5_B1/ASBF_LAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/ASBF_LLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/ASBF_MAXAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/ASBF_MINAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/ASBF_SAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/ASBF_SLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BLSF_LAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BLSF_LLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BLSF_MAXAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BLSF_MINAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BLSF_SAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BLSF_SLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BSSF_LAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BSSF_LLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BSSF_MAXAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BSSF_MINAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BSSF_SAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/BSSF_SLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BLSF_LAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BLSF_LLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BLSF_MAXAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BLSF_MINAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BLSF_SAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BLSF_SLAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BSSF_LAS.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/A5_B1/NEG_BSSF_LLAS.jpg
Binary file added docs/A5_B1/NEG_BSSF_MAXAS.jpg
Binary file added docs/A5_B1/NEG_BSSF_MINAS.jpg
Binary file added docs/A5_B1/NEG_BSSF_SAS.jpg
Binary file added docs/A5_B1/NEG_BSSF_SLAS.jpg
Binary file removed docs/bin.gif
Diff not rendered.
Binary file removed docs/bin.jpg
Diff not rendered.
Binary file added docs/demo.gif
212 changes: 212 additions & 0 deletions docs/generate_doc_images.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
<?php

require_once __DIR__ . '/../vendor/autoload.php';

use Padam87\BinPacker\BinPacker;
use Padam87\BinPacker\FitHeuristic;
use Padam87\BinPacker\FitHeuristic\BestAreaFit;
use Padam87\BinPacker\GrowthHeuristic;
use Padam87\BinPacker\GifMaker;
use Padam87\BinPacker\Model\Bin;
use Padam87\BinPacker\Model\Block;
use Padam87\BinPacker\NodeMerger;
use Padam87\BinPacker\NodeSlider;
use Padam87\BinPacker\SplitHeuristic;
use Padam87\BinPacker\State;
use Padam87\BinPacker\Visualizer;

A5_B1();
mergerAndSlider();
gif();
growth();

function A5_B1()
{
$fitHeuristics = [
new FitHeuristic\BestAreaFit(),
new FitHeuristic\BestLongSideFit(),
new FitHeuristic\BestShortSideFit(),
new FitHeuristic\NegateScoreFit(new FitHeuristic\BestAreaFit()),
new FitHeuristic\NegateScoreFit(new FitHeuristic\BestLongSideFit()),
new FitHeuristic\NegateScoreFit(new FitHeuristic\BestShortSideFit()),
new FitHeuristic\AssumeSameBlocksFit(),
];

$splitHeuristics = [
new SplitHeuristic\LongerAxisSplit(),
new SplitHeuristic\ShorterAxisSplit(),
new SplitHeuristic\LongerLeftoverAxisSplit(),
new SplitHeuristic\ShorterLeftoverAxisSplit(),
new SplitHeuristic\MaximizeAreaSplit(),
new SplitHeuristic\MinimizeAreaSplit(),
];

foreach ($fitHeuristics as $fitHeuristic) {
foreach ($splitHeuristics as $splitHeuristic) {
$binPacker = new BinPacker($fitHeuristic, $splitHeuristic);

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$state = $binPacker->pack($bin, $blocks);

$filename = implode('_', [$fitHeuristic->getName(), $splitHeuristic->getName()]) . '.jpg';

createImage($state, $fitHeuristic, $splitHeuristic, 'A5_B1', $filename);
}
}
}

function mergerAndSlider()
{
$fitHeuristic = new FitHeuristic\BestLongSideFit();
$splitHeuristic = new SplitHeuristic\LongerLeftoverAxisSplit();

$binPacker = new BinPacker($fitHeuristic, $splitHeuristic);

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$state = $binPacker->pack($bin, $blocks);

createImage($state, $fitHeuristic, $splitHeuristic, 'merger_slider', '1_regular.jpg');

// merger

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$merger = new NodeMerger();
$state = $binPacker->pack($bin, $blocks, function ($state, $node, $block) use ($merger) {
$merger($state);
});

createImage($state, $fitHeuristic, $splitHeuristic, 'merger_slider', '2_merger.jpg');

// slider

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$slider = new NodeSlider();
$state = $binPacker->pack($bin, $blocks, function ($state, $node, $block) use ($slider) {
$slider($state);
});

createImage($state, $fitHeuristic, $splitHeuristic, 'merger_slider', '3_slider.jpg');

// both

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$merger = new NodeMerger();
$slider = new NodeSlider();
$state = $binPacker->pack($bin, $blocks, function ($state, $node, $block) use ($slider) {
$slider($state);
});
$merger($state);

$state = $binPacker->pack($bin, $blocks, null, $state);

createImage($state, $fitHeuristic, $splitHeuristic, 'merger_slider', '4_both.jpg');
}

function gif()
{
ini_set('memory_limit', -1);

$fitHeuristic = new FitHeuristic\AssumeSameBlocksFit();
$splitHeuristic = new SplitHeuristic\MaximizeAreaSplit();

$binPacker = new BinPacker($fitHeuristic, $splitHeuristic);

$bin = new Bin(680, 980);
$blocks = [];

for ($i = 0; $i < 30; $i++) {
$blocks[] = new Block(148, 210, true, $i + 1);
}

$gifMaker = new GifMaker(new Visualizer());
$state = $binPacker->pack($bin, $blocks, $gifMaker);

$gif = $gifMaker->create();
$gif->writeImages(__DIR__ . DIRECTORY_SEPARATOR . 'demo.gif', true);
}

function growth()
{
ini_set('memory_limit', -1);

$fitHeuristic = new FitHeuristic\BestAreaFit();
$splitHeuristic = new SplitHeuristic\MaximizeAreaSplit();
$growthHeuristic = new GrowthHeuristic\GrowRightFirst();

$binPacker = new BinPacker($fitHeuristic, $splitHeuristic, $growthHeuristic);

$bin = new Bin(400, 400);
$blocks = [];

for ($i = 0; $i < 15; $i++) {
$blocks[] = new Block(200, 100, true, $i + 1);
}

$blocks[] = new Block(300, 300, true, $i + 1);

for ($i = 16; $i < 41; $i++) {
$blocks[] = new Block(100, 200, true, $i + 1);
}

$blocks[] = new Block(100, 100, true, $i + 1);

$gifMaker = new GifMaker(new Visualizer());
$state = $binPacker->pack($bin, $blocks, $gifMaker);

$gif = $gifMaker->create();
$gif->writeImages(__DIR__ . DIRECTORY_SEPARATOR . 'growth.gif', true);
}

function createImage(State $state, FitHeuristic\FitHeuristic $fitHeuristic, SplitHeuristic\SplitHeuristic $splitHeuristic, string $directory, string $filename)
{
$visualizer = new Visualizer();

$image = $visualizer->visualize($state, sprintf(
'Fit heuristic: %s; Split heuristic: %s; Fit count: %s',
$fitHeuristic->getName(),
$splitHeuristic->getName(),
count($state->getUsedNodes())
));

ob_start();

imagejpeg($image);
$content = ob_get_contents();

ob_end_clean();

echo $filename . PHP_EOL;

@mkdir(__DIR__ . DIRECTORY_SEPARATOR . $directory);
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . $directory . DIRECTORY_SEPARATOR . $filename, $content);
}
Binary file added docs/growth.gif
Binary file added docs/merger_slider/1_regular.jpg
Binary file added docs/merger_slider/2_merger.jpg
Binary file added docs/merger_slider/3_slider.jpg
Binary file added docs/merger_slider/4_both.jpg
Loading