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 0015 // see rotrex-grid.svg to understand everything. 0016 struct RotrexCell { 0017 GBClassicPlugParams horiz; 0018 GBClassicPlugParams vert; 0019 GBClassicPlugParams tl; 0020 GBClassicPlugParams tr; 0021 GBClassicPlugParams bl; 0022 GBClassicPlugParams br; 0023 0024 // piece ids... 0025 int id_corner; // of the piece centered in the top left cell corner (hex or rect) 0026 int id_tri; // of the triangular piece on the top cell corner 0027 int id_rect; // of the rect piece fully contained in the cell 0028 }; 0029 0030 void RotrexMode::generateGrid(GoldbergEngine *e, int piece_count) const { 0031 // fixed offsets in relative units of cell size. see image. 0032 // set as constants so we don't have to throw magic numbers around all the time. 0033 // square root of three - helper number 0034 const qreal _R3 = sqrt(3.0); 0035 const qreal _xunit = 1.5 + _R3; 0036 const qreal x1 = 0.5 / _xunit, x2 = 1.0 / _xunit, x3 = (0.5 + _R3) / _xunit, x4 = (1 + _R3) / _xunit; 0037 const qreal _yunit = 1.0 + 0.5*_R3; 0038 const qreal y1 = (0.5*_R3) / _yunit, y2 = 1.0 / _yunit; 0039 0040 0041 int next_piece_id = 0; 0042 0043 int collision_tries = 10 * e->m_plug_size * e->m_plug_size; 0044 if (collision_tries < 5) collision_tries = 5; 0045 const qreal collision_shrink_factor = 0.95; 0046 0047 // calculate piece counts 0048 const int width = e->get_image_width(), height = e->get_image_height(); 0049 int xCount; 0050 int yCount; 0051 getBestFitExtended(xCount, yCount, 1.0*width / height * _yunit / _xunit, piece_count, 3.0, 1.0, 2.0, 1.0); 0052 0053 qDebug() << "cell count x = " << xCount; 0054 qDebug() << "cell count y = " << yCount; 0055 0056 const qreal cellWidth = 1.0 * width / xCount, cellHeight = 1.0 * height / yCount; 0057 0058 0059 e->m_length_base = sqrt(cellWidth * cellHeight / 3.0) * e->m_plug_size; 0060 0061 // GENERATE BORDERS 0062 // grid is made 1 unit larger in both dimensions, to store the right and bottom borders. 0063 RotrexCell** cells = new RotrexCell*[xCount+1]; 0064 0065 for (int x=0; x<xCount+1; x++) { 0066 cells[x] = new RotrexCell[yCount+1]; 0067 for (int y=0; y<yCount+1; y++) { 0068 bool odd_cell = (x+y) % 2; 0069 0070 // generate usual borders first, and cater for the "edge" cases afterwards. 0071 cells[x][y].horiz = e->initEdge(false); 0072 cells[x][y].vert = e->initEdge(false); 0073 cells[x][y].tl = e->initEdge(false); 0074 cells[x][y].tr = e->initEdge(false); 0075 cells[x][y].bl = e->initEdge(false); 0076 cells[x][y].br = e->initEdge(false); 0077 0078 // determine border direction 0079 cells[x][y].horiz.flipped ^= (!odd_cell) ^ e->m_alternate_flip; 0080 cells[x][y].vert.flipped ^= (!odd_cell) ^ e->m_alternate_flip; 0081 cells[x][y].tl.flipped ^= odd_cell ^ e->m_alternate_flip; 0082 cells[x][y].tr.flipped ^= odd_cell ^ e->m_alternate_flip; 0083 cells[x][y].bl.flipped ^= (!odd_cell) ^ e->m_alternate_flip; 0084 cells[x][y].br.flipped ^= (!odd_cell) ^ e->m_alternate_flip; 0085 0086 // now for the mad sh**. Set edge vectors. 0087 cells[x][y].horiz.unit_x = QLineF( 0088 (x - x1) * cellWidth, 0089 (y + (odd_cell ? y2 : y1)) * cellHeight, 0090 (x + x1) * cellWidth, 0091 (y + (odd_cell ? y2 : y1)) * cellHeight); 0092 cells[x][y].vert.unit_x = QLineF( 0093 (x + (odd_cell ? x1 : x4)) * cellWidth, 0094 (y - y2) * cellHeight, 0095 (x + (odd_cell ? x1 : x4)) * cellWidth, 0096 (y + y2) * cellHeight); 0097 cells[x][y].tl.unit_x = QLineF( 0098 (x + (odd_cell ? x3 : x2)) * cellWidth, 0099 y * cellHeight, 0100 (x + x1) * cellWidth, 0101 (y + (odd_cell ? y2 : y1)) * cellHeight); 0102 cells[x][y].tr.unit_x = QLineF( 0103 (x + (odd_cell ? x3 : x2)) * cellWidth, 0104 y * cellHeight, 0105 (x + x4) * cellWidth, 0106 (y + (odd_cell ? y1 : y2)) * cellHeight); 0107 cells[x][y].bl.unit_x = QLineF( 0108 (x + x1) * cellWidth, 0109 (y + (odd_cell ? y2 : y1)) * cellHeight, 0110 (x + (odd_cell ? x2 : x3)) * cellWidth, 0111 (y + 1) * cellHeight); 0112 cells[x][y].br.unit_x = QLineF( 0113 (x + x4) * cellWidth, 0114 (y + (odd_cell ? y1 : y2)) * cellHeight, 0115 (x + (odd_cell ? x2 : x3)) * cellWidth, 0116 (y + 1) * cellHeight); 0117 0118 // pieces at frame 0119 // top edge 0120 if (y==0) { 0121 cells[x][y].vert.unit_x.setP1(QPointF( 0122 cells[x][y].vert.unit_x.x1(), 0123 y * cellHeight)); 0124 } 0125 // left edge 0126 if (x==0) { 0127 cells[x][y].horiz.unit_x.setP1(QPointF( 0128 x * cellWidth, 0129 cells[x][y].horiz.unit_x.y1())); 0130 } 0131 // right edge 0132 if (x==xCount) { 0133 cells[x][y].horiz.unit_x.setP2(QPointF( 0134 x * cellWidth, 0135 cells[x][y].horiz.unit_x.y2())); 0136 } 0137 0138 // bottom edge 0139 if (y == yCount) { 0140 cells[x][y].vert.unit_x.setP2(QPointF( 0141 cells[x][y].vert.unit_x.x2(), 0142 y * cellHeight)); 0143 } 0144 0145 // COLLISION CHECKS 0146 bool intersects; 0147 QList<GBClassicPlugParams*> offenders; 0148 0149 // horiz 0150 intersects = (y < yCount); 0151 for (int i=0; i<collision_tries && intersects; i++) { 0152 offenders.clear(); 0153 if (i>0 && intersects) { 0154 cells[x][y].horiz.size_correction *= collision_shrink_factor; 0155 e->reRandomizeEdge(cells[x][y].horiz); 0156 } 0157 if (x==0) { 0158 intersects = e->plugOutOfBounds(cells[x][y].horiz); 0159 } 0160 else { 0161 if (!odd_cell) { 0162 intersects = e->plugsIntersect(cells[x][y].horiz, cells[x-1][y].tr, &offenders) 0163 || e->plugsIntersect(cells[x][y].horiz, cells[x-1][y+1].vert, &offenders); 0164 } 0165 else { 0166 intersects = e->plugsIntersect(cells[x][y].horiz, cells[x-1][y].br, &offenders) 0167 || e->plugsIntersect(cells[x][y].horiz, cells[x-1][y].vert, &offenders); 0168 } 0169 } 0170 if (x==xCount) intersects |= e->plugOutOfBounds(cells[x][y].horiz); 0171 } 0172 if (intersects) { 0173 // give up and make colliding borders plugless. 0174 e->makePlugless(cells[x][y].horiz); 0175 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0176 } 0177 0178 // vert 0179 intersects = (x < xCount); 0180 for (int i=0; i<collision_tries && intersects; i++) { 0181 offenders.clear(); 0182 if (i>0 && intersects) { 0183 cells[x][y].vert.size_correction *= collision_shrink_factor; 0184 e->reRandomizeEdge(cells[x][y].vert); 0185 } 0186 intersects = false; 0187 if (y==0) { 0188 intersects |= e->plugOutOfBounds(cells[x][y].vert); 0189 } 0190 else { 0191 if (!odd_cell) { 0192 intersects |= e->plugsIntersect(cells[x][y].vert, cells[x][y-1].br, &offenders); 0193 } 0194 else { 0195 intersects |= e->plugsIntersect(cells[x][y].vert, cells[x][y-1].bl, &offenders); 0196 intersects |= e->plugsIntersect(cells[x][y].vert, cells[x][y-1].horiz, &offenders); 0197 } 0198 } 0199 if (odd_cell) intersects |= e->plugsIntersect(cells[x][y].vert, cells[x][y].horiz, &offenders); 0200 if (y==yCount) intersects |= e->plugOutOfBounds(cells[x][y].vert); 0201 } 0202 if (intersects) { 0203 // give up and make colliding borders plugless. 0204 e->makePlugless(cells[x][y].vert); 0205 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0206 } 0207 0208 if (x<xCount && y<yCount) { 0209 // tl 0210 intersects = true; 0211 for (int i=0; i<collision_tries && intersects; i++) { 0212 offenders.clear(); 0213 if (i>0 && intersects) { 0214 cells[x][y].tl.size_correction *= collision_shrink_factor; 0215 e->reRandomizeEdge(cells[x][y].tl); 0216 } 0217 intersects = (y>0 ? 0218 e->plugsIntersect(cells[x][y].tl, cells[x][y-1].bl, &offenders) : 0219 e->plugOutOfBounds(cells[x][y].tl)); 0220 intersects |= (odd_cell ? 0221 e->plugsIntersect(cells[x][y].tl, cells[x][y].vert, &offenders) : 0222 e->plugsIntersect(cells[x][y].tl, cells[x][y].horiz, &offenders)); 0223 } 0224 if (intersects) { 0225 // give up and make colliding borders plugless. 0226 e->makePlugless(cells[x][y].tl); 0227 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0228 } 0229 0230 // tr 0231 intersects = true; 0232 for (int i=0; i<collision_tries && intersects; i++) { 0233 offenders.clear(); 0234 if (i>0 && intersects) { 0235 cells[x][y].tr.size_correction *= collision_shrink_factor; 0236 e->reRandomizeEdge(cells[x][y].tr); 0237 } 0238 intersects = (y>0 ? 0239 e->plugsIntersect(cells[x][y].tr, cells[x][y-1].br, &offenders) : 0240 e->plugOutOfBounds(cells[x][y].tr)); 0241 intersects |= e->plugsIntersect(cells[x][y].tr, cells[x][y].tl, &offenders); 0242 if (!odd_cell) intersects |= e->plugsIntersect(cells[x][y].tr, cells[x][y].vert, &offenders); 0243 } 0244 if (intersects) { 0245 // give up and make colliding borders plugless. 0246 e->makePlugless(cells[x][y].tr); 0247 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0248 } 0249 0250 // bl 0251 intersects = true; 0252 for (int i=0; i<collision_tries && intersects; i++) { 0253 offenders.clear(); 0254 if (i>0 && intersects) { 0255 cells[x][y].bl.size_correction *= collision_shrink_factor; 0256 e->reRandomizeEdge(cells[x][y].bl); 0257 } 0258 intersects = e->plugsIntersect(cells[x][y].bl, cells[x][y].tl, &offenders); 0259 if (odd_cell) intersects |= e->plugsIntersect(cells[x][y].bl, cells[x][y].horiz, &offenders); 0260 if (y==yCount-1) intersects |= e->plugOutOfBounds(cells[x][y].bl); 0261 } 0262 if (intersects) { 0263 // give up and make colliding borders plugless. 0264 e->makePlugless(cells[x][y].bl); 0265 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0266 } 0267 0268 // br 0269 intersects = true; 0270 for (int i=0; i<collision_tries && intersects; i++) { 0271 offenders.clear(); 0272 if (i>0 && intersects) { 0273 cells[x][y].br.size_correction *= collision_shrink_factor; 0274 e->reRandomizeEdge(cells[x][y].br); 0275 } 0276 intersects = e->plugsIntersect(cells[x][y].br, cells[x][y].bl, &offenders); 0277 intersects |= e->plugsIntersect(cells[x][y].br, cells[x][y].tr, &offenders); 0278 if (y==yCount-1) intersects |= e->plugOutOfBounds(cells[x][y].br); 0279 } 0280 if (intersects) { 0281 // give up and make colliding borders plugless. 0282 e->makePlugless(cells[x][y].br); 0283 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0284 } 0285 } 0286 0287 } 0288 } 0289 0290 0291 // CREATE PIECES 0292 for (int x=0; x<xCount+1; x++) { 0293 for (int y=0; y<yCount+1; y++) { 0294 QPainterPath path; 0295 bool odd_cell = (x+y) % 2; 0296 0297 // separate logic for even and odd cell (easier...) 0298 // the inner rect piece is common for both 0299 if (!odd_cell) { 0300 // corner piece is hexagonal. 0301 // this is a real beast since it might be halved or even quartered. 0302 path = QPainterPath(); 0303 0304 // upper half 0305 path.moveTo((x + (x==0 ? 0.0 : -x2)) * cellWidth, y * cellHeight); 0306 0307 if (y==0) { 0308 path.lineTo((x + (x==xCount ? 0.0 : x2)) * cellWidth, 0.0); 0309 } 0310 else { 0311 if (x==0) { 0312 path.lineTo(cells[x][y-1].horiz.unit_x.p1()); 0313 } 0314 else { 0315 e->addPlugToPath(path, true, cells[x-1][y-1].br); 0316 } 0317 e->addPlugToPath(path, false, cells[x][y-1].horiz); 0318 if (x==xCount) { 0319 path.lineTo(xCount * cellWidth, y * cellHeight); 0320 } 0321 else { 0322 e->addPlugToPath(path, false, cells[x][y-1].bl); 0323 } 0324 } 0325 // lower half 0326 if (y==yCount) { 0327 path.lineTo((x + (x==0 ? 0.0 : -x2)) * cellWidth, y * cellHeight); 0328 } 0329 else { 0330 if (x==xCount) { 0331 path.lineTo(cells[x][y].horiz.unit_x.p2()); 0332 } 0333 else { 0334 e->addPlugToPath(path, false, cells[x][y].tl); 0335 } 0336 e->addPlugToPath(path, true, cells[x][y].horiz); 0337 if (x==0) { 0338 path.lineTo(x * cellWidth, y * cellHeight); 0339 } 0340 else { 0341 e->addPlugToPath(path, true, cells[x-1][y].tr); 0342 } 0343 } 0344 0345 cells[x][y].id_corner = next_piece_id++; 0346 e->makePieceFromPath(cells[x][y].id_corner, path); 0347 0348 // triangle piece 0349 if (x < xCount) { 0350 path = QPainterPath(); 0351 path.moveTo(cells[x][y].tl.unit_x.p1()); 0352 0353 if (y==0) { 0354 path.lineTo((x+x4) * cellWidth, 0.0); 0355 } 0356 else { 0357 e->addPlugToPath(path, true, cells[x][y-1].br); 0358 } 0359 e->addPlugToPath(path, false, cells[x][y].vert); 0360 if (y==yCount) { 0361 path.lineTo(cells[x][y].tl.unit_x.p1()); 0362 } 0363 else { 0364 e->addPlugToPath(path, true, cells[x][y].tr); 0365 } 0366 0367 cells[x][y].id_tri = next_piece_id++; 0368 e->makePieceFromPath(cells[x][y].id_tri, path); 0369 } 0370 0371 } 0372 else { 0373 // rect piece 0374 // might be halved or quartered. 0375 path = QPainterPath(); 0376 path.moveTo((x + (x==0 ? 0.0 : -x1)) * cellWidth, (y + (y==0 ? 0.0 : -y2)) * cellHeight); 0377 0378 if (y==0) { 0379 path.lineTo((x + (x==xCount ? 0.0 : x1)) * cellWidth, 0.0); 0380 } 0381 else { 0382 e->addPlugToPath(path, false, cells[x][y-1].horiz); 0383 } 0384 if (x==xCount) { 0385 path.lineTo(xCount * cellWidth, (y + (y==yCount ? 0.0 : y2)) * cellHeight); 0386 } 0387 else { 0388 e->addPlugToPath(path, false, cells[x][y].vert); 0389 } 0390 if (y==yCount) { 0391 path.lineTo((x + (x==0 ? 0 : -x1)) * cellWidth, yCount * cellHeight); 0392 } 0393 else { 0394 e->addPlugToPath(path, true, cells[x][y].horiz); 0395 } 0396 if (x==0) { 0397 path.lineTo(0.0, (y + (y==0 ? 0.0 : -y2)) * cellHeight); 0398 } 0399 else { 0400 e->addPlugToPath(path, true, cells[x-1][y].vert); 0401 } 0402 0403 cells[x][y].id_corner = next_piece_id++; 0404 e->makePieceFromPath(cells[x][y].id_corner, path); 0405 0406 // trigonal piece 0407 if (x < xCount) { 0408 path = QPainterPath(); 0409 path.moveTo(cells[x][y].tl.unit_x.p1()); 0410 0411 if (y==yCount) { 0412 path.lineTo((x+x1) * cellWidth, yCount * cellHeight); 0413 } 0414 else { 0415 e->addPlugToPath(path, false, cells[x][y].tl); 0416 } 0417 0418 e->addPlugToPath(path, true, cells[x][y].vert); 0419 0420 if (y==0) { 0421 path.lineTo(cells[x][y].tl.unit_x.p1()); 0422 } 0423 else { 0424 e->addPlugToPath(path, false, cells[x][y-1].bl); 0425 } 0426 cells[x][y].id_tri = next_piece_id++; 0427 e->makePieceFromPath(cells[x][y].id_tri, path); 0428 } 0429 } 0430 // inner rect piece 0431 if (x<xCount && y<yCount) { 0432 path = QPainterPath(); 0433 path.moveTo(cells[x][y].tr.unit_x.p1()); 0434 e->addPlugToPath(path, false, cells[x][y].tr); 0435 e->addPlugToPath(path, false, cells[x][y].br); 0436 e->addPlugToPath(path, true, cells[x][y].bl); 0437 e->addPlugToPath(path, true, cells[x][y].tl); 0438 0439 cells[x][y].id_rect = next_piece_id++; 0440 e->makePieceFromPath(cells[x][y].id_rect, path); 0441 } 0442 } 0443 } 0444 0445 // RELATIONS 0446 for (int x=0; x<xCount+1; x++) { 0447 for (int y=0; y<yCount+1; y++) { 0448 bool odd_cell = (x+y) % 2; 0449 0450 // Each cell takes care of the relations corresponding to the borders it contains. 0451 // horiz 0452 if (y<yCount) e->addRelation(cells[x][y].id_corner, cells[x][y+1].id_corner); 0453 // vert 0454 if (x<xCount) e->addRelation(cells[x][y].id_tri, cells[x + (odd_cell ? 0 : 1)][y].id_corner); 0455 0456 if (x<xCount && y<yCount) { 0457 e->addRelation(cells[x][y].id_tri, cells[x][y].id_rect); 0458 e->addRelation(cells[x][y].id_rect, cells[x][y+1].id_tri); 0459 e->addRelation(cells[x][y].id_rect, cells[x ][y + (odd_cell ? 1 : 0)].id_corner); 0460 e->addRelation(cells[x][y].id_rect, cells[x+1][y + (odd_cell ? 0 : 1)].id_corner); 0461 } 0462 } 0463 } 0464 0465 // CLEANUP 0466 for (int x=0; x<xCount+1; x++) { 0467 delete[] cells[x]; 0468 } 0469 delete[] cells; 0470 0471 }