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"