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 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | QQuickGraphsSurface::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 | |
37 | QQuickGraphsSurface::~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 | |
48 | QValue3DAxis *QQuickGraphsSurface::axisX() const |
49 | { |
50 | return static_cast<QValue3DAxis *>(m_surfaceController->axisX()); |
51 | } |
52 | |
53 | void QQuickGraphsSurface::setAxisX(QValue3DAxis *axis) |
54 | { |
55 | m_surfaceController->setAxisX(axis); |
56 | } |
57 | |
58 | QValue3DAxis *QQuickGraphsSurface::axisY() const |
59 | { |
60 | return static_cast<QValue3DAxis *>(m_surfaceController->axisY()); |
61 | } |
62 | |
63 | void QQuickGraphsSurface::setAxisY(QValue3DAxis *axis) |
64 | { |
65 | m_surfaceController->setAxisY(axis); |
66 | } |
67 | |
68 | QValue3DAxis *QQuickGraphsSurface::axisZ() const |
69 | { |
70 | return static_cast<QValue3DAxis *>(m_surfaceController->axisZ()); |
71 | } |
72 | |
73 | void QQuickGraphsSurface::setAxisZ(QValue3DAxis *axis) |
74 | { |
75 | m_surfaceController->setAxisZ(axis); |
76 | } |
77 | |
78 | void 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 | |
89 | void 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 | |
105 | void 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 | |
172 | QSurface3DSeries *QQuickGraphsSurface::selectedSeries() const |
173 | { |
174 | return m_surfaceController->selectedSeries(); |
175 | } |
176 | |
177 | void QQuickGraphsSurface::setFlipHorizontalGrid(bool flip) |
178 | { |
179 | m_surfaceController->setFlipHorizontalGrid(flip); |
180 | } |
181 | |
182 | bool QQuickGraphsSurface::flipHorizontalGrid() const |
183 | { |
184 | return m_surfaceController->flipHorizontalGrid(); |
185 | } |
186 | |
187 | QQmlListProperty<QSurface3DSeries> QQuickGraphsSurface::seriesList() |
188 | { |
189 | return QQmlListProperty<QSurface3DSeries>(this, this, |
190 | &QQuickGraphsSurface::appendSeriesFunc, |
191 | &QQuickGraphsSurface::countSeriesFunc, |
192 | &QQuickGraphsSurface::atSeriesFunc, |
193 | &QQuickGraphsSurface::clearSeriesFunc); |
194 | } |
195 | |
196 | void QQuickGraphsSurface::appendSeriesFunc(QQmlListProperty<QSurface3DSeries> *list, |
197 | QSurface3DSeries *series) |
198 | { |
199 | reinterpret_cast<QQuickGraphsSurface *>(list->data)->addSeries(series); |
200 | } |
201 | |
202 | qsizetype QQuickGraphsSurface::countSeriesFunc(QQmlListProperty<QSurface3DSeries> *list) |
203 | { |
204 | return reinterpret_cast<QQuickGraphsSurface *>(list->data)->m_surfaceController->surfaceSeriesList().size(); |
205 | } |
206 | |
207 | QSurface3DSeries *QQuickGraphsSurface::atSeriesFunc(QQmlListProperty<QSurface3DSeries> *list, |
208 | qsizetype index) |
209 | { |
210 | return reinterpret_cast<QQuickGraphsSurface *>(list->data)->m_surfaceController->surfaceSeriesList().at(i: index); |
211 | } |
212 | |
213 | void 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 | |
222 | void QQuickGraphsSurface::addSeries(QSurface3DSeries *series) |
223 | { |
224 | m_surfaceController->addSeries(series); |
225 | if (isReady()) |
226 | addModel(series); |
227 | } |
228 | |
229 | void 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 | |
248 | void QQuickGraphsSurface::handleAxisXChanged(QAbstract3DAxis *axis) |
249 | { |
250 | emit axisXChanged(axis: static_cast<QValue3DAxis *>(axis)); |
251 | } |
252 | |
253 | void QQuickGraphsSurface::handleAxisYChanged(QAbstract3DAxis *axis) |
254 | { |
255 | emit axisYChanged(axis: static_cast<QValue3DAxis *>(axis)); |
256 | } |
257 | |
258 | void QQuickGraphsSurface::handleAxisZChanged(QAbstract3DAxis *axis) |
259 | { |
260 | emit axisZChanged(axis: static_cast<QValue3DAxis *>(axis)); |
261 | } |
262 | |
263 | void 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 | |
286 | void 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 | |
310 | void 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 | |
378 | void 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 | |
391 | void 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 | |
600 | void 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 | |
683 | QVector3D 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 | |
713 | void 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 | |
847 | void 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 | } |
902 | void 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 | } |
936 | bool 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 | |
947 | bool 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 | |
961 | bool 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 | |
1017 | void 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 | |
1074 | void 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 | |
1176 | void 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 | |
1200 | void 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 | |
1262 | void QQuickGraphsSurface::updateSingleHighlightColor() |
1263 | { |
1264 | m_instancing->setColor(m_surfaceController->activeTheme()->singleHighlightColor()); |
1265 | if (sliceView()) |
1266 | m_sliceInstancing->setColor(m_surfaceController->activeTheme()->singleHighlightColor()); |
1267 | } |
1268 | |
1269 | void 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 | |
1279 | void QQuickGraphsSurface::handleThemeTypeChange() |
1280 | { |
1281 | for (auto model : m_model) |
1282 | updateMaterial(model); |
1283 | } |
1284 | |
1285 | QT_END_NAMESPACE |
1286 | |