Add graph animation; support for line graphs
[harbour-pedalo.git] / src / graph.cpp
1 #include <QBrush>
2 #include <QPainter>
3
4 #include "journey.h"
5 #include "graph.h"
6
7 Graph::Graph(QQuickItem *parent)
8 : QQuickPaintedItem(parent)
9 , primary(QColor("#ff0000"))
10 , secondary(QColor("#00ff00"))
11 , highlight(QColor("#0000ff"))
12 , axisThickness(0.02)
13 , bars(7)
14 , points(7)
15 , gap(0.1)
16 , miny(0.0f)
17 , maxy(1.0f)
18 , stepy(0.1f)
19 , unitsy("%")
20 , fontsize(24.0)
21 , animate(1.0)
22 {
23 bardata.clear();
24 linedata.clear();
25 labelsx.clear();
26 labelsy.clear();
27 }
28
29 void Graph::paint(QPainter *painter) {
30 QLocale locale;
31 float barwidth;
32 float axiswidth;
33 int count;
34 int barfullwidth;
35 int pointfullwidth;
36 int labelfullwidth;
37 float labelygap;
38 float labelxgap;
39 int labelsycount;
40 int labelsxcount;
41 float labelheight;
42 float steps;
43
44 QRectF size = contentsBoundingRect();
45
46 bars = bardata.length();
47 points = linedata.length();
48 labelsxcount = labelsx.length();
49
50 steps = labelsx.length();
51 if (steps == 0) {
52 steps = bars;
53 }
54
55 labelygap = size.height() * 0.1l;
56 labelxgap = size.width() * 0.11;
57 axiswidth = qMin(size.width() * axisThickness, size.height() * axisThickness);
58 barfullwidth = ((size.width() - labelxgap - axiswidth) / bars);
59 barwidth = barfullwidth * (1.0 - gap);
60 pointfullwidth = ((size.width() - labelxgap - axiswidth) / points);
61 labelsycount = labelsy.length() > 0 ? labelsy.length() : 1 + (maxy - miny) / stepy;
62 labelheight = (size.height() - labelygap - axiswidth) / (labelsycount - 0.5);
63 labelfullwidth = ((size.width() - labelxgap - axiswidth) / labelsxcount);
64
65 QBrush axiscolour(primary);
66 QBrush barcolour(secondary);
67 QBrush linecolour(highlight);
68
69 painter->setBrush(axiscolour);
70 painter->setPen(Qt::NoPen);
71 painter->setRenderHint(QPainter::Antialiasing);
72 painter->setOpacity(1.0);
73
74 const QPointF axis[6] = {
75 QPointF(labelxgap, (labelheight / 2.0)),
76 QPointF(labelxgap, size.height() - labelygap),
77 QPointF(size.width(), size.height() - labelygap),
78 QPointF(size.width(), size.height() - labelygap - axiswidth),
79 QPointF(labelxgap + axiswidth, size.height() - labelygap - axiswidth),
80 QPointF(labelxgap + axiswidth, (labelheight / 2.0))
81 };
82 painter->drawPolygon(axis, 6);
83
84 if (bars > 0) {
85 painter->setBrush(barcolour);
86
87 count = 0;
88 for (QList<float>::const_iterator iter = bardata.constBegin(); (count < bars) && (iter != bardata.constEnd()); iter++) {
89 float barheight = ((*iter) - miny) / (maxy - miny);
90 barheight = qBound(0.0f, barheight, 1.0f) * (size.height() - labelygap - axiswidth - (labelheight / 2.0));
91 barheight *= animate;
92 painter->drawRect(labelxgap + axiswidth + (barfullwidth * count) + (barfullwidth * gap / 2.0), size.height() - labelygap - barheight - axiswidth, barwidth, barheight);
93
94 count++;
95 }
96 }
97
98 if (points > 0) {
99 QPen pen = QPen(linecolour, 8.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
100 painter->setPen(pen);
101 painter->setBrush(Qt::NoBrush);
102
103 QPointF point[points];
104 count = 0;
105 for (QList<float>::const_iterator iter = linedata.constBegin(); (count < points) && (iter != linedata.constEnd()); iter++) {
106 float lineheight = ((*iter) - miny) / (maxy - miny);
107 lineheight = qBound(0.0f, lineheight, 1.0f) * (size.height() - labelygap - axiswidth - (labelheight / 2.0));
108 point[count] = QPointF(labelxgap + axiswidth + (pointfullwidth * count) + (pointfullwidth / 2.0), size.height() - labelygap - lineheight - axiswidth);
109 count++;
110 }
111 painter->setClipRect(0, 0, labelxgap + axiswidth + (pointfullwidth / 2.0) - 8.0 + (size.width() - labelxgap - axiswidth - pointfullwidth + 16.0) * animate, size.height(), Qt::ReplaceClip);
112 painter->setClipping(true);
113 painter->drawPolyline(point, points);
114 }
115
116 painter->setClipping(false);
117
118 QFont font = painter->font();
119 font.setPixelSize(fontsize);
120 painter->setFont(font);
121 painter->setPen(primary);
122 painter->setBrush(Qt::NoBrush);
123
124 if (labelsxcount > 0) {
125 for (count = 0; count < bars; count++) {
126 QRectF rect(labelxgap + axiswidth + (labelfullwidth * count), size.height() - labelygap + 8.0, labelfullwidth, labelygap - 8.0);
127 painter->drawText(rect, Qt::AlignHCenter | Qt::NoClip | Qt::TextSingleLine, labelsx[count]);
128 //painter->drawRect(rect);
129 }
130 }
131
132 if (labelsy.length() > 0) {
133 for (count = 0; count < labelsycount; count++) {
134 QRectF rect(0, size.height() - (labelheight * (count + 0.5)) - labelygap, labelxgap - 8.0, labelheight);
135 painter->drawText(rect, Qt::AlignVCenter | Qt::AlignRight | Qt::NoClip | Qt::TextSingleLine, labelsy[count]);
136 //painter->drawRect(rect);
137 }
138 }
139 else {
140 float labelvalue = miny;
141 for (count = 0; count < labelsycount; count++) {
142 QString labeltext = unitsy == "%" ? locale.toString(labelvalue * 100, 'f', 0) + "%" : locale.toString(labelvalue, 'g', 2) + unitsy;
143 QRectF rect(0, size.height() - (labelheight * (count + 0.5)) - labelygap, labelxgap - 8.0, labelheight);
144 painter->drawText(rect, Qt::AlignVCenter | Qt::AlignRight | Qt::NoClip | Qt::TextSingleLine, labeltext);
145 //painter->drawRect(rect);
146 labelvalue += stepy;
147 }
148
149 }
150 }
151
152 QList<float> Graph::getBarData() const {
153 return bardata;
154 }
155
156 void Graph::setBarData(QList<float> value) {
157 bardata = value;
158 emit barDataChanged();
159 }
160
161 QList<float> Graph::getLineData() const {
162 return linedata;
163 }
164
165 void Graph::setLineData(QList<float> value) {
166 linedata = value;
167 emit lineDataChanged();
168 }
169
170 QStringList Graph::getLabelsx() const {
171 return labelsx;
172 }
173
174 void Graph::setLabelsx(QStringList value) {
175 labelsx = value;
176 emit labelsxChanged();
177 }
178
179 QStringList Graph::getLabelsy() const {
180 return labelsy;
181 }
182
183 void Graph::setLabelsy(QStringList value) {
184 labelsy = value;
185 emit labelsyChanged();
186 }
187
188 QColor Graph::getPrimary() const {
189 return this->primary;
190 }
191
192 void Graph::setPrimary(QColor value) {
193 if (primary != value) {
194 primary = value;
195 emit primaryChanged(value);
196 }
197 }
198
199 QColor Graph::getSecondary() const {
200 return this->secondary;
201 }
202
203 void Graph::setSecondary(QColor value) {
204 if (secondary != value) {
205 secondary = value;
206 emit secondaryChanged(value);
207 }
208 }
209
210 QColor Graph::getHighlight() const {
211 return this->highlight;
212 }
213
214 void Graph::setHighlight(QColor value) {
215 if (highlight != value) {
216 highlight = value;
217 emit highlightChanged(value);
218 }
219 }
220
221 float Graph::getGap() const {
222 return gap;
223 }
224
225 void Graph::setGap(float value) {
226 if (gap != value) {
227 gap = value;
228 emit gapChanged(value);
229 }
230 }
231
232 float Graph::getMiny() const {
233 return miny;
234 }
235
236 void Graph::setMiny(float value) {
237 if (miny != value) {
238 miny = value;
239 emit minyChanged(value);
240 }
241 }
242
243 float Graph::getMaxy() const {
244 return maxy;
245 }
246
247 void Graph::setMaxy(float value) {
248 if (maxy != value) {
249 maxy = value;
250 emit maxyChanged(value);
251 }
252 }
253
254 float Graph::getStepy() const {
255 return stepy;
256 }
257
258 void Graph::setStepy(float value) {
259 if (stepy != value) {
260 stepy = value;
261 emit stepyChanged(value);
262 }
263 }
264
265 QString Graph::getUnitsy() const {
266 return unitsy;
267 }
268
269 void Graph::setUnitsy(QString value) {
270 if (unitsy != value) {
271 unitsy = value;
272 emit unitsyChanged();
273 }
274 }
275
276 float Graph::getFontsize() const {
277 return fontsize;
278 }
279
280 void Graph::setFontsize(float value) {
281 if (fontsize != value) {
282 fontsize = value;
283 emit fontsizeChanged(value);
284 }
285 }
286
287 float Graph::getAnimate() const {
288 return animate;
289 }
290
291 void Graph::setAnimate(float value) {
292 if (animate != value) {
293 animate = value;
294 emit animateChanged(value);
295 update();
296 }
297 }
298