diff --git a/.gitignore b/.gitignore index 3dcbda1..0f659ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ bin *.so *.dylib +*.o +BinPackTest diff --git a/MaxRectsBinPack.cpp b/MaxRectsBinPack.cpp index 00fcb99..ac86dfe 100644 --- a/MaxRectsBinPack.cpp +++ b/MaxRectsBinPack.cpp @@ -109,6 +109,23 @@ void MaxRectsBinPack::Insert(std::vector &rects, std::vector &ds } } +bool MaxRectsBinPack::Insert(std::vector &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();) diff --git a/MaxRectsBinPack.h b/MaxRectsBinPack.h index 345591b..cb46db7 100644 --- a/MaxRectsBinPack.h +++ b/MaxRectsBinPack.h @@ -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. @@ -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 &rects); + /// Computes the ratio of used surface area to the total bin area. double Occupancy() const; diff --git a/MaxRectsBinPackTest/BinPack.cpp b/MaxRectsBinPackTest/BinPack.cpp index f7328bb..d1b11e7 100644 --- a/MaxRectsBinPackTest/BinPack.cpp +++ b/MaxRectsBinPackTest/BinPack.cpp @@ -1,35 +1,88 @@ #include "../MaxRectsBinPack.h" #include +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 sizes; + std::vector 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("\n", binWidth, binHeight); + bin.Init(binWidth, binHeight); + + for(size_t i = 0; i < holes.size(); i++) + { + printf("\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. @@ -37,9 +90,15 @@ int main(int argc, char **argv) // 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("\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("\n"); } diff --git a/RectangleBinPack.pro b/RectangleBinPack.pro new file mode 100644 index 0000000..fd3e7cf --- /dev/null +++ b/RectangleBinPack.pro @@ -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 diff --git a/RectangleBinPack.qrc b/RectangleBinPack.qrc new file mode 100644 index 0000000..3d07e4e --- /dev/null +++ b/RectangleBinPack.qrc @@ -0,0 +1,3 @@ + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6676184 --- /dev/null +++ b/main.cpp @@ -0,0 +1,13 @@ +#include "mainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + srand((unsigned) time(NULL)); + + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..6a8b842 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,89 @@ +#include +#include +#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 += ""; + + // Create a bin to pack to, use the bin size from command line. + MaxRectsBinPack bin; + + bin.Init(width, height); + + 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(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(scrollPaves->widget()->layout())->insertWidget(paves.count(), pw); + paves.append(pw); + + process(); + } + + void MainWindow::trouDeleted(TrouWidget *item) + { + dynamic_cast(scrollTrous->widget()->layout())->removeWidget(item); + trous.removeOne(item); + + delete item; + } + + void MainWindow::paveDeleted(PaveWidget *item) + { + dynamic_cast(scrollPaves->widget()->layout())->removeWidget(item); + paves.removeOne(item); + + delete item; + } diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..2b91f87 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,31 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#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; + QListtrous; + QListpaves; + + 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 diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..e6b7c7f --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,354 @@ + + + MainWindow + + + + 0 + 0 + 996 + 673 + + + + + 0 + 0 + + + + Pavage + + + + + + + + 0 + 0 + + + + Paramètres + + + + + + + 0 + 25 + + + + Hauteur + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + Qt::LeftToRight + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Trous + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ajouter + + + + + + + + + + + 0 + 0 + + + + + 0 + 25 + + + + Largeur + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Pavés + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ajouter + + + + + + + + + + + 0 + 0 + + + + + 320 + 0 + + + + + 320 + 16777215 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOn + + + true + + + + + 0 + 0 + 306 + 519 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 280 + 0 + + + + + 280 + 16777215 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOn + + + true + + + + + 0 + 0 + 266 + 519 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 516 + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Résultat + + + + + + + + + diff --git a/pavewidget.cpp b/pavewidget.cpp new file mode 100644 index 0000000..e49f2b5 --- /dev/null +++ b/pavewidget.cpp @@ -0,0 +1,11 @@ +#include "pavewidget.h" + +PaveWidget::PaveWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); +} + +void PaveWidget::on_pbDelete_clicked(bool) +{ + emit(deleted(this)); +} diff --git a/pavewidget.h b/pavewidget.h new file mode 100644 index 0000000..1e8fa02 --- /dev/null +++ b/pavewidget.h @@ -0,0 +1,18 @@ +#ifndef PAVEWIDGET_H +#define PAVEWIDGET_H + +#include +#include "ui_pavewidget.h" + +class PaveWidget : public QWidget, private Ui::PaveWidget +{ + Q_OBJECT +public: + PaveWidget(QWidget *parent); +signals: + void deleted(PaveWidget *item); +private slots: + void on_pbDelete_clicked(bool checked = false); +}; + +#endif // PAVEWIDGET_H diff --git a/pavewidget.ui b/pavewidget.ui new file mode 100644 index 0000000..294871f --- /dev/null +++ b/pavewidget.ui @@ -0,0 +1,142 @@ + + + PaveWidget + + + + 0 + 0 + 260 + 30 + + + + + 0 + 0 + + + + + 260 + 30 + + + + + 260 + 30 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 80 + 22 + + + + + 80 + 22 + + + + Supprimer + + + + + + + + diff --git a/trouwidget.cpp b/trouwidget.cpp new file mode 100644 index 0000000..d1c264e --- /dev/null +++ b/trouwidget.cpp @@ -0,0 +1,11 @@ +#include "trouwidget.h" + +TrouWidget::TrouWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); +} + +void TrouWidget::on_pbDelete_clicked(bool) +{ + emit(deleted(this)); +} diff --git a/trouwidget.h b/trouwidget.h new file mode 100644 index 0000000..a39b594 --- /dev/null +++ b/trouwidget.h @@ -0,0 +1,18 @@ +#ifndef TROUWIDGET_H +#define TROUWIDGET_H + +#include +#include "ui_trouwidget.h" + +class TrouWidget : public QWidget, private Ui::TrouWidget +{ + Q_OBJECT +public: + TrouWidget(QWidget *parent); +signals: + void deleted(TrouWidget *item); +private slots: + void on_pbDelete_clicked(bool checked = false); +}; + +#endif // TROUWIDGET_H diff --git a/trouwidget.ui b/trouwidget.ui new file mode 100644 index 0000000..f6fe253 --- /dev/null +++ b/trouwidget.ui @@ -0,0 +1,164 @@ + + + TrouWidget + + + + 0 + 0 + 304 + 30 + + + + + 0 + 0 + + + + + 304 + 30 + + + + + 304 + 30 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 50 + 30 + + + + + + + + + 0 + 0 + + + + + 80 + 22 + + + + + 80 + 22 + + + + Supprimer + + + + + + + +