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 "qsurface3dseries_p.h"
31#include "surface3dcontroller_p.h"
32
33QT_BEGIN_NAMESPACE_DATAVISUALIZATION
34
35/*!
36 * \class QSurface3DSeries
37 * \inmodule QtDataVisualization
38 * \brief The QSurface3DSeries class represents a data series in a 3D surface
39 * graph.
40 * \since QtDataVisualization 1.0
41 *
42 * This class manages the series specific visual elements, as well as the series
43 * data (via a data proxy).
44 *
45 * If no data proxy is set explicitly for the series, the series creates a default
46 * proxy. Setting another proxy will destroy the existing proxy and all data added to it.
47 *
48 * The object mesh set via the QAbstract3DSeries::mesh property defines the selection
49 * pointer shape in a surface series.
50 *
51 * QSurface3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat():
52 * \table
53 * \row
54 * \li @xTitle \li Title from x-axis
55 * \row
56 * \li @yTitle \li Title from y-axis
57 * \row
58 * \li @zTitle \li Title from z-axis
59 * \row
60 * \li @xLabel \li Item value formatted using the format of the x-axis.
61 * For more information, see
62 * \l{QValue3DAxis::setLabelFormat()}.
63 * \row
64 * \li @yLabel \li Item value formatted using the format of the y-axis.
65 * For more information, see
66 * \l{QValue3DAxis::setLabelFormat()}.
67 * \row
68 * \li @zLabel \li Item value formatted using the format of the z-axis.
69 * For more information, see
70 * \l{QValue3DAxis::setLabelFormat()}.
71 * \row
72 * \li @seriesName \li Name of the series
73 * \endtable
74 *
75 * For example:
76 * \snippet doc_src_qtdatavisualization.cpp 1
77 *
78 * \sa {Qt Data Visualization Data Handling}
79 */
80
81/*!
82 * \qmltype Surface3DSeries
83 * \inqmlmodule QtDataVisualization
84 * \since QtDataVisualization 1.0
85 * \ingroup datavisualization_qml
86 * \instantiates QSurface3DSeries
87 * \inherits Abstract3DSeries
88 * \brief Represents a data series in a 3D surface graph.
89 *
90 * This type manages the series specific visual elements, as well as the series
91 * data (via a data proxy).
92 *
93 * For a more complete description, see QSurface3DSeries.
94 *
95 * \sa {Qt Data Visualization Data Handling}
96 */
97
98/*!
99 * \qmlproperty SurfaceDataProxy Surface3DSeries::dataProxy
100 *
101 * The active data proxy. The series assumes ownership of any proxy set to
102 * it and deletes any previously set proxy when a new one is added. The proxy cannot be null or
103 * set to another series.
104 */
105
106/*!
107 * \qmlproperty point Surface3DSeries::selectedPoint
108 *
109 * Sets the surface grid point in the position specified by a row and a column
110 * in the data array of the series as selected.
111 * Only one point can be selected at a time.
112 *
113 * To clear selection from this series, invalidSelectionPosition is set as the position.
114 * If this series is added to a graph, the graph can adjust the selection according to user
115 * interaction or if it becomes invalid.
116 *
117 * Removing rows from or inserting rows to the series before the row of the selected point
118 * will adjust the selection so that the same point will stay selected.
119 *
120 * \sa AbstractGraph3D::clearSelection()
121 */
122
123/*!
124 * \qmlproperty point Surface3DSeries::invalidSelectionPosition
125 * A constant property providing an invalid selection position.
126 * This position is set to the selectedPoint property to clear the selection
127 * from this series.
128 *
129 * \sa AbstractGraph3D::clearSelection()
130 */
131
132/*!
133 * \qmlproperty bool Surface3DSeries::flatShadingEnabled
134 *
135 * Sets surface flat shading to enabled. It is preset to \c true by default.
136 * When disabled, the normals on the surface are interpolated making the edges look round.
137 * When enabled, the normals are kept the same on a triangle making the color of the triangle solid.
138 * This makes the data more readable from the model.
139 * \note Flat shaded surfaces require at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.
140 * The value of the flatShadingSupported property indicates whether flat shading
141 * is supported at runtime.
142 */
143
144/*!
145 * \qmlproperty bool Surface3DSeries::flatShadingSupported
146 *
147 * Indicates whether flat shading for surfaces is supported by the current system.
148 * It requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.
149 *
150 * \note This read-only property is set to its correct value after the first
151 * render pass. Until then it is always \c true.
152 */
153
154/*!
155 * \qmlproperty DrawFlag Surface3DSeries::drawMode
156 *
157 * Sets the drawing mode to one of \l{QSurface3DSeries::DrawFlag}{Surface3DSeries.DrawFlag}.
158 * Clearing all flags is not allowed.
159 */
160
161/*!
162 * \qmlproperty string Surface3DSeries::textureFile
163 *
164 * The texture file name for the surface texture. To clear the texture, an empty
165 * file name is set.
166 */
167
168
169/*!
170 * \enum QSurface3DSeries::DrawFlag
171 *
172 * The drawing mode of the surface. Values of this enumeration can be combined
173 * with the OR operator.
174 *
175 * \value DrawWireframe
176 * Only the grid is drawn.
177 * \value DrawSurface
178 * Only the surface is drawn.
179 * \value DrawSurfaceAndWireframe
180 * Both the surface and grid are drawn.
181 */
182
183/*!
184 * Constructs a surface 3D series with the parent \a parent.
185 */
186QSurface3DSeries::QSurface3DSeries(QObject *parent) :
187 QAbstract3DSeries(new QSurface3DSeriesPrivate(this), parent)
188{
189 // Default proxy
190 dptr()->setDataProxy(new QSurfaceDataProxy);
191}
192
193/*!
194 * Constructs a surface 3D series with the data proxy \a dataProxy and the
195 * parent \a parent.
196 */
197QSurface3DSeries::QSurface3DSeries(QSurfaceDataProxy *dataProxy, QObject *parent) :
198 QAbstract3DSeries(new QSurface3DSeriesPrivate(this), parent)
199{
200 dptr()->setDataProxy(dataProxy);
201}
202
203/*!
204 * \internal
205 */
206QSurface3DSeries::QSurface3DSeries(QSurface3DSeriesPrivate *d, QObject *parent) :
207 QAbstract3DSeries(d, parent)
208{
209}
210
211/*!
212 * Deletes the surface 3D series.
213 */
214QSurface3DSeries::~QSurface3DSeries()
215{
216}
217
218/*!
219 * \property QSurface3DSeries::dataProxy
220 *
221 * \brief The active data proxy.
222 *
223 * The series assumes ownership of any proxy set to it and deletes any
224 * previously set proxy when a new one is added. The proxy cannot be null or
225 * set to another series.
226 */
227void QSurface3DSeries::setDataProxy(QSurfaceDataProxy *proxy)
228{
229 d_ptr->setDataProxy(proxy);
230}
231
232QSurfaceDataProxy *QSurface3DSeries::dataProxy() const
233{
234 return static_cast<QSurfaceDataProxy *>(d_ptr->dataProxy());
235}
236
237/*!
238 * \property QSurface3DSeries::selectedPoint
239 *
240 * \brief The surface grid point that is selected in the series.
241 */
242
243/*!
244 * Selects a surface grid point at the position \a position in the data array of
245 * the series specified by a row and a column.
246 *
247 * Only one point can be selected at a time.
248 *
249 * To clear selection from this series, invalidSelectionPosition() is set as \a position.
250 * If this series is added to a graph, the graph can adjust the selection according to user
251 * interaction or if it becomes invalid.
252 *
253 * Removing rows from or inserting rows to the series before the row of the selected point
254 * will adjust the selection so that the same point will stay selected.
255 *
256 * \sa QAbstract3DGraph::clearSelection()
257 */
258void QSurface3DSeries::setSelectedPoint(const QPoint &position)
259{
260 // Don't do this in private to avoid loops, as that is used for callback from controller.
261 if (d_ptr->m_controller)
262 static_cast<Surface3DController *>(d_ptr->m_controller)->setSelectedPoint(position, series: this, enterSlice: true);
263 else
264 dptr()->setSelectedPoint(position);
265}
266
267QPoint QSurface3DSeries::selectedPoint() const
268{
269 return dptrc()->m_selectedPoint;
270}
271
272/*!
273 * Returns the QPoint signifying an invalid selection position. This is set to
274 * the selectedPoint property to clear the selection from this series.
275 *
276 * \sa QAbstract3DGraph::clearSelection()
277 */
278QPoint QSurface3DSeries::invalidSelectionPosition()
279{
280 return Surface3DController::invalidSelectionPosition();
281}
282
283/*!
284 * \property QSurface3DSeries::flatShadingEnabled
285 *
286 * \brief Whether surface flat shading is enabled.
287 *
288 * Preset to \c true by default.
289 *
290 * When disabled, the normals on the surface are interpolated making the edges look round.
291 * When enabled, the normals are kept the same on a triangle making the color of the triangle solid.
292 * This makes the data more readable from the model.
293 * \note Flat shaded surfaces require at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.
294 * The value of the flatShadingSupported property indicates whether flat shading
295 * is supported at runtime.
296 */
297void QSurface3DSeries::setFlatShadingEnabled(bool enabled)
298{
299 if (dptr()->m_flatShadingEnabled != enabled) {
300 dptr()->setFlatShadingEnabled(enabled);
301 emit flatShadingEnabledChanged(enable: enabled);
302 }
303}
304
305bool QSurface3DSeries::isFlatShadingEnabled() const
306{
307 return dptrc()->m_flatShadingEnabled;
308}
309
310/*!
311 * \property QSurface3DSeries::flatShadingSupported
312 *
313 * \brief Whether surface flat shading is supported by the current system.
314 *
315 * Flat shading for surfaces requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.
316 * If \c true, flat shading for surfaces is supported.
317 * \note This read-only property is set to its correct value after the first
318 * render pass. Until then it is always \c true.
319 */
320bool QSurface3DSeries::isFlatShadingSupported() const
321{
322 if (d_ptr->m_controller)
323 return static_cast<Surface3DController *>(d_ptr->m_controller)->isFlatShadingSupported();
324 else
325 return true;
326}
327
328/*!
329 * \property QSurface3DSeries::drawMode
330 *
331 * The drawing mode.
332 *
333 * Possible values are the values of DrawFlag. Clearing all flags is not allowed.
334 */
335void QSurface3DSeries::setDrawMode(DrawFlags mode)
336{
337 if (dptr()->m_drawMode != mode) {
338 dptr()->setDrawMode(mode);
339 emit drawModeChanged(mode);
340 }
341}
342
343QSurface3DSeries::DrawFlags QSurface3DSeries::drawMode() const
344{
345 return dptrc()->m_drawMode;
346}
347
348/*!
349 * \property QSurface3DSeries::texture
350 *
351 * \brief The texture for the surface as a QImage.
352 *
353 * Setting an empty QImage clears the texture.
354 */
355void QSurface3DSeries::setTexture(const QImage &texture)
356{
357 if (dptr()->m_texture != texture) {
358 dptr()->setTexture(texture);
359
360 emit textureChanged(image: texture);
361 dptr()->m_textureFile.clear();
362 }
363}
364
365QImage QSurface3DSeries::texture() const
366{
367 return dptrc()->m_texture;
368}
369
370/*!
371 * \property QSurface3DSeries::textureFile
372 *
373 * \brief The texture for the surface as a file.
374 *
375 * Setting an empty file name clears the texture.
376 */
377void QSurface3DSeries::setTextureFile(const QString &filename)
378{
379 if (dptr()->m_textureFile != filename) {
380 if (filename.isEmpty()) {
381 setTexture(QImage());
382 } else {
383 QImage image(filename);
384 if (image.isNull()) {
385 qWarning() << "Warning: Tried to set invalid image file as surface texture.";
386 return;
387 }
388 setTexture(image);
389 }
390
391 dptr()->m_textureFile = filename;
392 emit textureFileChanged(filename);
393 }
394}
395
396QString QSurface3DSeries::textureFile() const
397{
398 return dptrc()->m_textureFile;
399}
400
401/*!
402 * \internal
403 */
404QSurface3DSeriesPrivate *QSurface3DSeries::dptr()
405{
406 return static_cast<QSurface3DSeriesPrivate *>(d_ptr.data());
407}
408
409/*!
410 * \internal
411 */
412const QSurface3DSeriesPrivate *QSurface3DSeries::dptrc() const
413{
414 return static_cast<const QSurface3DSeriesPrivate *>(d_ptr.data());
415}
416
417// QSurface3DSeriesPrivate
418
419QSurface3DSeriesPrivate::QSurface3DSeriesPrivate(QSurface3DSeries *q)
420 : QAbstract3DSeriesPrivate(q, QAbstract3DSeries::SeriesTypeSurface),
421 m_selectedPoint(Surface3DController::invalidSelectionPosition()),
422 m_flatShadingEnabled(true),
423 m_drawMode(QSurface3DSeries::DrawSurfaceAndWireframe)
424{
425 m_itemLabelFormat = QStringLiteral("@xLabel, @yLabel, @zLabel");
426 m_mesh = QAbstract3DSeries::MeshSphere;
427}
428
429QSurface3DSeriesPrivate::~QSurface3DSeriesPrivate()
430{
431}
432
433QSurface3DSeries *QSurface3DSeriesPrivate::qptr()
434{
435 return static_cast<QSurface3DSeries *>(q_ptr);
436}
437
438void QSurface3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy)
439{
440 Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeSurface);
441
442 QAbstract3DSeriesPrivate::setDataProxy(proxy);
443
444 emit qptr()->dataProxyChanged(proxy: static_cast<QSurfaceDataProxy *>(proxy));
445}
446
447void QSurface3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *newController)
448{
449 QSurfaceDataProxy *surfaceDataProxy = static_cast<QSurfaceDataProxy *>(m_dataProxy);
450
451 if (m_controller && surfaceDataProxy) {
452 //Disconnect old controller/old proxy
453 QObject::disconnect(sender: surfaceDataProxy, signal: 0, receiver: m_controller, member: 0);
454 QObject::disconnect(sender: q_ptr, signal: 0, receiver: m_controller, member: 0);
455 }
456
457 if (newController && surfaceDataProxy) {
458 Surface3DController *controller = static_cast<Surface3DController *>(newController);
459
460 QObject::connect(sender: surfaceDataProxy, signal: &QSurfaceDataProxy::arrayReset, receiver: controller,
461 slot: &Surface3DController::handleArrayReset);
462 QObject::connect(sender: surfaceDataProxy, signal: &QSurfaceDataProxy::rowsAdded, receiver: controller,
463 slot: &Surface3DController::handleRowsAdded);
464 QObject::connect(sender: surfaceDataProxy, signal: &QSurfaceDataProxy::rowsChanged, receiver: controller,
465 slot: &Surface3DController::handleRowsChanged);
466 QObject::connect(sender: surfaceDataProxy, signal: &QSurfaceDataProxy::rowsRemoved, receiver: controller,
467 slot: &Surface3DController::handleRowsRemoved);
468 QObject::connect(sender: surfaceDataProxy, signal: &QSurfaceDataProxy::rowsInserted, receiver: controller,
469 slot: &Surface3DController::handleRowsInserted);
470 QObject::connect(sender: surfaceDataProxy, signal: &QSurfaceDataProxy::itemChanged, receiver: controller,
471 slot: &Surface3DController::handleItemChanged);
472 QObject::connect(sender: qptr(), signal: &QSurface3DSeries::dataProxyChanged, receiver: controller,
473 slot: &Surface3DController::handleArrayReset);
474 }
475}
476
477void QSurface3DSeriesPrivate::createItemLabel()
478{
479 static const QString xTitleTag(QStringLiteral("@xTitle"));
480 static const QString yTitleTag(QStringLiteral("@yTitle"));
481 static const QString zTitleTag(QStringLiteral("@zTitle"));
482 static const QString xLabelTag(QStringLiteral("@xLabel"));
483 static const QString yLabelTag(QStringLiteral("@yLabel"));
484 static const QString zLabelTag(QStringLiteral("@zLabel"));
485 static const QString seriesNameTag(QStringLiteral("@seriesName"));
486
487 if (m_selectedPoint == QSurface3DSeries::invalidSelectionPosition()) {
488 m_itemLabel = QString();
489 return;
490 }
491
492 QValue3DAxis *axisX = static_cast<QValue3DAxis *>(m_controller->axisX());
493 QValue3DAxis *axisY = static_cast<QValue3DAxis *>(m_controller->axisY());
494 QValue3DAxis *axisZ = static_cast<QValue3DAxis *>(m_controller->axisZ());
495 QVector3D selectedPosition = qptr()->dataProxy()->itemAt(position: m_selectedPoint)->position();
496
497 m_itemLabel = m_itemLabelFormat;
498
499 m_itemLabel.replace(before: xTitleTag, after: axisX->title());
500 m_itemLabel.replace(before: yTitleTag, after: axisY->title());
501 m_itemLabel.replace(before: zTitleTag, after: axisZ->title());
502
503 if (m_itemLabel.contains(s: xLabelTag)) {
504 QString valueLabelText = axisX->formatter()->stringForValue(
505 value: qreal(selectedPosition.x()), format: axisX->labelFormat());
506 m_itemLabel.replace(before: xLabelTag, after: valueLabelText);
507 }
508 if (m_itemLabel.contains(s: yLabelTag)) {
509 QString valueLabelText = axisY->formatter()->stringForValue(
510 value: qreal(selectedPosition.y()), format: axisY->labelFormat());
511 m_itemLabel.replace(before: yLabelTag, after: valueLabelText);
512 }
513 if (m_itemLabel.contains(s: zLabelTag)) {
514 QString valueLabelText = axisZ->formatter()->stringForValue(
515 value: qreal(selectedPosition.z()), format: axisZ->labelFormat());
516 m_itemLabel.replace(before: zLabelTag, after: valueLabelText);
517 }
518 m_itemLabel.replace(before: seriesNameTag, after: m_name);
519}
520
521void QSurface3DSeriesPrivate::setSelectedPoint(const QPoint &position)
522{
523 if (position != m_selectedPoint) {
524 markItemLabelDirty();
525 m_selectedPoint = position;
526 emit qptr()->selectedPointChanged(position: m_selectedPoint);
527 }
528}
529
530void QSurface3DSeriesPrivate::setFlatShadingEnabled(bool enabled)
531{
532 m_flatShadingEnabled = enabled;
533 if (m_controller)
534 m_controller->markSeriesVisualsDirty();
535}
536
537void QSurface3DSeriesPrivate::setDrawMode(QSurface3DSeries::DrawFlags mode)
538{
539 if (mode.testFlag(flag: QSurface3DSeries::DrawWireframe)
540 || mode.testFlag(flag: QSurface3DSeries::DrawSurface)) {
541 m_drawMode = mode;
542 if (m_controller)
543 m_controller->markSeriesVisualsDirty();
544 } else {
545 qWarning(msg: "You may not clear all draw flags. Mode not changed.");
546 }
547}
548
549void QSurface3DSeriesPrivate::setTexture(const QImage &texture)
550{
551 m_texture = texture;
552 if (static_cast<Surface3DController *>(m_controller))
553 static_cast<Surface3DController *>(m_controller)->updateSurfaceTexture(series: qptr());
554}
555
556QT_END_NAMESPACE_DATAVISUALIZATION
557

source code of qtdatavis3d/src/datavisualization/data/qsurface3dseries.cpp