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 CairoCell { 0015 // corner : the border through the top left corner of the cell. 0016 GBClassicPlugParams corner; 0017 GBClassicPlugParams tl; 0018 GBClassicPlugParams tr; 0019 GBClassicPlugParams bl; 0020 GBClassicPlugParams br; 0021 0022 // top and left tile of cell 0023 int id_top; 0024 int id_left; 0025 }; 0026 0027 void CairoMode::generateGrid(GoldbergEngine *e, int piece_count) const { 0028 int next_piece_id=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 0035 // calculate piece counts 0036 const int width = e->get_image_width(), height = e->get_image_height(); 0037 int xCount; 0038 int yCount; 0039 getBestFitExtended(xCount, yCount, 1.0 * width / height, piece_count, 2.0, 1., 1., 0.); 0040 0041 qDebug() << "cell count x = " << xCount; 0042 qDebug() << "cell count y = " << yCount; 0043 0044 0045 const qreal cellWidth = 1.0 * width / xCount, cellHeight = 1.0 * height / yCount; 0046 0047 // rationale: knobs should visually cover the same fraction of area as for the rect grid. 0048 e->m_length_base = sqrt(cellWidth * cellHeight * 0.5) * e->m_plug_size; 0049 0050 // grid is made 1 unit larger in both dimensions, to store the right and bottom border cells. 0051 CairoCell** cells = new CairoCell*[xCount+1]; 0052 0053 qDebug() << "now generating edges"; 0054 0055 for (int x = 0; x < xCount+1; ++x) { 0056 cells[x] = new CairoCell[yCount + 1]; 0057 0058 for (int y = 0; y < yCount+1; ++y) { 0059 0060 bool odd_cell = (x+y) % 2; 0061 0062 // generate usual borders first, and cater for the "edge" cases afterwards. 0063 cells[x][y].corner = e->initEdge(false); 0064 cells[x][y].tr = e->initEdge(false); 0065 cells[x][y].tl = e->initEdge(false); 0066 cells[x][y].bl = e->initEdge(false); 0067 cells[x][y].br = e->initEdge(false); 0068 0069 // determine border direction 0070 if (odd_cell) { 0071 cells[x][y].tr.flipped ^= true; 0072 cells[x][y].tl.flipped ^= true; 0073 cells[x][y].bl.flipped ^= true; 0074 cells[x][y].br.flipped ^= true; 0075 } 0076 0077 cells[x][y].tr.flipped ^= e->m_alternate_flip; 0078 cells[x][y].br.flipped ^= e->m_alternate_flip; 0079 cells[x][y].bl.flipped ^= e->m_alternate_flip; 0080 cells[x][y].tl.flipped ^= e->m_alternate_flip; 0081 0082 // set vector 0083 if (odd_cell) { 0084 cells[x][y].corner.flipped ^= (y%2 == 1); 0085 cells[x][y].corner.unit_x = QLineF((x-0.25) * cellWidth, y * cellHeight, (x+0.25) * cellWidth, y * cellHeight); 0086 } 0087 else { 0088 cells[x][y].corner.flipped ^= (x%2 == 1); 0089 cells[x][y].corner.unit_x = QLineF(x * cellWidth, (y-0.25) * cellHeight, x * cellWidth, (y + 0.25) * cellHeight); 0090 } 0091 0092 cells[x][y].tl.unit_x = QLineF((x + (odd_cell?0.25:0.0 )) * cellWidth, 0093 (y + (odd_cell?0.0 :0.25)) * cellHeight, (x+0.5) * cellWidth, (y+0.5) * cellHeight); 0094 cells[x][y].tr.unit_x = QLineF((x + (odd_cell?1.0 :0.75)) * cellWidth, 0095 (y + (odd_cell?0.25:0.0 )) * cellHeight, (x+0.5) * cellWidth, (y+0.5) * cellHeight); 0096 cells[x][y].bl.unit_x = QLineF((x + (odd_cell?0.0 :0.25)) * cellWidth, 0097 (y + (odd_cell?0.75:1.0 )) * cellHeight, (x+0.5) * cellWidth, (y+0.5) * cellHeight); 0098 cells[x][y].br.unit_x = QLineF((x + (odd_cell?0.75:1.0 )) * cellWidth, 0099 (y + (odd_cell?1.0 :0.75)) * cellHeight, (x+0.5) * cellWidth, (y+0.5) * cellHeight); 0100 0101 e->smooth_join(cells[x][y].tl, cells[x][y].br); 0102 e->smooth_join(cells[x][y].tr, cells[x][y].bl); 0103 0104 // edges 0105 if (y==0) { 0106 if (!odd_cell) { 0107 cells[x][y].corner.unit_x = QLineF(x * cellWidth, y * cellHeight, x * cellWidth, (y+ 0.25) * cellHeight); 0108 } 0109 else { 0110 cells[x][y].corner.is_straight = true; 0111 } 0112 } 0113 if (x==0) { 0114 if (odd_cell) { 0115 cells[x][y].corner.unit_x = QLineF(x * cellWidth, y * cellHeight, (x+0.25) * cellWidth, y * cellHeight); 0116 } 0117 else { 0118 cells[x][y].corner.is_straight = true; 0119 } 0120 } 0121 0122 if (y==yCount) { 0123 if (!odd_cell) { 0124 cells[x][y].corner.unit_x = QLineF(x * cellWidth, (y-0.25) * cellHeight, x * cellWidth, y * cellHeight); 0125 } 0126 else { 0127 cells[x][y].corner.is_straight = true; 0128 } 0129 } 0130 if (x==xCount) { 0131 if (odd_cell) { 0132 cells[x][y].corner.unit_x = QLineF((x-0.25) * cellWidth, y * cellHeight, x * cellWidth, y * cellHeight); 0133 } 0134 else { 0135 cells[x][y].corner.is_straight = true; 0136 } 0137 } 0138 0139 // collision checking 0140 bool intersects; 0141 QList<GBClassicPlugParams*> offenders; 0142 0143 // CORNER 0144 intersects = !cells[x][y].corner.is_straight; 0145 for (int i=0; i<collision_tries && intersects; i++) { 0146 offenders.clear(); 0147 if (i>0 && intersects) { 0148 //qDebug() << "collision: corner edge, x=" << x << ", y=" << y; 0149 cells[x][y].corner.size_correction *= collision_shrink_factor; 0150 e->reRandomizeEdge(cells[x][y].corner); 0151 } 0152 intersects = false; 0153 if (x>0 && y>0) intersects |= e->plugsIntersect(cells[x][y].corner, cells[x-1][y-1].br, &offenders); 0154 intersects |= (x==0) ? e->plugOutOfBounds(cells[x][y].corner) : e->plugsIntersect(cells[x][y].corner, cells[x-1][y].tr, &offenders); 0155 intersects |= (y==0) ? e->plugOutOfBounds(cells[x][y].corner) : e->plugsIntersect(cells[x][y].corner, cells[x][y-1].bl, &offenders); 0156 if (x==xCount || y==yCount) intersects |= e->plugOutOfBounds(cells[x][y].corner); 0157 } 0158 if (intersects) { 0159 // give up and just make the colliding borders plugless. 0160 e->makePlugless(cells[x][y].corner); 0161 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0162 } 0163 0164 0165 // TL 0166 // don't bother with the "outer" cells, they do not matter. 0167 intersects = (x<xCount && y < yCount); 0168 for (int i=0; i<collision_tries && intersects; i++) { 0169 offenders.clear(); 0170 if (i>0 && intersects) { 0171 //qDebug() << "collision: top left edge, x=" << x << ", y=" << y; 0172 cells[x][y].tl.size_correction *= collision_shrink_factor; 0173 e->reRandomizeEdge(cells[x][y].tl, true); 0174 } 0175 intersects = e->plugsIntersect(cells[x][y].tl, cells[x][y].corner, &offenders); 0176 intersects |= (odd_cell ? 0177 ((y==0) ? e->plugOutOfBounds(cells[x][y].tl) : e->plugsIntersect(cells[x][y].tl, cells[x][y-1].bl, &offenders)) : 0178 ((x==0) ? e->plugOutOfBounds(cells[x][y].tl) : e->plugsIntersect(cells[x][y].tl, cells[x-1][y].tr, &offenders)) 0179 ); 0180 } 0181 if (intersects) { 0182 // give up and just make the colliding borders plugless. 0183 e->makePlugless(cells[x][y].tl); 0184 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0185 } 0186 0187 // TR 0188 // don't bother with the "outer" cells, they do not matter. 0189 intersects = (x<xCount && y < yCount); 0190 for (int i=0; i<collision_tries && intersects; i++) { 0191 offenders.clear(); 0192 if (i>0 && intersects) { 0193 //qDebug() << "collision: top right edge, x=" << x << ", y=" << y; 0194 cells[x][y].tr.size_correction *= collision_shrink_factor; 0195 e->reRandomizeEdge(cells[x][y].tr, true); 0196 } 0197 intersects = e->plugsIntersect(cells[x][y].tr, cells[x][y].tl, &offenders); 0198 intersects |= (odd_cell ? 0199 ((x==xCount-1) ? e->plugOutOfBounds(cells[x][y].tr) : false) : 0200 ((y==0) ? e->plugOutOfBounds(cells[x][y].tr) : e->plugsIntersect(cells[x][y].tr, cells[x][y-1].br, &offenders)) 0201 ); 0202 } 0203 if (intersects) { 0204 // give up and just make the colliding borders plugless. 0205 e->makePlugless(cells[x][y].tr); 0206 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0207 } 0208 0209 // BL 0210 // don't bother with the "outer" cells, they do not matter. 0211 intersects = (x<xCount && y < yCount); 0212 for (int i=0; i<collision_tries && intersects; i++) { 0213 offenders.clear(); 0214 if (i>0 && intersects) { 0215 //qDebug() << "collision: bottom left edge, x=" << x << ", y=" << y; 0216 cells[x][y].bl.size_correction *= collision_shrink_factor; 0217 e->reRandomizeEdge(cells[x][y].bl, true); 0218 } 0219 intersects = e->plugsIntersect(cells[x][y].bl, cells[x][y].tl, &offenders); 0220 intersects |= (odd_cell ? 0221 ((x==0) ? e->plugOutOfBounds(cells[x][y].bl) : e->plugsIntersect(cells[x][y].tr, cells[x-1][y].bl, &offenders)) : 0222 ((y==yCount-1) ? e->plugOutOfBounds(cells[x][y].bl) : false) 0223 ); 0224 } 0225 if (intersects) { 0226 // give up and just make the colliding borders plugless. 0227 e->makePlugless(cells[x][y].bl); 0228 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0229 } 0230 0231 // BR 0232 // don't bother with the "outer" cells, they do not matter. 0233 intersects = (x<xCount && y < yCount); 0234 for (int i=0; i<collision_tries && intersects; i++) { 0235 offenders.clear(); 0236 if (i>0 && intersects) { 0237 //qDebug() << "collision: bottom right edge, x=" << x << ", y=" << y; 0238 cells[x][y].br.size_correction *= collision_shrink_factor; 0239 e->reRandomizeEdge(cells[x][y].br, true); 0240 } 0241 intersects = e->plugsIntersect(cells[x][y].br, cells[x][y].tr, &offenders); 0242 intersects |= e->plugsIntersect(cells[x][y].br, cells[x][y].bl, &offenders); 0243 intersects |= (odd_cell ? 0244 ((y==yCount-1) ? e->plugOutOfBounds(cells[x][y].br) : false) : 0245 ((x==xCount-1) ? e->plugOutOfBounds(cells[x][y].br) : false) 0246 ); 0247 } 0248 if (intersects) { 0249 // give up and just make the colliding borders plugless. 0250 e->makePlugless(cells[x][y].br); 0251 for (int i=0; i<offenders.size(); i++) e->makePlugless(*(offenders.at(i))); 0252 } 0253 0254 } // end collision checking 0255 } 0256 0257 qDebug() << "now creating pieces"; 0258 0259 for (int x = 0; x < xCount+1; ++x) { 0260 // checkerboard pattern as above 0261 for (int y = 0; y < yCount+1; ++y) { 0262 //create the mask path 0263 QPainterPath path; 0264 0265 bool odd_cell = (x+y) % 2; 0266 0267 // we start after the "corner" edge. 0268 path.moveTo(cells[x][y].corner.unit_x.p2()); 0269 0270 // TOP PIECE 0271 if (x < xCount) { 0272 if (!odd_cell) e->addPlugToPath(path, true, cells[x][y].corner); 0273 if (y==0) { 0274 // half piece 0275 path.lineTo(cells[x+1][y].corner.unit_x.p1()); 0276 } 0277 else { 0278 e->addPlugToPath(path, false, cells[x][y-1].bl); 0279 e->addPlugToPath(path, true, cells[x][y-1].br); 0280 } 0281 if (odd_cell) e->addPlugToPath(path, false, cells[x+1][y].corner); 0282 0283 if (y==yCount) { 0284 // half piece 0285 path.lineTo(cells[x][y].corner.unit_x.p2()); 0286 } 0287 else { 0288 e->addPlugToPath(path, false, cells[x][y].tr); 0289 e->addPlugToPath(path, true, cells[x][y].tl); 0290 } 0291 0292 cells[x][y].id_top = next_piece_id++; 0293 e->makePieceFromPath(cells[x][y].id_top, path); 0294 } 0295 0296 // LEFT PIECE 0297 if (y < yCount) { 0298 path = QPainterPath(); 0299 path.moveTo(cells[x][y].corner.unit_x.p2()); 0300 if (x==xCount) { 0301 // half piece 0302 path.lineTo(odd_cell ? cells[x-1][y].br.unit_x.p1() : cells[x][y+1].corner.unit_x.p2()); 0303 } 0304 else { 0305 e->addPlugToPath(path, false, cells[x][y].tl); 0306 e->addPlugToPath(path, true, cells[x][y].bl); 0307 } 0308 if (!odd_cell) e->addPlugToPath(path, true, cells[x][y+1].corner); 0309 0310 if (x==0) { 0311 // half piece 0312 path.lineTo(odd_cell ? cells[x][y].corner.unit_x.p1() : cells[x][y].tl.unit_x.p1()); 0313 } 0314 else { 0315 e->addPlugToPath(path, false, cells[x-1][y].br); 0316 e->addPlugToPath(path, true, cells[x-1][y].tr); 0317 } 0318 0319 if (odd_cell) e->addPlugToPath(path, false, cells[x][y].corner); 0320 0321 cells[x][y].id_left = next_piece_id++; 0322 e->makePieceFromPath(cells[x][y].id_left, path); 0323 } 0324 } 0325 } 0326 0327 qDebug() << "now adding relations"; 0328 0329 //create relations 0330 for (int x = 0; x < xCount+1; ++x) { 0331 for (int y = 0; y < yCount+1; ++y) { 0332 bool odd_cell = (x+y) % 2; 0333 // corner 0334 if (odd_cell) { 0335 if (y>0 && y < yCount) e->addRelation(cells[x][y].id_left, cells[x][y-1].id_left); 0336 } 0337 else { 0338 if (x>0 && x < xCount) e->addRelation(cells[x][y].id_top, cells[x-1][y].id_top); 0339 } 0340 // inner-cell borders 0341 if (y < yCount && x < xCount) { 0342 // tl 0343 e->addRelation(cells[x][y].id_top, cells[x][y].id_left); 0344 // tr 0345 e->addRelation(cells[x][y].id_top, cells[x+1][y].id_left); 0346 // bl 0347 e->addRelation(cells[x][y].id_left, cells[x][y+1].id_top); 0348 //br 0349 e->addRelation(cells[x+1][y].id_left, cells[x][y+1].id_top); 0350 } 0351 } 0352 } 0353 0354 //cleanup 0355 for (int x = 0; x < xCount+1; ++x) { 0356 delete[] cells[x]; 0357 } 0358 delete[] cells; 0359 }