File indexing completed on 2024-05-12 04:06:20

0001 /*
0002     SPDX-FileCopyrightText: 2010 Johannes Loehnert <loehnert.kde@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "grid.h"
0008 
0009 #include <cmath>
0010 #include <QPainterPath>
0011 #include <QDebug>
0012 #include "utilities.h"
0013 
0014 struct HexCell {
0015     // the border at the left edge, upper one
0016     GBClassicPlugParams uleft;
0017     // the border at the left edge, lower one
0018     GBClassicPlugParams lleft;
0019     // the horizontal border, either at the top cell edge
0020     // or vertically in the middle (odd cell)
0021     GBClassicPlugParams horiz;
0022 
0023     // id of piece in cell (upper one for odd column)
0024     int id;
0025 };
0026 
0027 void HexMode::generateGrid(GoldbergEngine *e, int piece_count) const {
0028     const qreal ONE_SIXTH = 1/6.0;
0029 
0030     int collision_tries = 10 * e->m_plug_size * e->m_plug_size;
0031     if (collision_tries < 5) collision_tries = 5;
0032     const qreal collision_shrink_factor = 0.95;
0033 
0034     int next_piece_id = 0;
0035 
0036     // calculate piece counts
0037     const int width = e->get_image_width(), height = e->get_image_height();
0038     int xCount;
0039     int yCount;
0040     getBestFitExtended(xCount, yCount, 1.0*width / height * 1.7320508075689 / 1.5, piece_count, 1.0, 0., 0.5, 0.);
0041 
0042     qDebug() << "cell count x = " << xCount;
0043     qDebug() << "cell count y = " << yCount;
0044 
0045 
0046     const qreal cellWidth = 1.0 * width / xCount, cellHeight = 1.0 * height / yCount;
0047 
0048     // rationale: knobs should visually cover the same fraction of area as for the rect grid.
0049     e->m_length_base = sqrt(cellWidth * cellHeight) * e->m_plug_size;
0050 
0051     // generate borders
0052     // grid is made 1 unit larger in both dimensions, to store the right and bottom borders.
0053     HexCell** cells = new HexCell*[xCount + 1];
0054 
0055     qDebug() << "now generating edges";
0056 
0057     for (int x = 0; x < xCount+1; ++x) {
0058         cells[x] = new HexCell[yCount+1];
0059 
0060         for (int y = 0; y < yCount+1; ++y) {
0061             bool odd_column = x%2;
0062             // generate usual borders first, and cater for the "edge" cases afterwards.
0063             cells[x][y].uleft = e->initEdge(false);
0064             cells[x][y].lleft = e->initEdge(false);
0065             cells[x][y].horiz = e->initEdge(false);
0066 
0067             // determine border direction
0068             cells[x][y].horiz.flipped ^= !e->m_alternate_flip;
0069             cells[x][y].uleft.flipped ^= (!odd_column);
0070             cells[x][y].lleft.flipped ^= odd_column ^ e->m_alternate_flip;
0071 
0072             if (e->m_alternate_flip && (y%2)) {
0073                 cells[x][y].horiz.flipped ^= (!odd_column);
0074                 cells[x][y].uleft.flipped ^= true;
0075                 cells[x][y].lleft.flipped ^= true;
0076             }
0077 
0078             // determine border vectors
0079             qreal xleft1, xleft2, xright;
0080             xleft1 = odd_column? ((x-ONE_SIXTH) * cellWidth) : ((x + ONE_SIXTH) * cellWidth);
0081             xleft2 = odd_column? ((x+ONE_SIXTH) * cellWidth) : ((x - ONE_SIXTH) * cellWidth);
0082             xright = (x+1-ONE_SIXTH) * cellWidth;
0083             if (x==0 || x == xCount) {
0084                 xleft1 = x * cellWidth;
0085                 xleft2 = x * cellWidth;
0086             }
0087             if (x == xCount - 1) xright = (x+1)*cellWidth;
0088 
0089             // and set
0090             cells[x][y].uleft.unit_x = QLineF(xleft1, y * cellHeight, xleft2, (y + 0.5) * cellHeight);
0091             cells[x][y].lleft.unit_x = QLineF(xleft2, (y + 0.5) * cellHeight, xleft1, (y + 1.0) * cellHeight);
0092             cells[x][y].horiz.unit_x = QLineF(odd_column?xleft2:xleft1, (y + (odd_column?0.5:0.0)) * cellHeight, 
0093                                               xright, (y + (odd_column?0.5:0.0)) * cellHeight);
0094 
0095             // frame borders
0096             if (x==0 || x == xCount) {
0097                 cells[x][y].uleft.is_straight = true;
0098                 cells[x][y].lleft.is_straight = true;
0099             }
0100             if ((y==0 || y == yCount) && !odd_column) {
0101                 cells[x][y].horiz.is_straight = true;
0102             }
0103 
0104             // collision checking
0105             // don't bother with the "outer" cells, they do not matter.
0106             if (x < xCount && y < yCount) {
0107                 bool intersects = true;
0108                 QList<GBClassicPlugParams*> offenders;
0109                 // ULEFT
0110                 for (int i=0; i<collision_tries && intersects; i++) {
0111                     offenders.clear();
0112                     if (i>0 && intersects) {
0113                         //qDebug() << "collision: uleft edge, x=" << x << ", y=" << y;
0114                         cells[x][y].uleft.size_correction *= collision_shrink_factor;
0115                         e->reRandomizeEdge(cells[x][y].uleft);
0116                     }
0117                     intersects = false;
0118                     if (x>0) intersects |= e->plugsIntersect(cells[x][y].uleft, cells[x-1][y].horiz, &offenders);
0119                     if (y>0) intersects |= e->plugsIntersect(cells[x][y].uleft, cells[x][y-1].lleft, &offenders);
0120                     if (y==0) intersects |= e->plugOutOfBounds(cells[x][y].uleft);
0121                 }
0122                 if (intersects) {
0123                     // give up and make the colliding borders plugless.
0124                     e->makePlugless(cells[x][y].uleft);
0125                     for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i)));
0126                 }
0127 
0128                 // LLEFT
0129                 intersects = true;
0130                 for (int i=0; i<collision_tries && intersects; i++) {
0131                     offenders.clear();
0132                     if (i>0 && intersects) {
0133                         // qDebug() << "collision: lleft edge, x=" << x << ", y=" << y;
0134                         cells[x][y].lleft.size_correction *= collision_shrink_factor;
0135                         e->reRandomizeEdge(cells[x][y].lleft);
0136                     }
0137                     intersects = e->plugsIntersect(cells[x][y].lleft, cells[x][y].uleft, &offenders);
0138                     if (x!=0) { 
0139                         intersects |= (odd_column ? 
0140                                     e->plugsIntersect(cells[x][y].lleft, cells[x-1][y+1].horiz, &offenders) :
0141                                     e->plugsIntersect(cells[x][y].lleft, cells[x-1][y].horiz, &offenders)
0142                                 );
0143                     }
0144                     if (y==yCount-1) intersects |= e->plugOutOfBounds(cells[x][y].lleft);
0145                 }
0146                 if (intersects) {
0147                     // give up and make the colliding borders plugless.
0148                     e->makePlugless(cells[x][y].lleft);
0149                     for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i)));
0150                 }
0151 
0152                 // HORIZ
0153                 intersects = true;
0154                 for (int i=0; i<collision_tries && intersects; i++) {
0155                     offenders.clear();
0156                     if (i>0 && intersects) {
0157                         //qDebug() << "collision: horiz edge, x=" << x << ", y=" << y;
0158                         cells[x][y].horiz.size_correction *= collision_shrink_factor;
0159                         e->reRandomizeEdge(cells[x][y].horiz);
0160                     }
0161                     intersects = e->plugsIntersect(cells[x][y].horiz, cells[x][y].uleft, &offenders);
0162                     if (odd_column) {
0163                         intersects |= e->plugsIntersect(cells[x][y].horiz, cells[x][y].lleft, &offenders);
0164                     }
0165                     else {
0166                         if (y!=0) intersects |= e->plugsIntersect(cells[x][y].horiz, cells[x][y-1].lleft, &offenders);
0167                     }
0168                 }
0169                 if (intersects) {
0170                     // give up and make the colliding borders plugless.
0171                     e->makePlugless(cells[x][y].horiz);
0172                     for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i)));
0173                 }
0174             } // end collision checking
0175         }
0176     }
0177 
0178     // generate pieces
0179     qDebug() << "now creating pieces";
0180 
0181 
0182     for (int x = 0; x < xCount; ++x) {
0183         for (int y = 0; y < yCount+1; ++y) {
0184             QPainterPath path;
0185             bool odd_column = x%2;
0186 
0187             if (y==yCount && !odd_column) continue;
0188 
0189             path.moveTo(cells[x][y].uleft.unit_x.p1());
0190             if (!odd_column) {
0191                 e->addPlugToPath(path, false, cells[x][y].horiz);
0192                 e->addPlugToPath(path, false, cells[x+1][y].uleft);
0193                 e->addPlugToPath(path, false, cells[x+1][y].lleft);
0194                 e->addPlugToPath(path, true, cells[x][y+1].horiz);
0195                 e->addPlugToPath(path, true, cells[x][y].lleft);
0196                 e->addPlugToPath(path, true, cells[x][y].uleft);
0197             }
0198             else {
0199                 // now we have to deal with the half pieces
0200                 if (y==0) {
0201                     path.lineTo(cells[x+1][y].uleft.unit_x.p1());
0202                 }
0203                 else {
0204                     e->addPlugToPath(path, true, cells[x][y-1].lleft);
0205                     e->addPlugToPath(path, false, cells[x][y-1].horiz);
0206                     e->addPlugToPath(path, false, cells[x+1][y-1].lleft);
0207                 }
0208                 if (y==yCount) {
0209                     path.lineTo(cells[x][y].uleft.unit_x.p1());
0210                 }
0211                 else {
0212                     e->addPlugToPath(path, false, cells[x+1][y].uleft);
0213                     e->addPlugToPath(path, true, cells[x][y].horiz);
0214                     e->addPlugToPath(path, true, cells[x][y].uleft);
0215                 }
0216             }
0217 
0218             cells[x][y].id = next_piece_id++;
0219             e->makePieceFromPath(cells[x][y].id, path);
0220         }
0221     }
0222 
0223     // generate relations
0224     qDebug() << "now adding relations";
0225 
0226     for (int x=0; x<xCount; x++) {
0227         for (int y=0; y<yCount+1; y++) {
0228             // piece above
0229             if (y>0 && (y<yCount + x%2)) e->addRelation(cells[x][y].id, cells[x][y-1].id);
0230             // piece to the right
0231             if (x>0 && y < yCount) e->addRelation(cells[x][y].id, cells[x-1][y].id);
0232             // other piece to the right
0233             if (x%2) {
0234                 if (y>0) e->addRelation(cells[x][y].id, cells[x-1][y-1].id);
0235             }
0236             else {
0237                 if (x>0 && y<yCount) e->addRelation(cells[x][y].id, cells[x-1][y+1].id);
0238             }
0239         }
0240     }
0241 
0242     qDebug() << "cleanup";
0243     // cleanup
0244     for (int x=0; x<xCount+1; x++) {
0245         delete[] cells[x];
0246     }
0247     delete[] cells;
0248 }