File indexing completed on 2024-04-21 03:41:53
0001 /*************************************************************************** 0002 * Copyright (C) 2004-2007 by Albert Astals Cid * 0003 * aacid@kde.org * 0004 * Copyright (C) 2006 by Isaac Clerencia * 0005 * isaac@warp.es * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify * 0008 * it under the terms of the GNU General Public License as published by * 0009 * the Free Software Foundation; either version 2 of the License, or * 0010 * (at your option) any later version. * 0011 ***************************************************************************/ 0012 0013 #include "placeasker.h" 0014 0015 #include <cmath> 0016 #include <math.h> 0017 0018 #include <KLocalizedString> 0019 #include <KMessageBox> 0020 0021 #include <QLabel> 0022 #include <QLayout> 0023 #include <QScrollBar> 0024 #include <QString> 0025 0026 #include "map.h" 0027 #include "placemapwidget.h" 0028 0029 placeAsker::placeAsker(QWidget *parent, KGmap *m, QWidget *w, uint count) : askWidget(parent, m, w, count, true), p_currentDivisionImage(nullptr) 0030 { 0031 QVBoxLayout *lay = new QVBoxLayout(this); 0032 lay -> setContentsMargins(0, 0, 0, 0); 0033 lay -> setSpacing(0); 0034 0035 p_mapImage = new QImage(p_map->getMapFile()); 0036 p_mapWidget = new placeMapWidget(this); 0037 lay -> addWidget(p_mapWidget); 0038 0039 connect(p_mapWidget, &placeMapWidget::clicked, this, &placeAsker::handleMapClick); 0040 connect(p_mapWidget, &placeMapWidget::setMoveActionChecked, this, &placeAsker::setMoveActionChecked); 0041 connect(p_mapWidget, &placeMapWidget::setZoomActionChecked, this, &placeAsker::setZoomActionChecked); 0042 connect(p_mapWidget, &placeMapWidget::setMoveActionEnabled, this, &placeAsker::setMoveActionEnabled); 0043 0044 QVBoxLayout *vbl = static_cast<QVBoxLayout*>(w -> layout()); 0045 p_next = new QLabel(w); 0046 p_next -> setAlignment(Qt::AlignTop | Qt::AlignHCenter); 0047 p_next -> setWordWrap(true); 0048 p_fill = new QWidget(w); 0049 p_fill -> show(); 0050 vbl -> addWidget(p_next); 0051 vbl -> addWidget(p_fill, 1); 0052 p_placedPixelIndices = p_mapWidget -> outerPixelIndices(); 0053 // Set the background image before start asking 0054 p_mapWidget -> init(p_map, p_mapImage); 0055 0056 nextQuestion(); 0057 } 0058 0059 placeAsker::~placeAsker() 0060 { 0061 delete p_next; 0062 delete p_fill; 0063 delete p_mapImage; 0064 } 0065 0066 bool placeAsker::isAsker() const 0067 { 0068 return p_answers; 0069 } 0070 0071 void placeAsker::mousePressEvent(QMouseEvent*) 0072 { 0073 } 0074 0075 void placeAsker::setMovement(bool b) 0076 { 0077 p_mapWidget -> setMapMove(b); 0078 } 0079 0080 void placeAsker::setZoom(bool b) 0081 { 0082 askWidget::setZoom(b); 0083 p_mapWidget -> setMapZoom(b); 0084 } 0085 0086 void placeAsker::setOriginalZoom() 0087 { 0088 p_mapWidget -> setGameImage(); 0089 } 0090 0091 void placeAsker::setAutomaticZoom(bool automaticZoom) 0092 { 0093 p_mapWidget -> setAutomaticZoom(automaticZoom); 0094 } 0095 0096 void placeAsker::handleMapClick(QRgb c, const QPoint & , const QPointF &mapPoint) 0097 { 0098 QString aux; 0099 aux = p_map -> getWhatIs(c, false); 0100 if (aux == QLatin1String("nothing")) KMessageBox::error(this, i18nc("@info", "You have found a bug in a map. Please contact the author and tell the %1 map has nothing associated to color %2,%3,%4.", p_map -> getFile(), qRed(c), qGreen(c), qBlue(c))); 0101 else 0102 { 0103 p_mapWidget->placeDivision(p_currentDivisionRect); 0104 p_mapWidget->unsetCursor(); 0105 // the image is no longer needed 0106 delete p_currentDivisionImage; 0107 0108 double distX = p_currentDivisionRect.x() - mapPoint.x(); 0109 double distY = p_currentDivisionRect.y() - mapPoint.y(); 0110 double distance = sqrt(static_cast<double>(distX * distX + distY * distY)); 0111 0112 int indexOfCurrent = p_mapImage -> colorTable().indexOf(p_currentRgb); 0113 bool consideredGood = distance < 5.0; 0114 // if we consider it good enough don't transmit a may be wrong color 0115 if (consideredGood) c = p_currentRgb; 0116 if (! consideredGood) 0117 { 0118 bool hasBorderShown = false; 0119 for ( int i = p_placedPixelIndices.size() ; --i >= 0 && !hasBorderShown ; ) 0120 { 0121 uchar pixelIndex = p_placedPixelIndices[i]; 0122 size_t nb = p_mapWidget -> nbBorderPixels(pixelIndex, indexOfCurrent); 0123 hasBorderShown = nb > 3; 0124 } 0125 consideredGood = !hasBorderShown && distance < 16.0; 0126 if (consideredGood) c = p_currentRgb; 0127 } 0128 if (! consideredGood) 0129 { 0130 QRect definedRect(0, 0, p_mapImage -> width(), p_mapImage -> height()); 0131 QPoint v = QPoint(mapPoint.x(), mapPoint.y()) - p_currentDivisionRect.topLeft(); 0132 QRect initialRect(p_currentDivisionRect); 0133 QRect userRect = initialRect.translated(v); 0134 QRect definedRectUser = userRect & definedRect; 0135 QPoint definedFirstDiag = definedRectUser.bottomRight() - definedRectUser.topLeft(); 0136 QPoint origFirstDiag = userRect.bottomRight() - userRect.topLeft(); 0137 QPoint badDiff = origFirstDiag -definedFirstDiag; 0138 QPoint diagDiff = origFirstDiag -badDiff; 0139 QVector<size_t> stats(p_mapImage -> colorTable().size()); 0140 size_t goodCount = 0; 0141 size_t outCount = badDiff.x() * badDiff.y() + badDiff.x() * diagDiff.y() + diagDiff.x() * badDiff.y(); 0142 size_t badCount = outCount; 0143 for ( int dy = definedFirstDiag.y(); dy >= 0 ; dy-- ) 0144 { 0145 for ( int dx = definedFirstDiag.x(); dx >= 0 ; dx-- ) 0146 { 0147 int origPixelIndex = p_mapImage -> pixelIndex(initialRect.left() + dx, initialRect.top() + dy); 0148 if ( origPixelIndex != indexOfCurrent ) 0149 continue; 0150 int userPixelIndex = p_mapImage -> pixelIndex(definedRectUser.left() + dx, definedRectUser.top() + dy); 0151 if ( userPixelIndex == origPixelIndex ) goodCount++; 0152 else 0153 { 0154 stats[userPixelIndex]++; 0155 badCount++; 0156 } 0157 } 0158 } 0159 consideredGood = goodCount > 0.5 * (goodCount + badCount); 0160 if (consideredGood) c = p_currentRgb; 0161 else if (outCount > 0.5 * (goodCount + badCount)) 0162 { 0163 c = p_map -> getIgnoredDivisions(askMode())[0] -> getRGB(); 0164 } 0165 else 0166 { 0167 int indexOfMax = -1; 0168 size_t maxCount = 0; 0169 for ( int i = stats.size() -1 ; i >= 0 ; i-- ) 0170 { 0171 if ( stats[i] > maxCount ) 0172 { 0173 indexOfMax = i; 0174 maxCount = stats[i]; 0175 } 0176 } 0177 c = p_mapImage -> colorTable().at(indexOfMax); 0178 } 0179 } 0180 p_placedPixelIndices.append(indexOfCurrent); 0181 p_currentAnswer.setAnswer(QColor(c)); 0182 questionAnswered(consideredGood); 0183 nextQuestion(); 0184 } 0185 } 0186 0187 void placeAsker::nextQuestionHook(const division *div) 0188 { 0189 const QString divisionName = div -> getName(); 0190 p_next -> setText(i18nc("@info:status", "Please place in the map:<br/><b>%1</b>", divisionName)); 0191 p_next -> show(); 0192 p_currentAnswer.setQuestion(i18nc("@item:intable column Question, %1 is region name", "%1", divisionName)); 0193 QColor color = QColor(div -> getRGB()); 0194 p_currentRgb = color.rgb(); 0195 p_currentAnswer.setCorrectAnswer(color); 0196 setCurrentDivision(div); 0197 p_mapWidget->setCurrentDivisionImage(p_currentDivisionImage); 0198 } 0199 0200 QString placeAsker::getQuestionHook() const 0201 { 0202 QString divisionType = p_map->getDivisionsString(); 0203 return i18nc("@title", "Place %1 in Map", divisionType); 0204 } 0205 0206 QSize placeAsker::mapSize() const 0207 { 0208 return p_mapWidget -> mapSize(); 0209 } 0210 0211 void placeAsker::setCurrentDivision(const division *div) 0212 { 0213 int width = p_mapImage->width(); 0214 int height = p_mapImage->height(); 0215 0216 int minX = width; 0217 int maxX = 0; 0218 int minY = height; 0219 int maxY = 0; 0220 0221 QRgb divColor = div -> getRGB(); 0222 0223 //first iteration, detect size required by the image 0224 for (int x = 0; x < width; x++) 0225 { 0226 for (int y = 0; y < height; y++) 0227 { 0228 if (p_mapImage->pixel(x,y) == divColor) 0229 { 0230 if (x < minX) minX = x; 0231 if (x > maxX) maxX = x; 0232 if (y < minY) minY = y; 0233 if (y > maxY) maxY = y; 0234 } 0235 } 0236 } 0237 0238 p_currentDivisionImage = new QImage(maxX - minX + 1, maxY - minY + 1, QImage::Format_ARGB32); 0239 p_currentDivisionRect.setCoords(minX, minY, maxX, maxY); 0240 p_currentDivisionImage->fill(Qt::transparent); 0241 0242 //second iteration, copy the color to the new image 0243 for (int x = minX; x <= maxX; x++) 0244 { 0245 for (int y = minY; y <= maxY; y++) 0246 { 0247 if (p_mapImage->pixel(x,y) == divColor) 0248 p_currentDivisionImage->setPixel(x - minX, y - minY, divColor); 0249 } 0250 } 0251 } 0252 0253 #include "moc_placeasker.cpp"