Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bin
*.so
*.dylib
*.o
BinPackTest
17 changes: 17 additions & 0 deletions MaxRectsBinPack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ void MaxRectsBinPack::Insert(std::vector<RectSize> &rects, std::vector<Rect> &ds
}
}

bool MaxRectsBinPack::Insert(std::vector<Rect> &rects)
{
if (usedRectangles.size() == 0)
{
for(size_t i = 0; i < rects.size(); i++)
{
if ((rects[i].x + rects[i].width > binWidth && rects[i].y + rects[i].height > binHeight) || (rects[i].x + rects[i].width > binHeight && rects[i].y + rects[i].height > binWidth))
return false;

this->PlaceRect(rects[i]);
}

return true;
}
return false;
}

void MaxRectsBinPack::PlaceRect(const Rect &node)
{
for(size_t i = 0; i < freeRectangles.size();)
Expand Down
9 changes: 8 additions & 1 deletion MaxRectsBinPack.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MaxRectsBinPack
RectBestLongSideFit, ///< -BLSF: Positions the rectangle against the long side of a free rectangle into which it fits the best.
RectBestAreaFit, ///< -BAF: Positions the rectangle into the smallest free rect into which it fits.
RectBottomLeftRule, ///< -BL: Does the Tetris placement.
RectContactPointRule ///< -CP: Choosest the placement where the rectangle touches other rects as much as possible.
RectContactPointRule, ///< -CP: Choosest the placement where the rectangle touches other rects as much as possible.
};

/// Inserts the given list of rectangles in an offline/batch mode, possibly rotated.
Expand All @@ -48,6 +48,13 @@ class MaxRectsBinPack
/// Inserts a single rectangle into the bin, possibly rotated.
Rect Insert(int width, int height, FreeRectChoiceHeuristic method);

/// Inserts a single rectangle into the bin at the desire position.
/// This usefull to create an hile in the bin
/// You can't excute this after the start off the packing
/// @param rect The rectangle to insert.
/// @return True if rectangle was insert
bool Insert(std::vector<Rect> &rects);

/// Computes the ratio of used surface area to the total bin area.
double Occupancy() const;

Expand Down
99 changes: 79 additions & 20 deletions MaxRectsBinPackTest/BinPack.cpp
Original file line number Diff line number Diff line change
@@ -1,45 +1,104 @@
#include "../MaxRectsBinPack.h"
#include <cstdio>

int showUsage(void)
{
fprintf(stderr, "Usage: MaxRectsBinPackTest [options] binWidth binHeight w_0 h_0 w_1 h_1 w_2 h_2 ... w_n h_n\n");
fprintf(stderr, " options :\n");
fprintf(stderr, " -t (default) to print result in text format\n");
fprintf(stderr, " -s to print result in svg format\n");
fprintf(stderr, " -o x y w h to place on hole on specified coords\n");
fprintf(stderr, " where binWidth and binHeight define the size of the bin.\n");
fprintf(stderr, " w_i is the width of the i'th rectangle to pack, and h_i the height.\n");
fprintf(stderr, "Example: MaxRectsBinPackTest -s 256 256 30 20 50 20 10 80 90 20\n");

return 0;
}

