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 "scatterdatamodifier.h"
31#include <QtDataVisualization/qscatterdataproxy.h>
32#include <QtDataVisualization/qvalue3daxis.h>
33#include <QtDataVisualization/q3dscene.h>
34#include <QtDataVisualization/q3dcamera.h>
35#include <QtDataVisualization/qscatter3dseries.h>
36#include <QtDataVisualization/q3dtheme.h>
37#include <QtDataVisualization/QCustom3DItem>
38#include <QtCore/qmath.h>
39
40using namespace QtDataVisualization;
41
42static const float verticalRange = 8.0f;
43static const float horizontalRange = verticalRange;
44static const float ellipse_a = horizontalRange / 3.0f;
45static const float ellipse_b = verticalRange;
46static const float doublePi = float(M_PI) * 2.0f;
47static const float radiansToDegrees = 360.0f / doublePi;
48static const float animationFrames = 30.0f;
49
50ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter)
51 : m_graph(scatter),
52 m_fieldLines(12),
53 m_arrowsPerLine(16),
54 m_magneticField(new QScatter3DSeries),
55 m_sun(new QCustom3DItem),
56 m_magneticFieldArray(0),
57 m_angleOffset(0.0f),
58 m_angleStep(doublePi / m_arrowsPerLine / animationFrames)
59{
60 m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
61 m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
62
63 // Magnetic field lines use custom narrow arrow
64 m_magneticField->setItemSize(0.2f);
65 //! [3]
66 m_magneticField->setMesh(QAbstract3DSeries::MeshUserDefined);
67 m_magneticField->setUserDefinedMesh(QStringLiteral(":/mesh/narrowarrow.obj"));
68 //! [3]
69 //! [4]
70 QLinearGradient fieldGradient(0, 0, 16, 1024);
71 fieldGradient.setColorAt(pos: 0.0, color: Qt::black);
72 fieldGradient.setColorAt(pos: 1.0, color: Qt::white);
73 m_magneticField->setBaseGradient(fieldGradient);
74 m_magneticField->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
75 //! [4]
76
77 // For 'sun' we use a custom large sphere
78 m_sun->setScaling(QVector3D(0.07f, 0.07f, 0.07f));
79 m_sun->setMeshFile(QStringLiteral(":/mesh/largesphere.obj"));
80 QImage sunColor = QImage(2, 2, QImage::Format_RGB32);
81 sunColor.fill(color: QColor(0xff, 0xbb, 0x00));
82 m_sun->setTextureImage(sunColor);
83
84 m_graph->addSeries(series: m_magneticField);
85 m_graph->addCustomItem(item: m_sun);
86
87 // Configure the axes according to the data
88 m_graph->axisX()->setRange(min: -horizontalRange, max: horizontalRange);
89 m_graph->axisY()->setRange(min: -verticalRange, max: verticalRange);
90 m_graph->axisZ()->setRange(min: -horizontalRange, max: horizontalRange);
91 m_graph->axisX()->setSegmentCount(int(horizontalRange));
92 m_graph->axisZ()->setSegmentCount(int(horizontalRange));
93
94 QObject::connect(sender: &m_rotationTimer, signal: &QTimer::timeout, receiver: this,
95 slot: &ScatterDataModifier::triggerRotation);
96
97 toggleRotation();
98 generateData();
99}
100
101ScatterDataModifier::~ScatterDataModifier()
102{
103 delete m_graph;
104}
105
106void ScatterDataModifier::generateData()
107{
108 // Reusing existing array is computationally cheaper than always generating new array, even if
109 // all data items change in the array, if the array size doesn't change.
110 if (!m_magneticFieldArray)
111 m_magneticFieldArray = new QScatterDataArray;
112
113 int arraySize = m_fieldLines * m_arrowsPerLine;
114 if (arraySize != m_magneticFieldArray->size())
115 m_magneticFieldArray->resize(asize: arraySize);
116
117 QScatterDataItem *ptrToDataArray = &m_magneticFieldArray->first();
118
119 for (float i = 0; i < m_fieldLines; i++) {
120 float horizontalAngle = (doublePi * i) / m_fieldLines;
121 float xCenter = ellipse_a * qCos(v: horizontalAngle);
122 float zCenter = ellipse_a * qSin(v: horizontalAngle);
123
124 // Rotate - arrow always tangential to origin
125 //! [0]
126 QQuaternion yRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: horizontalAngle * radiansToDegrees);
127 //! [0]
128
129 for (float j = 0; j < m_arrowsPerLine; j++) {
130 // Calculate point on ellipse centered on origin and parallel to x-axis
131 float verticalAngle = ((doublePi * j) / m_arrowsPerLine) + m_angleOffset;
132 float xUnrotated = ellipse_a * qCos(v: verticalAngle);
133 float y = ellipse_b * qSin(v: verticalAngle);
134
135 // Rotate the ellipse around y-axis
136 float xRotated = xUnrotated * qCos(v: horizontalAngle);
137 float zRotated = xUnrotated * qSin(v: horizontalAngle);
138
139 // Add offset
140 float x = xCenter + xRotated;
141 float z = zCenter + zRotated;
142
143 //! [1]
144 QQuaternion zRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: verticalAngle * radiansToDegrees);
145 QQuaternion totalRotation = yRotation * zRotation;
146 //! [1]
147
148 ptrToDataArray->setPosition(QVector3D(x, y, z));
149 //! [2]
150 ptrToDataArray->setRotation(totalRotation);
151 //! [2]
152 ptrToDataArray++;
153 }
154 }
155
156 if (m_graph->selectedSeries() == m_magneticField)
157 m_graph->clearSelection();
158
159 m_magneticField->dataProxy()->resetArray(newArray: m_magneticFieldArray);
160}
161
162void ScatterDataModifier::setFieldLines(int lines)
163{
164 m_fieldLines = lines;
165 generateData();
166}
167
168void ScatterDataModifier::setArrowsPerLine(int arrows)
169{
170 m_angleOffset = 0.0f;
171 m_angleStep = doublePi / m_arrowsPerLine / animationFrames;
172 m_arrowsPerLine = arrows;
173 generateData();
174}
175
176void ScatterDataModifier::triggerRotation()
177{
178 m_angleOffset += m_angleStep;
179 generateData();
180}
181
182void ScatterDataModifier::toggleSun()
183{
184 m_sun->setVisible(!m_sun->isVisible());
185}
186
187void ScatterDataModifier::toggleRotation()
188{
189 if (m_rotationTimer.isActive())
190 m_rotationTimer.stop();
191 else
192 m_rotationTimer.start(msec: 15);
193}
194

source code of qtdatavis3d/examples/datavisualization/rotations/scatterdatamodifier.cpp