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

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