int main(int argc, char **argv)
{

if (argc < 5 || argc % 2 != 1)
{
printf("Usage: MaxRectsBinPackTest binWidth binHeight w_0 h_0 w_1 h_1 w_2 h_2 ... w_n h_n\n");
printf(" where binWidth and binHeight define the size of the bin.\n");
printf(" w_i is the width of the i'th rectangle to pack, and h_i the height.\n");
printf("Example: MaxRectsBinPackTest 256 256 30 20 50 20 10 80 90 20\n");
return 0;
}

using namespace rbp;

// Create a bin to pack to, use the bin size from command line.
MaxRectsBinPack bin;
int binWidth = atoi(argv[1]);
int binHeight = atoi(argv[2]);
printf("Initializing bin to size %dx%d.\n", binWidth, binHeight);
enum { TEXT_MODE, SVG_MODE } mode = TEXT_MODE;
size_t optind;

std::vector<int> sizes;
std::vector<Rect> holes;
for (optind = 1; optind < argc; optind++)
{
if(argv[optind][0] == '-')
{
switch(argv[optind][1])
{
case 't':
mode = TEXT_MODE;
break;
case 's':
mode = SVG_MODE;
break;
case 'o':
holes.push_back({ atoi(argv[++optind]), atoi(argv[++optind]), atoi(argv[++optind]), atoi(argv[++optind]) });
break;
default:
return showUsage();
}
}
else
{
sizes.push_back(atoi(argv[optind]));
}
}

if(sizes.size() % 2 != 0)
{
return showUsage();
}

int binWidth = sizes.at(0);
int binHeight = sizes.at(1);

sizes.erase(sizes.begin());
sizes.erase(sizes.begin());

if(mode == TEXT_MODE)
printf("Initializing bin to size %dx%d.\n", binWidth, binHeight);
else
printf("<svg width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\">\n", binWidth, binHeight);

bin.Init(binWidth, binHeight);

for(size_t i = 0; i < holes.size(); i++)
{
printf("<rect style=\"fill:#00000;stroke:#000000;stroke-width:0.234704;stroke-opacity:1\" width=\"%d\" height=\"%d\" x=\"%d\" y=\"%d\" />\n", holes[i].width, holes[i].height, holes[i].x, holes[i].y);
}
bin.Insert(holes);

// Pack each rectangle (w_i, h_i) the user inputted on the command line.
for(int i = 3; i < argc; i += 2)
for(int i = 0; i < sizes.size(); i += 2)
{
// Read next rectangle to pack.
int rectWidth = atoi(argv[i]);
int rectHeight = atoi(argv[i+1]);
printf("Packing rectangle of size %dx%d: ", rectWidth, rectHeight);
int rectWidth = sizes.at(i);
int rectHeight = sizes.at(i + 1);
if(mode == TEXT_MODE)
printf("Packing rectangle of size %dx%d: ", rectWidth, rectHeight);

// Perform the packing.
MaxRectsBinPack::FreeRectChoiceHeuristic heuristic = MaxRectsBinPack::RectBestShortSideFit; // This can be changed individually even for each rectangle packed.
Rect packedRect = bin.Insert(rectWidth, rectHeight, heuristic);

// Test success or failure.
if (packedRect.height > 0)
printf("Packed to (x,y)=(%d,%d), (w,h)=(%d,%d). Free space left: %.2f%%\n", packedRect.x, packedRect.y, packedRect.width, packedRect.height, 100.f - bin.Occupancy()*100.f);
if(mode == TEXT_MODE)
printf("Packed to (x,y)=(%d,%d), (w,h)=(%d,%d). Free space left: %.2f%%\n", packedRect.x, packedRect.y, packedRect.width, packedRect.height, 100.f - bin.Occupancy()*100.f);
else
printf("<rect style=\"fill:#fab1a0;stroke:#000000;stroke-width:0.234704;stroke-opacity:1\" width=\"%d\" height=\"%d\" x=\"%d\" y=\"%d\" />\n", packedRect.width, packedRect.height,packedRect.x, packedRect.y);
else
printf("Failed! Could not find a proper position to pack this rectangle into. Skipping this one.\n");
fprintf(stderr, "Failed! Could not find a proper position to pack this rectangle into. Skipping this one.\n");
}
printf("Done. All rectangles packed.\n");
if(mode == TEXT_MODE)
printf("Done. All rectangles packed.\n");
else
printf("</svg>\n");
}
41 changes: 41 additions & 0 deletions RectangleBinPack.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
######################################################################
# Automatically generated by qmake (3.1) Mon Oct 20 10:20:52 2025
######################################################################

TEMPLATE = app
TARGET = RectangleBinPack
INCLUDEPATH += .
QT += core gui svg

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# Please consult the documentation of the deprecated API in order to know
# how to port your code away from it.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0

