| 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 | #define NOMINMAX |
| 31 | |
| 32 | #include "data.h" |
| 33 | #include <QtDataVisualization/QValue3DAxis> |
| 34 | #include <QtDataVisualization/Q3DCamera> |
| 35 | #include <QtDataVisualization/QBar3DSeries> |
| 36 | #include <QtDataVisualization/QScatter3DSeries> |
| 37 | #include <QtDataVisualization/QSurface3DSeries> |
| 38 | #include <QtDataVisualization/Q3DTheme> |
| 39 | #include <QScrollBar> |
| 40 | #include <QSize> |
| 41 | #include <QImage> |
| 42 | |
| 43 | using namespace QtDataVisualization; |
| 44 | |
| 45 | Data::Data(Q3DSurface *surface, Q3DScatter *scatter, Q3DBars *bars, |
| 46 | QTextEdit *statusArea, QWidget *widget) : |
| 47 | m_surface(surface), |
| 48 | m_scatter(scatter), |
| 49 | m_bars(bars), |
| 50 | m_statusArea(statusArea), |
| 51 | m_widget(widget), |
| 52 | m_resize(true), |
| 53 | m_resolution(QSize(300, 300)), |
| 54 | m_resolutionLevel(0), |
| 55 | m_mode(Surface), |
| 56 | m_scatterDataArray(0), |
| 57 | m_barDataArray(0), |
| 58 | m_started(false) |
| 59 | { |
| 60 | // Initialize surface |
| 61 | m_surface->activeTheme()->setType(Q3DTheme::ThemeIsabelle); |
| 62 | QLinearGradient gradient; |
| 63 | gradient.setColorAt(pos: 0.0, color: Qt::black); |
| 64 | gradient.setColorAt(pos: 0.33, color: Qt::blue); |
| 65 | gradient.setColorAt(pos: 0.67, color: Qt::red); |
| 66 | gradient.setColorAt(pos: 1.0, color: Qt::yellow); |
| 67 | m_surface->setSelectionMode(QAbstract3DGraph::SelectionNone); |
| 68 | m_surface->activeTheme()->setGridEnabled(false); |
| 69 | m_surface->activeTheme()->setBackgroundEnabled(false); |
| 70 | m_surface->scene()->activeCamera()->setCameraPosition(horizontal: 0.0, vertical: 90.0, zoom: 150.0); |
| 71 | QSurface3DSeries *series1 = new QSurface3DSeries(new QHeightMapSurfaceDataProxy()); |
| 72 | series1->setFlatShadingEnabled(true); |
| 73 | series1->setDrawMode(QSurface3DSeries::DrawSurface); |
| 74 | series1->setColorStyle(Q3DTheme::ColorStyleRangeGradient); |
| 75 | series1->setBaseGradient(gradient); |
| 76 | m_surface->addSeries(series: series1); |
| 77 | |
| 78 | // Initialize scatter |
| 79 | m_scatter->activeTheme()->setType(Q3DTheme::ThemeStoneMoss); |
| 80 | m_scatter->setSelectionMode(QAbstract3DGraph::SelectionNone); |
| 81 | m_scatter->activeTheme()->setGridEnabled(false); |
| 82 | m_scatter->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftLow); |
| 83 | m_scatter->scene()->activeCamera()->setCameraPosition(horizontal: 0.0, vertical: 85.0, zoom: 150.0); |
| 84 | QScatter3DSeries *series2 = new QScatter3DSeries; |
| 85 | series2->setMesh(QAbstract3DSeries::MeshPoint); |
| 86 | m_scatter->addSeries(series: series2); |
| 87 | |
| 88 | // Initialize bars |
| 89 | m_bars->activeTheme()->setType(Q3DTheme::ThemeQt); |
| 90 | m_bars->setSelectionMode(QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice); |
| 91 | m_bars->activeTheme()->setGridEnabled(false); |
| 92 | m_bars->setShadowQuality(QAbstract3DGraph::ShadowQualityLow); |
| 93 | m_bars->setBarSpacing(QSizeF(0.0, 0.0)); |
| 94 | m_bars->scene()->activeCamera()->setCameraPosition(horizontal: 0.0, vertical: 75.0, zoom: 150.0); |
| 95 | QBar3DSeries *series3 = new QBar3DSeries; |
| 96 | series3->setMesh(QAbstract3DSeries::MeshBar); |
| 97 | m_bars->addSeries(series: series3); |
| 98 | |
| 99 | // Hide scroll bar |
| 100 | m_statusArea->verticalScrollBar()->setVisible(false); |
| 101 | } |
| 102 | |
| 103 | Data::~Data() |
| 104 | { |
| 105 | delete m_bars; |
| 106 | delete m_surface; |
| 107 | delete m_scatter; |
| 108 | delete m_widget; |
| 109 | delete m_scatterDataArray; // only portion of this array is set to graph |
| 110 | } |
| 111 | |
| 112 | void Data::updateData() |
| 113 | { |
| 114 | if (!m_started) { |
| 115 | m_statusArea->append(QStringLiteral("<i>We are stopped. The changes will take effect once started.</i>" )); |
| 116 | return; |
| 117 | } |
| 118 | QImage depthMap = QImage(":/australia.png" ); |
| 119 | if (m_resize) // Resize for better performance |
| 120 | depthMap = depthMap.scaled(s: m_resolution); |
| 121 | if (m_mode != Surface) |
| 122 | setData(depthMap); |
| 123 | else |
| 124 | static_cast<QHeightMapSurfaceDataProxy *>(m_surface->seriesList().at(i: 0)->dataProxy())->setHeightMap( |
| 125 | depthMap); |
| 126 | } |
| 127 | |
| 128 | void Data::clearData() |
| 129 | { |
| 130 | m_bars->seriesList().at(i: 0)->dataProxy()->resetArray(newArray: 0); |
| 131 | m_scatter->seriesList().at(i: 0)->dataProxy()->resetArray(newArray: 0); |
| 132 | m_surface->seriesList().at(i: 0)->dataProxy()->resetArray(newArray: 0); |
| 133 | } |
| 134 | |
| 135 | void Data::setResolution(int selection) |
| 136 | { |
| 137 | m_resolutionLevel = selection; |
| 138 | switch (selection) { |
| 139 | case 0: { |
| 140 | m_resize = true; |
| 141 | m_resolution = QSize(300, 300); |
| 142 | break; |
| 143 | } |
| 144 | case 1: { |
| 145 | m_resize = true; |
| 146 | m_resolution = QSize(600, 600); |
| 147 | break; |
| 148 | } |
| 149 | case 2: { |
| 150 | m_resize = true; |
| 151 | m_resolution = QSize(800, 800); |
| 152 | break; |
| 153 | } |
| 154 | case 3: { |
| 155 | m_resize = false; |
| 156 | m_resolution = QSize(1020, 1020); // image size |
| 157 | break; |
| 158 | } |
| 159 | }; |
| 160 | if (m_mode == Scatter) { |
| 161 | m_resize = true; |
| 162 | m_resolution /= 3; |
| 163 | delete m_scatterDataArray; |
| 164 | m_scatterDataArray = new QScatterDataArray; |
| 165 | m_scatterDataArray->resize(asize: m_resolution.width() * m_resolution.height()); |
| 166 | } else if (m_mode == Bars) { |
| 167 | m_resize = true; |
| 168 | m_resolution /= 6; |
| 169 | m_barDataArray = new QBarDataArray; |
| 170 | m_barDataArray->reserve(alloc: m_resolution.height()); |
| 171 | for (int i = 0; i < m_resolution.height(); i++) { |
| 172 | QBarDataRow *newProxyRow = new QBarDataRow(m_resolution.width()); |
| 173 | m_barDataArray->append(t: newProxyRow); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | m_statusArea->append(text: QString(QStringLiteral("<b>Resolution:</b> %1 x %2" )).arg( |
| 178 | a: m_resolution.width()).arg(a: m_resolution.height())); |
| 179 | |
| 180 | updateData(); |
| 181 | } |
| 182 | |
| 183 | void Data::scrollDown() |
| 184 | { |
| 185 | QScrollBar *scrollbar = m_statusArea->verticalScrollBar(); |
| 186 | scrollbar->setValue(scrollbar->maximum()); |
| 187 | } |
| 188 | |
| 189 | void Data::useGradientOne() |
| 190 | { |
| 191 | m_surface->activeTheme()->setType(Q3DTheme::ThemeIsabelle); |
| 192 | QLinearGradient gradient; |
| 193 | gradient.setColorAt(pos: 0.0, color: Qt::black); |
| 194 | gradient.setColorAt(pos: 0.33, color: Qt::blue); |
| 195 | gradient.setColorAt(pos: 0.67, color: Qt::red); |
| 196 | gradient.setColorAt(pos: 1.0, color: Qt::yellow); |
| 197 | m_surface->seriesList().at(i: 0)->setBaseGradient(gradient); |
| 198 | m_surface->seriesList().at(i: 0)->setColorStyle(Q3DTheme::ColorStyleRangeGradient); |
| 199 | m_statusArea->append(QStringLiteral("<b>Colors:</b> Thermal image imitation" )); |
| 200 | } |
| 201 | |
| 202 | void Data::useGradientTwo() |
| 203 | { |
| 204 | m_surface->activeTheme()->setType(Q3DTheme::ThemeQt); |
| 205 | QLinearGradient gradient; |
| 206 | gradient.setColorAt(pos: 0.0, color: Qt::white); |
| 207 | gradient.setColorAt(pos: 0.8, color: Qt::red); |
| 208 | gradient.setColorAt(pos: 1.0, color: Qt::green); |
| 209 | m_surface->seriesList().at(i: 0)->setBaseGradient(gradient); |
| 210 | m_surface->seriesList().at(i: 0)->setColorStyle(Q3DTheme::ColorStyleRangeGradient); |
| 211 | m_statusArea->append(QStringLiteral("<b>Colors:</b> Highlight foreground" )); |
| 212 | } |
| 213 | |
| 214 | void Data::setData(const QImage &image) |
| 215 | { |
| 216 | QImage heightImage = image; |
| 217 | |
| 218 | uchar *bits = heightImage.bits(); |
| 219 | |
| 220 | int imageHeight = heightImage.height(); |
| 221 | int imageWidth = heightImage.width(); |
| 222 | int bitCount = imageWidth * 4 * (imageHeight - 1); |
| 223 | int widthBits = imageWidth * 4; |
| 224 | |
| 225 | if (m_mode == Scatter) { |
| 226 | QScatterDataItem *ptrToDataArray = &m_scatterDataArray->first(); |
| 227 | |
| 228 | int limitsX = imageWidth / 2; |
| 229 | int limitsZ = imageHeight / 2; |
| 230 | float height = 0; |
| 231 | int count = 0; |
| 232 | |
| 233 | for (int i = -limitsZ; i < limitsZ; i++, bitCount -= widthBits) { |
| 234 | for (int j = -limitsX; j < limitsX; j++) { |
| 235 | height = float(bits[bitCount + ((j + limitsX) * 4)]) - 128.0; |
| 236 | if (height > -128) { |
| 237 | ptrToDataArray->setPosition(QVector3D(float(j), height, float(i))); |
| 238 | ptrToDataArray++; |
| 239 | count++; |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | QScatterDataArray *dataArray = new QScatterDataArray(m_scatterDataArray->mid(pos: 0, len: count)); |
| 245 | m_scatter->seriesList().at(i: 0)->dataProxy()->resetArray(newArray: dataArray); |
| 246 | } else { |
| 247 | QBarDataArray *dataArray = m_barDataArray; |
| 248 | for (int i = 0; i < imageHeight; i++, bitCount -= widthBits) { |
| 249 | QBarDataRow &newRow = *dataArray->at(i); |
| 250 | for (int j = 0; j < imageWidth; j++) |
| 251 | newRow[j] = float(bits[bitCount + (j * 4)]); |
| 252 | } |
| 253 | |
| 254 | m_bars->seriesList().at(i: 0)->dataProxy()->resetArray(newArray: dataArray); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | void Data::changeMode(int mode) |
| 259 | { |
| 260 | m_mode = VisualizationMode(mode); |
| 261 | switch (m_mode) { |
| 262 | case Surface: { |
| 263 | m_statusArea->append(QStringLiteral("<b>Visualization Type:</b> Surface" )); |
| 264 | break; |
| 265 | } |
| 266 | case Scatter: { |
| 267 | m_statusArea->append(QStringLiteral("<b>Visualization Type:</b> Scatter" )); |
| 268 | break; |
| 269 | } |
| 270 | default: { |
| 271 | m_statusArea->append(QStringLiteral("<b>Visualization Type:</b> Bars" )); |
| 272 | break; |
| 273 | } |
| 274 | } |
| 275 | // Reset resolution after mode change |
| 276 | setResolution(m_resolutionLevel); |
| 277 | } |
| 278 | |
| 279 | void Data::start() |
| 280 | { |
| 281 | m_started = true; |
| 282 | // Reset resolution before starting (otherwise restart will crash due to empty data) |
| 283 | setResolution(m_resolutionLevel); |
| 284 | updateData(); |
| 285 | m_statusArea->append(QStringLiteral("<b>Started<\b>" )); |
| 286 | } |
| 287 | |
| 288 | void Data::stop() |
| 289 | { |
| 290 | m_started = false; |
| 291 | clearData(); |
| 292 | m_statusArea->append(QStringLiteral("<b>Stopped<\b>" )); |
| 293 | } |
| 294 | |
| 295 | ContainerChanger::ContainerChanger(QWidget *surface, QWidget *scatter, QWidget *bars, |
| 296 | QWidget *buttonOne, QWidget *buttonTwo) |
| 297 | : m_surface(surface), |
| 298 | m_scatter(scatter), |
| 299 | m_bars(bars), |
| 300 | m_button1(buttonOne), |
| 301 | m_button2(buttonTwo) |
| 302 | { |
| 303 | } |
| 304 | |
| 305 | ContainerChanger::~ContainerChanger() |
| 306 | { |
| 307 | } |
| 308 | |
| 309 | void ContainerChanger::changeContainer(int container) |
| 310 | { |
| 311 | switch (container) { |
| 312 | case 0: { |
| 313 | m_scatter->setVisible(false); |
| 314 | m_bars->setVisible(false); |
| 315 | m_surface->setVisible(true); |
| 316 | m_button1->setEnabled(true); |
| 317 | m_button2->setEnabled(true); |
| 318 | break; |
| 319 | } |
| 320 | case 1: { |
| 321 | m_surface->setVisible(false); |
| 322 | m_bars->setVisible(false); |
| 323 | m_scatter->setVisible(true); |
| 324 | m_button1->setEnabled(false); |
| 325 | m_button2->setEnabled(false); |
| 326 | break; |
| 327 | } |
| 328 | case 2: { |
| 329 | m_surface->setVisible(false); |
| 330 | m_scatter->setVisible(false); |
| 331 | m_bars->setVisible(true); |
| 332 | m_button1->setEnabled(false); |
| 333 | m_button2->setEnabled(false); |
| 334 | break; |
| 335 | } |
| 336 | } |
| 337 | } |
| 338 | |