1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "private/qquick3drepeater_p.h"
5#include "qquickgraphssurface_p.h"
6#include <QtCore/QMutexLocker>
7
8#include "declarativescene_p.h"
9#include "surface3dcontroller_p.h"
10#include "surfaceselectioninstancing_p.h"
11#include "qvalue3daxis_p.h"
12#include "qcategory3daxis_p.h"
13#include "quickgraphstexturedata_p.h"
14
15#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
16#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
17#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
18
19QT_BEGIN_NAMESPACE
20
21QQuickGraphsSurface::QQuickGraphsSurface(QQuickItem *parent)
22 : QQuickGraphsItem(parent),
23 m_surfaceController(0)
24{
25 setAcceptedMouseButtons(Qt::AllButtons);
26
27 // Create the shared component on the main GUI thread.
28 m_surfaceController = new Surface3DController(boundingRect().toRect(), new Declarative3DScene);
29 setSharedController(m_surfaceController);
30
31 QObject::connect(sender: m_surfaceController, signal: &Surface3DController::selectedSeriesChanged,
32 context: this, slot: &QQuickGraphsSurface::selectedSeriesChanged);
33 QObject::connect(sender: m_surfaceController, signal: &Surface3DController::flipHorizontalGridChanged,
34 context: this, slot: &QQuickGraphsSurface::handleFlipHorizontalGridChanged);
35}
36
37QQuickGraphsSurface::~QQuickGraphsSurface()
38{
39 QMutexLocker locker(m_nodeMutex.data());
40 const QMutexLocker locker2(mutex());
41 delete m_surfaceController;
42 for (auto model : m_model)
43 delete model;
44 delete m_instancing;
45 delete m_sliceInstancing;
46}
47
48QValue3DAxis *QQuickGraphsSurface::axisX() const
49{
50 return static_cast<QValue3DAxis *>(m_surfaceController->axisX());
51}
52
53void QQuickGraphsSurface::setAxisX(QValue3DAxis *axis)
54{
55 m_surfaceController->setAxisX(axis);
56}
57
58QValue3DAxis *QQuickGraphsSurface::axisY() const
59{
60 return static_cast<QValue3DAxis *>(m_surfaceController->axisY());
61}
62
63void QQuickGraphsSurface::setAxisY(QValue3DAxis *axis)
64{
65 m_surfaceController->setAxisY(axis);
66}
67
68QValue3DAxis *QQuickGraphsSurface::axisZ() const
69{
70 return static_cast<QValue3DAxis *>(m_surfaceController->axisZ());
71}
72
73void QQuickGraphsSurface::setAxisZ(QValue3DAxis *axis)
74{
75 m_surfaceController->setAxisZ(axis);
76}
77
78void QQuickGraphsSurface::handleFlatShadingEnabledChanged()
79{
80 auto series = static_cast<QSurface3DSeries *>(sender());
81 for (auto model : m_model) {
82 if (model->series == series) {
83 updateModel(model);
84 break;
85 }
86 }
87}
88
89void QQuickGraphsSurface::handleWireframeColorChanged()
90{
91 for (auto model : m_model) {
92 QQmlListReference gridMaterialRef(model->gridModel, "materials");
93 auto gridMaterial = gridMaterialRef.at(0);
94 QColor gridColor = model->series->wireframeColor();
95 gridMaterial->setProperty(name: "gridColor", value: gridColor);
96
97 if (sliceView()) {
98 QQmlListReference gridMaterialRef(model->sliceGridModel, "materials");
99 auto gridMaterial = static_cast<QQuick3DPrincipledMaterial *>(gridMaterialRef.at(0));
100 gridMaterial->setBaseColor(gridColor);
101 }
102 }
103}
104
105void QQuickGraphsSurface::handleFlipHorizontalGridChanged(bool flip)
106{
107 if (!segmentLineRepeaterX()
108 || !segmentLineRepeaterZ()) {
109 return;
110 }
111 int gridLineCountX = segmentLineRepeaterX()->count();
112 int subGridLineCountX = subsegmentLineRepeaterX()->count();
113 int gridLineCountZ = segmentLineRepeaterZ()->count();
114 int subGridLineCountZ = subsegmentLineRepeaterZ()->count();
115
116 float factor = -1.0f;
117 if (isGridUpdated())
118 factor = flip ? -1.0f : 1.0f;
119
120 for (int i = 0; i < subGridLineCountZ; i++) {
121 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(index: i));
122 QVector3D pos = lineNode->position();
123 pos.setY(pos.y() * factor);
124 lineNode->setPosition(pos);
125 }
126 for (int i = 0; i < gridLineCountZ; i++) {
127 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(index: i));
128 QVector3D pos = lineNode->position();
129 pos.setY(pos.y() * factor);
130 lineNode->setPosition(pos);
131 }
132 for (int i = 0; i < subGridLineCountX; i++) {
133 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(index: i));
134 QVector3D pos = lineNode->position();
135 pos.setY(pos.y() * factor);
136 lineNode->setPosition(pos);
137 }
138 for (int i = 0; i < gridLineCountX; i++) {
139 QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(index: i));
140 QVector3D pos = lineNode->position();
141 pos.setY(pos.y() * factor);
142 lineNode->setPosition(pos);
143 }
144
145 for (int i = 0; i < repeaterX()->count(); i++) {
146 QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i));
147 QVector3D pos = obj->position();
148 pos.setY(pos.y() * factor);
149 obj->setPosition(pos);
150 }
151
152 for (int i = 0; i < repeaterZ()->count(); i++) {
153 QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i));
154 QVector3D pos = obj->position();
155 pos.setY(pos.y() * factor);
156 obj->setPosition(pos);
157 }
158
159 QVector3D pos = titleLabelX()->position();
160 pos.setY(pos.y() * factor);
161 titleLabelX()->setPosition(pos);
162
163 pos = titleLabelZ()->position();
164 pos.setY(pos.y() * factor);
165 titleLabelZ()->setPosition(pos);
166
167 setGridUpdated(false);
168 emit flipHorizontalGridChanged(flip);
169 m_surfaceController->setFlipHorizontalGridChanged(false);
170}
171
172QSurface3DSeries *QQuickGraphsSurface::selectedSeries() const
173{
174 return m_surfaceController->selectedSeries();
175}
176
177void QQuickGraphsSurface::setFlipHorizontalGrid(bool flip)
178{
179 m_surfaceController->setFlipHorizontalGrid(flip);
180}
181
182bool QQuickGraphsSurface::flipHorizontalGrid() const
183{
184 return m_surfaceController->flipHorizontalGrid();
185}
186
187QQmlListProperty<QSurface3DSeries> QQuickGraphsSurface::seriesList()
188{
189 return QQmlListProperty<QSurface3DSeries>(this, this,
190 &QQuickGraphsSurface::appendSeriesFunc,
191 &QQuickGraphsSurface::countSeriesFunc,
192 &QQuickGraphsSurface::atSeriesFunc,
193 &QQuickGraphsSurface::clearSeriesFunc);
194}
195
196void QQuickGraphsSurface::appendSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
197 QSurface3DSeries *series)
198{
199 reinterpret_cast<QQuickGraphsSurface *>(list->data)->addSeries(series);
200}
201
202qsizetype QQuickGraphsSurface::countSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
203{
204 return reinterpret_cast<QQuickGraphsSurface *>(list->data)->m_surfaceController->surfaceSeriesList().size();
205}
206
207QSurface3DSeries *QQuickGraphsSurface::atSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
208 qsizetype index)
209{
210 return reinterpret_cast<QQuickGraphsSurface *>(list->data)->m_surfaceController->surfaceSeriesList().at(i: index);
211}
212
213void QQuickGraphsSurface::clearSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
214{
215 QQuickGraphsSurface *declSurface = reinterpret_cast<QQuickGraphsSurface *>(list->data);
216 QList<QSurface3DSeries *> realList = declSurface->m_surfaceController->surfaceSeriesList();
217 int count = realList.size();
218 for (int i = 0; i < count; i++)
219 declSurface->removeSeries(series: realList.at(i));
220}
221
222void QQuickGraphsSurface::addSeries(QSurface3DSeries *series)
223{
224 m_surfaceController->addSeries(series);
225 if (isReady())
226 addModel(series);
227}
228
229void QQuickGraphsSurface::removeSeries(QSurface3DSeries *series)
230{
231 m_surfaceController->removeSeries(series);
232 series->setParent(this); // Reparent as removing will leave series parentless
233 for (int i = 0; i < m_model.size();) {
234 if (m_model[i]->series == series) {
235 m_model[i]->model->deleteLater();
236 m_model[i]->gridModel->deleteLater();
237 if (sliceView()) {
238 m_model[i]->sliceModel->deleteLater();
239 m_model[i]->sliceGridModel->deleteLater();
240 }
241 m_model.removeAt(i);
242 } else {
243 ++i;
244 }
245 }
246}
247
248void QQuickGraphsSurface::handleAxisXChanged(QAbstract3DAxis *axis)
249{
250 emit axisXChanged(axis: static_cast<QValue3DAxis *>(axis));
251}
252
253void QQuickGraphsSurface::handleAxisYChanged(QAbstract3DAxis *axis)
254{
255 emit axisYChanged(axis: static_cast<QValue3DAxis *>(axis));
256}
257
258void QQuickGraphsSurface::handleAxisZChanged(QAbstract3DAxis *axis)
259{
260 emit axisZChanged(axis: static_cast<QValue3DAxis *>(axis));
261}
262
263void QQuickGraphsSurface::componentComplete()
264{
265 QQuickGraphsItem::componentComplete();
266
267 for (auto series : m_surfaceController->surfaceSeriesList())
268 addModel(series);
269
270 QQuick3DNode *parent = rootNode();
271
272 m_selectionPointer = new QQuick3DModel();
273 m_selectionPointer->setParent(parent);
274 m_selectionPointer->setParentItem(parent);
275 m_selectionPointer->setSource(QUrl(QStringLiteral("#Sphere")));
276 auto pointerMaterial = new QQuick3DPrincipledMaterial();
277 pointerMaterial->setParent(this);
278 pointerMaterial->setBaseColor(m_surfaceController->activeTheme()->singleHighlightColor());
279 QQmlListReference materialRef(m_selectionPointer, "materials");
280 materialRef.append(pointerMaterial);
281 m_instancing = new SurfaceSelectionInstancing();
282 m_instancing->setScale(QVector3D(0.001f, 0.001f, 0.001f));
283 m_selectionPointer->setInstancing(m_instancing);
284}
285
286void QQuickGraphsSurface::synchData()
287{
288 QQuickGraphsItem::synchData();
289
290 if (m_surfaceController->isSelectedPointChanged()) {
291 if (m_surfaceController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionItem))
292 updateSelectedPoint();
293 m_surfaceController->setSelectedPointChanged(false);
294 }
295
296 if (isGridUpdated() || m_surfaceController->isFlipHorizontalGridChanged())
297 handleFlipHorizontalGridChanged(flip: m_surfaceController->flipHorizontalGrid());
298
299 if (m_surfaceController->isSurfaceTextureChanged()) {
300 if (!m_surfaceController->isChangedTexturesEmpty()) {
301 for (auto model : m_model) {
302 if (m_surfaceController->hasSeriesToChangeTexture(series: model->series))
303 updateMaterial(model);
304 }
305 }
306 m_surfaceController->setSurfaceTextureChanged(false);
307 }
308}
309
310void QQuickGraphsSurface::updateGraph()
311{
312 for (auto model : m_model) {
313 bool seriesVisible = model->series->isVisible();
314 if (m_surfaceController->isSeriesVisibilityDirty()) {
315 bool graphVisible = (model->model->visible() || model->gridModel->visible());
316
317 if (seriesVisible != graphVisible && m_surfaceController->isSlicingActive()) {
318 setSliceActivatedChanged(true);
319 }
320 if (!seriesVisible) {
321 model->model->setVisible(seriesVisible);
322 model->gridModel->setVisible(seriesVisible);
323 if (sliceView()) {
324 model->sliceModel->setVisible(seriesVisible);
325 model->sliceGridModel->setVisible(seriesVisible);
326 }
327 continue;
328 }
329 }
330
331 if (model->gridModel->visible() != seriesVisible)
332 model->gridModel->setVisible(seriesVisible);
333 if (model->model->visible() != seriesVisible)
334 model->model->setVisible(seriesVisible);
335 model->gridModel->setVisible(model->series->drawMode().testFlag(flag: QSurface3DSeries::DrawWireframe));
336 if (model->series->drawMode().testFlag(flag: QSurface3DSeries::DrawSurface))
337 model->model->setLocalOpacity(1.f);
338 else
339 model->model->setLocalOpacity(.0f);
340
341 if (sliceView() && sliceView()->isVisible()) {
342 model->sliceGridModel->setVisible(model->series->drawMode().testFlag(flag: QSurface3DSeries::DrawWireframe));
343 if (model->series->drawMode().testFlag(flag: QSurface3DSeries::DrawSurface))
344 model->sliceModel->setLocalOpacity(1.f);
345 else
346 model->sliceModel->setLocalOpacity(.0f);
347 }
348 updateMaterial(model);
349 }
350
351 m_surfaceController->setSeriesVisibilityDirty(false);
352 if (m_surfaceController->isDataDirty() ||
353 m_surfaceController->isSeriesVisualsDirty()) {
354
355 m_surfaceController->clearSelection();
356
357 if (m_surfaceController->hasChangedSeriesList()) {
358 handleChangedSeries();
359 } else {
360 for (auto model : m_model) {
361 bool visible = model->series->isVisible();
362 if (visible)
363 updateModel(model);
364 }
365 }
366
367 if (sliceView() && sliceView()->isVisible())
368 updateSliceGraph();
369
370 m_surfaceController->setDataDirty(false);
371 m_surfaceController->setSeriesVisualsDirty(false);
372 }
373
374 if (m_surfaceController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionItem))
375 updateSelectedPoint();
376}
377
378void QQuickGraphsSurface::handleChangedSeries()
379{
380 auto changedSeries = m_surfaceController->changedSeriesList();
381
382 for (auto series : changedSeries) {
383 for (auto model : m_model) {
384 if (model->series == series) {
385 updateModel(model);
386 }
387 }
388 }
389}
390
391void QQuickGraphsSurface::updateModel(SurfaceModel *model)
392{
393 const QSurfaceDataArray &array = *(model->series->dataProxy())->array();
394
395 if (!array.isEmpty()) {
396 int rowCount = array.size();
397 int columnCount = array.at(i: 0)->size();
398
399 if (rowCount <= 0 || columnCount <= 0)
400 return;
401
402 if (model->rowCount != rowCount) {
403 model->rowCount = rowCount;
404 m_isIndexDirty = true;
405 }
406 if (model->columnCount != columnCount) {
407 model->columnCount = columnCount;
408 m_isIndexDirty = true;
409 }
410
411 QAbstract3DAxis *axisX = m_surfaceController->axisX();
412 QAbstract3DAxis *axisZ = m_surfaceController->axisZ();
413
414 QPoint selC = model->selectedVertex.coord;
415 selC.setX(qMin(a: selC.x(), b: rowCount - 1));
416 selC.setY(qMin(a: selC.y(), b: columnCount -1));
417 QVector3D selP = array.at(i: selC.x())->at(i: selC.y()).position();
418
419 bool pickOutOfRange = false;
420 if (selP.x() < axisX->min() || selP.x() > axisX->max()
421 || selP.z() < axisZ->min() || selP.z() > axisZ->max()) {
422 pickOutOfRange = true;
423 }
424
425 if (m_isIndexDirty || pickOutOfRange) {
426 model->selectedVertex = SurfaceVertex();
427 if (sliceView() && sliceView()->isVisible()) {
428 m_surfaceController->setSlicingActive(false);
429 setSliceActivatedChanged(true);
430 }
431 }
432
433 int totalSize = rowCount * columnCount * 2;
434 float uvX = 1.0f / float(columnCount - 1);
435 float uvY = 1.0f / float(rowCount - 1);
436
437 // checkDirection
438 int dataDimensions = Surface3DController::BothAscending;
439 if (array.at(i: 0)->at(i: 0).x() > array.at(i: 0)->at(i: array.at(i: 0)->size() - 1).x())
440 dataDimensions |= Surface3DController::XDescending;
441 if (static_cast<QValue3DAxis *>(m_surfaceController->axisX())->reversed())
442 dataDimensions ^= Surface3DController::XDescending;
443
444 if (array.at(i: 0)->at(i: 0).z() > array.at(i: array.size() - 1)-> at(i: 0).z())
445 dataDimensions |= Surface3DController::ZDescending;
446 if (static_cast<QValue3DAxis *>(m_surfaceController->axisZ())->reversed())
447 dataDimensions ^= Surface3DController::ZDescending;
448
449 m_surfaceController->setDataDimensions(static_cast<Surface3DController::DataDimensions>(dataDimensions));
450
451 model->vertices.clear();
452 model->vertices.reserve(asize: totalSize);
453
454 bool isFlatShadingEnabled = model->series->isFlatShadingEnabled();
455
456 QVector3D boundsMin = model->boundsMin;
457 QVector3D boundsMax = model->boundsMax;
458
459 QVector<QVector4D> heights;
460 heights.reserve(asize: totalSize);
461
462 QQmlListReference materialRef(model->model, "materials");
463 auto material = materialRef.at(0);
464 QVariant heightInputAsVariant = material->property(name: "height");
465 QQuick3DShaderUtilsTextureInput *heightInput = heightInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
466 QQuick3DTexture *heightMap = heightInput->texture();
467 QQuick3DTextureData *heightMapData = nullptr;
468 if (!heightMap) {
469 heightMap = new QQuick3DTexture();
470 heightMap->setParent(this);
471 heightMap->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
472 heightMap->setVerticalTiling(QQuick3DTexture::ClampToEdge);
473 heightMapData = new QQuick3DTextureData();
474 heightMapData->setSize(QSize(columnCount, rowCount));
475 heightMapData->setFormat(QQuick3DTextureData::RGBA32F);
476 heightMapData->setParent(heightMap);
477 heightMapData->setParentItem(heightMap);
478 } else {
479 heightMapData = heightMap->textureData();
480 if (m_isIndexDirty)
481 heightMapData->setSize(QSize(columnCount , rowCount ));
482 }
483 material->setProperty(name: "xDiff", value: uvX);
484 material->setProperty(name: "yDiff", value: uvY);
485 material->setProperty (name: "flatShading", value: isFlatShadingEnabled);
486
487 QVector2D modelMin = QVector2D(array.at(i: 0)->at(i: 0).x(),array.at(i: 0)->at(i: 0).z());
488 QVector2D modelMax = QVector2D(array.at(i: 0)->at(i: columnCount -1).x(), array.at(i: rowCount -1)->at(i: 0).z());
489
490 bool xDesc = dataDimensions == Surface3DController::XDescending
491 || dataDimensions == Surface3DController::BothDescending;
492 bool zDesc = dataDimensions == Surface3DController::ZDescending
493 || dataDimensions == Surface3DController::BothDescending;
494
495 if (xDesc) {
496 modelMin.setX(array.at(i: 0)->at(i: columnCount -1).x());
497 modelMax.setX(array.at(i: 0)->at(i: 0).x());
498 }
499 if (zDesc) {
500 modelMin.setY(array.at(i: rowCount -1)->at(i: 0).z());
501 modelMax.setY(array.at(i: 0)->at(i: 0).z());
502 }
503
504 auto normalize = [](float x, float min, float max) -> float {
505 return (x - min) / (max - min);
506 };
507
508 float rangeMinX = normalize(axisX->min(), modelMin.x(), modelMax.x());
509 float rangeMinZ = normalize(axisZ->min(), modelMin.y(), modelMax.y());
510 float rangeMaxX = normalize(axisX->max(), modelMin.x(), modelMax.x());
511 float rangeMaxZ = normalize(axisZ->max(), modelMin.y(), modelMax.y());
512
513 QVector2D rangeMin = QVector2D(rangeMinX, rangeMinZ);
514 QVector2D rangeMax = QVector2D(rangeMaxX, rangeMaxZ);
515 material->setProperty(name: "rangeMin", value: rangeMin);
516 material->setProperty(name: "rangeMax", value: rangeMax);
517 material->setProperty(name: "xDesc", value: xDesc);
518 material->setProperty(name: "zDesc", value: zDesc);
519
520 model->rangeMin = rangeMin;
521 model->rangeMax = rangeMax;
522
523 bool isPolar = m_surfaceController->isPolar();
524 for (int i = 0 ; i < rowCount ; i++) {
525 const QSurfaceDataRow &row = *array.at(i);
526 for (int j = 0 ; j < columnCount ; j++) {
527 // getNormalizedVertex
528 QVector3D pos = getNormalizedVertex(data: row.at(i: j), polar: isPolar, flipXZ: false);
529 heights.push_back(t: QVector4D(pos, .0f));
530 SurfaceVertex vertex;
531 vertex.position = pos;
532 vertex.uv = QVector2D(j * uvX, i * uvY);
533 vertex.coord = QPoint(i, j);
534 model->vertices.push_back(t: vertex);
535 if (boundsMin.isNull())
536 boundsMin = pos;
537 else
538 boundsMin = QVector3D(qMin(a: boundsMin.x(), b: pos.x()), qMin(a: boundsMin.y(), b: pos.y()), qMin(a: boundsMin.z(), b: pos.z()));
539 if (boundsMax.isNull())
540 boundsMax = pos;
541 else
542 boundsMax = QVector3D(qMax(a: boundsMax.x(), b: pos.x()), qMax(a: boundsMax.y(), b: pos.y()), qMax(a: boundsMax.z(), b: pos.z()));
543 }
544 }
545 model->boundsMin = boundsMin;
546 model->boundsMax = boundsMax;
547
548 model->indices.clear();
549 QByteArray heightData = QByteArray(reinterpret_cast<char *>(heights.data()), heights.size() * sizeof(QVector4D));
550 heightMapData->setTextureData(heightData);
551 heightMap->setTextureData(heightMapData);
552 heightInput->setTexture(heightMap);
553 model->heightTexture = heightMap;
554
555 if (m_isIndexDirty) {
556 createSmoothIndices(model, x: 0, y: 0, endX: columnCount, endY: rowCount);
557
558 auto geometry = model->model->geometry();
559 geometry->vertexData().clear();
560 QByteArray vertexBuffer(reinterpret_cast<char *>(model->vertices.data()),
561 model->vertices.size() * sizeof(SurfaceVertex));
562 geometry->setVertexData(vertexBuffer);
563 QByteArray indexBuffer(reinterpret_cast<char *>(model->indices.data()),
564 model->indices.size() * sizeof(quint32));
565 geometry->setIndexData(indexBuffer);
566 geometry->setBounds(min: boundsMin, max: boundsMax);
567 geometry->update();
568 }
569
570 updateMaterial(model);
571 if (m_isIndexDirty) {
572 createGridlineIndices(model, x: 0, y: 0, endX: columnCount, endY: rowCount);
573 auto gridGeometry = model->gridModel->geometry();
574 gridGeometry->vertexData().clear();
575 QByteArray vertexBuffer(reinterpret_cast<char *>(model->vertices.data()),
576 model->vertices.size() * sizeof(SurfaceVertex));
577 gridGeometry->setVertexData(vertexBuffer);
578 QByteArray gridIndexBuffer(reinterpret_cast<char *>(model->gridIndices.data()),
579 model->gridIndices.size() * sizeof(quint32));
580 gridGeometry->setIndexData(gridIndexBuffer);
581 gridGeometry->setBounds(min: boundsMin, max: boundsMax);
582 gridGeometry->update();
583 m_isIndexDirty = false;
584 }
585 QQmlListReference gridMaterialRef(model->gridModel, "materials");
586 auto gridMaterial = gridMaterialRef.at(0);
587 QVariant gridHeightInputAsVariant = gridMaterial->property(name: "height");
588 QQuick3DShaderUtilsTextureInput *gridHeightInput = gridHeightInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
589 gridHeightInput->setTexture(heightMap);
590 QColor gridColor = model->series->wireframeColor();
591 gridMaterial->setProperty(name: "gridColor", value: gridColor);
592 gridMaterial->setProperty(name: "rangeMin", value: rangeMin);
593 gridMaterial->setProperty(name: "rangeMax", value: rangeMax);
594 gridMaterial->setProperty(name: "xDesc", value: xDesc);
595 gridMaterial->setProperty(name: "zDesc", value: zDesc);
596 }
597 updateSelectedPoint();
598}
599
600void QQuickGraphsSurface::updateMaterial(SurfaceModel *model)
601{
602 QQmlListReference materialRef(model->model, "materials");
603
604 QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(object: materialRef.at(0));
605
606 if (!material) {
607 material = createQmlCustomMaterial(QStringLiteral(":/materials/SurfaceMaterial"));
608 model->customMaterial = material;
609 }
610
611 bool textured = !(model->series->texture().isNull() && model->series->textureFile().isEmpty());
612
613 if (m_surfaceController->isSeriesVisualsDirty()
614 || !textured) {
615
616 float xDiff = 1.0f / float(model->columnCount - 1);
617 float yDiff = 1.0f / float(model->rowCount - 1);
618 float minY = model->boundsMin.y();
619 float maxY = model->boundsMax.y();
620 float range = maxY - minY;
621
622 switch (model->series->colorStyle()) {
623 case(Q3DTheme::ColorStyleObjectGradient):
624 material->setProperty(name: "colorStyle", value: 0);
625 material->setProperty(name: "gradientMin", value: -(minY / range));
626 material->setProperty(name: "gradientHeight", value: 1.0f / range);
627 break;
628 case(Q3DTheme::ColorStyleRangeGradient):
629 material->setProperty(name: "colorStyle", value: 1);
630 break;
631 case(Q3DTheme::ColorStyleUniform):
632 material->setProperty(name: "colorStyle", value: 2);
633 material->setProperty(name: "uniformColor",value: model->series->baseColor());
634 break;
635 }
636
637 QVariant textureInputAsVariant = material->property(name: "custex");
638 QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
639 auto textureData = static_cast<QuickGraphsTextureData *>(model->texture->textureData());
640 textureData->createGradient(gradient: model->series->baseGradient());
641 textureInput->setTexture(model->texture);
642
643 QVariant heightInputAsVariant = material->property(name: "height");
644 QQuick3DShaderUtilsTextureInput *heightInput = heightInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
645 heightInput->setTexture(model->heightTexture);
646 material->setParent(model->model);
647 material->setParentItem(model->model);
648 material->setCullMode(QQuick3DMaterial::NoCulling);
649 material->setProperty(name: "xDiff", value: xDiff);
650 material->setProperty(name: "yDiff", value: yDiff);
651 material->setProperty(name: "flatShading", value: model->series->isFlatShadingEnabled());
652 material->setProperty(name: "rangeMin", value: model->rangeMin);
653 material->setProperty(name: "rangeMax", value: model->rangeMax);
654 }
655
656 if (textured) {
657 material->setProperty(name: "colorStyle", value: 3);
658 QQuick3DShaderUtilsTextureInput *texInput = material->property(name: "baseColor").value<QQuick3DShaderUtilsTextureInput *>();
659 if (!texInput->texture()) {
660 QQuick3DTexture *texture = new QQuick3DTexture();
661 texture->setParent(material);
662 texture->setParentItem(material);
663 texInput->setTexture(texture);
664 }
665 if (!model->series->textureFile().isEmpty()) {
666 texInput->texture()->setSource(QUrl::fromLocalFile(localfile: model->series->textureFile()));
667 } else if (!model->series->texture().isNull()) {
668 QImage image = model->series->texture();
669 image.convertTo(f: QImage::Format_RGBA32FPx4);
670 auto textureData = static_cast<QuickGraphsTextureData *>(model->texture->textureData());
671 textureData->setFormat(QQuick3DTextureData::RGBA32F);
672 textureData->setSize(image.size());
673 textureData->setTextureData(QByteArray(reinterpret_cast<const char*>(image.bits()),
674 image.sizeInBytes()));
675 texInput->texture()->setTextureData(textureData);
676 } else {
677 texInput->texture()->setSource(QUrl());
678 }
679 }
680 material->update();
681}
682
683QVector3D QQuickGraphsSurface::getNormalizedVertex(const QSurfaceDataItem &data, bool polar, bool flipXZ)
684{
685 Q_UNUSED(flipXZ);
686
687 QValue3DAxis* axisX = static_cast<QValue3DAxis *>(m_surfaceController->axisX());
688 QValue3DAxis* axisY = static_cast<QValue3DAxis *>(m_surfaceController->axisY());
689 QValue3DAxis* axisZ = static_cast<QValue3DAxis *>(m_surfaceController->axisZ());
690
691 float normalizedX = axisX->positionAt(x: data.x());
692 float normalizedY;
693 float normalizedZ = axisZ->positionAt(x: data.z());
694 // TODO : Need to handle, flipXZ
695
696 float scale, translate;
697 if (polar) {
698 float angle = normalizedX * M_PI * 2.0f;
699 float radius = normalizedZ;
700 normalizedX = radius * qSin(v: angle) * 1.0f;
701 normalizedZ = -(radius * qCos(v: angle)) * 1.0f;
702 } else {
703 scale = translate = this->scaleWithBackground().x();
704 normalizedX = normalizedX * scale * 2.0f - translate;
705 scale = translate = this->scaleWithBackground().z();
706 normalizedZ = normalizedZ * -scale * 2.0f + translate;
707 }
708 scale = translate = this->scale().y();
709 normalizedY = axisY->positionAt(x: data.y()) * scale * 2.0f - translate;
710 return QVector3D(normalizedX, normalizedY, normalizedZ);
711}
712
713void QQuickGraphsSurface::updateSliceGraph()
714{
715 QQuickGraphsItem::updateSliceGraph();
716
717 m_surfaceController->setSelectedPointChanged(true);
718
719 if (!sliceView()->isVisible())
720 return;
721
722 auto selectionMode = m_surfaceController->selectionMode();
723 for (auto model : m_model) {
724 bool visible = model->series->isVisible();
725
726 model->sliceModel->setVisible(visible);
727 model->sliceGridModel->setVisible(visible);
728
729 if (!selectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries)
730 && !model->picked) {
731 model->sliceModel->setVisible(false);
732 model->sliceGridModel->setVisible(false);
733 continue;
734 } else {
735 model->sliceGridModel->setVisible(model->series->drawMode().testFlag(flag: QSurface3DSeries::DrawWireframe));
736 if (model->series->drawMode().testFlag(flag: QSurface3DSeries::DrawSurface))
737 model->sliceModel->setLocalOpacity(1.f);
738 else
739 model->sliceModel->setLocalOpacity(.0f);
740 }
741
742 QVector<SurfaceVertex> selectedSeries;
743
744 int rowCount = model->rowCount;
745 int columnCount = model->columnCount;
746
747 auto clamp = [](float x) -> float {
748 x = qMin(a: x, b: 1.0);
749 x = qMax(a: x, b: 0.0);
750 return x;
751 };
752 int rowStart = model->rowCount * clamp(model->rangeMin.y());
753 int columnStart = model->columnCount * clamp(model->rangeMin.x());
754 int rowLimit = model->rowCount * clamp(model->rangeMax.y());
755 int columnLimit = model->columnCount * clamp(model->rangeMax.x());
756
757 int indexCount = 0;
758 if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) {
759 int selectedRow = model->selectedVertex.coord.x() * columnCount;
760 selectedSeries.reserve(asize: columnCount * 2);
761 QVector<SurfaceVertex> list;
762 for (int i = columnStart; i < columnLimit; i++) {
763 SurfaceVertex vertex = model->vertices.at(i: selectedRow + i);
764 vertex.position.setY(vertex.position.y() - .025f);
765 vertex.position.setZ(.0f);
766 selectedSeries.append(t: vertex);
767 vertex.position.setY(vertex.position.y() + .05f);
768 list.append(t: vertex);
769 }
770 selectedSeries.append(l: list);
771 indexCount = columnLimit - columnStart - 1;
772 }
773
774 if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
775 int selectedColumn = model->selectedVertex.coord.y();
776 selectedSeries.reserve(asize: rowCount * 2);
777 QVector<SurfaceVertex> list;
778 for (int i = rowStart; i < rowLimit; i++) {
779 SurfaceVertex vertex = model->vertices.at(i: (i * columnCount) + selectedColumn);
780 vertex.position.setX(-vertex.position.z());
781 vertex.position.setY(vertex.position.y() - .025f);
782 vertex.position.setZ(0);
783 selectedSeries.append(t: vertex);
784 vertex.position.setY(vertex.position.y() + .05f);
785 list.append(t: vertex);
786 }
787 selectedSeries.append(l: list);
788 indexCount = rowLimit - rowStart - 1;
789
790 QQmlListReference materialRef(model->sliceModel, "materials");
791 auto material = materialRef.at(0);
792 material->setProperty(name: "isColumn", value: true);
793 }
794
795 QVector<quint32> indices;
796 indices.reserve(asize: indexCount * 6);
797 for (int i = 0; i < indexCount; i++) {
798 indices.push_back(t: i + 1);
799 indices.push_back(t: i + indexCount + 1);
800 indices.push_back(t: i);
801 indices.push_back(t: i + indexCount + 2);
802 indices.push_back(t: i + indexCount + 1);
803 indices.push_back(t: i + 1);
804 }
805
806 auto geometry = model->sliceModel->geometry();
807 geometry->vertexData().clear();
808 geometry->indexData().clear();
809 QByteArray vertexBuffer(reinterpret_cast<char *>(selectedSeries.data()),
810 selectedSeries.size() * sizeof(SurfaceVertex));
811 geometry->setVertexData(vertexBuffer);
812 QByteArray indexBuffer(reinterpret_cast<char *>(indices.data()),
813 indices.size() * sizeof(quint32));
814 geometry->setIndexData(indexBuffer);
815 geometry->update();
816
817 geometry = model->sliceGridModel->geometry();
818 geometry->vertexData().clear();
819 geometry->indexData().clear();
820 geometry->setVertexData(vertexBuffer);
821
822 QVector<quint32> gridIndices;
823 gridIndices.reserve(asize: indexCount * 4);
824 for (int i = 0; i < indexCount; i++) {
825 gridIndices.push_back(t: i);
826 gridIndices.push_back(t: i + indexCount + 1);
827
828 gridIndices.push_back(t: i);
829 gridIndices.push_back(t: i + 1);
830 }
831 geometry->indexData().clear();
832 QByteArray gridIndexBuffer(reinterpret_cast<char *>(gridIndices.data()),
833 gridIndices.size() * sizeof(quint32));
834 geometry->setIndexData(gridIndexBuffer);
835 geometry->update();
836
837 QQmlListReference gridMaterialRef(model->sliceGridModel, "materials");
838 auto gridMaterial = static_cast<QQuick3DPrincipledMaterial *>(gridMaterialRef.at(0));
839 QColor gridColor = model->series->wireframeColor();
840 gridMaterial->setBaseColor(gridColor);
841
842 updateSelectedPoint();
843 }
844}
845
846
847void QQuickGraphsSurface::createSmoothIndices(SurfaceModel *model, int x, int y, int endX, int endY)
848{
849 model->indices.clear();
850 int columnCount = model->columnCount;
851 int rowCount = model->rowCount;
852 Surface3DController::DataDimensions dataDimensions = m_surfaceController->dataDimensions();
853
854 if (endX >= columnCount)
855 endX = columnCount - 1;
856 if (endY >= rowCount)
857 endY = rowCount - 1;
858 if (x > endX)
859 x = endX - 1;
860 if (y > endY)
861 y = endY - 1;
862
863 int indexCount = 6 * (endX - x) * (endY - y);
864
865 QVector<quint32> *indices = &model->indices;
866
867 indices->clear();
868 indices->resize(size: indexCount);
869
870 int rowEnd = endY * columnCount;
871 for (int row = y * columnCount ; row < rowEnd ; row += columnCount) {
872 for (int j = x ; j < endX ; j++) {
873 if (dataDimensions == Surface3DController::BothAscending
874 || dataDimensions == Surface3DController::BothDescending) {
875 indices->push_back(t: row + j + 1);
876 indices->push_back(t: row + columnCount + j);
877 indices->push_back(t: row + j);
878
879 indices->push_back(t: row + columnCount + j + 1);
880 indices->push_back(t: row + columnCount + j);
881 indices->push_back(t: row + j + 1);
882 } else if (dataDimensions == Surface3DController::XDescending) {
883 indices->push_back(t: row + columnCount + j);
884 indices->push_back(t: row + columnCount + j + 1);
885 indices->push_back(t: row + j);
886
887 indices->push_back(t: row + j);
888 indices->push_back(t: row + columnCount + j + 1);
889 indices->push_back(t: row + j + 1);
890 } else {
891 indices->push_back(t: row + columnCount + j);
892 indices->push_back(t: row + columnCount + j + 1);
893 indices->push_back(t: row + j + 1);
894
895 indices->push_back(t: row + j);
896 indices->push_back(t: row + columnCount + j);
897 indices->push_back(t: row + j + 1);
898 }
899 }
900 }
901}
902void QQuickGraphsSurface::createGridlineIndices(SurfaceModel *model, int x, int y, int endX, int endY)
903{
904 int columnCount = model->columnCount;
905 int rowCount = model->rowCount;
906
907 if (endX >= columnCount)
908 endX = columnCount - 1;
909 if (endY >= rowCount)
910 endY = rowCount - 1;
911 if (x > endX)
912 x = endX - 1;
913 if (y > endY)
914 y = endY - 1;
915
916 int nColumns = endX - x + 1;
917 int nRows = endY - y + 1;
918
919 int gridIndexCount = 2 * nColumns * (nRows - 1) + 2 * nRows * (nColumns - 1);
920 model->gridIndices.clear();
921 model->gridIndices.resize(size: gridIndexCount);
922
923 for (int i = y, row = columnCount * y ; i <= endY ; i++, row += columnCount) {
924 for (int j = x ; j < endX ; j++) {
925 model->gridIndices.push_back(t: row + j);
926 model->gridIndices.push_back(t: row + j + 1);
927 }
928 }
929 for (int i = y, row = columnCount * y ; i < endY ; i++, row += columnCount) {
930 for (int j = x ; j <= endX ; j++) {
931 model->gridIndices.push_back(t: row + j);
932 model->gridIndices.push_back(t: row + j + columnCount);
933 }
934 }
935}
936bool QQuickGraphsSurface::handleMousePressedEvent(QMouseEvent *event)
937{
938 if (!QQuickGraphsItem::handleMousePressedEvent(event))
939 return true;
940
941 if (Qt::LeftButton == event->button())
942 doPicking(position: event->pos());
943
944 return true;
945}
946
947bool QQuickGraphsSurface::handleTouchEvent(QTouchEvent *event)
948{
949 if (!QQuickGraphsItem::handleTouchEvent(event))
950 return true;
951
952 if (scene()->selectionQueryPosition() != scene()->invalidSelectionPoint()
953 && !event->isUpdateEvent()) {
954 doPicking(position: event->point(i: 0).position());
955 scene()->setSelectionQueryPosition(scene()->invalidSelectionPoint());
956 }
957
958 return true;
959}
960
961bool QQuickGraphsSurface::doPicking(const QPointF &position)
962{
963 if (!QQuickGraphsItem::doPicking(point: position))
964 return false;
965
966 auto pickResult = pickAll(x: position.x(), y: position.y());
967 QVector3D pickedPos(0.0f, 0.0f, 0.0f);
968 QQuick3DModel *pickedModel = nullptr;
969
970 auto selectionMode = m_surfaceController->selectionMode();
971 if (!selectionMode.testFlag(flag: QAbstract3DGraph::SelectionNone)) {
972 if (!sliceView() && selectionMode.testFlag(flag: QAbstract3DGraph::SelectionSlice))
973 createSliceView();
974
975 for (auto picked : pickResult) {
976 if (picked.objectHit()
977 && picked.objectHit()->objectName().contains(QStringLiteral("SurfaceModel"))) {
978 pickedPos = picked.position();
979 pickedModel = picked.objectHit();
980 if (!pickedPos.isNull())
981 break;
982 }
983 }
984
985 if (!pickedPos.isNull()) {
986 float min = -1.0f;
987
988 for (auto model : m_model) {
989 if (!model->series->isVisible())
990 continue;
991
992 model->picked = (model->model == pickedModel);
993
994 SurfaceVertex selectedVertex;
995 for (auto vertex : model->vertices) {
996 QVector3D pos = vertex.position;
997 float dist = pickedPos.distanceToPoint(point: pos);
998 if (selectedVertex.position.isNull() || dist < min) {
999 min = dist;
1000 selectedVertex = vertex;
1001 }
1002 }
1003 model->selectedVertex = selectedVertex;
1004 if (!selectedVertex.position.isNull()
1005 && model->picked) {
1006 model->series->setSelectedPoint(selectedVertex.coord);
1007 m_surfaceController->setSlicingActive(false);
1008 if (isSliceEnabled())
1009 setSliceActivatedChanged(true);
1010 }
1011 }
1012 }
1013 }
1014 return true;
1015}
1016
1017void QQuickGraphsSurface::updateSelectedPoint()
1018{
1019 bool labelVisible = false;
1020 m_instancing->resetPositions();
1021 if (sliceView() && sliceView()->isVisible())
1022 m_sliceInstancing->resetPositions();
1023 for (auto model : m_model) {
1024 if ((!m_surfaceController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionMultiSeries) &&
1025 !model->picked)|| model->selectedVertex.position.isNull())
1026 continue;
1027 QPoint selectedCoord = model->selectedVertex.coord;
1028 int index = selectedCoord.x() * model->columnCount + selectedCoord.y();
1029 SurfaceVertex selectedVertex = model->vertices.at(i: index);
1030 if (model->series->isVisible() &&
1031 !selectedVertex.position.isNull() &&
1032 m_surfaceController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionItem)) {
1033 m_instancing->addPosition(position: selectedVertex.position);
1034 QVector3D slicePosition = selectedVertex.position;
1035 if (sliceView() && sliceView()->isVisible()) {
1036 if (m_surfaceController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn))
1037 slicePosition.setX(-slicePosition.z());
1038 slicePosition.setZ(.0f);
1039 m_sliceInstancing->addPosition(position: slicePosition);
1040 }
1041 if (model->picked) {
1042 QVector3D labelPosition = selectedVertex.position;
1043 QString label = model->series->itemLabel();
1044 m_surfaceController->setSelectedPoint(position: selectedVertex.coord, series: model->series, enterSlice: false);
1045
1046 updateItemLabel(position: labelPosition);
1047 itemLabel()->setProperty(name: "labelText", value: label);
1048 labelVisible = true;
1049
1050 if (sliceView() && sliceView()->isVisible()) {
1051 QFontMetrics fm(m_surfaceController->activeTheme()->font());
1052 float textPadding = 12.0f;
1053 float labelHeight = fm.height() + textPadding;
1054 float labelWidth = fm.horizontalAdvance(label) + textPadding;
1055 QVector3D scale = sliceItemLabel()->scale();
1056 scale.setX(scale.y() * labelWidth / labelHeight);
1057 sliceItemLabel()->setProperty(name: "labelWidth", value: labelWidth);
1058 sliceItemLabel()->setProperty(name: "labelHeight", value: labelHeight);
1059 sliceItemLabel()->setScale(scale);
1060 labelPosition = slicePosition;
1061 labelPosition.setZ(.1f);
1062 labelPosition.setY(labelPosition.y() + .05f);
1063 sliceItemLabel()->setPosition(labelPosition);
1064 sliceItemLabel()->setProperty(name: "labelText", value: label);
1065 }
1066 }
1067 }
1068 }
1069 itemLabel()->setVisible(labelVisible);
1070 if (sliceView() && sliceView()->isVisible())
1071 sliceItemLabel()->setVisible(labelVisible);
1072}
1073
1074void QQuickGraphsSurface::addModel(QSurface3DSeries *series)
1075{
1076 auto parent = graphNode();
1077 bool visible = series->isVisible();
1078
1079 auto model = new QQuick3DModel();
1080 model->setParent(parent);
1081 model->setParentItem(parent);
1082 model->setObjectName(QStringLiteral("SurfaceModel"));
1083 model->setVisible(visible);
1084 if (m_surfaceController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionNone))
1085 model->setPickable(false);
1086 else
1087 model->setPickable(true);
1088
1089 auto geometry = new QQuick3DGeometry();
1090 geometry->setParent(model);
1091 geometry->setStride(sizeof(SurfaceVertex));
1092 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
1093 geometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic,
1094 offset: 0,
1095 componentType: QQuick3DGeometry::Attribute::F32Type);
1096 geometry->addAttribute(semantic: QQuick3DGeometry::Attribute::TexCoord0Semantic,
1097 offset: sizeof(QVector3D),
1098 componentType: QQuick3DGeometry::Attribute::F32Type);
1099 geometry->addAttribute(semantic: QQuick3DGeometry::Attribute::IndexSemantic,
1100 offset: 0,
1101 componentType: QQuick3DGeometry::Attribute::U32Type);
1102 model->setGeometry(geometry);
1103
1104
1105 QQuick3DTexture *texture = new QQuick3DTexture();
1106 texture->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
1107 texture->setVerticalTiling(QQuick3DTexture::ClampToEdge);
1108 QuickGraphsTextureData *textureData = new QuickGraphsTextureData();
1109 textureData->setParent(texture);
1110 textureData->setParentItem(texture);
1111 texture->setTextureData(textureData);
1112
1113 QQmlListReference materialRef(model, "materials");
1114
1115 QQuick3DCustomMaterial *customMaterial = createQmlCustomMaterial(QStringLiteral(":/materials/SurfaceMaterial"));
1116
1117 customMaterial->setParent(model);
1118 customMaterial->setParentItem(model);
1119 customMaterial->setCullMode(QQuick3DMaterial::NoCulling);
1120 QVariant textureInputAsVariant = customMaterial->property(name: "custex");
1121 QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
1122 textureInput->setTexture(texture);
1123
1124 texture->setParent(customMaterial);
1125
1126 materialRef.append(customMaterial);
1127
1128 auto gridModel = new QQuick3DModel();
1129 gridModel->setParent(parent);
1130 gridModel->setParentItem(parent);
1131 gridModel->setObjectName(QStringLiteral("SurfaceModel"));
1132 gridModel->setVisible(visible);
1133 gridModel->setDepthBias(1.0f);
1134 auto gridGeometry = new QQuick3DGeometry();
1135 gridGeometry->setParent(this);
1136 gridGeometry->setStride(sizeof(SurfaceVertex));
1137 gridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
1138 gridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic,
1139 offset: 0,
1140 componentType: QQuick3DGeometry::Attribute::F32Type);
1141 gridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::TexCoord0Semantic,
1142 offset: sizeof(QVector3D),
1143 componentType: QQuick3DGeometry::Attribute::F32Type);
1144 gridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::IndexSemantic,
1145 offset: 0,
1146 componentType: QQuick3DGeometry::Attribute::U32Type);
1147 gridModel->setGeometry(gridGeometry);
1148 QQmlListReference gridMaterialRef(gridModel, "materials");
1149 auto gridMaterial = createQmlCustomMaterial(QStringLiteral(":/materials/GridSurfaceMaterial"));
1150 gridMaterial->setParent(gridModel);
1151 gridMaterial->setParentItem(gridModel);
1152 gridMaterialRef.append(gridMaterial);
1153
1154 SurfaceModel *surfaceModel = new SurfaceModel();
1155 surfaceModel->model = model;
1156 surfaceModel->gridModel = gridModel;
1157 surfaceModel->series = series;
1158 surfaceModel->texture = texture;
1159 surfaceModel->customMaterial = customMaterial;
1160
1161 m_model.push_back(t: surfaceModel);
1162
1163 connect(sender: series,
1164 signal: &QSurface3DSeries::flatShadingEnabledChanged,
1165 context: this,
1166 slot: &QQuickGraphsSurface::handleFlatShadingEnabledChanged);
1167 connect(sender: series,
1168 signal: &QSurface3DSeries::wireframeColorChanged,
1169 context: this,
1170 slot: &QQuickGraphsSurface::handleWireframeColorChanged);
1171
1172 if (sliceView())
1173 addSliceModel(model: surfaceModel);
1174}
1175
1176void QQuickGraphsSurface::createSliceView()
1177{
1178 QQuickGraphsItem::createSliceView();
1179
1180 for (auto surfaceModel : m_model)
1181 addSliceModel(model: surfaceModel);
1182
1183 QQuick3DViewport *sliceParent = sliceView();
1184
1185 m_sliceSelectionPointer = new QQuick3DModel();
1186 m_sliceSelectionPointer->setParent(sliceParent->scene());
1187 m_sliceSelectionPointer->setParentItem(sliceParent->scene());
1188 m_sliceSelectionPointer->setSource(QUrl(QStringLiteral("#Sphere")));
1189 QQuick3DPrincipledMaterial *pointerMaterial = new QQuick3DPrincipledMaterial();
1190 pointerMaterial->setParent(m_sliceSelectionPointer);
1191 pointerMaterial->setBaseColor(m_surfaceController->activeTheme()->singleHighlightColor());
1192 QQmlListReference sliceMaterialRef(m_sliceSelectionPointer, "materials");
1193 sliceMaterialRef.append(pointerMaterial);
1194 m_sliceInstancing = new SurfaceSelectionInstancing();
1195 m_sliceInstancing->setScale(QVector3D(0.001f, 0.001f, 0.001f));
1196 m_sliceSelectionPointer->setInstancing(m_sliceInstancing);
1197 m_sliceInstancing->setColor(m_surfaceController->activeTheme()->singleHighlightColor());
1198}
1199
1200void QQuickGraphsSurface::addSliceModel(SurfaceModel *model)
1201{
1202 QQuick3DViewport *sliceParent = sliceView();
1203
1204 auto surfaceModel = new QQuick3DModel();
1205 surfaceModel->setParent(sliceParent->scene());
1206 surfaceModel->setParentItem(sliceParent->scene());
1207 surfaceModel->setVisible(model->series->isVisible());
1208
1209 auto geometry = new QQuick3DGeometry();
1210 geometry->setParent(surfaceModel);
1211 geometry->setParentItem(surfaceModel);
1212 geometry->setStride(sizeof(SurfaceVertex));
1213 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
1214 geometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic,
1215 offset: 0,
1216 componentType: QQuick3DGeometry::Attribute::F32Type);
1217 geometry->addAttribute(semantic: QQuick3DGeometry::Attribute::TexCoord0Semantic,
1218 offset: sizeof(QVector3D),
1219 componentType: QQuick3DGeometry::Attribute::F32Type);
1220 geometry->addAttribute(semantic: QQuick3DGeometry::Attribute::IndexSemantic,
1221 offset: 0,
1222 componentType: QQuick3DGeometry::Attribute::U32Type);
1223 surfaceModel->setGeometry(geometry);
1224
1225 QQmlListReference materialRef(surfaceModel, "materials");
1226 auto material = createQmlCustomMaterial(QStringLiteral(":/materials/SurfaceSliceMaterial"));
1227 material->setCullMode(QQuick3DMaterial::NoCulling);
1228 QVariant textureInputAsVariant = material->property(name: "custex");
1229 QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
1230 QQuick3DTexture *texture = model->texture;
1231 textureInput->setTexture(texture);
1232 materialRef.append(material);
1233
1234 model->sliceModel = surfaceModel;
1235
1236 QQuick3DModel *gridModel = new QQuick3DModel();
1237 gridModel->setParent(sliceParent->scene());
1238 gridModel->setParentItem(sliceParent->scene());
1239 gridModel->setVisible(model->series->isVisible());
1240 gridModel->setDepthBias(1.0f);
1241 QQuick3DGeometry *gridGeometry = new QQuick3DGeometry();
1242 gridGeometry->setParent(gridModel);
1243 gridGeometry->setStride(sizeof(SurfaceVertex));
1244 gridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
1245 gridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic,
1246 offset: 0,
1247 componentType: QQuick3DGeometry::Attribute::F32Type);
1248 gridGeometry->addAttribute(semantic: QQuick3DGeometry::Attribute::IndexSemantic,
1249 offset: 0,
1250 componentType: QQuick3DGeometry::Attribute::U32Type);
1251 gridModel->setGeometry(gridGeometry);
1252 QQmlListReference gridMaterialRef(gridModel, "materials");
1253 QQuick3DPrincipledMaterial *gridMaterial = new QQuick3DPrincipledMaterial();
1254 gridMaterial->setParent(gridModel);
1255 gridMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
1256 gridMaterial->setParent(gridModel);
1257 gridMaterialRef.append(gridMaterial);
1258
1259 model->sliceGridModel = gridModel;
1260}
1261
1262void QQuickGraphsSurface::updateSingleHighlightColor()
1263{
1264 m_instancing->setColor(m_surfaceController->activeTheme()->singleHighlightColor());
1265 if (sliceView())
1266 m_sliceInstancing->setColor(m_surfaceController->activeTheme()->singleHighlightColor());
1267}
1268
1269void QQuickGraphsSurface::updateLightStrength()
1270{
1271 for (auto model : m_model) {
1272 QQmlListReference materialRef(model->model, "materials");
1273 QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(object: materialRef.at(0));
1274 material->setProperty(name: "specularBrightness",
1275 value: m_surfaceController->activeTheme()->lightStrength() * 0.05);
1276 }
1277}
1278
1279void QQuickGraphsSurface::handleThemeTypeChange()
1280{
1281 for (auto model : m_model)
1282 updateMaterial(model);
1283}
1284
1285QT_END_NAMESPACE
1286

source code of qtgraphs/src/graphs/qml/qquickgraphssurface.cpp