1 /***************************************************************************
2  *   Copyright (C) 2004-2007 by Albert Astals Cid                          *
3  *   aacid@kde.org                                                         *
4  *   Copyright (C) 2006 by Isaac Clerencia                                 *
5  *   isaac@warp.es                                                         *
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  ***************************************************************************/
12 
13 #include "placeasker.h"
14 
15 #include <cmath>
16 #include <math.h>
17 
18 #include <KLocalizedString>
19 #include <KMessageBox>
20 
21 #include <QLabel>
22 #include <QLayout>
23 #include <QScrollBar>
24 #include <QString>
25 
26 #include "map.h"
27 #include "placemapwidget.h"
28 
placeAsker(QWidget * parent,KGmap * m,QWidget * w,uint count)29 placeAsker::placeAsker(QWidget *parent, KGmap *m, QWidget *w, uint count) : askWidget(parent, m, w, count, true), p_currentDivisionImage(0)
30 {
31 	QVBoxLayout *lay = new QVBoxLayout(this);
32 	lay -> setContentsMargins(0, 0, 0, 0);
33 	lay -> setSpacing(0);
34 
35 	p_mapImage = new QImage(p_map->getMapFile());
36 	p_mapWidget = new placeMapWidget(this);
37 	lay -> addWidget(p_mapWidget);
38 
39 	connect(p_mapWidget, &placeMapWidget::clicked, this, &placeAsker::handleMapClick);
40 	connect(p_mapWidget, &placeMapWidget::setMoveActionChecked, this, &placeAsker::setMoveActionChecked);
41 	connect(p_mapWidget, &placeMapWidget::setZoomActionChecked, this, &placeAsker::setZoomActionChecked);
42 	connect(p_mapWidget, &placeMapWidget::setMoveActionEnabled, this, &placeAsker::setMoveActionEnabled);
43 
44 	QVBoxLayout *vbl = static_cast<QVBoxLayout*>(w -> layout());
45 	p_next = new QLabel(w);
46 	p_next -> setAlignment(Qt::AlignTop | Qt::AlignHCenter);
47 	p_next -> setWordWrap(true);
48 	p_fill = new QWidget(w);
49 	p_fill -> show();
50 	vbl -> addWidget(p_next);
51 	vbl -> addWidget(p_fill, 1);
52 	p_placedPixelIndices = p_mapWidget -> outerPixelIndices();
53 	// Set the background image before start asking
54 	p_mapWidget -> init(p_map, p_mapImage);
55 
56 	nextQuestion();
57 }
58 
~placeAsker()59 placeAsker::~placeAsker()
60 {
61 	delete p_next;
62 	delete p_fill;
63 	delete p_mapImage;
64 }
65 
isAsker() const66 bool placeAsker::isAsker() const
67 {
68 	return p_answers;
69 }
70 
mousePressEvent(QMouseEvent *)71 void placeAsker::mousePressEvent(QMouseEvent*)
72 {
73 }
74 
setMovement(bool b)75 void placeAsker::setMovement(bool b)
76 {
77 	p_mapWidget -> setMapMove(b);
78 }
79 
setZoom(bool b)80 void placeAsker::setZoom(bool b)
81 {
82 	askWidget::setZoom(b);
83 	p_mapWidget -> setMapZoom(b);
84 }
85 
setOriginalZoom()86 void placeAsker::setOriginalZoom()
87 {
88 	p_mapWidget -> setGameImage();
89 }
90 
setAutomaticZoom(bool automaticZoom)91 void placeAsker::setAutomaticZoom(bool automaticZoom)
92 {
93 	p_mapWidget -> setAutomaticZoom(automaticZoom);
94 }
95 
handleMapClick(QRgb c,const QPoint &,const QPointF & mapPoint)96 void placeAsker::handleMapClick(QRgb c, const QPoint & , const QPointF &mapPoint)
97 {
98 	QString aux;
99 	aux = p_map -> getWhatIs(c, false);
100 	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)));
101 	else
102 	{
103 		p_mapWidget->placeDivision(p_currentDivisionRect);
104 		p_mapWidget->unsetCursor();
105 		// the image is no longer needed
106 		delete p_currentDivisionImage;
107 
108 		double distX = p_currentDivisionRect.x() - mapPoint.x();
109 		double distY = p_currentDivisionRect.y() - mapPoint.y();
110 		double distance = sqrt(static_cast<double>(distX * distX + distY * distY));
111 
112 		int indexOfCurrent = p_mapImage -> colorTable().indexOf(p_currentRgb);
113 		bool consideredGood = distance < 5.0;
114 		// if we consider it good enough don't transmit a may be wrong color
115 		if (consideredGood) c = p_currentRgb;
116 		if (! consideredGood)
117 		{
118 			bool hasBorderShown = false;
119 			for ( int i = p_placedPixelIndices.size() ; --i >= 0 && !hasBorderShown ; )
120 			{
121 				uchar pixelIndex = p_placedPixelIndices[i];
122 				size_t nb = p_mapWidget -> nbBorderPixels(pixelIndex, indexOfCurrent);
123 				hasBorderShown = nb > 3;
124 			}
125 			consideredGood = !hasBorderShown && distance < 16.0;
126 			if (consideredGood) c = p_currentRgb;
127 		}
128 		if (! consideredGood)
129 		{
130 			QRect definedRect(0, 0, p_mapImage -> width(), p_mapImage -> height());
131 			QPoint v = QPoint(mapPoint.x(), mapPoint.y()) - p_currentDivisionRect.topLeft();
132 			QRect initialRect(p_currentDivisionRect);
133 			QRect userRect = initialRect.translated(v);
134 			QRect definedRectUser = userRect & definedRect;
135 			QPoint definedFirstDiag = definedRectUser.bottomRight() - definedRectUser.topLeft();
136 			QPoint origFirstDiag = userRect.bottomRight() - userRect.topLeft();
137 			QPoint badDiff = origFirstDiag -definedFirstDiag;
138 			QPoint diagDiff = origFirstDiag -badDiff;
139 			QVector<size_t> stats(p_mapImage -> colorTable().size());
140 			size_t goodCount = 0;
141 			size_t outCount = badDiff.x() * badDiff.y() + badDiff.x() * diagDiff.y() + diagDiff.x() * badDiff.y();
142 			size_t badCount = outCount;
143 			for ( int dy = definedFirstDiag.y() -1 ; dy >= 0 ; dy-- )
144 			{
145 				for ( int dx = definedFirstDiag.x() -1 ; dx >= 0 ; dx-- )
146 				{
147 					int origPixelIndex = p_mapImage -> pixelIndex(initialRect.left() + dx, initialRect.top() + dy);
148 					if ( origPixelIndex != indexOfCurrent )
149 						continue;
150 					int userPixelIndex = p_mapImage -> pixelIndex(definedRectUser.left() + dx, definedRectUser.top() + dy);
151 					if ( userPixelIndex == origPixelIndex ) goodCount++;
152 					else
153 					{
154 						stats[userPixelIndex]++;
155 						badCount++;
156 					}
157 				}
158 			}
159 			consideredGood = goodCount > 0.5 * (goodCount + badCount);
160 			if (consideredGood) c = p_currentRgb;
161 			else if (outCount > 0.5 * (goodCount + badCount))
162 			{
163 				c = p_map -> getIgnoredDivisions(askMode())[0] -> getRGB();
164 			}
165 			else
166 			{
167 				int indexOfMax = -1;
168 				size_t maxCount = 0;
169 				for ( int i = stats.size() -1 ; i >= 0 ; i-- )
170 				{
171 					if ( stats[i] > maxCount )
172 					{
173 						indexOfMax = i;
174 						maxCount = stats[i];
175 					}
176 				}
177 				c = p_mapImage -> colorTable().at(indexOfMax);
178 			}
179 		}
180 		p_placedPixelIndices.append(indexOfCurrent);
181 		p_currentAnswer.setAnswer(QColor(c));
182 		questionAnswered(consideredGood);
183 		nextQuestion();
184 	}
185 }
186 
nextQuestionHook(const division * div)187 void placeAsker::nextQuestionHook(const division *div)
188 {
189 	const QString divisionName = div -> getName();
190 	p_next -> setText(i18nc("@info:status", "Please place in the map:<br/><b>%1</b>", divisionName));
191 	p_next -> show();
192 	p_currentAnswer.setQuestion(i18nc("@item:intable column Question, %1 is region name", "%1", divisionName));
193 	QColor color = QColor(div -> getRGB());
194 	p_currentRgb = color.rgb();
195 	p_currentAnswer.setCorrectAnswer(color);
196 	setCurrentDivision(div);
197 	p_mapWidget->setCurrentDivisionImage(p_currentDivisionImage);
198 }
199 
getQuestionHook() const200 QString placeAsker::getQuestionHook() const
201 {
202 	QString divisionType = p_map->getDivisionsString();
203 	return i18nc("@title", "Place %1 in Map", divisionType);
204 }
205 
mapSize() const206 QSize placeAsker::mapSize() const
207 {
208 	return p_mapWidget -> mapSize();
209 }
210 
setCurrentDivision(const division * div)211 void placeAsker::setCurrentDivision(const division *div)
212 {
213 	int width = p_mapImage->width();
214 	int height = p_mapImage->height();
215 
216 	int minX = width;
217 	int maxX = 0;
218 	int minY = height;
219 	int maxY = 0;
220 
221 	QRgb divColor = div -> getRGB();
222 
223 	//first iteration, detect size required by the image
224 	for (int x = 0; x < width; x++)
225 	{
226 		for (int y = 0; y < height; y++)
227 		{
228 			if (p_mapImage->pixel(x,y) == divColor)
229 			{
230 				if (x < minX) minX = x;
231 				if (x > maxX) maxX = x;
232 				if (y < minY) minY = y;
233 				if (y > maxY) maxY = y;
234 			}
235 		}
236 	}
237 
238 	p_currentDivisionImage = new QImage(maxX - minX, maxY - minY, QImage::Format_ARGB32);
239 	p_currentDivisionRect.setCoords(minX, minY, maxX, maxY);
240 	p_currentDivisionImage->fill(Qt::transparent);
241 
242 	//second iteration, copy the color to the new image
243 	for (int x = minX; x < maxX; x++)
244 	{
245 		for (int y = minY; y < maxY; y++)
246 		{
247 			if (p_mapImage->pixel(x,y) == divColor)
248 				p_currentDivisionImage->setPixel(x - minX, y - minY, divColor);
249 		}
250 	}
251 }
252 
253 
254