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 }