1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Data Visualization module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include "graphmodifier.h" |
31 | #include <QtDataVisualization/qcategory3daxis.h> |
32 | #include <QtDataVisualization/qvalue3daxis.h> |
33 | #include <QtDataVisualization/qbardataproxy.h> |
34 | #include <QtDataVisualization/q3dscene.h> |
35 | #include <QtDataVisualization/q3dcamera.h> |
36 | #include <QtDataVisualization/qbar3dseries.h> |
37 | #include <QtDataVisualization/q3dtheme.h> |
38 | #include <QtCore/QTime> |
39 | #include <QtWidgets/QComboBox> |
40 | #include <QtCore/qmath.h> |
41 | |
42 | using namespace QtDataVisualization; |
43 | |
44 | const QString celsiusString = QString(QChar(0xB0)) + "C" ; |
45 | |
46 | //! [0] |
47 | GraphModifier::GraphModifier(Q3DBars *bargraph) |
48 | : m_graph(bargraph), |
49 | m_xRotation(0.0f), |
50 | m_yRotation(0.0f), |
51 | m_fontSize(30), |
52 | m_segments(4), |
53 | m_subSegments(3), |
54 | m_minval(-20.0f), |
55 | m_maxval(20.0f), |
56 | //! [1] |
57 | m_temperatureAxis(new QValue3DAxis), |
58 | m_yearAxis(new QCategory3DAxis), |
59 | m_monthAxis(new QCategory3DAxis), |
60 | m_primarySeries(new QBar3DSeries), |
61 | m_secondarySeries(new QBar3DSeries), |
62 | //! [1] |
63 | m_barMesh(QAbstract3DSeries::MeshBevelBar), |
64 | m_smooth(false) |
65 | { |
66 | //! [2] |
67 | m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftMedium); |
68 | m_graph->activeTheme()->setBackgroundEnabled(false); |
69 | m_graph->activeTheme()->setFont(QFont("Times New Roman" , m_fontSize)); |
70 | m_graph->activeTheme()->setLabelBackgroundEnabled(true); |
71 | m_graph->setMultiSeriesUniform(true); |
72 | //! [2] |
73 | |
74 | m_months << "January" << "February" << "March" << "April" << "May" << "June" << "July" << "August" << "September" << "October" << "November" << "December" ; |
75 | m_years << "2006" << "2007" << "2008" << "2009" << "2010" << "2011" << "2012" << "2013" ; |
76 | |
77 | //! [3] |
78 | m_temperatureAxis->setTitle("Average temperature" ); |
79 | m_temperatureAxis->setSegmentCount(m_segments); |
80 | m_temperatureAxis->setSubSegmentCount(m_subSegments); |
81 | m_temperatureAxis->setRange(min: m_minval, max: m_maxval); |
82 | m_temperatureAxis->setLabelFormat(QString(QStringLiteral("%.1f " ) + celsiusString)); |
83 | m_temperatureAxis->setLabelAutoRotation(30.0f); |
84 | m_temperatureAxis->setTitleVisible(true); |
85 | |
86 | m_yearAxis->setTitle("Year" ); |
87 | m_yearAxis->setLabelAutoRotation(30.0f); |
88 | m_yearAxis->setTitleVisible(true); |
89 | m_monthAxis->setTitle("Month" ); |
90 | m_monthAxis->setLabelAutoRotation(30.0f); |
91 | m_monthAxis->setTitleVisible(true); |
92 | |
93 | m_graph->setValueAxis(m_temperatureAxis); |
94 | m_graph->setRowAxis(m_yearAxis); |
95 | m_graph->setColumnAxis(m_monthAxis); |
96 | //! [3] |
97 | |
98 | //! [8] |
99 | m_primarySeries->setItemLabelFormat(QStringLiteral("Oulu - @colLabel @rowLabel: @valueLabel" )); |
100 | m_primarySeries->setMesh(QAbstract3DSeries::MeshBevelBar); |
101 | m_primarySeries->setMeshSmooth(false); |
102 | |
103 | m_secondarySeries->setItemLabelFormat(QStringLiteral("Helsinki - @colLabel @rowLabel: @valueLabel" )); |
104 | m_secondarySeries->setMesh(QAbstract3DSeries::MeshBevelBar); |
105 | m_secondarySeries->setMeshSmooth(false); |
106 | m_secondarySeries->setVisible(false); |
107 | //! [8] |
108 | |
109 | //! [4] |
110 | m_graph->addSeries(series: m_primarySeries); |
111 | m_graph->addSeries(series: m_secondarySeries); |
112 | //! [4] |
113 | |
114 | //! [6] |
115 | changePresetCamera(); |
116 | //! [6] |
117 | |
118 | //! [9] |
119 | resetTemperatureData(); |
120 | //! [9] |
121 | |
122 | // Set up property animations for zooming to the selected bar |
123 | //! [12] |
124 | Q3DCamera *camera = m_graph->scene()->activeCamera(); |
125 | m_defaultAngleX = camera->xRotation(); |
126 | m_defaultAngleY = camera->yRotation(); |
127 | m_defaultZoom = camera->zoomLevel(); |
128 | m_defaultTarget = camera->target(); |
129 | |
130 | m_animationCameraX.setTargetObject(camera); |
131 | m_animationCameraY.setTargetObject(camera); |
132 | m_animationCameraZoom.setTargetObject(camera); |
133 | m_animationCameraTarget.setTargetObject(camera); |
134 | |
135 | m_animationCameraX.setPropertyName("xRotation" ); |
136 | m_animationCameraY.setPropertyName("yRotation" ); |
137 | m_animationCameraZoom.setPropertyName("zoomLevel" ); |
138 | m_animationCameraTarget.setPropertyName("target" ); |
139 | |
140 | int duration = 1700; |
141 | m_animationCameraX.setDuration(duration); |
142 | m_animationCameraY.setDuration(duration); |
143 | m_animationCameraZoom.setDuration(duration); |
144 | m_animationCameraTarget.setDuration(duration); |
145 | |
146 | // The zoom always first zooms out above the graph and then zooms in |
147 | qreal zoomOutFraction = 0.3; |
148 | m_animationCameraX.setKeyValueAt(step: zoomOutFraction, value: QVariant::fromValue(value: 0.0f)); |
149 | m_animationCameraY.setKeyValueAt(step: zoomOutFraction, value: QVariant::fromValue(value: 90.0f)); |
150 | m_animationCameraZoom.setKeyValueAt(step: zoomOutFraction, value: QVariant::fromValue(value: 50.0f)); |
151 | m_animationCameraTarget.setKeyValueAt(step: zoomOutFraction, |
152 | value: QVariant::fromValue(value: QVector3D(0.0f, 0.0f, 0.0f))); |
153 | //! [12] |
154 | } |
155 | //! [0] |
156 | |
157 | GraphModifier::~GraphModifier() |
158 | { |
159 | delete m_graph; |
160 | } |
161 | |
162 | void GraphModifier::resetTemperatureData() |
163 | { |
164 | //! [5] |
165 | // Set up data |
166 | static const float tempOulu[8][12] = { |
167 | {-6.7f, -11.7f, -9.7f, 3.3f, 9.2f, 14.0f, 16.3f, 17.8f, 10.2f, 2.1f, -2.6f, -0.3f}, // 2006 |
168 | {-6.8f, -13.3f, 0.2f, 1.5f, 7.9f, 13.4f, 16.1f, 15.5f, 8.2f, 5.4f, -2.6f, -0.8f}, // 2007 |
169 | {-4.2f, -4.0f, -4.6f, 1.9f, 7.3f, 12.5f, 15.0f, 12.8f, 7.6f, 5.1f, -0.9f, -1.3f}, // 2008 |
170 | {-7.8f, -8.8f, -4.2f, 0.7f, 9.3f, 13.2f, 15.8f, 15.5f, 11.2f, 0.6f, 0.7f, -8.4f}, // 2009 |
171 | {-14.4f, -12.1f, -7.0f, 2.3f, 11.0f, 12.6f, 18.8f, 13.8f, 9.4f, 3.9f, -5.6f, -13.0f}, // 2010 |
172 | {-9.0f, -15.2f, -3.8f, 2.6f, 8.3f, 15.9f, 18.6f, 14.9f, 11.1f, 5.3f, 1.8f, -0.2f}, // 2011 |
173 | {-8.7f, -11.3f, -2.3f, 0.4f, 7.5f, 12.2f, 16.4f, 14.1f, 9.2f, 3.1f, 0.3f, -12.1f}, // 2012 |
174 | {-7.9f, -5.3f, -9.1f, 0.8f, 11.6f, 16.6f, 15.9f, 15.5f, 11.2f, 4.0f, 0.1f, -1.9f} // 2013 |
175 | }; |
176 | |
177 | static const float tempHelsinki[8][12] = { |
178 | {-3.7f, -7.8f, -5.4f, 3.4f, 10.7f, 15.4f, 18.6f, 18.7f, 14.3f, 8.5f, 2.9f, 4.1f}, // 2006 |
179 | {-1.2f, -7.5f, 3.1f, 5.5f, 10.3f, 15.9f, 17.4f, 17.9f, 11.2f, 7.3f, 1.1f, 0.5f}, // 2007 |
180 | {-0.6f, 1.2f, 0.2f, 6.3f, 10.2f, 13.8f, 18.1f, 15.1f, 10.1f, 9.4f, 2.5f, 0.4f}, // 2008 |
181 | {-2.9f, -3.5f, -0.9f, 4.7f, 10.9f, 14.0f, 17.4f, 16.8f, 13.2f, 4.1f, 2.6f, -2.3f}, // 2009 |
182 | {-10.2f, -8.0f, -1.9f, 6.6f, 11.3f, 14.5f, 21.0f, 18.8f, 12.6f, 6.1f, -0.5f, -7.3f}, // 2010 |
183 | {-4.4f, -9.1f, -2.0f, 5.5f, 9.9f, 15.6f, 20.8f, 17.8f, 13.4f, 8.9f, 3.6f, 1.5f}, // 2011 |
184 | {-3.5f, -3.2f, -0.7f, 4.0f, 11.1f, 13.4f, 17.3f, 15.8f, 13.1f, 6.4f, 4.1f, -5.1f}, // 2012 |
185 | {-4.8f, -1.8f, -5.0f, 2.9f, 12.8f, 17.2f, 18.0f, 17.1f, 12.5f, 7.5f, 4.5f, 2.3f} // 2013 |
186 | }; |
187 | |
188 | // Create data arrays |
189 | QBarDataArray *dataSet = new QBarDataArray; |
190 | QBarDataArray *dataSet2 = new QBarDataArray; |
191 | QBarDataRow *dataRow; |
192 | QBarDataRow *dataRow2; |
193 | |
194 | dataSet->reserve(alloc: m_years.size()); |
195 | for (int year = 0; year < m_years.size(); year++) { |
196 | // Create a data row |
197 | dataRow = new QBarDataRow(m_months.size()); |
198 | dataRow2 = new QBarDataRow(m_months.size()); |
199 | for (int month = 0; month < m_months.size(); month++) { |
200 | // Add data to the row |
201 | (*dataRow)[month].setValue(tempOulu[year][month]); |
202 | (*dataRow2)[month].setValue(tempHelsinki[year][month]); |
203 | } |
204 | // Add the row to the set |
205 | dataSet->append(t: dataRow); |
206 | dataSet2->append(t: dataRow2); |
207 | } |
208 | |
209 | // Add data to the data proxy (the data proxy assumes ownership of it) |
210 | m_primarySeries->dataProxy()->resetArray(newArray: dataSet, rowLabels: m_years, columnLabels: m_months); |
211 | m_secondarySeries->dataProxy()->resetArray(newArray: dataSet2, rowLabels: m_years, columnLabels: m_months); |
212 | //! [5] |
213 | } |
214 | |
215 | void GraphModifier::changeRange(int range) |
216 | { |
217 | if (range >= m_years.count()) |
218 | m_yearAxis->setRange(min: 0, max: m_years.count() - 1); |
219 | else |
220 | m_yearAxis->setRange(min: range, max: range); |
221 | } |
222 | |
223 | void GraphModifier::changeStyle(int style) |
224 | { |
225 | QComboBox *comboBox = qobject_cast<QComboBox *>(object: sender()); |
226 | if (comboBox) { |
227 | m_barMesh = QAbstract3DSeries::Mesh(comboBox->itemData(index: style).toInt()); |
228 | m_primarySeries->setMesh(m_barMesh); |
229 | m_secondarySeries->setMesh(m_barMesh); |
230 | } |
231 | } |
232 | |
233 | void GraphModifier::changePresetCamera() |
234 | { |
235 | m_animationCameraX.stop(); |
236 | m_animationCameraY.stop(); |
237 | m_animationCameraZoom.stop(); |
238 | m_animationCameraTarget.stop(); |
239 | |
240 | // Restore camera target in case animation has changed it |
241 | m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f)); |
242 | |
243 | //! [10] |
244 | static int preset = Q3DCamera::CameraPresetFront; |
245 | |
246 | m_graph->scene()->activeCamera()->setCameraPreset((Q3DCamera::CameraPreset)preset); |
247 | |
248 | if (++preset > Q3DCamera::CameraPresetDirectlyBelow) |
249 | preset = Q3DCamera::CameraPresetFrontLow; |
250 | //! [10] |
251 | } |
252 | |
253 | void GraphModifier::changeTheme(int theme) |
254 | { |
255 | Q3DTheme *currentTheme = m_graph->activeTheme(); |
256 | currentTheme->setType(Q3DTheme::Theme(theme)); |
257 | emit backgroundEnabledChanged(enabled: currentTheme->isBackgroundEnabled()); |
258 | emit gridEnabledChanged(enabled: currentTheme->isGridEnabled()); |
259 | emit fontChanged(font: currentTheme->font()); |
260 | emit fontSizeChanged(size: currentTheme->font().pointSize()); |
261 | } |
262 | |
263 | void GraphModifier::changeLabelBackground() |
264 | { |
265 | m_graph->activeTheme()->setLabelBackgroundEnabled(!m_graph->activeTheme()->isLabelBackgroundEnabled()); |
266 | } |
267 | |
268 | void GraphModifier::changeSelectionMode(int selectionMode) |
269 | { |
270 | QComboBox *comboBox = qobject_cast<QComboBox *>(object: sender()); |
271 | if (comboBox) { |
272 | int flags = comboBox->itemData(index: selectionMode).toInt(); |
273 | m_graph->setSelectionMode(QAbstract3DGraph::SelectionFlags(flags)); |
274 | } |
275 | } |
276 | |
277 | void GraphModifier::changeFont(const QFont &font) |
278 | { |
279 | QFont newFont = font; |
280 | m_graph->activeTheme()->setFont(newFont); |
281 | } |
282 | |
283 | void GraphModifier::changeFontSize(int fontsize) |
284 | { |
285 | m_fontSize = fontsize; |
286 | QFont font = m_graph->activeTheme()->font(); |
287 | font.setPointSize(m_fontSize); |
288 | m_graph->activeTheme()->setFont(font); |
289 | } |
290 | |
291 | void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq) |
292 | { |
293 | int quality = int(sq); |
294 | // Updates the UI component to show correct shadow quality |
295 | emit shadowQualityChanged(quality); |
296 | } |
297 | |
298 | void GraphModifier::changeLabelRotation(int rotation) |
299 | { |
300 | m_temperatureAxis->setLabelAutoRotation(float(rotation)); |
301 | m_monthAxis->setLabelAutoRotation(float(rotation)); |
302 | m_yearAxis->setLabelAutoRotation(float(rotation)); |
303 | } |
304 | |
305 | void GraphModifier::setAxisTitleVisibility(bool enabled) |
306 | { |
307 | m_temperatureAxis->setTitleVisible(enabled); |
308 | m_monthAxis->setTitleVisible(enabled); |
309 | m_yearAxis->setTitleVisible(enabled); |
310 | } |
311 | |
312 | void GraphModifier::setAxisTitleFixed(bool enabled) |
313 | { |
314 | m_temperatureAxis->setTitleFixed(enabled); |
315 | m_monthAxis->setTitleFixed(enabled); |
316 | m_yearAxis->setTitleFixed(enabled); |
317 | } |
318 | |
319 | //! [11] |
320 | void GraphModifier::zoomToSelectedBar() |
321 | { |
322 | m_animationCameraX.stop(); |
323 | m_animationCameraY.stop(); |
324 | m_animationCameraZoom.stop(); |
325 | m_animationCameraTarget.stop(); |
326 | |
327 | Q3DCamera *camera = m_graph->scene()->activeCamera(); |
328 | float currentX = camera->xRotation(); |
329 | float currentY = camera->yRotation(); |
330 | float currentZoom = camera->zoomLevel(); |
331 | QVector3D currentTarget = camera->target(); |
332 | |
333 | m_animationCameraX.setStartValue(QVariant::fromValue(value: currentX)); |
334 | m_animationCameraY.setStartValue(QVariant::fromValue(value: currentY)); |
335 | m_animationCameraZoom.setStartValue(QVariant::fromValue(value: currentZoom)); |
336 | m_animationCameraTarget.setStartValue(QVariant::fromValue(value: currentTarget)); |
337 | |
338 | QPoint selectedBar = m_graph->selectedSeries() |
339 | ? m_graph->selectedSeries()->selectedBar() |
340 | : QBar3DSeries::invalidSelectionPosition(); |
341 | |
342 | if (selectedBar != QBar3DSeries::invalidSelectionPosition()) { |
343 | // Normalize selected bar position within axis range to determine target coordinates |
344 | //! [13] |
345 | QVector3D endTarget; |
346 | float xMin = m_graph->columnAxis()->min(); |
347 | float xRange = m_graph->columnAxis()->max() - xMin; |
348 | float zMin = m_graph->rowAxis()->min(); |
349 | float zRange = m_graph->rowAxis()->max() - zMin; |
350 | endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f); |
351 | endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f); |
352 | //! [13] |
353 | |
354 | // Rotate the camera so that it always points approximately to the graph center |
355 | //! [15] |
356 | qreal endAngleX = 90.0 - qRadiansToDegrees(radians: qAtan(v: qreal(endTarget.z() / endTarget.x()))); |
357 | if (endTarget.x() > 0.0f) |
358 | endAngleX -= 180.0f; |
359 | float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(rowIndex: selectedBar.x(), |
360 | columnIndex: selectedBar.y())->value(); |
361 | float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f; |
362 | if (m_graph->valueAxis()->reversed()) |
363 | endAngleY *= -1.0f; |
364 | //! [15] |
365 | |
366 | m_animationCameraX.setEndValue(QVariant::fromValue(value: float(endAngleX))); |
367 | m_animationCameraY.setEndValue(QVariant::fromValue(value: endAngleY)); |
368 | m_animationCameraZoom.setEndValue(QVariant::fromValue(value: 250)); |
369 | //! [14] |
370 | m_animationCameraTarget.setEndValue(QVariant::fromValue(value: endTarget)); |
371 | //! [14] |
372 | } else { |
373 | // No selected bar, so return to the default view |
374 | m_animationCameraX.setEndValue(QVariant::fromValue(value: m_defaultAngleX)); |
375 | m_animationCameraY.setEndValue(QVariant::fromValue(value: m_defaultAngleY)); |
376 | m_animationCameraZoom.setEndValue(QVariant::fromValue(value: m_defaultZoom)); |
377 | m_animationCameraTarget.setEndValue(QVariant::fromValue(value: m_defaultTarget)); |
378 | } |
379 | |
380 | m_animationCameraX.start(); |
381 | m_animationCameraY.start(); |
382 | m_animationCameraZoom.start(); |
383 | m_animationCameraTarget.start(); |
384 | } |
385 | //! [11] |
386 | |
387 | void GraphModifier::changeShadowQuality(int quality) |
388 | { |
389 | QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); |
390 | m_graph->setShadowQuality(sq); |
391 | emit shadowQualityChanged(quality); |
392 | } |
393 | |
394 | //! [7] |
395 | void GraphModifier::rotateX(int rotation) |
396 | { |
397 | m_xRotation = rotation; |
398 | m_graph->scene()->activeCamera()->setCameraPosition(horizontal: m_xRotation, vertical: m_yRotation); |
399 | } |
400 | |
401 | void GraphModifier::rotateY(int rotation) |
402 | { |
403 | m_yRotation = rotation; |
404 | m_graph->scene()->activeCamera()->setCameraPosition(horizontal: m_xRotation, vertical: m_yRotation); |
405 | } |
406 | //! [7] |
407 | |
408 | void GraphModifier::setBackgroundEnabled(int enabled) |
409 | { |
410 | m_graph->activeTheme()->setBackgroundEnabled(bool(enabled)); |
411 | } |
412 | |
413 | void GraphModifier::setGridEnabled(int enabled) |
414 | { |
415 | m_graph->activeTheme()->setGridEnabled(bool(enabled)); |
416 | } |
417 | |
418 | void GraphModifier::setSmoothBars(int smooth) |
419 | { |
420 | m_smooth = bool(smooth); |
421 | m_primarySeries->setMeshSmooth(m_smooth); |
422 | m_secondarySeries->setMeshSmooth(m_smooth); |
423 | } |
424 | |
425 | void GraphModifier::setSeriesVisibility(int enabled) |
426 | { |
427 | m_secondarySeries->setVisible(bool(enabled)); |
428 | } |
429 | |
430 | void GraphModifier::setReverseValueAxis(int enabled) |
431 | { |
432 | m_graph->valueAxis()->setReversed(enabled); |
433 | } |
434 | |
435 | void GraphModifier::setReflection(bool enabled) |
436 | { |
437 | m_graph->setReflection(enabled); |
438 | } |
439 | |