1 /**********************************************************************************************
2 Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>
3 Copyright (C) 2015 Christian Eichler <code@christian-eichler.de>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 **********************************************************************************************/
19
20 #include "canvas/CCanvas.h"
21 #include "helpers/CDraw.h"
22
23 #include <QDebug>
24 #include <QImage>
25 #include <QPainterPath>
26 #include <QPointF>
27 #include <QtMath>
28
29 QPen CDraw::penBorderBlue(QColor(10, 10, 150, 220), 2);
30 QPen CDraw::penBorderGray(Qt::lightGray, 2);
31 QPen CDraw::penBorderBlack(QColor(0, 0, 0, 200), 2);
32 QBrush CDraw::brushBackWhite(QColor(255, 255, 255, 255));
33 QBrush CDraw::brushBackYellow(QColor(0xff, 0xff, 0xcc, 0xE0));
34
35
createBasicArrow(const QBrush & brush,qreal scale)36 QImage CDraw::createBasicArrow(const QBrush& brush, qreal scale)
37 {
38 QImage arrow(21 * scale, 16 * scale, QImage::Format_ARGB32);
39 arrow.fill(qRgba(0, 0, 0, 0));
40
41 QPainter painter(&arrow);
42 USE_ANTI_ALIASING(painter, true);
43
44 // white background, same foreground as p
45 painter.setPen(QPen(Qt::white, 2));
46 painter.setBrush(brush);
47
48 QPointF arrowPoints[4] =
49 {
50 QPointF(20.0 * scale, 7.0 * scale), // front
51 QPointF( 0.0 * scale, 0.0 * scale), // upper tail
52 QPointF( 5.0 * scale, 7.0 * scale), // mid tail
53 QPointF( 0.0 * scale, 15.0 * scale) // lower tail
54 };
55 painter.drawPolygon(arrowPoints, 4);
56 painter.end();
57
58 return arrow;
59 }
60
61 /**
62 @brief Calculates the square distance between two points
63 @return (int) ( (x2 - x1)^2 + (y2 - y1)^2 )
64 */
65
pointDistanceSquare(const QPointF & p1,const QPointF & p2)66 static inline int pointDistanceSquare(const QPointF& p1, const QPointF& p2)
67 {
68 return (p2.x() - p1.x()) * (p2.x() - p1.x()) + (p2.y() - p1.y()) * (p2.y() - p1.y());
69 }
70
arrows(const QPolygonF & line,const QRectF & viewport,QPainter & p,int minPointDist,int minArrowDist,qreal scale)71 void CDraw::arrows(const QPolygonF& line, const QRectF& viewport, QPainter& p, int minPointDist, int minArrowDist, qreal scale)
72 {
73 QImage arrow = createBasicArrow(p.brush(), scale);
74 qreal xoff = qCeil(arrow.width() / 2.0);
75 qreal yoff = qFloor((arrow.height() - 1) / 2.0);
76
77 const qreal minArrowDistSquare = minArrowDist * minArrowDist;
78 const qreal minPointDistSquare = minPointDist * minPointDist;
79
80 QPointF prevArrow;
81 bool firstArrow = true;
82 for(int i = 1; i < line.size(); i++)
83 {
84 const QPointF& pt = line[i ];
85 const QPointF& prevPt = line[i - 1];
86
87 // ensure there is enough space between two line points
88 if( pointDistanceSquare(pt, prevPt) >= minPointDistSquare )
89 {
90 QPointF arrowPos = prevPt + (pt - prevPt) / 2;
91
92 if( (viewport.contains(pt) || 0 == viewport.height()) // ensure the point is visible
93 && (firstArrow || pointDistanceSquare(prevArrow, arrowPos) >= minArrowDistSquare) )
94 {
95 p.save();
96
97 // rotate and draw the arrow (between bullets)
98 p.translate(arrowPos);
99 qreal direction = ( qAtan2((pt.y() - prevPt.y()), (pt.x() - prevPt.x())) * 180.) / M_PI;
100 p.rotate(direction);
101 p.drawImage(-xoff, -yoff, arrow);
102
103 p.restore();
104
105 prevArrow = arrowPos;
106 firstArrow = false;
107 }
108 }
109 }
110 }
111
text(const QString & str,QPainter & p,const QPoint & center,const QColor & color,const QFont & font)112 void CDraw::text(const QString& str, QPainter& p, const QPoint& center, const QColor& color, const QFont& font)
113 {
114 QFontMetrics fm(font);
115 QRect r = fm.boundingRect(str);
116
117 r.moveCenter(center);
118 p.setFont(font);
119
120 // draw the white `shadow`
121 p.setPen(Qt::white);
122 p.drawText(r.topLeft() - QPoint(-1, -1), str);
123 p.drawText(r.topLeft() - QPoint( 0, -1), str);
124 p.drawText(r.topLeft() - QPoint(+1, -1), str);
125
126 p.drawText(r.topLeft() - QPoint(-1, 0), str);
127 p.drawText(r.topLeft() - QPoint(+1, 0), str);
128
129 p.drawText(r.topLeft() - QPoint(-1, +1), str);
130 p.drawText(r.topLeft() - QPoint( 0, +1), str);
131 p.drawText(r.topLeft() - QPoint(+1, +1), str);
132
133 p.setPen(color);
134 p.drawText(r.topLeft(), str);
135 }
136
text(const QString & str,QPainter & p,const QRect & r,const QColor & color)137 void CDraw::text(const QString& str, QPainter& p, const QRect& r, const QColor& color)
138 {
139 p.setPen(Qt::white);
140 p.setFont(CMainWindow::self().getMapFont());
141
142 // draw the white `shadow`
143 p.drawText(r.adjusted(-1, -1, -1, -1), Qt::AlignCenter, str);
144 p.drawText(r.adjusted( 0, -1, 0, -1), Qt::AlignCenter, str);
145 p.drawText(r.adjusted(+1, -1, +1, -1), Qt::AlignCenter, str);
146
147 p.drawText(r.adjusted(-1, 0, -1, 0), Qt::AlignCenter, str);
148 p.drawText(r.adjusted(+1, 0, +1, 0), Qt::AlignCenter, str);
149
150 p.drawText(r.adjusted(-1, +1, -1, +1), Qt::AlignCenter, str);
151 p.drawText(r.adjusted( 0, +1, 0, +1), Qt::AlignCenter, str);
152 p.drawText(r.adjusted(+1, +1, +1, +1), Qt::AlignCenter, str);
153
154 p.setPen(color);
155 p.drawText(r, Qt::AlignCenter, str);
156 }
157
bubble(QPainter & p,const QRect & contentRect,const QPoint & pointerPos,int pointerBaseWidth,float pointerBasePos)158 QPoint CDraw::bubble(QPainter& p, const QRect& contentRect, const QPoint& pointerPos, int pointerBaseWidth, float pointerBasePos)
159 {
160 QPainterPath bubblePath;
161 bubblePath.addRoundedRect(contentRect, RECT_RADIUS, RECT_RADIUS);
162
163 // draw the arrow
164 int pointerBaseCenterX = (pointerBasePos <= 1)
165 ? contentRect.left() + (pointerBasePos * contentRect.width())
166 : contentRect.left() + (int) pointerBasePos;
167
168 int pointerHeight = 0;
169 if(pointerPos.y() < contentRect.top())
170 {
171 pointerHeight = contentRect.top() - pointerPos.y() + 1;
172 }
173 else if(pointerPos.y() > contentRect.bottom())
174 {
175 pointerHeight = contentRect.bottom() - pointerPos.y() - 1;
176 }
177 else
178 {
179 qDebug() << "cannot calculate pointerHeight/pointerBaseCenterX due to invalid parameters";
180 }
181
182 if(0 != pointerHeight)
183 {
184 QPolygonF pointerPoly;
185 pointerPoly << pointerPos
186 << QPointF(pointerBaseCenterX - pointerBaseWidth / 2, pointerPos.y() + pointerHeight)
187 << QPointF(pointerBaseCenterX + pointerBaseWidth / 2, pointerPos.y() + pointerHeight)
188 << pointerPos;
189
190 QPainterPath pointerPath;
191 pointerPath.addPolygon(pointerPoly);
192
193 bubblePath = bubblePath.united(pointerPath);
194 }
195
196 p.setPen (CDraw::penBorderGray);
197 p.setBrush(CDraw::brushBackWhite);
198
199 p.drawPolygon(bubblePath.toFillPolygon());
200
201 return contentRect.topLeft();
202 }
203
drawCrossHairDot(QPainter & p,const QPointF & pt)204 void CDraw::drawCrossHairDot(QPainter& p, const QPointF& pt)
205 {
206 USE_ANTI_ALIASING(p, false);
207 p.setBrush(Qt::NoBrush);
208 QRectF dot2(0, 0, 7, 7);
209 p.setPen(QPen(Qt::white, 3));
210 p.drawLine(pt.x(), pt.y() + 3, pt.x(), pt.y() + 20);
211 p.drawLine(pt.x(), pt.y() - 3, pt.x(), pt.y() - 20);
212 p.drawLine(pt.x() - 3, pt.y(), pt.x() - +20, pt.y());
213 p.drawLine(pt.x() + 3, pt.y(), pt.x() + 20, pt.y());
214 p.setPen(QPen(Qt::red, 1));
215 p.drawLine(pt.x(), pt.y() + 3, pt.x(), pt.y() + 20);
216 p.drawLine(pt.x(), pt.y() - 3, pt.x(), pt.y() - 20);
217 p.drawLine(pt.x() - 3, pt.y(), pt.x() - +20, pt.y());
218 p.drawLine(pt.x() + 3, pt.y(), pt.x() + 20, pt.y());
219
220 dot2.moveCenter(pt);
221 p.setPen(QPen(Qt::white, 3));
222 p.drawRect(dot2);
223 p.setPen(QPen(Qt::red, 1));
224 p.drawRect(dot2);
225 USE_ANTI_ALIASING(p, true);
226 }
227
drawRectangle(QPainter & p,const QRectF & rect,const QPen & pen,const QBrush & brush)228 void CDraw::drawRectangle(QPainter& p, const QRectF& rect, const QPen& pen, const QBrush& brush)
229 {
230 p.setBrush(brush);
231 p.setPen(QPen(Qt::white, pen.width() + 2));
232 p.drawRect(rect);
233 p.setPen(pen);
234 p.drawRect(rect);
235 }
236
drawRectangle(QPainter & p,const QRectF & rect,const Qt::GlobalColor & pen,const Qt::GlobalColor & brush)237 void CDraw::drawRectangle(QPainter& p, const QRectF& rect, const Qt::GlobalColor& pen, const Qt::GlobalColor& brush)
238 {
239 drawRectangle(p, rect, QPen(pen), QBrush(brush));
240 }
241