# Input
HEADERS += MaxRectsBinPack.h \
Rect.h \
mainwindow.h \
pavewidget.h \
trouwidget.h
SOURCES += MaxRectsBinPack.cpp \
Rect.cpp \
# MaxRectsBinPackTest/BinPack.cpp \
main.cpp \
mainwindow.cpp \
pavewidget.cpp \
trouwidget.cpp

FORMS += \
mainwindow.ui \
pavewidget.ui \
trouwidget.ui

RESOURCES += \
RectangleBinPack.qrc
3 changes: 3 additions & 0 deletions RectangleBinPack.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<RCC>
<qresource prefix="/images" lang="fr"/>
</RCC>
13 changes: 13 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
srand((unsigned) time(NULL));

QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
89 changes: 89 additions & 0 deletions mainwindow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <QVBoxLayout>
#include <QtDebug>
#include "mainwindow.h"
#include "MaxRectsBinPack.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);

svgWidget = new QSvgWidget(this);

QVBoxLayout * verticalLayout = new QVBoxLayout(gbResultat); // make a layout to put the svgwidget it so it scales with the window
verticalLayout->setContentsMargins(0, 0, 0, 0); // no borders
verticalLayout->addWidget(svgWidget);// the widget to it
gbResultat->setLayout(verticalLayout);
}

MainWindow::~MainWindow()
{
delete svgWidget;
}

QString MainWindow::process(void)
{
using namespace rbp;
int width = leWidth->text().toInt();
int height = leHeight->text().toInt();
QString svg = "<svg width=\""+QString::number(width)+"\" height=\""+QString::number(height)+"\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\">";

svg += "<rect style=\"fill:#FFF;stroke:#FFF;stroke-width:0.1;stroke-opacity:1\" x=\"0\" y=\"0\" width=\""+QString::number(width)+"\" height=\""+QString::number(height)+"\" />";

// Create a bin to pack to, use the bin size from command line.
MaxRectsBinPack bin;

bin.Init(width, height);

svg += "</svg>";

return svg;
}

void MainWindow::on_leWidth_textChanged()
{
QString svg = process();
svgWidget->load(svg.toLocal8Bit());
}

void MainWindow::on_leHeight_textChanged()
{
svgWidget->load(process().toLocal8Bit());
}

void MainWindow::on_pbAddTrou_clicked(bool)
{
TrouWidget *tw = new TrouWidget(this);
connect(tw, SIGNAL(deleted(TrouWidget *)), this, SLOT(trouDeleted(TrouWidget *)));

dynamic_cast<QVBoxLayout *>(scrollTrous->widget()->layout())->insertWidget(trous.count(), tw);
trous.append(tw);

process();
}

void MainWindow::on_pbAddPave_clicked(bool)
{
PaveWidget *pw = new PaveWidget(this);
connect(pw, SIGNAL(deleted(PaveWidget *)), this, SLOT(paveDeleted(PaveWidget *)));

dynamic_cast<QVBoxLayout *>(scrollPaves->widget()->layout())->insertWidget(paves.count(), pw);
paves.append(pw);

process();
}

void MainWindow::trouDeleted(TrouWidget *item)
{
dynamic_cast<QVBoxLayout *>(scrollTrous->widget()->layout())->removeWidget(item);
trous.removeOne(item);

delete item;
}

void MainWindow::paveDeleted(PaveWidget *item)
{
dynamic_cast<QVBoxLayout *>(scrollPaves->widget()->layout())->removeWidget(item);
paves.removeOne(item);

delete item;
}
31 changes: 31 additions & 0 deletions mainwindow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtSvg>
#include "ui_mainwindow.h"
#include "pavewidget.h"
#include "trouwidget.h"

class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QSvgWidget *svgWidget;
QList<TrouWidget *>trous;
QList<PaveWidget *>paves;

QString process(void);
private slots:
void on_leWidth_textChanged();
void on_leHeight_textChanged();
void on_pbAddTrou_clicked(bool checked = false);
void on_pbAddPave_clicked(bool checked = false);
void trouDeleted(TrouWidget *item);
void paveDeleted(PaveWidget *item);
};

#endif // MAINWINDOW_H
Loading