Results 1 to 3 of 3

Thread: Packing known number of uniform-sized squares into a known rect

  1. #1
    Join Date
    Jan 2006
    Location
    Germany
    Posts
    258
    Thanks
    22
    Thanked 19 Times in 16 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows Android

    Default Packing known number of uniform-sized squares into a known rect

    Hi,
    A known number of uniform-sized squares must be packed into a container rectangle (that can but may not be a square) while making optimal use of the available area. The items are not rotated or otherwise distorted. They neither overlap each other nor do they go beyond the bounds of the container rectangle. Hence, it's the uniform size of the squares, that I am looking for. The equation (or rather inequality) that came right into my mind was this:
    Qt Code:
    1. floor(w / s) * floor(h / s) >= n.
    To copy to clipboard, switch view to plain text mode 
    w,h and n are known! w is the width and h the height of the container rect and n the number of inner square items. Due to the occurrence of floor it seems like it is not possible to transform the inequality into a form that isolates s which is the length of a side of the inner squares.


    Known parameters:
    • The size (width and height) of the container rectangle.
    • The number of inner square items.


    Characteristics:
    • items are squares
    • all items share the same size
    • items are not rotated or otherwise distorted
    • items don't overlap
    • items don't go beyond the bounds of the container rectangle




    Wanted:
    • The uniform size (or rather lenght of a side) of a square
    .

    I wrote a bruteforce function that uses a binary search to find an approximation of the value I am looking for. It uses about 10-15 iterations to get a result but I am looking for a more elegant solution, maybe in the form of a mathematical formula. Can someone point me towards the right direction?

    Thanks in advance

    p.s.: Save the provided attachment (or save the following code into a file main.cpp) and run qmake -project && qmake && make to compile it.

    Qt Code:
    1. #include <QtGui>
    2. #include <cmath>
    3. #include <limits>
    4. using namespace std;
    5.  
    6. class Widget : public QWidget
    7. {
    8. Q_OBJECT
    9. public:
    10. Widget(QWidget* parent = 0)
    11. : QWidget(parent)
    12. , m_piecesCount(0) {
    13. for (int i = 0; i < 100; ++i)
    14. m_colors << QColor(qrand() % 256, qrand() % 256, qrand() % 256);
    15. }
    16.  
    17. protected:
    18. void paintEvent(QPaintEvent* e) {
    19.  
    20. Q_UNUSED(e);
    21.  
    22. opt.init(this);
    23. QPainter p(this);
    24. p.setRenderHints(QPainter::Antialiasing);
    25. p.translate(0.5, 0.5);
    26. style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    27.  
    28. QRectF container = contentsRect();
    29. container.adjust(container.width() * 0.25,
    30. container.height() * 0.25,
    31. container.width() * -0.25,
    32. container.height() * -0.25);
    33.  
    34. p.setPen(Qt::black);
    35. p.drawRect(container);
    36.  
    37. const qreal spacing = 3;
    38. container.adjust(spacing, spacing, -spacing, -spacing);
    39. p.setPen(QPen(Qt::black, 1, Qt::DashLine));
    40.  
    41. if (container.width() < 1 || container.height() < 1)
    42. return;
    43.  
    44. qreal side = squareSide(container, m_piecesCount);
    45. int rows = container.height() / side;
    46. int cols = container.width() / side;
    47. emit infoChanged(QString("Rows: %1\tCols: %2").arg(rows).arg(cols));
    48.  
    49. for (int i = 0; i < m_piecesCount; ++i) {
    50. int r = i / cols;
    51. int c = i % cols;
    52.  
    53. QRectF square(container.left() + c * side, container.top() + r * side, side, side);
    54. if (i < m_colors.size())
    55. p.setBrush(m_colors.at(i)); // just to make sure ;)
    56. p.drawRect(square);
    57.  
    58. if (side > 14) // don't draw text if square is too small
    59. p.drawText(square, Qt::AlignCenter, QString::number(i));
    60. }
    61. }
    62.  
    63. public slots:
    64. void setPiecesCount(int count) {
    65. m_piecesCount = count;
    66. update();
    67. }
    68.  
    69. signals:
    70. void infoChanged(const QString&);
    71.  
    72. private:
    73. int m_piecesCount;
    74. QList<QColor> m_colors;
    75.  
    76. // The function that needs to be improved!!!
    77. qreal squareSide(const QRectF& r, int piecesCount) {
    78. const qreal w = r.width();
    79. const qreal h = r.height();
    80. qreal top = qMin(w, h);
    81. qreal bottom = 1;
    82.  
    83. while (top - bottom > 0.01) {
    84. qreal mid = bottom + (top - bottom) / 2;
    85. if (floor(w / mid) * floor(h / mid) < piecesCount)
    86. top = mid;
    87. else
    88. bottom = mid;
    89. }
    90.  
    91. return bottom;
    92. }
    93. };
    94.  
    95. class MainWindow : public QMainWindow
    96. {
    97. Q_OBJECT
    98. public:
    99. MainWindow(QWidget* parent = 0) : QMainWindow(parent) {
    100.  
    101. m_widget = new Widget;
    102. connect(m_widget, SIGNAL(infoChanged(const QString&)), this, SLOT(updateStatusBar(const QString&)));
    103.  
    104. m_spinBox = new QSpinBox;
    105. connect(m_spinBox, SIGNAL(valueChanged(int)), m_widget, SLOT(setPiecesCount(int)));
    106. m_spinBox->setRange(1, 100);
    107. m_spinBox->setValue(5);
    108.  
    109. QWidget* cw = new QWidget;
    110. cw->setLayout(lo);
    111. lo->addWidget(m_widget);
    112. lo->addWidget(m_spinBox);
    113. setCentralWidget(cw);
    114.  
    115. m_infoLabel = new QLabel;
    116. statusBar()->addPermanentWidget(m_infoLabel);
    117. }
    118.  
    119. public slots:
    120. void updateStatusBar(const QString& info) {
    121. m_infoLabel->setText(info);
    122. }
    123.  
    124. private:
    125. QLabel* m_infoLabel;
    126. QSpinBox* m_spinBox;
    127. Widget* m_widget;
    128. };
    129.  
    130. int main(int argc, char** argv)
    131. {
    132. QApplication app(argc, argv);
    133.  
    134. MainWindow mw;
    135. qsrand(QTime::currentTime().msec());
    136. mw.show();
    137. return app.exec();
    138. }
    139.  
    140. #include "main.moc"
    To copy to clipboard, switch view to plain text mode 
    Attached Files Attached Files

  2. #2
    Join Date
    Jan 2010
    Posts
    73
    Thanks
    6
    Thanked 8 Times in 8 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Packing known number of uniform-sized squares into a known rect

    If you state that s must be an integer, then I guess that I missed it, so i will assume that it is not an integer.

    You know that the total area is given by wh and that there are n squares. Divide wh by n and you have the maximum size for each square. So, you can set an upper bound on s as (s^2 <= w * h / n).

    We are not using an integer for s, so we know that there will be an exact fit on either the width or the height. So, take the square root of (w * h / n) and set this to s.

    Let a = w / s - floor(w/s)
    Let b = h / s - floor(h/s)

    No time to finish my thoughts, and I have not worked this out, but, I am thinking about reducing s such that either w / s or h / s is an integer (which should be a simple calculation). Uggg!

  3. #3
    Join Date
    Jan 2010
    Posts
    73
    Thanks
    6
    Thanked 8 Times in 8 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Packing known number of uniform-sized squares into a known rect

    The entire floor thing is not correct I think. I believe the following to be true (assuming s is known):

    w/s * floor(h/s) <= n <= w / s * ceiling(h/s) <= w / s * h / s = w*h / s^2

    I think that I would explore an initial guess of s as D = sqrt(w * h / n), then do something silly like

    We know that s <= D.

    E = min(h / ceiling(h/D), w / ceiling(w/D))

    E is designed such that either h/E or W/E is an integer. The point to note is that this provides a value for s such that it completely fills one of the dimensions. My guess is that (if there is no rounding) that this will provide you with the optimal solution.

Similar Threads

  1. QListView Icons with uniform sizes
    By psih128 in forum Qt Programming
    Replies: 2
    Last Post: 23rd November 2009, 10:25
  2. How to make a QMainWindow fixed sized?
    By Goldmmbr in forum Newbie
    Replies: 2
    Last Post: 18th November 2009, 08:52
  3. Non-uniform cells
    By ttvo in forum Qt Programming
    Replies: 2
    Last Post: 22nd April 2009, 19:20
  4. how to put different sized icons in a QListView
    By namume in forum Qt Programming
    Replies: 1
    Last Post: 5th February 2008, 10:57
  5. Dynamically sized QTableWidget
    By therealjag in forum Qt Programming
    Replies: 3
    Last Post: 31st March 2006, 21:06

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.