1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qabstract3dinputhandler_p.h" |
5 | #include "qquickgraphsitem_p.h" |
6 | |
7 | #include "abstract3dcontroller_p.h" |
8 | #include "declarativetheme_p.h" |
9 | #include "declarativescene_p.h" |
10 | #include "q3dscene_p.h" |
11 | #include "qcustom3dlabel.h" |
12 | #include "qcustom3ditem.h" |
13 | #include "qcustom3ditem_p.h" |
14 | #include "qtouch3dinputhandler.h" |
15 | #include "qvalue3daxis.h" |
16 | #include "qcategory3daxis.h" |
17 | #include "utils_p.h" |
18 | #include "qcustom3dvolume.h" |
19 | |
20 | #include <QtGui/QGuiApplication> |
21 | |
22 | #include <QtQuick3D/private/qquick3dperspectivecamera_p.h> |
23 | #include <QtQuick3D/private/qquick3dorthographiccamera_p.h> |
24 | #include <QtQuick3D/private/qquick3dcustommaterial_p.h> |
25 | #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h> |
26 | #include <QtQuick3D/private/qquick3ddirectionallight_p.h> |
27 | #include <QtQuick3D/private/qquick3drepeater_p.h> |
28 | #include <QtQuick3D/private/qquick3dloader_p.h> |
29 | #include <QtQuick/private/qquickitem_p.h> |
30 | |
31 | #if defined(Q_OS_IOS) |
32 | #include <QtCore/QTimer> |
33 | #endif |
34 | |
35 | #if defined(Q_OS_MACOS) |
36 | #include <qpa/qplatformnativeinterface.h> |
37 | #endif |
38 | |
39 | QT_BEGIN_NAMESPACE |
40 | |
41 | QQuickGraphsItem::QQuickGraphsItem(QQuickItem *parent) : |
42 | QQuick3DViewport(parent), |
43 | m_controller(nullptr) |
44 | { |
45 | m_nodeMutex = QSharedPointer<QMutex>::create(); |
46 | |
47 | QQuick3DSceneEnvironment *scene = environment(); |
48 | scene->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes::Color); |
49 | scene->setClearColor(Qt::transparent); |
50 | |
51 | auto sceneManager = QQuick3DObjectPrivate::get(item: rootNode())->sceneManager; |
52 | connect(sender: sceneManager.data(), signal: &QQuick3DSceneManager::windowChanged, context: this, slot: &QQuickGraphsItem::handleWindowChanged); |
53 | // Set contents to false in case we are in qml designer to make component look nice |
54 | m_runningInDesigner = QGuiApplication::applicationDisplayName() == QLatin1String("Qml2Puppet" ); |
55 | setFlag(flag: ItemHasContents/*, !m_runningInDesigner*/); // Is this relevant anymore? |
56 | |
57 | // Set 4x MSAA by default |
58 | setRenderingMode(QAbstract3DGraph::RenderIndirect); |
59 | setMsaaSamples(4); |
60 | |
61 | // Accept touchevents |
62 | setAcceptTouchEvents(true); |
63 | } |
64 | |
65 | QQuickGraphsItem::~QQuickGraphsItem() |
66 | { |
67 | disconnect(sender: this, signal: 0, receiver: this, member: 0); |
68 | checkWindowList(window: 0); |
69 | |
70 | m_repeaterX->model().clear(); |
71 | m_repeaterY->model().clear(); |
72 | m_repeaterZ->model().clear(); |
73 | m_repeaterX->deleteLater(); |
74 | m_repeaterY->deleteLater(); |
75 | m_repeaterZ->deleteLater(); |
76 | |
77 | m_segmentLineRepeaterX->model().clear(); |
78 | m_segmentLineRepeaterY->model().clear(); |
79 | m_segmentLineRepeaterZ->model().clear(); |
80 | m_segmentLineRepeaterX->deleteLater(); |
81 | m_segmentLineRepeaterY->deleteLater(); |
82 | m_segmentLineRepeaterZ->deleteLater(); |
83 | |
84 | m_subsegmentLineRepeaterX->model().clear(); |
85 | m_subsegmentLineRepeaterY->model().clear(); |
86 | m_subsegmentLineRepeaterZ->model().clear(); |
87 | m_subsegmentLineRepeaterX->deleteLater(); |
88 | m_subsegmentLineRepeaterY->deleteLater(); |
89 | m_subsegmentLineRepeaterZ->deleteLater(); |
90 | |
91 | if (m_sliceVerticalGridRepeater) { |
92 | m_sliceVerticalGridRepeater->model().clear(); |
93 | m_sliceHorizontalGridRepeater->model().clear(); |
94 | m_sliceHorizontalLabelRepeater->model().clear(); |
95 | m_sliceVerticalLabelRepeater->model().clear(); |
96 | m_sliceVerticalGridRepeater->deleteLater(); |
97 | m_sliceHorizontalGridRepeater->deleteLater(); |
98 | m_sliceHorizontalLabelRepeater->deleteLater(); |
99 | m_sliceVerticalLabelRepeater->deleteLater(); |
100 | } |
101 | |
102 | // Make sure not deleting locked mutex |
103 | QMutexLocker locker(&m_mutex); |
104 | locker.unlock(); |
105 | |
106 | m_nodeMutex.clear(); |
107 | } |
108 | |
109 | void QQuickGraphsItem::setRenderingMode(QAbstract3DGraph::RenderingMode mode) |
110 | { |
111 | if (mode == m_renderMode) |
112 | return; |
113 | |
114 | QAbstract3DGraph::RenderingMode previousMode = m_renderMode; |
115 | |
116 | m_renderMode = mode; |
117 | |
118 | m_initialisedSize = QSize(0, 0); |
119 | setFlag(flag: ItemHasContents/*, !m_runningInDesigner*/); |
120 | |
121 | // TODO - Need to check if the mode is set properly |
122 | switch (mode) { |
123 | case QAbstract3DGraph::RenderDirectToBackground: |
124 | update(); |
125 | setRenderMode(QQuick3DViewport::Underlay); |
126 | if (previousMode == QAbstract3DGraph::RenderIndirect) { |
127 | checkWindowList(window: window()); |
128 | setAntialiasing(m_windowSamples > 0); |
129 | if (m_windowSamples != m_samples) |
130 | emit msaaSamplesChanged(samples: m_windowSamples); |
131 | } |
132 | break; |
133 | case QAbstract3DGraph::RenderIndirect: |
134 | update(); |
135 | setRenderMode(QQuick3DViewport::Offscreen); |
136 | break; |
137 | } |
138 | |
139 | updateWindowParameters(); |
140 | |
141 | emit renderingModeChanged(mode); |
142 | } |
143 | |
144 | QAbstract3DGraph::RenderingMode QQuickGraphsItem::renderingMode() const |
145 | { |
146 | return m_renderMode; |
147 | } |
148 | |
149 | void QQuickGraphsItem::keyPressEvent(QKeyEvent *ev) |
150 | { |
151 | ev->ignore(); |
152 | setFlag(flag: ItemHasContents); |
153 | update(); |
154 | } |
155 | |
156 | bool QQuickGraphsItem::handleMousePressedEvent(QMouseEvent *event) |
157 | { |
158 | if (Qt::LeftButton == event->button()) { |
159 | if (scene()->isSlicingActive()) { |
160 | m_sliceActivatedChanged = true; |
161 | return false; |
162 | } |
163 | checkSliceEnabled(); |
164 | } |
165 | return true; |
166 | } |
167 | |
168 | bool QQuickGraphsItem::handleTouchEvent(QTouchEvent *event) |
169 | { |
170 | if (!event->isUpdateEvent()) { |
171 | if (scene()->isSlicingActive()) { |
172 | m_sliceActivatedChanged = true; |
173 | return false; |
174 | } |
175 | checkSliceEnabled(); |
176 | } |
177 | return true; |
178 | } |
179 | |
180 | void QQuickGraphsItem::checkSliceEnabled() |
181 | { |
182 | auto selectionMode = m_controller->selectionMode(); |
183 | if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionSlice) |
184 | && (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn) |
185 | != selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))) { |
186 | m_sliceEnabled = true; |
187 | } else { |
188 | m_sliceEnabled = false; |
189 | } |
190 | } |
191 | |
192 | void QQuickGraphsItem::handleThemeTypeChange() |
193 | { |
194 | } |
195 | |
196 | void QQuickGraphsItem::handleFpsChanged() |
197 | { |
198 | int fps = renderStats()->fps(); |
199 | if (m_currentFps != fps) { |
200 | m_currentFps = fps; |
201 | emit currentFpsChanged(fps); |
202 | } |
203 | } |
204 | |
205 | void QQuickGraphsItem::handleParentWidthChange() |
206 | { |
207 | if (m_sliceView->isVisible()) |
208 | setWidth(parentItem()->width() * .2f); |
209 | else |
210 | setWidth(parentItem()->width()); |
211 | |
212 | if (m_sliceView) { |
213 | const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height()); |
214 | QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(m_sliceView->camera()); |
215 | const float magnificationScaleFactor = .16f; // this controls the size of the slice view |
216 | const float magnification = scale * magnificationScaleFactor; |
217 | camera->setHorizontalMagnification(magnification); |
218 | camera->setVerticalMagnification(magnification); |
219 | } |
220 | } |
221 | |
222 | void QQuickGraphsItem::handleParentHeightChange() |
223 | { |
224 | if (m_sliceView->isVisible()) |
225 | setHeight(parentItem()->height() * .2f); |
226 | else |
227 | setHeight(parentItem()->height()); |
228 | |
229 | if (m_sliceView) { |
230 | const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height()); |
231 | QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(m_sliceView->camera()); |
232 | const float magnificationScaleFactor = .16f; // this controls the size of the slice view |
233 | const float magnification = scale * magnificationScaleFactor; |
234 | camera->setHorizontalMagnification(magnification); |
235 | camera->setVerticalMagnification(magnification); |
236 | } |
237 | } |
238 | |
239 | void QQuickGraphsItem::componentComplete() |
240 | { |
241 | QQuick3DViewport::componentComplete(); |
242 | |
243 | auto url = QUrl(QStringLiteral("defaultMeshes/backgroundMesh" )); |
244 | m_background = new QQuick3DModel(); |
245 | m_backgroundScale = new QQuick3DNode(); |
246 | m_backgroundRotation = new QQuick3DNode(); |
247 | m_graphNode = new QQuick3DNode(); |
248 | |
249 | m_backgroundScale->setParent(rootNode()); |
250 | m_backgroundScale->setParentItem(rootNode()); |
251 | |
252 | m_backgroundRotation->setParent(m_backgroundScale); |
253 | m_backgroundRotation->setParentItem(m_backgroundScale); |
254 | |
255 | m_background->setObjectName("Background" ); |
256 | m_background->setParent(m_backgroundRotation); |
257 | m_background->setParentItem(m_backgroundRotation); |
258 | |
259 | m_background->setSource(url); |
260 | |
261 | m_backgroundBB = new QQuick3DModel(); |
262 | m_backgroundBB->setObjectName("BackgroundBB" ); |
263 | m_backgroundBB->setParent(m_background); |
264 | m_backgroundBB->setParentItem(m_background); |
265 | m_backgroundBB->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull" ))); |
266 | m_backgroundBB->setPickable(true); |
267 | |
268 | m_graphNode->setParent(rootNode()); |
269 | m_graphNode->setParentItem(rootNode()); |
270 | |
271 | setUpCamera(); |
272 | setUpLight(); |
273 | |
274 | // Create repeaters for each axis X, Y, Z |
275 | m_repeaterX = createRepeater(); |
276 | m_repeaterY = createRepeater(); |
277 | m_repeaterZ = createRepeater(); |
278 | |
279 | auto delegateModelX = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel" )); |
280 | auto delegateModelY = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel" )); |
281 | auto delegateModelZ = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel" )); |
282 | |
283 | m_repeaterX->setDelegate(delegateModelX); |
284 | m_repeaterY->setDelegate(delegateModelY); |
285 | m_repeaterZ->setDelegate(delegateModelZ); |
286 | |
287 | // title labels for axes |
288 | m_titleLabelX = createTitleLabel(); |
289 | m_titleLabelX->setVisible(m_controller->axisX()->isTitleVisible()); |
290 | m_titleLabelX->setProperty(name: "labelText" , value: m_controller->axisX()->title()); |
291 | |
292 | m_titleLabelY = createTitleLabel(); |
293 | m_titleLabelY->setVisible(m_controller->axisY()->isTitleVisible()); |
294 | m_titleLabelY->setProperty(name: "labelText" , value: m_controller->axisY()->title()); |
295 | |
296 | m_titleLabelZ = createTitleLabel(); |
297 | m_titleLabelZ->setVisible(m_controller->axisZ()->isTitleVisible()); |
298 | m_titleLabelZ->setProperty(name: "labelText" , value: m_controller->axisZ()->title()); |
299 | |
300 | // Testing gridline |
301 | |
302 | // X lines |
303 | m_segmentLineRepeaterX = createRepeater(); |
304 | |
305 | auto segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
306 | m_segmentLineRepeaterX->setDelegate(segmentLineDelegate); |
307 | |
308 | m_subsegmentLineRepeaterX = createRepeater(); |
309 | |
310 | auto subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
311 | m_subsegmentLineRepeaterX->setDelegate(subsegmentLineDelegate); |
312 | |
313 | // Y lines |
314 | m_segmentLineRepeaterY = createRepeater(); |
315 | |
316 | segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
317 | m_segmentLineRepeaterY->setDelegate(segmentLineDelegate); |
318 | |
319 | m_subsegmentLineRepeaterY = createRepeater(); |
320 | |
321 | subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
322 | m_subsegmentLineRepeaterY->setDelegate(subsegmentLineDelegate); |
323 | |
324 | // Z lines |
325 | m_segmentLineRepeaterZ = createRepeater(); |
326 | |
327 | segmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
328 | m_segmentLineRepeaterZ->setDelegate(segmentLineDelegate); |
329 | |
330 | m_subsegmentLineRepeaterZ = createRepeater(); |
331 | |
332 | subsegmentLineDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
333 | m_subsegmentLineRepeaterZ->setDelegate(subsegmentLineDelegate); |
334 | |
335 | createItemLabel(); |
336 | |
337 | auto axis = m_controller->axisX(); |
338 | int segmentCount = 0; |
339 | int subSegmentCount = 0; |
340 | int gridLineCount = 0; |
341 | int subGridLineCount = 0; |
342 | if (axis->type() & QAbstract3DAxis::AxisTypeValue) { |
343 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); |
344 | segmentCount = valueAxis->segmentCount(); |
345 | subSegmentCount = valueAxis->subSegmentCount(); |
346 | gridLineCount = 2 * (segmentCount + 1); |
347 | subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1)); |
348 | } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) { |
349 | gridLineCount = axis->labels().size(); |
350 | } |
351 | m_segmentLineRepeaterX->setModel(gridLineCount); |
352 | m_subsegmentLineRepeaterX->setModel(subGridLineCount); |
353 | m_repeaterX->setModel(axis->labels().size()); |
354 | m_controller->handleAxisLabelsChangedBySender(sender: m_controller->axisX()); |
355 | |
356 | axis = m_controller->axisY(); |
357 | if (axis->type() & QAbstract3DAxis::AxisTypeValue) { |
358 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); |
359 | segmentCount = valueAxis->segmentCount(); |
360 | subSegmentCount = valueAxis->subSegmentCount(); |
361 | gridLineCount = 2 * (segmentCount + 1); |
362 | subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1)); |
363 | } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) { |
364 | gridLineCount = axis->labels().size(); |
365 | } |
366 | m_segmentLineRepeaterY->setModel(gridLineCount); |
367 | m_subsegmentLineRepeaterY->setModel(subGridLineCount); |
368 | m_repeaterY->setModel(2 * axis->labels().size()); |
369 | m_controller->handleAxisLabelsChangedBySender(sender: m_controller->axisY()); |
370 | |
371 | axis = m_controller->axisZ(); |
372 | if (axis->type() & QAbstract3DAxis::AxisTypeValue) { |
373 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); |
374 | segmentCount = valueAxis->segmentCount(); |
375 | subSegmentCount = valueAxis->subSegmentCount(); |
376 | gridLineCount = 2 * (segmentCount + 1); |
377 | subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1)); |
378 | } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) { |
379 | gridLineCount = axis->labels().size(); |
380 | } |
381 | m_segmentLineRepeaterZ->setModel(gridLineCount); |
382 | m_subsegmentLineRepeaterZ->setModel(subGridLineCount); |
383 | m_repeaterZ->setModel(axis->labels().size()); |
384 | m_controller->handleAxisLabelsChangedBySender(sender: m_controller->axisZ()); |
385 | |
386 | if (!m_pendingCustomItemList.isEmpty()) { |
387 | foreach (auto item, m_pendingCustomItemList) |
388 | addCustomItem(item); |
389 | } |
390 | |
391 | // Create initial default input handler, if one hasn't already been created by QML |
392 | if (!activeInputHandler()) { |
393 | QAbstract3DInputHandler *inputHandler; |
394 | inputHandler = new QTouch3DInputHandler(this); |
395 | inputHandler->d_func()->m_isDefaultHandler = true; |
396 | setActiveInputHandler(inputHandler); |
397 | } |
398 | } |
399 | |
400 | QQuick3DDirectionalLight *QQuickGraphsItem::light() const |
401 | { |
402 | return m_light; |
403 | } |
404 | |
405 | // TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812 |
406 | // begin.. |
407 | void QQuickGraphsItem::addTheme(Q3DTheme *theme) |
408 | { |
409 | m_controller->addTheme(theme); |
410 | } |
411 | |
412 | void QQuickGraphsItem::releaseTheme(Q3DTheme *theme) |
413 | { |
414 | m_controller->releaseTheme(theme); |
415 | } |
416 | |
417 | QList<Q3DTheme *> QQuickGraphsItem::themes() const |
418 | { |
419 | return m_controller->themes(); |
420 | } |
421 | |
422 | void QQuickGraphsItem::setTheme(Q3DTheme *theme) |
423 | { |
424 | m_controller->setActiveTheme(theme, force: isComponentComplete()); |
425 | } |
426 | |
427 | Q3DTheme *QQuickGraphsItem::theme() const |
428 | { |
429 | return m_controller->activeTheme(); |
430 | } |
431 | |
432 | void QQuickGraphsItem::clearSelection() |
433 | { |
434 | m_controller->clearSelection(); |
435 | } |
436 | |
437 | bool QQuickGraphsItem::hasSeries(QAbstract3DSeries *series) |
438 | { |
439 | return m_controller->hasSeries(series); |
440 | } |
441 | |
442 | void QQuickGraphsItem::setSelectionMode(QAbstract3DGraph::SelectionFlags mode) |
443 | { |
444 | int intmode = int(mode); |
445 | m_controller->setSelectionMode(QAbstract3DGraph::SelectionFlags(intmode)); |
446 | } |
447 | |
448 | QAbstract3DGraph::SelectionFlags QQuickGraphsItem::selectionMode() const |
449 | { |
450 | return m_controller->selectionMode(); |
451 | } |
452 | |
453 | void QQuickGraphsItem::setShadowQuality(QAbstract3DGraph::ShadowQuality quality) |
454 | { |
455 | m_controller->setShadowQuality(quality); |
456 | } |
457 | |
458 | QAbstract3DGraph::ShadowQuality QQuickGraphsItem::shadowQuality() const |
459 | { |
460 | return m_controller->shadowQuality(); |
461 | } |
462 | |
463 | int QQuickGraphsItem::addCustomItem(QCustom3DItem *item) |
464 | { |
465 | if (isComponentComplete()) { |
466 | if (m_controller->isCustomLabelItem(item)) { |
467 | QQuick3DNode *label = createTitleLabel(); |
468 | QCustom3DLabel *key = static_cast<QCustom3DLabel *>(item); |
469 | m_customLabelList.insert(key, value: label); |
470 | } else if (m_controller->isCustomVolumeItem(item)) { |
471 | QQuick3DModel *model = new QQuick3DModel(); |
472 | model->setParent(graphNode()); |
473 | model->setParentItem(graphNode()); |
474 | m_customItemList.insert(key: item, value: model); |
475 | } else { |
476 | QQuick3DModel *model = new QQuick3DModel(); |
477 | model->setParent(graphNode()); |
478 | model->setParentItem(graphNode()); |
479 | QQmlListReference materialsRef(model, "materials" ); |
480 | QQuick3DPrincipledMaterial *material = new QQuick3DPrincipledMaterial(); |
481 | material->setParent(model); |
482 | material->setParentItem(model); |
483 | materialsRef.append(material); |
484 | if (!selectionMode().testFlag(flag: QAbstract3DGraph::SelectionNone)) |
485 | model->setPickable(true); |
486 | m_customItemList.insert(key: item, value: model); |
487 | } |
488 | } else { |
489 | m_pendingCustomItemList.append(t: item); |
490 | } |
491 | return m_controller->addCustomItem(item); |
492 | } |
493 | |
494 | void QQuickGraphsItem::removeCustomItems() |
495 | { |
496 | m_customItemList.clear(); |
497 | m_customLabelList.clear(); |
498 | m_controller->deleteCustomItems(); |
499 | } |
500 | |
501 | void QQuickGraphsItem::removeCustomItem(QCustom3DItem *item) |
502 | { |
503 | if (m_controller->isCustomLabelItem(item)) { |
504 | m_customLabelList.remove(key: static_cast<QCustom3DLabel *>(item)); |
505 | } else if (m_controller->isCustomVolumeItem(item)) { |
506 | m_customItemList.remove(key: item); |
507 | auto volume = static_cast<QCustom3DVolume *>(item); |
508 | if (m_customVolumes.contains(key: volume)) { |
509 | m_customVolumes[volume].model->deleteLater(); |
510 | m_customVolumes.remove(key: volume); |
511 | } |
512 | } else { |
513 | m_customItemList.remove(key: item); |
514 | } |
515 | m_controller->deleteCustomItem(item); |
516 | } |
517 | |
518 | void QQuickGraphsItem::removeCustomItemAt(const QVector3D &position) |
519 | { |
520 | auto labelIterator = m_customLabelList.constBegin(); |
521 | while (labelIterator != m_customLabelList.constEnd()) { |
522 | QCustom3DLabel *label = labelIterator.key(); |
523 | if (label->position() == position) { |
524 | labelIterator.value()->setVisible(false); |
525 | labelIterator = m_customLabelList.erase(it: labelIterator); |
526 | } else { |
527 | ++labelIterator; |
528 | } |
529 | } |
530 | |
531 | auto itemIterator = m_customItemList.constBegin(); |
532 | while (itemIterator != m_customItemList.constEnd()) { |
533 | QCustom3DItem *item = itemIterator.key(); |
534 | if (item->position() == position) { |
535 | itemIterator.value()->setVisible(false); |
536 | itemIterator = m_customItemList.erase(it: itemIterator); |
537 | if (m_controller->isCustomVolumeItem(item)) { |
538 | auto volume = static_cast<QCustom3DVolume *>(item); |
539 | if (m_customVolumes.contains(key: volume)) { |
540 | m_customVolumes[volume].model->deleteLater(); |
541 | m_customVolumes.remove(key: volume); |
542 | } |
543 | } |
544 | } else { |
545 | ++itemIterator; |
546 | } |
547 | } |
548 | m_controller->deleteCustomItem(position); |
549 | } |
550 | |
551 | void QQuickGraphsItem::releaseCustomItem(QCustom3DItem *item) |
552 | { |
553 | if (m_controller->isCustomLabelItem(item)) { |
554 | m_customLabelList.remove(key: static_cast<QCustom3DLabel *>(item)); |
555 | } else if (m_controller->isCustomVolumeItem(item)) { |
556 | m_customItemList.remove(key: item); |
557 | auto volume = static_cast<QCustom3DVolume *>(item); |
558 | if (m_customVolumes.contains(key: volume)) { |
559 | m_customVolumes[volume].model->deleteLater(); |
560 | m_customVolumes.remove(key: volume); |
561 | } |
562 | } else { |
563 | m_customItemList.remove(key: item); |
564 | } |
565 | m_controller->releaseCustomItem(item); |
566 | } |
567 | |
568 | int QQuickGraphsItem::selectedLabelIndex() const |
569 | { |
570 | return m_controller->selectedLabelIndex(); |
571 | } |
572 | |
573 | QAbstract3DAxis *QQuickGraphsItem::selectedAxis() const |
574 | { |
575 | return m_controller->selectedAxis(); |
576 | } |
577 | |
578 | int QQuickGraphsItem::selectedCustomItemIndex() const |
579 | { |
580 | return m_controller->selectedCustomItemIndex(); |
581 | } |
582 | |
583 | QCustom3DItem *QQuickGraphsItem::selectedCustomItem() const |
584 | { |
585 | return m_controller->selectedCustomItem(); |
586 | } |
587 | // ...end |
588 | // TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812 |
589 | |
590 | QQmlListProperty<QCustom3DItem> QQuickGraphsItem::customItemList() |
591 | { |
592 | return QQmlListProperty<QCustom3DItem>(this, this, |
593 | &QQuickGraphsItem::appendCustomItemFunc, |
594 | &QQuickGraphsItem::countCustomItemFunc, |
595 | &QQuickGraphsItem::atCustomItemFunc, |
596 | &QQuickGraphsItem::clearCustomItemFunc); |
597 | } |
598 | |
599 | void QQuickGraphsItem::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, |
600 | QCustom3DItem *item) |
601 | { |
602 | QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data); |
603 | decl->addCustomItem(item); |
604 | } |
605 | |
606 | qsizetype QQuickGraphsItem::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) |
607 | { |
608 | Q_UNUSED(list); |
609 | return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_controller->m_customItems.size(); |
610 | } |
611 | |
612 | QCustom3DItem *QQuickGraphsItem::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, |
613 | qsizetype index) |
614 | { |
615 | Q_UNUSED(list); |
616 | Q_UNUSED(index); |
617 | return reinterpret_cast<QQuickGraphsItem *>(list->data)->m_controller->m_customItems.at(i: index); |
618 | } |
619 | |
620 | void QQuickGraphsItem::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) |
621 | { |
622 | QQuickGraphsItem *decl = reinterpret_cast<QQuickGraphsItem *>(list->data); |
623 | decl->removeCustomItems(); |
624 | } |
625 | |
626 | void QQuickGraphsItem::setSharedController(Abstract3DController *controller) |
627 | { |
628 | Q_ASSERT(controller); |
629 | m_controller = controller; |
630 | m_controller->m_qml = this; |
631 | |
632 | // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D. |
633 | DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D; |
634 | defaultTheme->d_func()->setDefaultTheme(true); |
635 | defaultTheme->setType(Q3DTheme::ThemeQt); |
636 | m_controller->setActiveTheme(theme: defaultTheme); |
637 | |
638 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::shadowQualityChanged, context: this, |
639 | slot: &QQuickGraphsItem::handleShadowQualityChange); |
640 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::activeThemeChanged, context: this, |
641 | slot: &QQuickGraphsItem::themeChanged); |
642 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::themeTypeChanged, context: this, |
643 | slot: &QQuickGraphsItem::handleThemeTypeChange); |
644 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::selectionModeChanged, context: this, |
645 | slot: &QQuickGraphsItem::handleSelectionModeChange); |
646 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::elementSelected, context: this, |
647 | slot: &QQuickGraphsItem::handleSelectedElementChange); |
648 | |
649 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisXChanged, context: this, |
650 | slot: &QQuickGraphsItem::handleAxisXChanged); |
651 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisYChanged, context: this, |
652 | slot: &QQuickGraphsItem::handleAxisYChanged); |
653 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisZChanged, context: this, |
654 | slot: &QQuickGraphsItem::handleAxisZChanged); |
655 | |
656 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::orthoProjectionChanged, context: this, |
657 | slot: &QQuickGraphsItem::orthoProjectionChanged); |
658 | |
659 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::aspectRatioChanged, context: this, |
660 | slot: &QQuickGraphsItem::aspectRatioChanged); |
661 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::optimizationHintsChanged, context: this, |
662 | slot: &QQuickGraphsItem::handleOptimizationHintChange); |
663 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::polarChanged, context: this, |
664 | slot: &QQuickGraphsItem::polarChanged); |
665 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::radialLabelOffsetChanged, context: this, |
666 | slot: &QQuickGraphsItem::radialLabelOffsetChanged); |
667 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::horizontalAspectRatioChanged, context: this, |
668 | slot: &QQuickGraphsItem::horizontalAspectRatioChanged); |
669 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectionChanged, context: this, |
670 | slot: &QQuickGraphsItem::reflectionChanged); |
671 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectivityChanged, context: this, |
672 | slot: &QQuickGraphsItem::reflectivityChanged); |
673 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::localeChanged, context: this, |
674 | slot: &QQuickGraphsItem::localeChanged); |
675 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::queriedGraphPositionChanged, context: this, |
676 | slot: &QQuickGraphsItem::queriedGraphPositionChanged); |
677 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::marginChanged, context: this, |
678 | slot: &QQuickGraphsItem::marginChanged); |
679 | } |
680 | |
681 | void QQuickGraphsItem::synchData() |
682 | { |
683 | if (!isVisible()) |
684 | return; |
685 | m_controller->m_renderPending = false; |
686 | |
687 | if (m_controller->m_changeTracker.selectionModeChanged) { |
688 | updateSelectionMode(newMode: m_controller->selectionMode()); |
689 | m_controller->m_changeTracker.selectionModeChanged = false; |
690 | } |
691 | |
692 | bool recalculateScale = false; |
693 | if (m_controller->m_changeTracker.aspectRatioChanged) { |
694 | recalculateScale = true; |
695 | m_controller->m_changeTracker.aspectRatioChanged = false; |
696 | } |
697 | |
698 | if (m_controller->m_changeTracker.horizontalAspectRatioChanged) { |
699 | recalculateScale = true; |
700 | m_controller->m_changeTracker.horizontalAspectRatioChanged = false; |
701 | } |
702 | |
703 | if (m_controller->m_changeTracker.marginChanged) { |
704 | recalculateScale = true; |
705 | m_controller->m_changeTracker.marginChanged = false; |
706 | } |
707 | |
708 | if (recalculateScale) |
709 | calculateSceneScalingFactors(); |
710 | |
711 | bool axisDirty = recalculateScale; |
712 | if (m_controller->m_changeTracker.axisXFormatterChanged) { |
713 | m_controller->m_changeTracker.axisXFormatterChanged = false; |
714 | QAbstract3DAxis *axisX = m_controller->axisX(); |
715 | if (axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
716 | QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX); |
717 | valueAxisX->recalculate(); |
718 | repeaterX()->setModel(valueAxisX->formatter()->labelPositions().size()); |
719 | } |
720 | axisDirty = true; |
721 | } |
722 | |
723 | if (m_controller->m_changeTracker.axisYFormatterChanged) { |
724 | m_controller->m_changeTracker.axisYFormatterChanged = false; |
725 | QAbstract3DAxis *axisY = m_controller->axisY(); |
726 | if (axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
727 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY); |
728 | valueAxisY->recalculate(); |
729 | repeaterY()->setModel(2 * valueAxisY->formatter()->labelPositions().size()); |
730 | } |
731 | axisDirty = true; |
732 | } |
733 | |
734 | if (m_controller->m_changeTracker.axisZFormatterChanged) { |
735 | m_controller->m_changeTracker.axisZFormatterChanged = false; |
736 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
737 | if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
738 | QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ); |
739 | valueAxisZ->recalculate(); |
740 | repeaterZ()->setModel(valueAxisZ->formatter()->labelPositions().size()); |
741 | } |
742 | axisDirty = true; |
743 | } |
744 | |
745 | if (m_controller->m_changeTracker.axisXSegmentCountChanged) { |
746 | QAbstract3DAxis *axisX = m_controller->axisX(); |
747 | if (axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
748 | QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX); |
749 | valueAxisX->recalculate(); |
750 | } |
751 | handleSegmentLineCountChanged(axis: axisX, repeater: m_segmentLineRepeaterX); |
752 | m_controller->m_changeTracker.axisXSegmentCountChanged = false; |
753 | axisDirty = true; |
754 | } |
755 | |
756 | if (m_controller->m_changeTracker.axisYSegmentCountChanged) { |
757 | QAbstract3DAxis *axisY = m_controller->axisY(); |
758 | if (axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
759 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY); |
760 | valueAxisY->recalculate(); |
761 | } |
762 | handleSegmentLineCountChanged(axis: axisY, repeater: m_segmentLineRepeaterY); |
763 | m_controller->m_changeTracker.axisYSegmentCountChanged = false; |
764 | axisDirty = true; |
765 | } |
766 | |
767 | if (m_controller->m_changeTracker.axisZSegmentCountChanged) { |
768 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
769 | if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
770 | QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ); |
771 | valueAxisZ->recalculate(); |
772 | } |
773 | handleSegmentLineCountChanged(axis: axisZ, repeater: m_segmentLineRepeaterZ); |
774 | m_controller->m_changeTracker.axisZSegmentCountChanged = false; |
775 | axisDirty = true; |
776 | } |
777 | |
778 | if (m_controller->m_changeTracker.axisXSubSegmentCountChanged) { |
779 | QAbstract3DAxis *axisX = m_controller->axisX(); |
780 | if (axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
781 | QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(axisX); |
782 | valueAxisX->recalculate(); |
783 | } |
784 | handleSubSegmentLineCountChanged(axis: axisX, repeater: m_subsegmentLineRepeaterX); |
785 | m_controller->m_changeTracker.axisXSubSegmentCountChanged = false; |
786 | axisDirty = true; |
787 | } |
788 | |
789 | if (m_controller->m_changeTracker.axisYSubSegmentCountChanged) { |
790 | QAbstract3DAxis *axisY = m_controller->axisY(); |
791 | if (axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
792 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(axisY); |
793 | valueAxisY->recalculate(); |
794 | } |
795 | handleSubSegmentLineCountChanged(axis: axisY, repeater: m_subsegmentLineRepeaterY); |
796 | m_controller->m_changeTracker.axisYSubSegmentCountChanged = false; |
797 | axisDirty = true; |
798 | } |
799 | |
800 | if (m_controller->m_changeTracker.axisZSubSegmentCountChanged) { |
801 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
802 | if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
803 | QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(axisZ); |
804 | valueAxisZ->recalculate(); |
805 | } |
806 | handleSubSegmentLineCountChanged(axis: axisZ, repeater: m_subsegmentLineRepeaterZ); |
807 | m_controller->m_changeTracker.axisZSubSegmentCountChanged = false; |
808 | axisDirty = true; |
809 | } |
810 | |
811 | if (m_controller->m_changeTracker.axisXLabelsChanged) { |
812 | QAbstract3DAxis *axisX = m_controller->axisX(); |
813 | if (axisX->type() & QAbstract3DAxis::AxisTypeValue) { |
814 | auto valueAxisX = static_cast<QValue3DAxis *>(axisX); |
815 | valueAxisX->recalculate(); |
816 | repeaterX()->setModel(valueAxisX->formatter()->labelPositions().size()); |
817 | } else if (axisX->type() & QAbstract3DAxis::AxisTypeCategory) { |
818 | auto categoryAxis = static_cast<QCategory3DAxis *>(axisX); |
819 | repeaterX()->setModel(categoryAxis->labels().size()); |
820 | } |
821 | |
822 | handleSegmentLineCountChanged(axis: axisX, repeater: segmentLineRepeaterX()); |
823 | handleSubSegmentLineCountChanged(axis: axisX, repeater: subsegmentLineRepeaterX()); |
824 | m_controller->m_changeTracker.axisXLabelsChanged = false; |
825 | handleLabelCountChanged(repeater: m_repeaterX); |
826 | axisDirty = true; |
827 | } |
828 | |
829 | if (m_controller->m_changeTracker.axisYLabelsChanged) { |
830 | QAbstract3DAxis *axisY = m_controller->axisY(); |
831 | if (axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
832 | auto valueAxisY = static_cast<QValue3DAxis *>(axisY); |
833 | valueAxisY->recalculate(); |
834 | repeaterY()->setModel(2 * valueAxisY->formatter()->labelPositions().size()); |
835 | } else if (axisY->type() & QAbstract3DAxis::AxisTypeCategory) { |
836 | auto categoryAxis = static_cast<QCategory3DAxis *>(axisY); |
837 | repeaterY()->setModel(2 * categoryAxis->labels().size()); |
838 | } |
839 | |
840 | handleSegmentLineCountChanged(axis: axisY, repeater: segmentLineRepeaterY()); |
841 | handleSubSegmentLineCountChanged(axis: axisY, repeater: subsegmentLineRepeaterY()); |
842 | m_controller->m_changeTracker.axisYLabelsChanged = false; |
843 | handleLabelCountChanged(repeater: m_repeaterY); |
844 | axisDirty = true; |
845 | } |
846 | |
847 | if (m_controller->m_changeTracker.axisZLabelsChanged) { |
848 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
849 | if (axisZ->type() & QAbstract3DAxis::AxisTypeValue) { |
850 | auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ); |
851 | valueAxisZ->recalculate(); |
852 | repeaterZ()->setModel(valueAxisZ->formatter()->labelPositions().size()); |
853 | } else if (axisZ->type() & QAbstract3DAxis::AxisTypeCategory) { |
854 | auto categoryAxis = static_cast<QCategory3DAxis *>(axisZ); |
855 | repeaterZ()->setModel(categoryAxis->labels().size()); |
856 | } |
857 | |
858 | handleSegmentLineCountChanged(axis: axisZ, repeater: segmentLineRepeaterZ()); |
859 | handleSubSegmentLineCountChanged(axis: axisZ, repeater: subsegmentLineRepeaterZ()); |
860 | m_controller->m_changeTracker.axisZLabelsChanged = false; |
861 | handleLabelCountChanged(repeater: m_repeaterZ); |
862 | axisDirty = true; |
863 | } |
864 | |
865 | updateTitleLabels(); |
866 | |
867 | if (m_controller->m_changeTracker.shadowQualityChanged) { |
868 | updateShadowQuality(quality: shadowQuality()); |
869 | m_controller->m_changeTracker.shadowQualityChanged = false; |
870 | } |
871 | |
872 | if (m_controller->m_changeTracker.axisXRangeChanged) { |
873 | axisDirty = true; |
874 | calculateSceneScalingFactors(); |
875 | m_controller->m_changeTracker.axisXRangeChanged = false; |
876 | } |
877 | |
878 | if (m_controller->m_changeTracker.axisYRangeChanged) { |
879 | axisDirty = true; |
880 | QAbstract3DAxis *axis = m_controller->axisY(); |
881 | updateAxisRange(min: axis->min(), max: axis->max()); |
882 | calculateSceneScalingFactors(); |
883 | m_controller->m_changeTracker.axisYRangeChanged = false; |
884 | } |
885 | |
886 | if (m_controller->m_changeTracker.axisZRangeChanged) { |
887 | axisDirty = true; |
888 | calculateSceneScalingFactors(); |
889 | m_controller->m_changeTracker.axisZRangeChanged = false; |
890 | } |
891 | |
892 | if (m_controller->m_changeTracker.axisYReversedChanged) { |
893 | m_controller->m_changeTracker.axisYReversedChanged = false; |
894 | if (m_controller->m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { |
895 | QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_controller->m_axisY); |
896 | updateAxisReversed(enable: valueAxisY->reversed()); |
897 | } |
898 | } |
899 | |
900 | if (m_controller->m_changeTracker.polarChanged) { |
901 | axisDirty = true; |
902 | m_controller->m_changeTracker.polarChanged = false; |
903 | } |
904 | |
905 | if (m_controller->m_changeTracker.axisXLabelAutoRotationChanged) { |
906 | axisDirty = true; |
907 | m_controller->m_changeTracker.axisXLabelAutoRotationChanged = false; |
908 | } |
909 | |
910 | if (m_controller->m_changeTracker.axisYLabelAutoRotationChanged) { |
911 | axisDirty = true; |
912 | m_controller->m_changeTracker.axisYLabelAutoRotationChanged = false; |
913 | } |
914 | |
915 | if (m_controller->m_changeTracker.axisZLabelAutoRotationChanged) { |
916 | axisDirty = true; |
917 | m_controller->m_changeTracker.axisZLabelAutoRotationChanged = false; |
918 | } |
919 | |
920 | if (m_controller->m_changeTracker.axisXTitleFixedChanged) { |
921 | axisDirty = true; |
922 | m_controller->m_changeTracker.axisXTitleFixedChanged = false; |
923 | } |
924 | |
925 | if (m_controller->m_changeTracker.axisYTitleFixedChanged) { |
926 | axisDirty = true; |
927 | m_controller->m_changeTracker.axisYTitleFixedChanged = false; |
928 | } |
929 | |
930 | if (m_controller->m_changeTracker.axisZTitleFixedChanged) { |
931 | axisDirty = true; |
932 | m_controller->m_changeTracker.axisZTitleFixedChanged = false; |
933 | } |
934 | |
935 | updateCamera(); |
936 | |
937 | QVector3D forward = camera()->forward(); |
938 | auto targetRotation = cameraTarget()->rotation(); |
939 | if (m_yFlipped != (targetRotation.x() > 0)) { |
940 | m_yFlipped = (targetRotation.x() > 0); |
941 | axisDirty = true; |
942 | } |
943 | if (m_xFlipped != (forward.x() > 0)) { |
944 | m_xFlipped = (forward.x() > 0); |
945 | axisDirty = true; |
946 | } |
947 | if (m_zFlipped != ((forward.z() > .1f))) { |
948 | m_zFlipped = ((forward.z() > .1f)); |
949 | axisDirty = true; |
950 | } |
951 | |
952 | if (axisDirty) { |
953 | updateGrid(); |
954 | updateLabels(); |
955 | updateCustomData(); |
956 | if (m_sliceView) { |
957 | updateSliceGrid(); |
958 | updateSliceLabels(); |
959 | } |
960 | m_gridUpdated = true; |
961 | } |
962 | |
963 | if (m_controller->m_changeTracker.radialLabelOffsetChanged) { |
964 | updateRadialLabelOffset(); |
965 | m_controller->m_changeTracker.radialLabelOffsetChanged = false; |
966 | } |
967 | |
968 | QMatrix4x4 modelMatrix; |
969 | m_backgroundScale->setScale(m_scaleWithBackground + m_backgroundScaleMargin); |
970 | |
971 | QVector3D rotVec; |
972 | if (!m_yFlipped) { |
973 | rotVec = QVector3D(0, 270, 0); |
974 | if (m_xFlipped && m_zFlipped) |
975 | rotVec.setY(90); |
976 | else if (!m_xFlipped && m_zFlipped) |
977 | rotVec.setY(0); |
978 | else if (m_xFlipped && !m_zFlipped) |
979 | rotVec.setY(180); |
980 | } else { |
981 | rotVec = QVector3D(0, 180, 180); |
982 | if (m_xFlipped && m_zFlipped) |
983 | rotVec.setY(0); |
984 | else if (!m_xFlipped && m_zFlipped) |
985 | rotVec.setY(270); |
986 | else if (m_xFlipped && !m_zFlipped) |
987 | rotVec.setY(90); |
988 | } |
989 | |
990 | auto rotation = Utils::calculateRotation(xyzRotations: rotVec); |
991 | if (m_yFlipped) { |
992 | m_backgroundRotation->setRotation(rotation); |
993 | } else { |
994 | modelMatrix.rotate(quaternion: rotation); |
995 | m_backgroundRotation->setRotation(rotation); |
996 | } |
997 | |
998 | if (m_controller->graphPositionQueryPending()) |
999 | graphPositionAt(point: m_controller->scene()->graphPositionQuery()); |
1000 | |
1001 | bool forceUpdateCustomVolumes = false; |
1002 | if (m_controller->m_changeTracker.projectionChanged) { |
1003 | forceUpdateCustomVolumes = true; |
1004 | bool useOrtho = m_controller->isOrthoProjection(); |
1005 | if (useOrtho) |
1006 | setCamera(m_oCamera); |
1007 | else |
1008 | setCamera(m_pCamera); |
1009 | m_controller->m_changeTracker.projectionChanged = false; |
1010 | } |
1011 | |
1012 | Q3DTheme *theme = m_controller->activeTheme(); |
1013 | if (m_controller->m_changeTracker.themeChanged) { |
1014 | environment()->setClearColor(theme->windowColor()); |
1015 | m_controller->m_changeTracker.themeChanged = false; |
1016 | } |
1017 | |
1018 | if (theme->d_func()->m_dirtyBits.lightStrengthDirty) { |
1019 | light()->setBrightness(theme->lightStrength() * .2f); |
1020 | if (qFuzzyIsNull(f: light()->brightness())) |
1021 | light()->setBrightness(.0000001f); |
1022 | updateLightStrength(); |
1023 | theme->d_func()->m_dirtyBits.lightStrengthDirty = false; |
1024 | } |
1025 | |
1026 | if (theme->d_func()->m_dirtyBits.ambientLightStrengthDirty) { |
1027 | float ambientStrength = theme->ambientLightStrength(); |
1028 | QColor ambientColor = QColor::fromRgbF(r: ambientStrength, g: ambientStrength, b: ambientStrength); |
1029 | light()->setAmbientColor(ambientColor); |
1030 | if (qFuzzyIsNull(f: light()->brightness())) |
1031 | light()->setBrightness(.0000001f); |
1032 | theme->d_func()->m_dirtyBits.ambientLightStrengthDirty = false; |
1033 | } |
1034 | |
1035 | if (theme->d_func()->m_dirtyBits.lightColorDirty) { |
1036 | light()->setColor(theme->lightColor()); |
1037 | theme->d_func()->m_dirtyBits.lightColorDirty = false; |
1038 | } |
1039 | |
1040 | if (theme->d_func()->m_dirtyBits.shadowStrengthDirty) { |
1041 | light()->setShadowFactor(theme->shadowStrength()); |
1042 | theme->d_func()->m_dirtyBits.shadowStrengthDirty = false; |
1043 | } |
1044 | |
1045 | // label Adjustments |
1046 | if (theme->d_func()->m_dirtyBits.labelBackgroundColorDirty) { |
1047 | QColor labelBackgroundColor = theme->labelBackgroundColor(); |
1048 | changeLabelBackgroundColor(repeater: m_repeaterX, color: labelBackgroundColor); |
1049 | changeLabelBackgroundColor(repeater: m_repeaterY, color: labelBackgroundColor); |
1050 | changeLabelBackgroundColor(repeater: m_repeaterZ, color: labelBackgroundColor); |
1051 | m_titleLabelX->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1052 | m_titleLabelY->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1053 | m_titleLabelZ->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1054 | m_itemLabel->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1055 | |
1056 | if (m_sliceView) { |
1057 | changeLabelBackgroundColor(repeater: m_sliceHorizontalLabelRepeater, color: labelBackgroundColor); |
1058 | changeLabelBackgroundColor(repeater: m_sliceVerticalLabelRepeater, color: labelBackgroundColor); |
1059 | m_sliceItemLabel->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1060 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1061 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundColor" , value: labelBackgroundColor); |
1062 | } |
1063 | theme->d_func()->m_dirtyBits.labelBackgroundColorDirty = false; |
1064 | } |
1065 | |
1066 | if (theme->d_func()->m_dirtyBits.labelBackgroundEnabledDirty) { |
1067 | bool enabled = theme->isLabelBackgroundEnabled(); |
1068 | changeLabelBackgroundEnabled(repeater: m_repeaterX, enabled); |
1069 | changeLabelBackgroundEnabled(repeater: m_repeaterY, enabled); |
1070 | changeLabelBackgroundEnabled(repeater: m_repeaterZ, enabled); |
1071 | m_titleLabelX->setProperty(name: "backgroundEnabled" , value: enabled); |
1072 | m_titleLabelY->setProperty(name: "backgroundEnabled" , value: enabled); |
1073 | m_titleLabelZ->setProperty(name: "backgroundEnabled" , value: enabled); |
1074 | m_itemLabel->setProperty(name: "backgroundEnabled" , value: enabled); |
1075 | |
1076 | if (m_sliceView) { |
1077 | changeLabelBackgroundEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled); |
1078 | changeLabelBackgroundEnabled(repeater: m_sliceVerticalLabelRepeater, enabled); |
1079 | m_sliceItemLabel->setProperty(name: "backgroundEnabled" , value: enabled); |
1080 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundEnabled" , value: enabled); |
1081 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundEnabled" , value: enabled); |
1082 | } |
1083 | theme->d_func()->m_dirtyBits.labelBackgroundEnabledDirty = false; |
1084 | } |
1085 | |
1086 | if (theme->d_func()->m_dirtyBits.labelBorderEnabledDirty) { |
1087 | bool enabled = theme->isLabelBorderEnabled(); |
1088 | changeLabelBorderEnabled(repeater: m_repeaterX, enabled); |
1089 | changeLabelBorderEnabled(repeater: m_repeaterY, enabled); |
1090 | changeLabelBorderEnabled(repeater: m_repeaterZ, enabled); |
1091 | m_titleLabelX->setProperty(name: "borderEnabled" , value: enabled); |
1092 | m_titleLabelY->setProperty(name: "borderEnabled" , value: enabled); |
1093 | m_titleLabelZ->setProperty(name: "borderEnabled" , value: enabled); |
1094 | m_itemLabel->setProperty(name: "borderEnabled" , value: enabled); |
1095 | |
1096 | if (m_sliceView) { |
1097 | changeLabelBorderEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled); |
1098 | changeLabelBorderEnabled(repeater: m_sliceVerticalLabelRepeater, enabled); |
1099 | m_sliceItemLabel->setProperty(name: "borderEnabled" , value: enabled); |
1100 | m_sliceHorizontalTitleLabel->setProperty(name: "borderEnabled" , value: enabled); |
1101 | m_sliceVerticalTitleLabel->setProperty(name: "borderEnabled" , value: enabled); |
1102 | } |
1103 | theme->d_func()->m_dirtyBits.labelBorderEnabledDirty = false; |
1104 | } |
1105 | |
1106 | if (theme->d_func()->m_dirtyBits.labelTextColorDirty) { |
1107 | QColor labelTextColor = theme->labelTextColor(); |
1108 | changeLabelTextColor(repeater: m_repeaterX, color: labelTextColor); |
1109 | changeLabelTextColor(repeater: m_repeaterY, color: labelTextColor); |
1110 | changeLabelTextColor(repeater: m_repeaterZ, color: labelTextColor); |
1111 | m_titleLabelX->setProperty(name: "labelTextColor" , value: labelTextColor); |
1112 | m_titleLabelY->setProperty(name: "labelTextColor" , value: labelTextColor); |
1113 | m_titleLabelZ->setProperty(name: "labelTextColor" , value: labelTextColor); |
1114 | m_itemLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
1115 | |
1116 | if (m_sliceView) { |
1117 | changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: labelTextColor); |
1118 | changeLabelTextColor(repeater: m_sliceVerticalLabelRepeater, color: labelTextColor); |
1119 | m_sliceItemLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
1120 | m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
1121 | m_sliceVerticalTitleLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
1122 | } |
1123 | theme->d_func()->m_dirtyBits.labelTextColorDirty = false; |
1124 | } |
1125 | |
1126 | if (theme->d_func()->m_dirtyBits.fontDirty) { |
1127 | auto font = theme->font(); |
1128 | changeLabelFont(repeater: m_repeaterX, font); |
1129 | changeLabelFont(repeater: m_repeaterY, font); |
1130 | changeLabelFont(repeater: m_repeaterZ, font); |
1131 | m_titleLabelX->setProperty(name: "labelFont" , value: font); |
1132 | m_titleLabelY->setProperty(name: "labelFont" , value: font); |
1133 | m_titleLabelZ->setProperty(name: "labelFont" , value: font); |
1134 | m_itemLabel->setProperty(name: "labelFont" , value: font); |
1135 | updateLabels(); |
1136 | |
1137 | if (m_sliceView) { |
1138 | changeLabelFont(repeater: m_sliceHorizontalLabelRepeater, font); |
1139 | changeLabelFont(repeater: m_sliceVerticalLabelRepeater, font); |
1140 | m_sliceItemLabel->setProperty(name: "labelFont" , value: font); |
1141 | m_sliceHorizontalTitleLabel->setProperty(name: "labelFont" , value: font); |
1142 | m_sliceVerticalTitleLabel->setProperty(name: "labelFont" , value: font); |
1143 | updateSliceLabels(); |
1144 | } |
1145 | theme->d_func()->m_dirtyBits.fontDirty = false; |
1146 | } |
1147 | |
1148 | if (theme->d_func()->m_dirtyBits.labelsEnabledDirty) { |
1149 | bool enabled = theme->isLabelsEnabled(); |
1150 | changeLabelsEnabled(repeater: m_repeaterX, enabled); |
1151 | changeLabelsEnabled(repeater: m_repeaterY, enabled); |
1152 | changeLabelsEnabled(repeater: m_repeaterZ, enabled); |
1153 | m_titleLabelX->setProperty(name: "visible" , value: enabled && m_controller->axisX()->isTitleVisible()); |
1154 | m_titleLabelY->setProperty(name: "visible" , value: enabled && m_controller->axisY()->isTitleVisible()); |
1155 | m_titleLabelZ->setProperty(name: "visible" , value: enabled && m_controller->axisZ()->isTitleVisible()); |
1156 | m_itemLabel->setProperty(name: "visible" , value: enabled); |
1157 | |
1158 | if (m_sliceView) { |
1159 | changeLabelsEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled); |
1160 | changeLabelsEnabled(repeater: m_sliceVerticalLabelRepeater, enabled); |
1161 | m_sliceItemLabel->setProperty(name: "visible" , value: enabled); |
1162 | m_sliceHorizontalTitleLabel->setProperty(name: "visible" , value: enabled); |
1163 | m_sliceVerticalTitleLabel->setProperty(name: "visible" , value: enabled); |
1164 | } |
1165 | theme->d_func()->m_dirtyBits.labelsEnabledDirty = false; |
1166 | } |
1167 | |
1168 | // Grid and background adjustments |
1169 | if (theme->d_func()->m_dirtyBits.backgroundColorDirty) { |
1170 | QQmlListReference materialsRef(m_background, "materials" ); |
1171 | QQuick3DPrincipledMaterial *bgMat; |
1172 | if (!materialsRef.size()) { |
1173 | bgMat = new QQuick3DPrincipledMaterial(); |
1174 | bgMat->setParent(m_background); |
1175 | bgMat->setMetalness(0.f); |
1176 | bgMat->setRoughness(.3f); |
1177 | bgMat->setEmissiveFactor(QVector3D(.001f, .001f, .001f)); |
1178 | materialsRef.append(bgMat); |
1179 | } else { |
1180 | bgMat = static_cast<QQuick3DPrincipledMaterial *>(materialsRef.at(0)); |
1181 | } |
1182 | bgMat->setBaseColor(theme->backgroundColor()); |
1183 | theme->d_func()->m_dirtyBits.backgroundColorDirty = false; |
1184 | } |
1185 | |
1186 | if (theme->d_func()->m_dirtyBits.backgroundEnabledDirty) { |
1187 | m_background->setLocalOpacity(theme->isBackgroundEnabled()); |
1188 | theme->d_func()->m_dirtyBits.backgroundEnabledDirty = false; |
1189 | } |
1190 | |
1191 | if (theme->d_func()->m_dirtyBits.gridEnabledDirty) { |
1192 | bool enabled = theme->isGridEnabled(); |
1193 | m_segmentLineRepeaterX->setVisible(enabled); |
1194 | m_segmentLineRepeaterY->setVisible(enabled); |
1195 | m_segmentLineRepeaterZ->setVisible(enabled); |
1196 | |
1197 | m_subsegmentLineRepeaterX->setVisible(enabled); |
1198 | m_subsegmentLineRepeaterY->setVisible(enabled); |
1199 | m_subsegmentLineRepeaterZ->setVisible(enabled); |
1200 | |
1201 | if (m_sliceEnabled && m_controller->isSlicingActive()) { |
1202 | m_sliceHorizontalGridRepeater->setVisible(enabled); |
1203 | m_sliceVerticalGridRepeater->setVisible(enabled); |
1204 | } |
1205 | theme->d_func()->m_dirtyBits.gridEnabledDirty = false; |
1206 | } |
1207 | |
1208 | if (theme->d_func()->m_dirtyBits.gridLineColorDirty) { |
1209 | QColor gridLineColor = theme->gridLineColor(); |
1210 | changeGridLineColor(repeater: m_segmentLineRepeaterX, color: gridLineColor); |
1211 | changeGridLineColor(repeater: m_subsegmentLineRepeaterX, color: gridLineColor); |
1212 | changeGridLineColor(repeater: m_segmentLineRepeaterY, color: gridLineColor); |
1213 | changeGridLineColor(repeater: m_subsegmentLineRepeaterY, color: gridLineColor); |
1214 | changeGridLineColor(repeater: m_segmentLineRepeaterZ, color: gridLineColor); |
1215 | changeGridLineColor(repeater: m_subsegmentLineRepeaterZ, color: gridLineColor); |
1216 | theme->d_func()->m_dirtyBits.gridLineColorDirty = false; |
1217 | } |
1218 | |
1219 | if (theme->d_func()->m_dirtyBits.singleHighlightColorDirty) { |
1220 | updateSingleHighlightColor(); |
1221 | theme->d_func()->m_dirtyBits.singleHighlightColorDirty = false; |
1222 | } |
1223 | |
1224 | // Other adjustments |
1225 | if (theme->d_func()->m_dirtyBits.windowColorDirty) { |
1226 | window()->setColor(theme->windowColor()); |
1227 | environment()->setClearColor(theme->windowColor()); |
1228 | theme->d_func()->m_dirtyBits.windowColorDirty = false; |
1229 | } |
1230 | if (m_controller->activeTheme()->windowColor() != window()->color()) { |
1231 | window()->setColor(m_controller->activeTheme()->windowColor()); |
1232 | } |
1233 | |
1234 | if (m_controller->isCustomDataDirty()) { |
1235 | forceUpdateCustomVolumes = true; |
1236 | updateCustomData(); |
1237 | m_controller->setCustomDataDirty(false); |
1238 | } |
1239 | |
1240 | if (m_controller->m_changedSeriesList.size()) { |
1241 | forceUpdateCustomVolumes = true; |
1242 | updateGraph(); |
1243 | m_controller->m_changedSeriesList.clear(); |
1244 | } |
1245 | |
1246 | if (m_controller->m_isDataDirty) { |
1247 | forceUpdateCustomVolumes = true; |
1248 | updateGraph(); |
1249 | m_controller->m_isDataDirty = false; |
1250 | } |
1251 | |
1252 | if (m_controller->m_isSeriesVisualsDirty) { |
1253 | forceUpdateCustomVolumes = true; |
1254 | updateLabels(); |
1255 | updateGraph(); |
1256 | m_controller->m_isSeriesVisualsDirty = false; |
1257 | } |
1258 | |
1259 | if (m_sliceActivatedChanged) |
1260 | updateSliceGraph(); |
1261 | |
1262 | if (m_controller->isCustomItemDirty() || forceUpdateCustomVolumes) |
1263 | updateCustomVolumes(); |
1264 | } |
1265 | |
1266 | void QQuickGraphsItem::calculateSceneScalingFactors() |
1267 | { |
1268 | float scaleX, scaleY, scaleZ; |
1269 | float marginH, marginV; |
1270 | float aspectRatio = m_controller->aspectRatio(); |
1271 | float horizontalAspectRatio = m_controller->horizontalAspectRatio(); |
1272 | |
1273 | float margin = m_controller->margin(); |
1274 | if (margin < 0.0f) { |
1275 | marginH = .1f; |
1276 | marginV = .1f; |
1277 | } else { |
1278 | marginH = margin; |
1279 | marginV = margin; |
1280 | } |
1281 | |
1282 | QSizeF areaSize; |
1283 | if (qFuzzyIsNull(f: horizontalAspectRatio)) { |
1284 | areaSize.setHeight(m_controller->axisZ()->max() - m_controller->axisZ()->min()); |
1285 | areaSize.setWidth(m_controller->axisX()->max() - m_controller->axisX()->min()); |
1286 | } else { |
1287 | areaSize.setHeight(1.0); |
1288 | areaSize.setWidth(horizontalAspectRatio); |
1289 | } |
1290 | |
1291 | float horizontalMaxDimension; |
1292 | if (aspectRatio > 2.0f) { |
1293 | horizontalMaxDimension = 2.0f; |
1294 | scaleY = 2.0f / aspectRatio; |
1295 | } else { |
1296 | horizontalMaxDimension = aspectRatio; |
1297 | scaleY = 1.0f; |
1298 | } |
1299 | |
1300 | float scaleFactor = qMax(a: areaSize.width(), b: areaSize.height()); |
1301 | scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor; |
1302 | scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor; |
1303 | |
1304 | m_scale = QVector3D(scaleX, scaleY, scaleZ); |
1305 | m_scaleWithBackground = QVector3D(scaleX, scaleY, scaleZ); |
1306 | m_backgroundScaleMargin = QVector3D(marginH, marginV, marginH); |
1307 | } |
1308 | |
1309 | void QQuickGraphsItem::updateGrid() |
1310 | { |
1311 | int gridLineCountX = segmentLineRepeaterX()->count(); |
1312 | int subGridLineCountX = subsegmentLineRepeaterX()->count(); |
1313 | int gridLineCountY = segmentLineRepeaterY()->count() / 2; |
1314 | int subGridLineCountY = subsegmentLineRepeaterY()->count() / 2; |
1315 | int gridLineCountZ = segmentLineRepeaterZ()->count(); |
1316 | int subGridLineCountZ = subsegmentLineRepeaterZ()->count(); |
1317 | |
1318 | if (!m_isFloorGridInRange) { |
1319 | gridLineCountX /= 2; |
1320 | subGridLineCountX /= 2; |
1321 | gridLineCountZ /= 2; |
1322 | subGridLineCountZ /= 2; |
1323 | } |
1324 | |
1325 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
1326 | QVector3D scaleX(backgroundScale.x() * lineLengthScaleFactor(), lineWidthScaleFactor(), lineWidthScaleFactor()); |
1327 | QVector3D scaleY(lineWidthScaleFactor(), backgroundScale.y() * lineLengthScaleFactor(), lineWidthScaleFactor()); |
1328 | QVector3D scaleZ(backgroundScale.z() * lineLengthScaleFactor(), lineWidthScaleFactor(), lineWidthScaleFactor()); |
1329 | |
1330 | auto axisX = m_controller->axisX(); |
1331 | auto axisY = m_controller->axisY(); |
1332 | auto axisZ = m_controller->axisZ(); |
1333 | |
1334 | const bool xFlipped = isXFlipped(); |
1335 | const bool yFlipped = isYFlipped(); |
1336 | const bool zFlipped = isZFlipped(); |
1337 | |
1338 | QQuaternion lineRotation(.0f, .0f, .0f, .0f); |
1339 | QVector3D rotation(90.0f, 0.0f, 0.0f); |
1340 | |
1341 | // Floor horizontal line |
1342 | float linePosX = 0.0f; |
1343 | float linePosY = backgroundScale.y(); |
1344 | float linePosZ = 0.0f; |
1345 | float scale = m_scaleWithBackground.z(); |
1346 | |
1347 | if (!yFlipped) { |
1348 | linePosY *= -1.0f; |
1349 | rotation.setZ(180.0f); |
1350 | } |
1351 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1352 | for (int i = 0; i < subGridLineCountZ; i++) { |
1353 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(index: i)); |
1354 | if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) { |
1355 | linePosZ = static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(gridLine: i) * -scale * 2.0f + scale; |
1356 | } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) { |
1357 | linePosZ = calculateCategoryGridLinePosition(axis: axisZ, index: i); |
1358 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1359 | } |
1360 | if (isPolar()) { |
1361 | lineNode->setPosition(QVector3D(0.0f, linePosY, 0.0f)); |
1362 | lineNode->setScale(QVector3D(scaleX.x() * .5f, scaleX.x() * .5f, scaleX.z())); |
1363 | lineNode->setProperty(name: "polarRadius" , |
1364 | value: static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(gridLine: i) |
1365 | / (scaleX.x() * .5f) * 2.0f); |
1366 | } else { |
1367 | positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ)); |
1368 | } |
1369 | lineNode->setRotation(lineRotation); |
1370 | lineNode->setProperty(name: "isPolar" , value: isPolar()); |
1371 | } |
1372 | |
1373 | for (int i = 0; i < gridLineCountZ; i++) { |
1374 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(index: i)); |
1375 | if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) { |
1376 | linePosZ = static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(gridLine: i) * -scale * 2.0f + scale; |
1377 | } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) { |
1378 | linePosZ = calculateCategoryGridLinePosition(axis: axisZ, index: i); |
1379 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1380 | } |
1381 | if (isPolar()) { |
1382 | lineNode->setPosition(QVector3D(0.0f, linePosY, 0.0f)); |
1383 | lineNode->setScale(QVector3D(scaleX.x() * .5f, scaleX.x() * .5f, scaleX.z())); |
1384 | lineNode->setProperty(name: "polarRadius" , |
1385 | value: static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(gridLine: i) |
1386 | / (scaleX.x() * .5f) * 2.0f); |
1387 | } else { |
1388 | positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ)); |
1389 | } |
1390 | lineNode->setRotation(lineRotation); |
1391 | lineNode->setProperty(name: "isPolar" , value: isPolar()); |
1392 | } |
1393 | |
1394 | // Side vertical line |
1395 | linePosX = -backgroundScale.x(); |
1396 | linePosY = 0.0f; |
1397 | rotation = QVector3D(0.0f, 90.0f, 0.0f); |
1398 | if (xFlipped) { |
1399 | linePosX *= -1.0f; |
1400 | rotation.setY(-90.0f); |
1401 | } |
1402 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1403 | if (m_hasVerticalSegmentLine) { |
1404 | for (int i = 0; i < subGridLineCountZ; i++) { |
1405 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterZ()->objectAt(index: i + subGridLineCountZ)); |
1406 | if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) { |
1407 | linePosZ = static_cast<QValue3DAxis *>(axisZ)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1408 | } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) { |
1409 | linePosX = calculateCategoryGridLinePosition(axis: axisZ, index: i); |
1410 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1411 | } |
1412 | positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ)); |
1413 | lineNode->setRotation(lineRotation); |
1414 | } |
1415 | for (int i = 0; i < gridLineCountZ; i++) { |
1416 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterZ()->objectAt(index: i + gridLineCountZ)); |
1417 | if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) { |
1418 | linePosZ = static_cast<QValue3DAxis *>(axisZ)->gridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1419 | } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) { |
1420 | linePosX = calculateCategoryGridLinePosition(axis: axisZ, index: i); |
1421 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1422 | } |
1423 | positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ)); |
1424 | lineNode->setRotation(lineRotation); |
1425 | } |
1426 | } |
1427 | |
1428 | // Side horizontal line |
1429 | linePosZ = 0.0f; |
1430 | scale = m_scaleWithBackground.y(); |
1431 | rotation = QVector3D(180.0f, -90.0f, 0.0f); |
1432 | if (xFlipped) |
1433 | rotation.setY(90.0f); |
1434 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1435 | for (int i = 0; i < gridLineCountY; i++) { |
1436 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterY()->objectAt(index: i)); |
1437 | if (axisY->type() == QAbstract3DAxis::AxisTypeValue) |
1438 | linePosY = static_cast<QValue3DAxis *>(axisY)->gridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1439 | else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory) |
1440 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1441 | positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ)); |
1442 | lineNode->setRotation(lineRotation); |
1443 | } |
1444 | |
1445 | for (int i = 0; i < subGridLineCountY; i++) { |
1446 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterY()->objectAt(index: i)); |
1447 | if (axisY->type() == QAbstract3DAxis::AxisTypeValue) |
1448 | linePosY = static_cast<QValue3DAxis *>(axisY)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1449 | else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory) |
1450 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1451 | positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ)); |
1452 | lineNode->setRotation(lineRotation); |
1453 | } |
1454 | |
1455 | // Floor vertical line |
1456 | linePosZ = 0.0f; |
1457 | linePosY = -backgroundScale.y(); |
1458 | rotation = QVector3D(-90.0f, 90.0f, 0.0f); |
1459 | if (yFlipped) { |
1460 | linePosY *= -1.0f; |
1461 | rotation.setZ(180.0f); |
1462 | } |
1463 | scale = m_scaleWithBackground.x(); |
1464 | for (int i = 0; i < subGridLineCountX; i++) { |
1465 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(index: i)); |
1466 | if (axisX->type() == QAbstract3DAxis::AxisTypeValue) { |
1467 | linePosX = static_cast<QValue3DAxis *>(axisX)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1468 | } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) { |
1469 | linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i); |
1470 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1471 | } |
1472 | if (isPolar()) { |
1473 | lineNode->setPosition(QVector3D(.0f, linePosY, .0f)); |
1474 | rotation.setY(static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * 360.0f); |
1475 | } else { |
1476 | positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ)); |
1477 | } |
1478 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1479 | lineNode->setRotation(lineRotation); |
1480 | } |
1481 | for (int i = 0; i < gridLineCountX; i++) { |
1482 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(index: i)); |
1483 | if (axisX->type() == QAbstract3DAxis::AxisTypeValue) { |
1484 | linePosX = static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1485 | } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) { |
1486 | linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i); |
1487 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1488 | } |
1489 | if (isPolar()) { |
1490 | lineNode->setPosition(QVector3D(.0f, linePosY, .0f)); |
1491 | rotation.setY(static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * 360.0f); |
1492 | } else { |
1493 | positionAndScaleLine(lineNode, scale: scaleZ, position: QVector3D(linePosX, linePosY, linePosZ)); |
1494 | } |
1495 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1496 | lineNode->setRotation(lineRotation); |
1497 | } |
1498 | |
1499 | // Back horizontal line |
1500 | linePosX = 0.0f; |
1501 | linePosZ = -backgroundScale.z(); |
1502 | rotation = QVector3D(0.0f, 0.0f, 0.0f); |
1503 | if (zFlipped) { |
1504 | linePosZ *= -1.0f; |
1505 | rotation.setX(180.0f); |
1506 | } |
1507 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1508 | scale = m_scaleWithBackground.y(); |
1509 | for (int i = 0; i < subGridLineCountY; i++) { |
1510 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterY()->objectAt(index: i + subGridLineCountY)); |
1511 | if (axisY->type() == QAbstract3DAxis::AxisTypeValue) |
1512 | linePosY = static_cast<QValue3DAxis *>(axisY)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1513 | else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory) |
1514 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1515 | positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ)); |
1516 | lineNode->setRotation(lineRotation); |
1517 | } |
1518 | |
1519 | for (int i = 0; i < gridLineCountY; i++) { |
1520 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterY()->objectAt(index: i + gridLineCountY)); |
1521 | if (axisY->type() == QAbstract3DAxis::AxisTypeValue) |
1522 | linePosY = static_cast<QValue3DAxis *>(axisY)->gridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1523 | else if (axisY->type() == QAbstract3DAxis::AxisTypeCategory) |
1524 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1525 | positionAndScaleLine(lineNode, scale: scaleX, position: QVector3D(linePosX, linePosY, linePosZ)); |
1526 | lineNode->setRotation(lineRotation); |
1527 | } |
1528 | |
1529 | // Back vertical line |
1530 | linePosY = 0.0f; |
1531 | scale = m_scaleWithBackground.x(); |
1532 | rotation = QVector3D(0.0f, 0.0f, 0.0f); |
1533 | if (zFlipped) |
1534 | rotation.setY(180.0f); |
1535 | lineRotation = Utils::calculateRotation(xyzRotations: rotation); |
1536 | if (m_hasVerticalSegmentLine) { |
1537 | for (int i = 0; i < gridLineCountX; i++) { |
1538 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(segmentLineRepeaterX()->objectAt(index: i + gridLineCountX)); |
1539 | if (axisX->type() == QAbstract3DAxis::AxisTypeValue) { |
1540 | linePosX = static_cast<QValue3DAxis *>(axisX)->gridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1541 | } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) { |
1542 | linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i); |
1543 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1544 | } |
1545 | positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ)); |
1546 | lineNode->setRotation(lineRotation); |
1547 | } |
1548 | |
1549 | for (int i = 0; i < subGridLineCountX; i++) { |
1550 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(subsegmentLineRepeaterX()->objectAt(index: i + subGridLineCountX)); |
1551 | if (axisX->type() == QAbstract3DAxis::AxisTypeValue) { |
1552 | linePosX = static_cast<QValue3DAxis *>(axisX)->subGridPositionAt(gridLine: i) * scale * 2.0f - scale; |
1553 | } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) { |
1554 | linePosX = calculateCategoryGridLinePosition(axis: axisX, index: i); |
1555 | linePosY = calculateCategoryGridLinePosition(axis: axisY, index: i); |
1556 | } |
1557 | positionAndScaleLine(lineNode, scale: scaleY, position: QVector3D(linePosX, linePosY, linePosZ)); |
1558 | lineNode->setRotation(lineRotation); |
1559 | } |
1560 | } |
1561 | } |
1562 | |
1563 | float QQuickGraphsItem::fontScaleFactor(float pointSize) |
1564 | { |
1565 | return m_fontScaleFactorA * pointSize |
1566 | + m_fontScaleFactorB; |
1567 | } |
1568 | |
1569 | float QQuickGraphsItem::labelAdjustment(float width) |
1570 | { |
1571 | float a = -2.43761e-13f; |
1572 | float b = 4.23579e-10f; |
1573 | float c = 0.00414881f; |
1574 | |
1575 | float factor = a * qPow(x: width, y: 3) + b * qPow(x: width, y: 2) + c; |
1576 | #if defined(Q_OS_WIN) |
1577 | factor *= .8f; |
1578 | #endif |
1579 | float ret = width * .5f * factor; |
1580 | return ret; |
1581 | } |
1582 | |
1583 | void QQuickGraphsItem::updateLabels() |
1584 | { |
1585 | auto axisX = m_controller->axisX(); |
1586 | |
1587 | auto labels = axisX->labels(); |
1588 | int labelCount = labels.size(); |
1589 | float labelAutoAngle = axisX->labelAutoRotation(); |
1590 | float labelAngleFraction = labelAutoAngle / 90.0f; |
1591 | float fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction; |
1592 | float fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction; |
1593 | |
1594 | QVector3D labelRotation = QVector3D(0.0f, 0.0f, 0.0f); |
1595 | |
1596 | float xPos = 0.0f; |
1597 | float yPos = 0.0f; |
1598 | float zPos = 0.0f; |
1599 | |
1600 | const bool xFlipped = isXFlipped(); |
1601 | const bool yFlipped = isYFlipped(); |
1602 | const bool zFlipped = isZFlipped(); |
1603 | |
1604 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
1605 | |
1606 | if (labelAutoAngle == 0.0f) { |
1607 | labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); |
1608 | if (xFlipped) |
1609 | labelRotation.setY(-90.0f); |
1610 | if (yFlipped) { |
1611 | if (xFlipped) |
1612 | labelRotation.setY(-90.0f); |
1613 | else |
1614 | labelRotation.setY(90.0f); |
1615 | labelRotation.setX(90.0f); |
1616 | } |
1617 | } else { |
1618 | if (xFlipped) |
1619 | labelRotation.setY(-90.0f); |
1620 | else |
1621 | labelRotation.setY(90.0f); |
1622 | if (yFlipped) { |
1623 | if (zFlipped) { |
1624 | if (xFlipped) { |
1625 | labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) |
1626 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
1627 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
1628 | } else { |
1629 | labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX) |
1630 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
1631 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
1632 | } |
1633 | } else { |
1634 | if (xFlipped) { |
1635 | labelRotation.setX(90.0f + fractionCamX |
1636 | * -(labelAutoAngle + fractionCamY) / labelAutoAngle); |
1637 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
1638 | } else { |
1639 | labelRotation.setX(90.0f - fractionCamX |
1640 | * (-labelAutoAngle - fractionCamY) / labelAutoAngle); |
1641 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
1642 | } |
1643 | } |
1644 | } else { |
1645 | if (zFlipped) { |
1646 | if (xFlipped) { |
1647 | labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX) |
1648 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
1649 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
1650 | } else { |
1651 | labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX) |
1652 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
1653 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
1654 | } |
1655 | } else { |
1656 | if (xFlipped) { |
1657 | labelRotation.setX(-90.0f - fractionCamX |
1658 | * (-labelAutoAngle + fractionCamY) / labelAutoAngle); |
1659 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
1660 | } else { |
1661 | labelRotation.setX(-90.0f + fractionCamX |
1662 | * -(labelAutoAngle - fractionCamY) / labelAutoAngle); |
1663 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
1664 | } |
1665 | } |
1666 | } |
1667 | } |
1668 | if (isPolar()) |
1669 | labelRotation.setY(0.0f); |
1670 | auto totalRotation = Utils::calculateRotation(xyzRotations: labelRotation); |
1671 | |
1672 | float scale = backgroundScale.x() - m_backgroundScaleMargin.x(); |
1673 | |
1674 | float pointSize = m_controller->activeTheme()->font().pointSizeF(); |
1675 | |
1676 | float textPadding = pointSize * .5f; |
1677 | |
1678 | float labelsMaxWidth = float(findLabelsMaxWidth(labels: axisX->labels())) + textPadding; |
1679 | QFontMetrics fm(m_controller->activeTheme()->font()); |
1680 | float labelHeight = fm.height() + textPadding; |
1681 | |
1682 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
1683 | float fontRatio = labelsMaxWidth / labelHeight; |
1684 | QVector3D fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
1685 | auto adjustment = labelAdjustment(width: labelsMaxWidth); |
1686 | zPos = backgroundScale.z() + adjustment + m_labelMargin; |
1687 | |
1688 | adjustment *= qAbs(t: qSin(v: qDegreesToRadians(degrees: labelRotation.z()))); |
1689 | yPos = backgroundScale.y() + adjustment; |
1690 | |
1691 | if (!yFlipped) |
1692 | yPos *= -1.0f; |
1693 | |
1694 | if (zFlipped) |
1695 | zPos *= -1.0f; |
1696 | |
1697 | auto labelTrans = QVector3D(0.0f, yPos, zPos); |
1698 | float polarLabelZPos = 0.0f; |
1699 | |
1700 | if (axisX->type() == QAbstract3DAxis::AxisTypeValue) { |
1701 | auto valueAxisX = static_cast<QValue3DAxis *>(axisX); |
1702 | for (int i = 0; i < repeaterX()->count(); i++) { |
1703 | if (labelCount <= i) |
1704 | break; |
1705 | auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i)); |
1706 | if (isPolar()) { |
1707 | if (i == repeaterX()->count() - 1) { |
1708 | obj->setVisible(false); |
1709 | break; |
1710 | } |
1711 | float rad = qDegreesToRadians(degrees: valueAxisX->labelPositionAt(index: i) * 360.0f); |
1712 | labelTrans.setX(-qSin(v: rad) * -scale + qSin(v: rad) * m_labelMargin * 2.0f); |
1713 | labelTrans.setY(yPos); |
1714 | labelTrans.setZ(qCos(v: rad) * -scale - qCos(v: rad) * m_labelMargin * 2.0f); |
1715 | if (i == 0) |
1716 | polarLabelZPos = labelTrans.z(); |
1717 | } else { |
1718 | labelTrans.setX(valueAxisX->labelPositionAt(index: i) * scale * 2.0f - scale); |
1719 | } |
1720 | obj->setObjectName(QStringLiteral("ElementAxisXLabel" )); |
1721 | obj->setScale(fontScaled); |
1722 | obj->setPosition(labelTrans); |
1723 | obj->setRotation(totalRotation); |
1724 | obj->setProperty(name: "labelText" , value: labels[i]); |
1725 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
1726 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
1727 | } |
1728 | } else if (axisX->type() == QAbstract3DAxis::AxisTypeCategory) { |
1729 | for (int i = 0; i < repeaterX()->count(); i++) { |
1730 | if (labelCount <= i) |
1731 | break; |
1732 | labelTrans = calculateCategoryLabelPosition(axis: axisX, labelPosition: labelTrans, index: i); |
1733 | auto obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(index: i)); |
1734 | obj->setObjectName(QStringLiteral("ElementAxisXLabel" )); |
1735 | obj->setScale(fontScaled); |
1736 | obj->setPosition(labelTrans); |
1737 | obj->setRotation(totalRotation); |
1738 | obj->setProperty(name: "labelText" , value: labels[i]); |
1739 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
1740 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
1741 | } |
1742 | } |
1743 | |
1744 | float x = labelTrans.x(); |
1745 | labelTrans.setX(0.0f); |
1746 | updateXTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, scale: fontScaled); |
1747 | if (isPolar()) { |
1748 | m_titleLabelX->setZ(polarLabelZPos - m_labelMargin * 2.0f); |
1749 | m_titleLabelX->setRotation(totalRotation); |
1750 | } |
1751 | labelTrans.setX(x); |
1752 | |
1753 | auto axisY = m_controller->axisY(); |
1754 | labels = axisY->labels(); |
1755 | labelCount = labels.size(); |
1756 | labelAutoAngle = axisY->labelAutoRotation(); |
1757 | labelAngleFraction = labelAutoAngle / 90.0f; |
1758 | fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction; |
1759 | fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction; |
1760 | |
1761 | QVector3D sideLabelRotation(0.0f, -90.0f, 0.0f); |
1762 | QVector3D backLabelRotation(0.0f, 0.0f, 0.0f); |
1763 | |
1764 | if (labelAutoAngle == 0.0f) { |
1765 | if (!xFlipped) |
1766 | sideLabelRotation.setY(90.0f); |
1767 | if (zFlipped) |
1768 | backLabelRotation.setY(180.f); |
1769 | } else { |
1770 | // Orient side labels somewhat towards the camera |
1771 | if (xFlipped) { |
1772 | if (zFlipped) |
1773 | backLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX); |
1774 | else |
1775 | backLabelRotation.setY(-fractionCamX); |
1776 | sideLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX); |
1777 | } else { |
1778 | if (zFlipped) |
1779 | backLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX); |
1780 | else |
1781 | backLabelRotation.setY(-fractionCamX); |
1782 | sideLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX); |
1783 | } |
1784 | } |
1785 | |
1786 | backLabelRotation.setX(-fractionCamY); |
1787 | sideLabelRotation.setX(-fractionCamY); |
1788 | |
1789 | totalRotation = Utils::calculateRotation(xyzRotations: sideLabelRotation); |
1790 | scale = backgroundScale.y() - m_backgroundScaleMargin.y(); |
1791 | labelsMaxWidth = float(findLabelsMaxWidth(labels: axisY->labels())) + textPadding; |
1792 | fontRatio = labelsMaxWidth / labelHeight; |
1793 | fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
1794 | |
1795 | xPos = backgroundScale.x(); |
1796 | if (!xFlipped) |
1797 | xPos *= -1.0f; |
1798 | labelTrans.setX(xPos); |
1799 | |
1800 | adjustment = labelAdjustment(width: labelsMaxWidth); |
1801 | zPos = backgroundScale.z() + adjustment + m_labelMargin; |
1802 | if (zFlipped) |
1803 | zPos *= -1.0f; |
1804 | labelTrans.setZ(zPos); |
1805 | |
1806 | for (int i = 0; i < repeaterY()->count() / 2; i++) { |
1807 | if (labelCount <= i) |
1808 | break; |
1809 | auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(index: i)); |
1810 | labelTrans.setY(static_cast<QValue3DAxis *>(axisY)->labelPositionAt(index: i) * scale * 2.0f - scale); |
1811 | obj->setObjectName(QStringLiteral("ElementAxisYLabel" )); |
1812 | obj->setScale(fontScaled); |
1813 | obj->setPosition(labelTrans); |
1814 | obj->setRotation(totalRotation); |
1815 | obj->setProperty(name: "labelText" , value: labels[i]); |
1816 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
1817 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
1818 | } |
1819 | |
1820 | auto sideLabelTrans = labelTrans; |
1821 | auto totalSideLabelRotation = totalRotation; |
1822 | |
1823 | auto axisZ = m_controller->axisZ(); |
1824 | labels = axisZ->labels(); |
1825 | labelCount = labels.size(); |
1826 | labelAutoAngle = axisZ->labelAutoRotation(); |
1827 | labelAngleFraction = labelAutoAngle / 90.0f; |
1828 | fractionCamX = m_controller->scene()->activeCamera()->xRotation() * labelAngleFraction; |
1829 | fractionCamY = m_controller->scene()->activeCamera()->yRotation() * labelAngleFraction; |
1830 | |
1831 | if (labelAutoAngle == 0.0f) { |
1832 | labelRotation = QVector3D(90.0f, 0.0f, 0.0f); |
1833 | if (zFlipped) |
1834 | labelRotation.setY(180.0f); |
1835 | if (yFlipped) { |
1836 | if (zFlipped) |
1837 | labelRotation.setY(180.0f); |
1838 | else |
1839 | labelRotation.setY(0.0f); |
1840 | labelRotation.setX(90.0f); |
1841 | } else { |
1842 | labelRotation.setX(-90.0f); |
1843 | } |
1844 | } else { |
1845 | if (zFlipped) |
1846 | labelRotation.setY(180.0f); |
1847 | else |
1848 | labelRotation.setY(0.0f); |
1849 | if (yFlipped) { |
1850 | if (zFlipped) { |
1851 | if (xFlipped) { |
1852 | labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) |
1853 | * (-labelAutoAngle - fractionCamY) / labelAutoAngle); |
1854 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
1855 | } else { |
1856 | labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX) |
1857 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
1858 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
1859 | } |
1860 | } else { |
1861 | if (xFlipped) { |
1862 | labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX) |
1863 | * -(labelAutoAngle + fractionCamY) / labelAutoAngle); |
1864 | labelRotation.setZ(-labelAutoAngle - fractionCamY); |
1865 | } else { |
1866 | labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX) |
1867 | * (labelAutoAngle + fractionCamY) / labelAutoAngle); |
1868 | labelRotation.setZ(labelAutoAngle + fractionCamY); |
1869 | } |
1870 | } |
1871 | } else { |
1872 | if (zFlipped) { |
1873 | if (xFlipped) { |
1874 | labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX) |
1875 | * (-labelAutoAngle + fractionCamY) / labelAutoAngle); |
1876 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
1877 | } else { |
1878 | labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX) |
1879 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
1880 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
1881 | } |
1882 | } else { |
1883 | if (xFlipped) { |
1884 | labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX) |
1885 | * (-labelAutoAngle + fractionCamY) / labelAutoAngle); |
1886 | labelRotation.setZ(labelAutoAngle - fractionCamY); |
1887 | } else { |
1888 | labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX) |
1889 | * (labelAutoAngle - fractionCamY) / labelAutoAngle); |
1890 | labelRotation.setZ(-labelAutoAngle + fractionCamY); |
1891 | } |
1892 | } |
1893 | } |
1894 | } |
1895 | |
1896 | totalRotation = Utils::calculateRotation(xyzRotations: labelRotation); |
1897 | |
1898 | scale = backgroundScale.z() - m_backgroundScaleMargin.z(); |
1899 | labelsMaxWidth = float(findLabelsMaxWidth(labels: axisZ->labels())) + textPadding ; |
1900 | fontRatio = labelsMaxWidth / labelHeight; |
1901 | fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
1902 | adjustment = labelAdjustment(width: labelsMaxWidth); |
1903 | xPos = backgroundScale.x() + adjustment + m_labelMargin; |
1904 | if (xFlipped) |
1905 | xPos *= -1.0f; |
1906 | |
1907 | adjustment *= qAbs(t: qSin(v: qDegreesToRadians(degrees: labelRotation.z()))); |
1908 | yPos = backgroundScale.y() + adjustment; |
1909 | if (!yFlipped) |
1910 | yPos *= -1.0f; |
1911 | |
1912 | labelTrans = QVector3D(xPos, yPos, 0.0f); |
1913 | |
1914 | if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) { |
1915 | auto valueAxisZ = static_cast<QValue3DAxis *>(axisZ); |
1916 | float offset = m_controller->radialLabelOffset(); |
1917 | for (int i = 0; i < repeaterZ()->count(); i++) { |
1918 | if (labelCount <= i) |
1919 | break; |
1920 | auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
1921 | if (isPolar()) { |
1922 | float polarX = backgroundScale.x() * offset + m_labelMargin * 2.0f; |
1923 | if (xFlipped) |
1924 | polarX *= -1; |
1925 | labelTrans.setX(polarX); |
1926 | labelTrans.setY(yPos); |
1927 | labelTrans.setZ(-valueAxisZ->labelPositionAt(index: i)); |
1928 | } else { |
1929 | labelTrans.setZ(valueAxisZ->labelPositionAt(index: i) * scale * -2.0f + scale); |
1930 | } |
1931 | obj->setObjectName(QStringLiteral("ElementAxisZLabel" )); |
1932 | obj->setScale(fontScaled); |
1933 | obj->setPosition(labelTrans); |
1934 | obj->setRotation(totalRotation); |
1935 | obj->setProperty(name: "labelText" , value: labels[i]); |
1936 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
1937 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
1938 | } |
1939 | } else if (axisZ->type() == QAbstract3DAxis::AxisTypeCategory) { |
1940 | for (int i = 0; i < repeaterZ()->count(); i++) { |
1941 | if (labelCount <= i) |
1942 | break; |
1943 | labelTrans = calculateCategoryLabelPosition(axis: axisZ, labelPosition: labelTrans, index: i); |
1944 | auto obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
1945 | obj->setObjectName(QStringLiteral("ElementAxisZLabel" )); |
1946 | obj->setScale(fontScaled); |
1947 | obj->setPosition(labelTrans); |
1948 | obj->setRotation(totalRotation); |
1949 | obj->setProperty(name: "labelText" , value: labels[i]); |
1950 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
1951 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
1952 | } |
1953 | } |
1954 | |
1955 | float z = labelTrans.z(); |
1956 | labelTrans.setZ(0.0f); |
1957 | updateZTitle(labelRotation, labelTrans, totalRotation, labelsMaxWidth, scale: fontScaled); |
1958 | labelTrans.setZ(z); |
1959 | |
1960 | labels = axisY->labels(); |
1961 | labelCount = labels.size(); |
1962 | totalRotation = Utils::calculateRotation(xyzRotations: backLabelRotation); |
1963 | scale = backgroundScale.y() - m_backgroundScaleMargin.y(); |
1964 | labelsMaxWidth = float(findLabelsMaxWidth(labels: axisY->labels())) + textPadding; |
1965 | fontRatio = labelsMaxWidth / labelHeight; |
1966 | fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
1967 | adjustment = labelAdjustment(width: labelsMaxWidth); |
1968 | |
1969 | xPos = backgroundScale.x() + adjustment + m_labelMargin; |
1970 | if (xFlipped) |
1971 | xPos *= -1.0f; |
1972 | labelTrans.setX(xPos); |
1973 | |
1974 | zPos = -backgroundScale.z(); |
1975 | if (zFlipped) |
1976 | zPos *= -1.0f; |
1977 | labelTrans.setZ(zPos); |
1978 | |
1979 | for (int i = 0; i < repeaterY()->count() / 2; i++) { |
1980 | if (labelCount <= i) |
1981 | break; |
1982 | auto obj = static_cast<QQuick3DNode *>(repeaterY()->objectAt(index: i + (repeaterY()->count() / 2))); |
1983 | labelTrans.setY(static_cast<QValue3DAxis *>(axisY)->labelPositionAt(index: i) * scale * 2.0f - scale); |
1984 | obj->setScale(fontScaled); |
1985 | obj->setPosition(labelTrans); |
1986 | obj->setRotation(totalRotation); |
1987 | obj->setProperty(name: "labelText" , value: labels[i]); |
1988 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
1989 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
1990 | } |
1991 | |
1992 | auto backLabelTrans = labelTrans; |
1993 | auto totalBackLabelRotation = totalRotation; |
1994 | updateYTitle(sideLabelRotation, backLabelRotation, |
1995 | sideLabelTrans, backLabelTrans, |
1996 | totalSideRotation: totalSideLabelRotation, totalBackRotation: totalBackLabelRotation, |
1997 | labelsMaxWidth, scale: fontScaled); |
1998 | } |
1999 | |
2000 | void QQuickGraphsItem::updateRadialLabelOffset() |
2001 | { |
2002 | if (!isPolar()) |
2003 | return; |
2004 | |
2005 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
2006 | QVector3D backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
2007 | float offset = m_controller->radialLabelOffset(); |
2008 | float scale = backgroundScale.x() + (m_backgroundScaleMargin.x()); |
2009 | float polarX = scale * offset + m_labelMargin * 2.0f; |
2010 | if (isXFlipped()) |
2011 | polarX *= -1; |
2012 | if (axisZ->type() == QAbstract3DAxis::AxisTypeValue) { |
2013 | for (int i = 0; i < repeaterZ()->count(); i++) { |
2014 | QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(index: i)); |
2015 | QVector3D pos = obj->position(); |
2016 | pos.setX(polarX); |
2017 | obj->setPosition(pos); |
2018 | } |
2019 | } |
2020 | |
2021 | polarX += m_labelMargin * 2.5f; |
2022 | QVector3D pos = m_titleLabelZ->position(); |
2023 | pos.setX(polarX); |
2024 | m_titleLabelZ->setPosition(pos); |
2025 | } |
2026 | |
2027 | void QQuickGraphsItem::positionAndScaleLine(QQuick3DNode *lineNode, QVector3D scale, QVector3D position) |
2028 | { |
2029 | lineNode->setScale(scale); |
2030 | lineNode->setPosition(position); |
2031 | } |
2032 | |
2033 | void QQuickGraphsItem::graphPositionAt(const QPoint &point) |
2034 | { |
2035 | bool isHit = false; |
2036 | auto result = pick(x: point.x(), y: point.y()); |
2037 | if (result.objectHit()) { |
2038 | isHit = true; |
2039 | m_controller->setQueriedGraphPosition(QVector3D(result.scenePosition().x(), |
2040 | result.scenePosition().y(), |
2041 | result.scenePosition().z())); |
2042 | } |
2043 | |
2044 | if (!isHit) |
2045 | m_controller->setQueriedGraphPosition(QVector3D(0,0,0)); |
2046 | |
2047 | emit queriedGraphPositionChanged(data: m_controller->queriedGraphPosition()); |
2048 | emit m_controller->queriedGraphPositionChanged(data: m_controller->queriedGraphPosition()); |
2049 | m_controller->setGraphPositionQueryPending(false); |
2050 | scene()->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); |
2051 | } |
2052 | |
2053 | void QQuickGraphsItem::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) |
2054 | { |
2055 | if (quality != QAbstract3DGraph::ShadowQualityNone) { |
2056 | light()->setCastsShadow(true); |
2057 | light()->setShadowFactor(25.f); |
2058 | |
2059 | QQuick3DAbstractLight::QSSGShadowMapQuality shadowMapQuality; |
2060 | switch (quality) { |
2061 | case QAbstract3DGraph::ShadowQualityLow: |
2062 | case QAbstract3DGraph::ShadowQualitySoftLow: |
2063 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityMedium; |
2064 | break; |
2065 | case QAbstract3DGraph::ShadowQualityMedium: |
2066 | case QAbstract3DGraph::ShadowQualitySoftMedium: |
2067 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh; |
2068 | break; |
2069 | case QAbstract3DGraph::ShadowQualityHigh: |
2070 | case QAbstract3DGraph::ShadowQualitySoftHigh: |
2071 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityVeryHigh; |
2072 | break; |
2073 | default: |
2074 | shadowMapQuality = QQuick3DAbstractLight::QSSGShadowMapQuality::ShadowMapQualityHigh; |
2075 | break; |
2076 | } |
2077 | light()->setShadowMapQuality(shadowMapQuality); |
2078 | if (quality >= QAbstract3DGraph::ShadowQualitySoftLow) |
2079 | light()->setShadowFilter(10.f); |
2080 | else |
2081 | light()->setShadowFilter(2.f); |
2082 | } else { |
2083 | light()->setCastsShadow(false); |
2084 | light()->setShadowFactor(0.f); |
2085 | } |
2086 | } |
2087 | |
2088 | void QQuickGraphsItem::updateItemLabel(const QVector3D &position) |
2089 | { |
2090 | if (m_labelPosition != position) |
2091 | m_labelPosition = position; |
2092 | QVector3D pos2d = mapFrom3DScene(scenePos: m_labelPosition); |
2093 | int pointSize = m_controller->activeTheme()->font().pointSize(); |
2094 | float scale = m_labelScale.x() * ((-10.0f * pointSize) + 650.0f) / pos2d.z(); |
2095 | if (m_sliceView && m_sliceView->isVisible()) |
2096 | m_itemLabel->setScale(scale * .2f); |
2097 | else |
2098 | m_itemLabel->setScale(scale); |
2099 | pos2d.setX(pos2d.x() - (m_itemLabel->width() / 2.f)); |
2100 | pos2d.setY(pos2d.y() - (m_itemLabel->height() / 2.f) - (m_itemLabel->height() * m_itemLabel->scale())); |
2101 | m_itemLabel->setPosition(pos2d.toPointF()); |
2102 | } |
2103 | |
2104 | void QQuickGraphsItem::createVolumeMaterial(QCustom3DVolume *volume, Volume &volumeItem) |
2105 | { |
2106 | if (volumeItem.texture) |
2107 | volumeItem.texture->deleteLater(); |
2108 | volumeItem.texture = new QQuick3DTexture(); |
2109 | auto texture = volumeItem.texture; |
2110 | |
2111 | texture->setParent(this); |
2112 | texture->setMinFilter(QQuick3DTexture::Filter::Nearest); |
2113 | texture->setMagFilter(QQuick3DTexture::Filter::Nearest); |
2114 | texture->setHorizontalTiling(QQuick3DTexture::TilingMode::ClampToEdge); |
2115 | texture->setVerticalTiling(QQuick3DTexture::TilingMode::ClampToEdge); |
2116 | |
2117 | if (volumeItem.textureData) |
2118 | volumeItem.textureData->deleteLater(); |
2119 | volumeItem.textureData = new QQuick3DTextureData(); |
2120 | auto textureData = volumeItem.textureData; |
2121 | |
2122 | int color8Bit = (volume->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; |
2123 | |
2124 | textureData->setParent(texture); |
2125 | textureData->setParentItem(texture); |
2126 | textureData->setSize(QSize(volume->textureWidth(), volume->textureHeight())); |
2127 | textureData->setDepth(volume->textureDepth()); |
2128 | if (color8Bit) |
2129 | textureData->setFormat(QQuick3DTextureData::R8); |
2130 | else |
2131 | textureData->setFormat(QQuick3DTextureData::RGBA8); |
2132 | textureData->setTextureData(QByteArray::fromRawData( |
2133 | data: reinterpret_cast<const char *>(volume->textureData()->constData()), |
2134 | size: volume->textureData()->size())); |
2135 | texture->setTextureData(textureData); |
2136 | |
2137 | QObject::connect(sender: volume, signal: &QCustom3DVolume::textureDataChanged, context: this, slot: [this, volume] { |
2138 | m_customVolumes[volume].updateTextureData = true; |
2139 | }); |
2140 | |
2141 | if (color8Bit) { |
2142 | if (volumeItem.colorTexture) |
2143 | volumeItem.colorTexture->deleteLater(); |
2144 | volumeItem.colorTexture = new QQuick3DTexture(); |
2145 | auto colorTexture = volumeItem.colorTexture; |
2146 | |
2147 | colorTexture->setParent(this); |
2148 | colorTexture->setMinFilter(QQuick3DTexture::Filter::Nearest); |
2149 | colorTexture->setMagFilter(QQuick3DTexture::Filter::Nearest); |
2150 | |
2151 | QByteArray colorTableBytes; |
2152 | const QList<QRgb> &colorTable = volume->colorTable(); |
2153 | for (int i = 0; i < colorTable.size(); i++) { |
2154 | QRgb shifted = qRgba(r: qBlue(rgb: colorTable[i]), g: qGreen(rgb: colorTable[i]), b: qRed(rgb: colorTable[i]), a: qAlpha(rgb: colorTable[i])); |
2155 | colorTableBytes.append(a: QByteArray::fromRawData(data: reinterpret_cast<const char *>(&shifted), size: sizeof(shifted))); |
2156 | } |
2157 | |
2158 | if (volumeItem.colorTextureData) |
2159 | volumeItem.colorTextureData->deleteLater(); |
2160 | volumeItem.colorTextureData = new QQuick3DTextureData(); |
2161 | auto colorTextureData = volumeItem.colorTextureData; |
2162 | |
2163 | colorTextureData->setParent(colorTexture); |
2164 | colorTextureData->setParentItem(colorTexture); |
2165 | colorTextureData->setSize(QSize(volume->colorTable().size(), 1)); |
2166 | colorTextureData->setFormat(QQuick3DTextureData::RGBA8); |
2167 | colorTextureData->setTextureData(colorTableBytes); |
2168 | colorTexture->setTextureData(colorTextureData); |
2169 | |
2170 | QObject::connect(sender: volume, signal: &QCustom3DVolume::colorTableChanged, context: this, slot: [this, volume] { |
2171 | m_customVolumes[volume].updateColorTextureData = true; |
2172 | }); |
2173 | } |
2174 | |
2175 | auto model = volumeItem.model; |
2176 | QQmlListReference materialsRef(model, "materials" ); |
2177 | |
2178 | QQuick3DCustomMaterial *material = nullptr; |
2179 | |
2180 | if (volume->drawSlices()) |
2181 | material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeSliceMaterial" )); |
2182 | else if (volume->useHighDefShader()) |
2183 | material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeMaterial" )); |
2184 | else |
2185 | material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeLowDefMaterial" )); |
2186 | |
2187 | auto textureSamplerVariant = material->property(name: "textureSampler" ); |
2188 | auto textureSampler = textureSamplerVariant.value<QQuick3DShaderUtilsTextureInput *>(); |
2189 | textureSampler->setTexture(volumeItem.texture); |
2190 | |
2191 | if (color8Bit) { |
2192 | auto colorSamplerVariant = material->property(name: "colorSampler" ); |
2193 | auto colorSampler = colorSamplerVariant.value<QQuick3DShaderUtilsTextureInput *>(); |
2194 | colorSampler->setTexture(volumeItem.colorTexture); |
2195 | } |
2196 | |
2197 | material->setProperty(name: "textureDimensions" , value: QVector3D(1.0f / float(volume->textureWidth()), |
2198 | 1.0f / float(volume->textureHeight()), |
2199 | 1.0f / float(volume->textureDepth()))); |
2200 | |
2201 | materialsRef.append(material); |
2202 | |
2203 | volumeItem.useHighDefShader = volume->useHighDefShader(); |
2204 | volumeItem.drawSlices = volume->drawSlices(); |
2205 | } |
2206 | |
2207 | QQuick3DModel *QQuickGraphsItem::createSliceFrame(Volume &volumeItem) |
2208 | { |
2209 | QQuick3DModel *model = new QQuick3DModel(); |
2210 | model->setParent(volumeItem.model); |
2211 | model->setParentItem(volumeItem.model); |
2212 | model->setSource(QUrl(QStringLiteral("defaultMeshes/barMeshFull" ))); |
2213 | model->setScale(QVector3D(1, 1, 0.01f)); |
2214 | model->setDepthBias(-100.0f); |
2215 | |
2216 | QQmlListReference materialsRef(model, "materials" ); |
2217 | QQuick3DCustomMaterial *material = createQmlCustomMaterial(QStringLiteral(":/materials/VolumeFrameMaterial" )); |
2218 | material->setParent(model); |
2219 | material->setParentItem(model); |
2220 | material->setCullMode(QQuick3DMaterial::NoCulling); |
2221 | materialsRef.append(material); |
2222 | |
2223 | return model; |
2224 | } |
2225 | |
2226 | void QQuickGraphsItem::updateSliceFrameMaterials(QCustom3DVolume *volume, Volume &volumeItem) |
2227 | { |
2228 | QQmlListReference materialsRefX(volumeItem.sliceFrameX, "materials" ); |
2229 | QQmlListReference materialsRefY(volumeItem.sliceFrameY, "materials" ); |
2230 | QQmlListReference materialsRefZ(volumeItem.sliceFrameZ, "materials" ); |
2231 | |
2232 | QVector2D frameWidth; |
2233 | QVector3D frameScaling; |
2234 | |
2235 | frameScaling = QVector3D(volume->scaling().z() |
2236 | + (volume->scaling().z() * volume->sliceFrameGaps().z()) |
2237 | + (volume->scaling().z() * volume->sliceFrameWidths().z()), |
2238 | volume->scaling().y() |
2239 | + (volume->scaling().y() * volume->sliceFrameGaps().y()) |
2240 | + (volume->scaling().y() * volume->sliceFrameWidths().y()), |
2241 | volume->scaling().x() * volume->sliceFrameThicknesses().x()); |
2242 | |
2243 | frameWidth = QVector2D(volume->scaling().z() * volume->sliceFrameWidths().z(), |
2244 | volume->scaling().y() * volume->sliceFrameWidths().y()); |
2245 | |
2246 | frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); |
2247 | frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); |
2248 | |
2249 | auto material = materialsRefX.at(0); |
2250 | material->setProperty(name: "color" , value: volume->sliceFrameColor()); |
2251 | material->setProperty(name: "sliceFrameWidth" , value: frameWidth); |
2252 | |
2253 | frameScaling = QVector3D(volume->scaling().x() |
2254 | + (volume->scaling().x() * volume->sliceFrameGaps().x()) |
2255 | + (volume->scaling().x() * volume->sliceFrameWidths().x()), |
2256 | volume->scaling().z() |
2257 | + (volume->scaling().z() * volume->sliceFrameGaps().z()) |
2258 | + (volume->scaling().z() * volume->sliceFrameWidths().z()), |
2259 | volume->scaling().y() * volume->sliceFrameThicknesses().y()); |
2260 | frameWidth = QVector2D(volume->scaling().x() * volume->sliceFrameWidths().x(), |
2261 | volume->scaling().z() * volume->sliceFrameWidths().z()); |
2262 | |
2263 | frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); |
2264 | frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); |
2265 | |
2266 | material = materialsRefY.at(0); |
2267 | material->setProperty(name: "color" , value: volume->sliceFrameColor()); |
2268 | material->setProperty(name: "sliceFrameWidth" , value: frameWidth); |
2269 | |
2270 | frameScaling = QVector3D(volume->scaling().x() |
2271 | + (volume->scaling().x() * volume->sliceFrameGaps().x()) |
2272 | + (volume->scaling().x() * volume->sliceFrameWidths().x()), |
2273 | volume->scaling().y() |
2274 | + (volume->scaling().y() * volume->sliceFrameGaps().y()) |
2275 | + (volume->scaling().y() * volume->sliceFrameWidths().y()), |
2276 | volume->scaling().z() * volume->sliceFrameThicknesses().z()); |
2277 | frameWidth = QVector2D(volume->scaling().x() * volume->sliceFrameWidths().x(), |
2278 | volume->scaling().y() * volume->sliceFrameWidths().y()); |
2279 | |
2280 | frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); |
2281 | frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); |
2282 | |
2283 | material = materialsRefZ.at(0); |
2284 | material->setProperty(name: "color" , value: volume->sliceFrameColor()); |
2285 | material->setProperty(name: "sliceFrameWidth" , value: frameWidth); |
2286 | } |
2287 | |
2288 | void QQuickGraphsItem::updateCustomVolumes() |
2289 | { |
2290 | QAbstract3DAxis *axisX = m_controller->axisX(); |
2291 | QAbstract3DAxis *axisY = m_controller->axisY(); |
2292 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
2293 | |
2294 | int maxX = axisX->max(); |
2295 | int minX = axisX->min(); |
2296 | int maxY = axisY->max(); |
2297 | int minY = axisY->min(); |
2298 | int maxZ = axisZ->max(); |
2299 | int minZ = axisZ->min(); |
2300 | QVector3D adjustment = m_scaleWithBackground * QVector3D(1.0f, 1.0f, -1.0f); |
2301 | bool isPolar = m_controller->isPolar(); |
2302 | |
2303 | auto itemIterator = m_customItemList.constBegin(); |
2304 | while (itemIterator != m_customItemList.constEnd()) { |
2305 | QCustom3DItem *item = itemIterator.key(); |
2306 | QQuick3DModel *model = itemIterator.value(); |
2307 | |
2308 | if (auto volume = qobject_cast<QCustom3DVolume *>(object: item)) { |
2309 | QVector3D pos = item->position(); |
2310 | if (!item->isPositionAbsolute()) { |
2311 | if (item->position().x() < minX |
2312 | || item->position().x() > maxX |
2313 | || item->position().y() < minY |
2314 | || item->position().y() > maxY |
2315 | || item->position().z() < minZ |
2316 | || item->position().z() > maxZ) { |
2317 | model->setVisible(false); |
2318 | ++itemIterator; |
2319 | continue; |
2320 | } |
2321 | float xNormalizer = maxX - minX; |
2322 | float xPos = (pos.x() - minX) / xNormalizer; |
2323 | float yNormalizer = maxY - minY; |
2324 | float yPos = (pos.y() - minY) / yNormalizer; |
2325 | float zNormalizer = maxZ - minZ; |
2326 | float zPos = (pos.z() - minZ) / zNormalizer; |
2327 | pos = QVector3D(xPos, yPos, zPos); |
2328 | if (isPolar) { |
2329 | float angle = xPos * M_PI * 2.0f; |
2330 | float radius = zPos; |
2331 | xPos = radius * qSin(v: angle) * 1.0f; |
2332 | zPos = -(radius * qCos(v: angle)) * 1.0f; |
2333 | yPos = yPos * adjustment.y() * 2.0f - adjustment.y(); |
2334 | pos = QVector3D(xPos, yPos, zPos); |
2335 | } else { |
2336 | pos = pos * adjustment * 2.0f - adjustment; |
2337 | } |
2338 | } |
2339 | model->setPosition(pos); |
2340 | |
2341 | auto &&volumeItem = m_customVolumes[volume]; |
2342 | |
2343 | QQmlListReference materialsRef(model, "materials" ); |
2344 | if (volumeItem.useHighDefShader != volume->useHighDefShader()) { |
2345 | materialsRef.clear(); |
2346 | createVolumeMaterial(volume, volumeItem); |
2347 | } |
2348 | |
2349 | if (volumeItem.drawSlices != volume->drawSlices()) { |
2350 | materialsRef.clear(); |
2351 | createVolumeMaterial(volume, volumeItem); |
2352 | } |
2353 | |
2354 | QVector3D sliceIndices((float(volume->sliceIndexX()) + 0.5f) / float(volume->textureWidth()) * 2.0 - 1.0, |
2355 | (float(volume->sliceIndexY()) + 0.5f) / float(volume->textureHeight()) * 2.0 - 1.0, |
2356 | (float(volume->sliceIndexZ()) + 0.5f) / float(volume->textureDepth()) * 2.0 - 1.0); |
2357 | |
2358 | if (volumeItem.drawSliceFrames != volume->drawSliceFrames()) { |
2359 | if (volume->drawSliceFrames()) { |
2360 | volumeItem.sliceFrameX->setVisible(true); |
2361 | volumeItem.sliceFrameY->setVisible(true); |
2362 | volumeItem.sliceFrameZ->setVisible(true); |
2363 | |
2364 | volumeItem.sliceFrameX->setRotation(QQuaternion::fromEulerAngles(pitch: 0, yaw: 90, roll: 0)); |
2365 | volumeItem.sliceFrameY->setRotation(QQuaternion::fromEulerAngles(pitch: 90, yaw: 0, roll: 0)); |
2366 | |
2367 | updateSliceFrameMaterials(volume, volumeItem); |
2368 | } else { |
2369 | volumeItem.sliceFrameX->setVisible(false); |
2370 | volumeItem.sliceFrameY->setVisible(false); |
2371 | volumeItem.sliceFrameZ->setVisible(false); |
2372 | } |
2373 | volumeItem.drawSliceFrames = volume->drawSliceFrames(); |
2374 | } |
2375 | |
2376 | auto material = materialsRef.at(0); |
2377 | |
2378 | QVector3D minBounds(-1, 1, 1); |
2379 | QVector3D maxBounds(1, -1, -1); |
2380 | QVector3D translation(0, 0, 0); |
2381 | QVector3D scaling(1, 1, 1); |
2382 | |
2383 | if (!volume->isScalingAbsolute() && !volume->isPositionAbsolute()) { |
2384 | if (m_controller->axisX()->type() == QAbstract3DAxis::AxisTypeValue) { |
2385 | auto axis = static_cast<QValue3DAxis *>(m_controller->axisX()); |
2386 | translation.setX(axis->positionAt(x: volume->position().x()) + translate().x() / scale().x()); |
2387 | scaling.setX((axis->max() - axis->min()) / volume->scaling().x()); |
2388 | minBounds.setX(minBounds.x() * scaling.x() - translation.x()); |
2389 | maxBounds.setX(maxBounds.x() * scaling.x() - translation.x()); |
2390 | } |
2391 | |
2392 | if (m_controller->axisY()->type() == QAbstract3DAxis::AxisTypeValue) { |
2393 | auto axis = static_cast<QValue3DAxis *>(m_controller->axisY()); |
2394 | translation.setY(axis->positionAt(x: volume->position().y()) + translate().y() / scale().y()); |
2395 | scaling.setY((axis->max() - axis->min()) / volume->scaling().y()); |
2396 | minBounds.setY(minBounds.y() * scaling.y() + translation.y()); |
2397 | maxBounds.setY(maxBounds.y() * scaling.y() + translation.y()); |
2398 | } |
2399 | |
2400 | if (m_controller->axisZ()->type() == QAbstract3DAxis::AxisTypeValue) { |
2401 | auto axis = static_cast<QValue3DAxis *>(m_controller->axisZ()); |
2402 | translation.setZ(axis->positionAt(x: volume->position().z()) + translate().z() / scale().z()); |
2403 | scaling.setZ((axis->max() - axis->min()) / volume->scaling().z()); |
2404 | minBounds.setZ(minBounds.z() * scaling.z() - translation.z()); |
2405 | maxBounds.setZ(maxBounds.z() * scaling.z() - translation.z()); |
2406 | } |
2407 | |
2408 | model->setPosition(QVector3D()); |
2409 | model->setScale(QVector3D(qAbs(t: scale().x()) / 2, qAbs(t: scale().y()) / 2, qAbs(t: scale().z()) / 2)); |
2410 | } else { |
2411 | model->setScale(volume->scaling()); |
2412 | } |
2413 | model->setRotation(volume->rotation()); |
2414 | |
2415 | material->setProperty(name: "minBounds" , value: minBounds); |
2416 | material->setProperty(name: "maxBounds" , value: maxBounds); |
2417 | |
2418 | if (volume->drawSlices()) |
2419 | material->setProperty(name: "volumeSliceIndices" , value: sliceIndices); |
2420 | |
2421 | if (volume->drawSliceFrames()) { |
2422 | float sliceFrameX = sliceIndices.x(); |
2423 | float sliceFrameY = sliceIndices.y(); |
2424 | float sliceFrameZ = sliceIndices.z(); |
2425 | if (volume->sliceIndexX() >= 0 && scaling.x() > 0) |
2426 | sliceFrameX = (sliceFrameX + translation.x()) / scaling.x(); |
2427 | if (volume->sliceIndexY() >= 0 && scaling.y() > 0) |
2428 | sliceFrameY = (sliceFrameY - translation.y()) / scaling.y(); |
2429 | if (volume->sliceIndexZ() >= 0 && scaling.z() > 0) |
2430 | sliceFrameZ = (sliceFrameZ + translation.z()) / scaling.z(); |
2431 | |
2432 | if (sliceFrameX < - 1 || sliceFrameX > 1) { |
2433 | volumeItem.sliceFrameX->setVisible(false); |
2434 | } else { |
2435 | volumeItem.sliceFrameX->setVisible(true); |
2436 | } |
2437 | |
2438 | if (sliceFrameY < - 1 || sliceFrameY > 1) { |
2439 | volumeItem.sliceFrameY->setVisible(false); |
2440 | } else { |
2441 | volumeItem.sliceFrameY->setVisible(true); |
2442 | } |
2443 | |
2444 | if (sliceFrameZ < - 1 || sliceFrameZ > 1) { |
2445 | volumeItem.sliceFrameZ->setVisible(false); |
2446 | } else { |
2447 | volumeItem.sliceFrameZ->setVisible(true); |
2448 | } |
2449 | |
2450 | volumeItem.sliceFrameX->setX(sliceFrameX); |
2451 | volumeItem.sliceFrameY->setY(-sliceFrameY); |
2452 | volumeItem.sliceFrameZ->setZ(-sliceFrameZ); |
2453 | } |
2454 | |
2455 | material->setProperty(name: "alphaMultiplier" , value: volume->alphaMultiplier()); |
2456 | material->setProperty(name: "preserveOpacity" , value: volume->preserveOpacity()); |
2457 | material->setProperty(name: "useOrtho" , value: m_controller->isOrthoProjection()); |
2458 | |
2459 | int sampleCount = volume->textureWidth() + volume->textureHeight() + volume->textureDepth(); |
2460 | material->setProperty(name: "sampleCount" , value: sampleCount); |
2461 | |
2462 | int color8Bit = (volume->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; |
2463 | material->setProperty(name: "color8Bit" , value: color8Bit); |
2464 | |
2465 | if (volumeItem.updateTextureData) { |
2466 | auto textureData = volumeItem.textureData; |
2467 | textureData->setSize(QSize(volume->textureWidth(), volume->textureHeight())); |
2468 | textureData->setDepth(volume->textureDepth()); |
2469 | |
2470 | if (color8Bit) |
2471 | textureData->setFormat(QQuick3DTextureData::R8); |
2472 | else |
2473 | textureData->setFormat(QQuick3DTextureData::RGBA8); |
2474 | |
2475 | textureData->setTextureData(QByteArray::fromRawData( |
2476 | data: reinterpret_cast<const char *>(volume->textureData()->constData()), |
2477 | size: volume->textureData()->size())); |
2478 | |
2479 | material->setProperty(name: "textureDimensions" , value: QVector3D(1.0f / float(volume->textureWidth()), |
2480 | 1.0f / float(volume->textureHeight()), |
2481 | 1.0f / float(volume->textureDepth()))); |
2482 | |
2483 | volumeItem.updateTextureData = false; |
2484 | } |
2485 | |
2486 | if (volumeItem.updateColorTextureData) { |
2487 | auto colorTextureData = volumeItem.colorTextureData; |
2488 | QByteArray colorTableBytes; |
2489 | const QList<QRgb> &colorTable = volume->colorTable(); |
2490 | for (int i = 0; i < colorTable.size(); i++) { |
2491 | QRgb shifted = qRgba(r: qBlue(rgb: colorTable[i]), g: qGreen(rgb: colorTable[i]), b: qRed(rgb: colorTable[i]), a: qAlpha(rgb: colorTable[i])); |
2492 | colorTableBytes.append(a: QByteArray::fromRawData(data: reinterpret_cast<const char *>(&shifted), size: sizeof(shifted))); |
2493 | } |
2494 | colorTextureData->setTextureData(colorTableBytes); |
2495 | } |
2496 | } |
2497 | ++itemIterator; |
2498 | } |
2499 | } |
2500 | |
2501 | void QQuickGraphsItem::updateAxisRange(float min, float max) |
2502 | { |
2503 | Q_UNUSED(min); |
2504 | Q_UNUSED(max); |
2505 | } |
2506 | |
2507 | void QQuickGraphsItem::updateAxisReversed(bool enable) |
2508 | { |
2509 | Q_UNUSED(enable); |
2510 | } |
2511 | |
2512 | int QQuickGraphsItem::findLabelsMaxWidth(const QStringList &labels) |
2513 | { |
2514 | int labelWidth = 0; |
2515 | QFontMetrics labelFM(m_controller->activeTheme()->font()); |
2516 | |
2517 | for (const auto &label : std::as_const(t: labels)) { |
2518 | auto width = labelFM.horizontalAdvance(label); |
2519 | if (labelWidth < width) |
2520 | labelWidth = width; |
2521 | } |
2522 | return labelWidth; |
2523 | } |
2524 | |
2525 | QVector3D QQuickGraphsItem::calculateCategoryLabelPosition(QAbstract3DAxis *axis, QVector3D labelPosition, int index) |
2526 | { |
2527 | Q_UNUSED(axis); |
2528 | Q_UNUSED(index); |
2529 | return labelPosition; |
2530 | } |
2531 | |
2532 | float QQuickGraphsItem::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index) |
2533 | { |
2534 | Q_UNUSED(axis); |
2535 | Q_UNUSED(index); |
2536 | return 0.0f; |
2537 | } |
2538 | |
2539 | void QQuickGraphsItem::updateXTitle(const QVector3D &labelRotation, const QVector3D &labelTrans, |
2540 | const QQuaternion &totalRotation, float labelsMaxWidth, |
2541 | const QVector3D &scale) |
2542 | { |
2543 | float pointSize = m_controller->activeTheme()->font().pointSizeF(); |
2544 | float textPadding = pointSize * .5f; |
2545 | QFontMetrics fm(m_controller->activeTheme()->font()); |
2546 | float height = fm.height() + textPadding; |
2547 | float width = fm.horizontalAdvance(m_controller->axisX()->title()) + textPadding; |
2548 | |
2549 | float titleOffset; |
2550 | |
2551 | bool radial = false; |
2552 | if (radial) |
2553 | titleOffset = -2.0f * (m_labelMargin + scale.y()); |
2554 | else |
2555 | titleOffset = 2.0f * m_labelMargin + (labelsMaxWidth * scale.y()); |
2556 | |
2557 | float zRotation = 0.0f; |
2558 | float yRotation = 0.0f; |
2559 | float xRotation = -90.0f + labelRotation.z(); |
2560 | float offsetRotation = labelRotation.z(); |
2561 | float = -90.0f; |
2562 | if (m_yFlipped) { |
2563 | zRotation = 180.0f; |
2564 | if (m_zFlipped) { |
2565 | titleOffset = -titleOffset; |
2566 | if (m_xFlipped) { |
2567 | offsetRotation = -offsetRotation; |
2568 | extraRotation = -extraRotation; |
2569 | } else { |
2570 | xRotation = -90.0f - labelRotation.z(); |
2571 | } |
2572 | } else { |
2573 | yRotation = 180.0f; |
2574 | if (m_xFlipped) { |
2575 | offsetRotation = -offsetRotation; |
2576 | xRotation = -90.0f - labelRotation.z(); |
2577 | } else { |
2578 | extraRotation = -extraRotation; |
2579 | } |
2580 | } |
2581 | } else { |
2582 | if (m_zFlipped) { |
2583 | titleOffset = -titleOffset; |
2584 | if (m_xFlipped) { |
2585 | offsetRotation = -offsetRotation; |
2586 | } else { |
2587 | xRotation = -90.0f - labelRotation.z(); |
2588 | extraRotation = -extraRotation; |
2589 | } |
2590 | yRotation = 180.0f; |
2591 | if (m_yFlipped) { |
2592 | extraRotation = -extraRotation; |
2593 | if (m_xFlipped) |
2594 | xRotation = 90.0f + labelRotation.z(); |
2595 | else |
2596 | xRotation = 90.0f - labelRotation.z(); |
2597 | } |
2598 | } else { |
2599 | if (m_xFlipped) { |
2600 | offsetRotation = -offsetRotation; |
2601 | xRotation = -90.0f - labelRotation.z(); |
2602 | extraRotation = -extraRotation; |
2603 | } |
2604 | if (m_yFlipped) { |
2605 | xRotation = 90.0f + labelRotation.z(); |
2606 | extraRotation = -extraRotation; |
2607 | if (m_xFlipped) |
2608 | xRotation = 90.0f - labelRotation.z(); |
2609 | } |
2610 | } |
2611 | } |
2612 | |
2613 | if (offsetRotation == 180.0f || offsetRotation == -180.0f) |
2614 | offsetRotation = 0.0f; |
2615 | |
2616 | QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: offsetRotation); |
2617 | QVector3D titleOffsetVector = |
2618 | offsetRotator.rotatedVector(vector: QVector3D(0.0f, 0.0f, titleOffset)); |
2619 | |
2620 | QQuaternion titleRotation; |
2621 | if (m_controller->axisX()->isTitleFixed()) { |
2622 | titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: zRotation) |
2623 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation) |
2624 | * QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: xRotation); |
2625 | } else { |
2626 | titleRotation = totalRotation |
2627 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: extraRotation); |
2628 | } |
2629 | |
2630 | QVector3D titleScale = scale; |
2631 | titleScale.setX(titleScale.y() * width / height); |
2632 | m_titleLabelX->setScale(titleScale); |
2633 | m_titleLabelX->setPosition(labelTrans + titleOffsetVector); |
2634 | m_titleLabelX->setRotation(titleRotation); |
2635 | m_titleLabelX->setProperty(name: "labelWidth" , value: width); |
2636 | m_titleLabelX->setProperty(name: "labelHeight" , value: height); |
2637 | } |
2638 | |
2639 | void QQuickGraphsItem::updateYTitle(const QVector3D &sideLabelRotation, |
2640 | const QVector3D &backLabelRotation, |
2641 | const QVector3D &sideLabelTrans, |
2642 | const QVector3D &backLabelTrans, |
2643 | const QQuaternion &totalSideRotation, |
2644 | const QQuaternion &totalBackRotation, |
2645 | float labelsMaxWidth, |
2646 | const QVector3D &scale) |
2647 | { |
2648 | float pointSize = m_controller->activeTheme()->font().pointSizeF(); |
2649 | float textPadding = pointSize * .5f; |
2650 | QFontMetrics fm(m_controller->activeTheme()->font()); |
2651 | float height = fm.height() + textPadding; |
2652 | float width = fm.horizontalAdvance(m_controller->axisY()->title()) + textPadding; |
2653 | |
2654 | float titleOffset = m_labelMargin + (labelsMaxWidth * scale.x()); |
2655 | |
2656 | QQuaternion zRightAngleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: 90.0f); |
2657 | float yRotation; |
2658 | QVector3D titleTrans; |
2659 | QQuaternion totalRotation; |
2660 | if (m_xFlipped != m_zFlipped) { |
2661 | yRotation = backLabelRotation.y(); |
2662 | titleTrans = backLabelTrans; |
2663 | totalRotation = totalBackRotation; |
2664 | } else { |
2665 | yRotation = sideLabelRotation.y(); |
2666 | titleTrans = sideLabelTrans; |
2667 | totalRotation = totalSideRotation; |
2668 | } |
2669 | titleTrans.setY(.0f); |
2670 | |
2671 | QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation); |
2672 | QVector3D titleOffsetVector = |
2673 | offsetRotator.rotatedVector(vector: QVector3D(-titleOffset, 0.0f, 0.0f)); |
2674 | |
2675 | QQuaternion titleRotation; |
2676 | if (m_controller->axisY()->isTitleFixed()) { |
2677 | titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation) |
2678 | * zRightAngleRotation; |
2679 | } else { |
2680 | titleRotation = totalRotation * zRightAngleRotation; |
2681 | } |
2682 | |
2683 | QVector3D titleScale = scale; |
2684 | titleScale.setX(titleScale.y() * width / height); |
2685 | m_titleLabelY->setScale(titleScale); |
2686 | m_titleLabelY->setPosition(titleTrans + titleOffsetVector); |
2687 | m_titleLabelY->setRotation(titleRotation); |
2688 | m_titleLabelY->setProperty(name: "labelWidth" , value: width); |
2689 | m_titleLabelY->setProperty(name: "labelHeight" , value: height); |
2690 | } |
2691 | |
2692 | void QQuickGraphsItem::updateZTitle(const QVector3D &labelRotation, const QVector3D &labelTrans, |
2693 | const QQuaternion &totalRotation, float labelsMaxWidth, |
2694 | const QVector3D &scale) |
2695 | { |
2696 | float pointSize = m_controller->activeTheme()->font().pointSizeF(); |
2697 | float textPadding = pointSize * .5f; |
2698 | QFontMetrics fm(m_controller->activeTheme()->font()); |
2699 | float height = fm.height() + textPadding; |
2700 | float width = fm.horizontalAdvance(m_controller->axisZ()->title()) + textPadding; |
2701 | |
2702 | float titleOffset = m_labelMargin + (labelsMaxWidth * scale.x()); |
2703 | |
2704 | float zRotation = labelRotation.z(); |
2705 | float yRotation = -90.0f; |
2706 | float xRotation = -90.0f; |
2707 | float = 90.0f; |
2708 | |
2709 | if (m_yFlipped) { |
2710 | xRotation = -xRotation; |
2711 | if (m_zFlipped) { |
2712 | if (m_xFlipped) { |
2713 | titleOffset = -titleOffset; |
2714 | zRotation = -zRotation; |
2715 | extraRotation = -extraRotation; |
2716 | } else { |
2717 | zRotation = -zRotation; |
2718 | yRotation = -yRotation; |
2719 | } |
2720 | } else { |
2721 | if (m_xFlipped) { |
2722 | titleOffset = -titleOffset; |
2723 | } else { |
2724 | extraRotation = -extraRotation; |
2725 | yRotation = -yRotation; |
2726 | } |
2727 | } |
2728 | } else { |
2729 | if (m_zFlipped) { |
2730 | zRotation = -zRotation; |
2731 | if (m_xFlipped) { |
2732 | titleOffset = -titleOffset; |
2733 | } else { |
2734 | extraRotation = -extraRotation; |
2735 | yRotation = -yRotation; |
2736 | } |
2737 | } else { |
2738 | if (m_xFlipped) { |
2739 | titleOffset = -titleOffset; |
2740 | extraRotation = -extraRotation; |
2741 | } else { |
2742 | yRotation = -yRotation; |
2743 | } |
2744 | } |
2745 | if (m_yFlipped) { |
2746 | xRotation = -xRotation; |
2747 | extraRotation = -extraRotation; |
2748 | } |
2749 | } |
2750 | |
2751 | float offsetRotation = zRotation; |
2752 | if (offsetRotation == 180.0f || offsetRotation == -180.0f) |
2753 | offsetRotation = 0.0f; |
2754 | |
2755 | QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: offsetRotation); |
2756 | QVector3D titleOffsetVector = |
2757 | offsetRotator.rotatedVector(vector: QVector3D(titleOffset, 0.0f, 0.0f)); |
2758 | |
2759 | QQuaternion titleRotation; |
2760 | if (m_controller->axisZ()->isTitleFixed()) { |
2761 | titleRotation = QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: zRotation) |
2762 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 1.0f, z: 0.0f, angle: yRotation) |
2763 | * QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: xRotation); |
2764 | } else { |
2765 | titleRotation = totalRotation |
2766 | * QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: extraRotation); |
2767 | } |
2768 | |
2769 | QVector3D titleScale = scale; |
2770 | titleScale.setX(titleScale.y() * width / height); |
2771 | m_titleLabelZ->setScale(titleScale); |
2772 | m_titleLabelZ->setPosition(labelTrans + titleOffsetVector); |
2773 | m_titleLabelZ->setRotation(titleRotation); |
2774 | m_titleLabelZ->setProperty(name: "labelWidth" , value: width); |
2775 | m_titleLabelZ->setProperty(name: "labelHeight" , value: height); |
2776 | } |
2777 | |
2778 | void QQuickGraphsItem::updateCamera() |
2779 | { |
2780 | QVector3D lookingPosition = m_controller->scene()->activeCamera()->target(); |
2781 | float zoomLevel = m_controller->scene()->activeCamera()->zoomLevel(); |
2782 | |
2783 | const float scale = qMin(a: width(), b: height() * 1.6f); |
2784 | const float magnificationScaleFactor = 1.0f / 640.0f; |
2785 | const float magnification = scale * magnificationScaleFactor; |
2786 | |
2787 | auto useOrtho = m_controller->isOrthoProjection(); |
2788 | if (useOrtho) { |
2789 | if (m_sliceView && m_sliceView->isVisible()) { |
2790 | m_oCamera->setVerticalMagnification(zoomLevel * .4f); |
2791 | m_oCamera->setHorizontalMagnification(zoomLevel * .4f); |
2792 | } else { |
2793 | m_oCamera->setVerticalMagnification(zoomLevel * magnification); |
2794 | m_oCamera->setHorizontalMagnification(zoomLevel * magnification); |
2795 | } |
2796 | } |
2797 | cameraTarget()->setPosition(lookingPosition); |
2798 | auto rotation = QVector3D( |
2799 | -m_controller->scene()->activeCamera()->yRotation(), |
2800 | -m_controller->scene()->activeCamera()->xRotation(), |
2801 | 0); |
2802 | cameraTarget()->setEulerRotation(rotation); |
2803 | float zoom = 720.f / zoomLevel; |
2804 | m_pCamera->setZ(zoom); |
2805 | updateCustomLabelsRotation(); |
2806 | updateItemLabel(position: m_labelPosition); |
2807 | } |
2808 | |
2809 | void QQuickGraphsItem::handleSegmentLineCountChanged(QAbstract3DAxis *axis, QQuick3DRepeater *repeater) |
2810 | { |
2811 | int gridLineCount = 0; |
2812 | if (axis->type() == QAbstract3DAxis::AxisTypeValue) { |
2813 | auto valueAxis = static_cast<QValue3DAxis *>(axis); |
2814 | gridLineCount = 2 * valueAxis->gridSize(); |
2815 | } else if (axis->type() == QAbstract3DAxis::AxisTypeCategory) { |
2816 | gridLineCount = axis->labels().size(); |
2817 | } |
2818 | repeater->setModel(gridLineCount); |
2819 | changeGridLineColor(repeater, color: m_controller->activeTheme()->gridLineColor()); |
2820 | m_controller->handleAxisSubSegmentCountChangedBySender(sender: axis); |
2821 | } |
2822 | |
2823 | void QQuickGraphsItem::handleSubSegmentLineCountChanged(QAbstract3DAxis *axis, QQuick3DRepeater *repeater) |
2824 | { |
2825 | int subGridLineCount = 0; |
2826 | |
2827 | if (axis->type() == QAbstract3DAxis::AxisTypeValue) { |
2828 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); |
2829 | subGridLineCount = 2 * valueAxis->subGridSize(); |
2830 | } else if (axis->type() == QAbstract3DAxis::AxisTypeCategory) { |
2831 | subGridLineCount = 0; |
2832 | } |
2833 | |
2834 | repeater->setModel(subGridLineCount); |
2835 | changeGridLineColor(repeater, color: m_controller->activeTheme()->gridLineColor()); |
2836 | } |
2837 | |
2838 | void QQuickGraphsItem::handleLabelCountChanged(QQuick3DRepeater *repeater) |
2839 | { |
2840 | Q3DTheme *theme = m_controller->activeTheme(); |
2841 | changeLabelBackgroundColor(repeater, color: theme->labelBackgroundColor()); |
2842 | changeLabelBackgroundEnabled(repeater, enabled: theme->isLabelBackgroundEnabled()); |
2843 | changeLabelBorderEnabled(repeater, enabled: theme->isLabelBorderEnabled()); |
2844 | changeLabelTextColor(repeater, color: theme->labelTextColor()); |
2845 | changeLabelFont(repeater, font: theme->font()); |
2846 | |
2847 | if (m_sliceView) { |
2848 | changeLabelBackgroundColor(repeater: m_sliceHorizontalLabelRepeater, color: theme->labelBackgroundColor()); |
2849 | changeLabelBackgroundColor(repeater: m_sliceVerticalLabelRepeater, color: theme->labelBackgroundColor()); |
2850 | changeLabelBackgroundEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled: theme->isLabelBackgroundEnabled()); |
2851 | changeLabelBackgroundEnabled(repeater: m_sliceVerticalLabelRepeater, enabled: theme->isLabelBackgroundEnabled()); |
2852 | changeLabelBorderEnabled(repeater: m_sliceHorizontalLabelRepeater, enabled: theme->isLabelBorderEnabled()); |
2853 | changeLabelBorderEnabled(repeater: m_sliceVerticalLabelRepeater, enabled: theme->isLabelBorderEnabled()); |
2854 | changeLabelTextColor(repeater: m_sliceHorizontalLabelRepeater, color: theme->labelTextColor()); |
2855 | changeLabelTextColor(repeater: m_sliceVerticalLabelRepeater, color: theme->labelTextColor()); |
2856 | changeLabelFont(repeater: m_sliceHorizontalLabelRepeater, font: theme->font()); |
2857 | changeLabelFont(repeater: m_sliceVerticalLabelRepeater, font: theme->font()); |
2858 | } |
2859 | } |
2860 | |
2861 | void QQuickGraphsItem::updateCustomData() |
2862 | { |
2863 | QAbstract3DAxis *axisX = m_controller->axisX(); |
2864 | QAbstract3DAxis *axisY = m_controller->axisY(); |
2865 | QAbstract3DAxis *axisZ = m_controller->axisZ(); |
2866 | |
2867 | int maxX = axisX->max(); |
2868 | int minX = axisX->min(); |
2869 | int maxY = axisY->max(); |
2870 | int minY = axisY->min(); |
2871 | int maxZ = axisZ->max(); |
2872 | int minZ = axisZ->min(); |
2873 | QVector3D adjustment = m_scaleWithBackground * QVector3D(1.0f, 1.0f, -1.0f); |
2874 | bool isPolar = m_controller->isPolar(); |
2875 | |
2876 | auto labelIterator = m_customLabelList.constBegin(); |
2877 | while (labelIterator != m_customLabelList.constEnd()) { |
2878 | QCustom3DLabel *label = labelIterator.key(); |
2879 | QQuick3DNode *customLabel = labelIterator.value(); |
2880 | |
2881 | QVector3D pos = label->position(); |
2882 | if (!label->isPositionAbsolute()) { |
2883 | if (label->position().x() < minX |
2884 | || label->position().x() > maxX |
2885 | || label->position().y() < minY |
2886 | || label->position().y() > maxY |
2887 | || label->position().z() < minZ |
2888 | || label->position().z() > maxZ) { |
2889 | customLabel->setVisible(false); |
2890 | ++labelIterator; |
2891 | continue; |
2892 | } |
2893 | |
2894 | float xNormalizer = maxX - minX; |
2895 | float xPos = (pos.x() - minX) / xNormalizer; |
2896 | float yNormalizer = maxY - minY; |
2897 | float yPos = (pos.y() - minY) / yNormalizer; |
2898 | float zNormalizer = maxZ - minZ; |
2899 | float zPos = (pos.z() - minZ) / zNormalizer; |
2900 | pos = QVector3D(xPos, yPos, zPos); |
2901 | if (isPolar) { |
2902 | float angle = xPos * M_PI * 2.0f; |
2903 | float radius = zPos; |
2904 | xPos = radius * qSin(v: angle) * 1.0f; |
2905 | zPos = -(radius * qCos(v: angle)) * 1.0f; |
2906 | yPos = yPos * adjustment.y() * 2.0f - adjustment.y(); |
2907 | pos = QVector3D(xPos, yPos, zPos); |
2908 | } else { |
2909 | pos = pos * adjustment * 2.0f - adjustment; |
2910 | } |
2911 | } |
2912 | |
2913 | QFontMetrics fm(label->font()); |
2914 | int width = fm.horizontalAdvance(label->text()); |
2915 | int height = fm.height(); |
2916 | customLabel->setProperty(name: "labelWidth" , value: width); |
2917 | customLabel->setProperty(name: "labelHeight" , value: height); |
2918 | customLabel->setPosition(pos); |
2919 | QQuaternion rotation = label->rotation(); |
2920 | if (label->isFacingCamera()) { |
2921 | rotation = Utils::calculateRotation(xyzRotations: QVector3D( |
2922 | -m_controller->scene()->activeCamera()->yRotation(), |
2923 | -m_controller->scene()->activeCamera()->xRotation(), |
2924 | 0)); |
2925 | } |
2926 | customLabel->setRotation(rotation); |
2927 | float pointSize = m_controller->activeTheme()->font().pointSizeF(); |
2928 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
2929 | float fontRatio = float(height) / float(width); |
2930 | QVector3D fontScaled = QVector3D(scaleFactor / fontRatio, scaleFactor, 0.0f); |
2931 | customLabel->setScale(fontScaled); |
2932 | customLabel->setProperty(name: "labelText" , value: label->text()); |
2933 | customLabel->setProperty(name: "labelTextColor" , value: label->textColor()); |
2934 | customLabel->setProperty(name: "labelFont" , value: label->font()); |
2935 | customLabel->setProperty(name: "backgroundEnabled" , value: label->isBackgroundEnabled()); |
2936 | customLabel->setProperty(name: "backgroundColor" , value: label->backgroundColor()); |
2937 | customLabel->setProperty(name: "borderEnabled" , value: label->isBorderEnabled()); |
2938 | customLabel->setVisible(label->isVisible()); |
2939 | |
2940 | ++labelIterator; |
2941 | } |
2942 | |
2943 | auto itemIterator = m_customItemList.constBegin(); |
2944 | while (itemIterator != m_customItemList.constEnd()) { |
2945 | QCustom3DItem *item = itemIterator.key(); |
2946 | QQuick3DModel *model = itemIterator.value(); |
2947 | |
2948 | QVector3D pos = item->position(); |
2949 | if (!item->isPositionAbsolute()) { |
2950 | if (item->position().x() < minX |
2951 | || item->position().x() > maxX |
2952 | || item->position().y() < minY |
2953 | || item->position().y() > maxY |
2954 | || item->position().z() < minZ |
2955 | || item->position().z() > maxZ) { |
2956 | model->setVisible(false); |
2957 | ++itemIterator; |
2958 | continue; |
2959 | } |
2960 | float xNormalizer = maxX - minX; |
2961 | float xPos = (pos.x() - minX) / xNormalizer; |
2962 | float yNormalizer = maxY - minY; |
2963 | float yPos = (pos.y() - minY) / yNormalizer; |
2964 | float zNormalizer = maxZ - minZ; |
2965 | float zPos = (pos.z() - minZ) / zNormalizer; |
2966 | pos = QVector3D(xPos, yPos, zPos); |
2967 | if (isPolar) { |
2968 | float angle = xPos * M_PI * 2.0f; |
2969 | float radius = zPos; |
2970 | xPos = radius * qSin(v: angle) * 1.0f; |
2971 | zPos = -(radius * qCos(v: angle)) * 1.0f; |
2972 | yPos = yPos * adjustment.y() * 2.0f - adjustment.y(); |
2973 | pos = QVector3D(xPos, yPos, zPos); |
2974 | } else { |
2975 | pos = pos * adjustment * 2.0f - adjustment; |
2976 | } |
2977 | } |
2978 | model->setPosition(pos); |
2979 | |
2980 | if (auto volume = qobject_cast<QCustom3DVolume *>(object: item)) { |
2981 | if (!m_customVolumes.contains(key: volume)) { |
2982 | auto &&volumeItem = m_customVolumes[volume]; |
2983 | |
2984 | volumeItem.model = model; |
2985 | model->setSource(QUrl(volume->meshFile())); |
2986 | |
2987 | volumeItem.useHighDefShader = volume->useHighDefShader(); |
2988 | volumeItem.drawSlices = volume->drawSlices(); |
2989 | |
2990 | createVolumeMaterial(volume, volumeItem); |
2991 | |
2992 | volumeItem.sliceFrameX = createSliceFrame(volumeItem); |
2993 | volumeItem.sliceFrameY = createSliceFrame(volumeItem); |
2994 | volumeItem.sliceFrameZ = createSliceFrame(volumeItem); |
2995 | |
2996 | if (volume->drawSliceFrames()) { |
2997 | volumeItem.sliceFrameX->setVisible(true); |
2998 | volumeItem.sliceFrameY->setVisible(true); |
2999 | volumeItem.sliceFrameZ->setVisible(true); |
3000 | |
3001 | QVector3D sliceIndices((float(volume->sliceIndexX()) + 0.5f) / float(volume->textureWidth()) * 2.0 - 1.0, |
3002 | (float(volume->sliceIndexY()) + 0.5f) / float(volume->textureHeight()) * 2.0 - 1.0, |
3003 | (float(volume->sliceIndexZ()) + 0.5f) / float(volume->textureDepth()) * 2.0 - 1.0); |
3004 | |
3005 | volumeItem.sliceFrameX->setX(sliceIndices.x()); |
3006 | volumeItem.sliceFrameY->setY(-sliceIndices.y()); |
3007 | volumeItem.sliceFrameZ->setZ(-sliceIndices.z()); |
3008 | |
3009 | volumeItem.sliceFrameX->setRotation(QQuaternion::fromEulerAngles(pitch: 0, yaw: 90, roll: 0)); |
3010 | volumeItem.sliceFrameY->setRotation(QQuaternion::fromEulerAngles(pitch: 90, yaw: 0, roll: 0)); |
3011 | |
3012 | updateSliceFrameMaterials(volume, volumeItem); |
3013 | } else { |
3014 | volumeItem.sliceFrameX->setVisible(false); |
3015 | volumeItem.sliceFrameY->setVisible(false); |
3016 | volumeItem.sliceFrameZ->setVisible(false); |
3017 | } |
3018 | volumeItem.drawSliceFrames = volume->drawSliceFrames(); |
3019 | m_customItemList.insert(key: item, value: model); |
3020 | } |
3021 | } else { |
3022 | model->setSource(QUrl::fromLocalFile(localfile: item->meshFile())); |
3023 | QQmlListReference materialsRef(model, "materials" ); |
3024 | QQuick3DPrincipledMaterial *material = static_cast<QQuick3DPrincipledMaterial *>(materialsRef.at(0)); |
3025 | QQuick3DTexture *texture = material->baseColorMap(); |
3026 | if (!texture) { |
3027 | texture = new QQuick3DTexture(); |
3028 | texture->setParent(model); |
3029 | texture->setParentItem(model); |
3030 | material->setBaseColorMap(texture); |
3031 | } |
3032 | if (!item->textureFile().isEmpty()) { |
3033 | texture->setSource(QUrl::fromLocalFile(localfile: item->textureFile())); |
3034 | } else { |
3035 | QImage textureImage = m_controller->customTextureImage(item); |
3036 | textureImage.convertTo(f: QImage::Format_RGBA32FPx4); |
3037 | QQuick3DTextureData *textureData = texture->textureData(); |
3038 | if (!textureData) { |
3039 | textureData = new QQuick3DTextureData(); |
3040 | textureData->setParent(texture); |
3041 | textureData->setParentItem(texture); |
3042 | textureData->setFormat(QQuick3DTextureData::RGBA32F); |
3043 | texture->setTextureData(textureData); |
3044 | } |
3045 | textureData->setSize(textureImage.size()); |
3046 | textureData->setTextureData(QByteArray(reinterpret_cast<const char*>(textureImage.bits()), |
3047 | textureImage.sizeInBytes())); |
3048 | } |
3049 | model->setRotation(item->rotation()); |
3050 | model->setScale(item->scaling()); |
3051 | model->setVisible(item->isVisible()); |
3052 | } |
3053 | ++itemIterator; |
3054 | } |
3055 | } |
3056 | |
3057 | void QQuickGraphsItem::updateCustomLabelsRotation() |
3058 | { |
3059 | auto labelIterator = m_customLabelList.constBegin(); |
3060 | while (labelIterator != m_customLabelList.constEnd()) { |
3061 | QCustom3DLabel *label = labelIterator.key(); |
3062 | QQuick3DNode *customLabel = labelIterator.value(); |
3063 | QQuaternion rotation = label->rotation(); |
3064 | if (label->isFacingCamera()) { |
3065 | rotation = Utils::calculateRotation(xyzRotations: QVector3D( |
3066 | -m_controller->scene()->activeCamera()->yRotation(), |
3067 | -m_controller->scene()->activeCamera()->xRotation(), |
3068 | 0)); |
3069 | } |
3070 | customLabel->setRotation(rotation); |
3071 | ++labelIterator; |
3072 | } |
3073 | } |
3074 | |
3075 | int QQuickGraphsItem::msaaSamples() const |
3076 | { |
3077 | if (m_renderMode == QAbstract3DGraph::RenderIndirect) |
3078 | return m_samples; |
3079 | else |
3080 | return m_windowSamples; |
3081 | } |
3082 | |
3083 | void QQuickGraphsItem::setMsaaSamples(int samples) |
3084 | { |
3085 | if (m_renderMode != QAbstract3DGraph::RenderIndirect) { |
3086 | qWarning(msg: "Multisampling cannot be adjusted in this render mode" ); |
3087 | } else if (m_samples != samples) { |
3088 | m_samples = samples; |
3089 | setAntialiasing(m_samples > 0); |
3090 | auto sceneEnv = environment(); |
3091 | sceneEnv->setAntialiasingMode(m_samples > 0 |
3092 | ? QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::MSAA |
3093 | : QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues::NoAA); |
3094 | switch (m_samples) { |
3095 | case 0: |
3096 | // no-op |
3097 | break; |
3098 | case 2: |
3099 | sceneEnv->setAntialiasingQuality( |
3100 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::Medium); |
3101 | break; |
3102 | case 4: |
3103 | sceneEnv->setAntialiasingQuality( |
3104 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High); |
3105 | break; |
3106 | case 8: |
3107 | sceneEnv->setAntialiasingQuality( |
3108 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::VeryHigh); |
3109 | break; |
3110 | default: |
3111 | qWarning(msg: "Invalid multisampling sample number, using 4x instead" ); |
3112 | sceneEnv->setAntialiasingQuality( |
3113 | QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues::High); |
3114 | m_samples = 4; |
3115 | break; |
3116 | } |
3117 | emit msaaSamplesChanged(samples: m_samples); |
3118 | update(); |
3119 | } |
3120 | } |
3121 | |
3122 | Declarative3DScene *QQuickGraphsItem::scene() const |
3123 | { |
3124 | return static_cast<Declarative3DScene *>(m_controller->scene()); |
3125 | } |
3126 | |
3127 | void QQuickGraphsItem::handleWindowChanged(/*QQuickWindow *window*/) |
3128 | { |
3129 | auto window = QQuick3DObjectPrivate::get(item: rootNode())->sceneManager->window(); |
3130 | checkWindowList(window); |
3131 | if (!window) |
3132 | return; |
3133 | |
3134 | #if defined(Q_OS_MACOS) |
3135 | bool previousVisibility = window->isVisible(); |
3136 | // Enable touch events for Mac touchpads |
3137 | window->setVisible(true); |
3138 | typedef void * (*EnableTouch)(QWindow*, bool); |
3139 | EnableTouch enableTouch = |
3140 | (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow" ); |
3141 | if (enableTouch) |
3142 | enableTouch(window, true); |
3143 | window->setVisible(previousVisibility); |
3144 | #endif |
3145 | |
3146 | connect(sender: window, signal: &QObject::destroyed, context: this, slot: &QQuickGraphsItem::windowDestroyed); |
3147 | |
3148 | int oldWindowSamples = m_windowSamples; |
3149 | m_windowSamples = window->format().samples(); |
3150 | if (m_windowSamples < 0) |
3151 | m_windowSamples = 0; |
3152 | |
3153 | connect(sender: window, signal: &QQuickWindow::beforeSynchronizing, |
3154 | context: this, slot: &QQuickGraphsItem::synchData); |
3155 | |
3156 | if (m_renderMode == QAbstract3DGraph::RenderDirectToBackground) { |
3157 | setAntialiasing(m_windowSamples > 0); |
3158 | if (m_windowSamples != oldWindowSamples) |
3159 | emit msaaSamplesChanged(samples: m_windowSamples); |
3160 | } |
3161 | |
3162 | connect(sender: m_controller.data(), signal: &Abstract3DController::needRender, context: window, slot: &QQuickWindow::update); |
3163 | // Force camera update before rendering the first frame |
3164 | // to workaround a Quick3D device pixel ratio bug |
3165 | connect(sender: window, signal: &QQuickWindow::beforeRendering, context: this, slot: [this, window]() { |
3166 | m_oCamera->setClipNear(0.001f); |
3167 | disconnect(sender: window, signal: &QQuickWindow::beforeRendering, receiver: this, zero: nullptr); |
3168 | }); |
3169 | updateWindowParameters(); |
3170 | |
3171 | #if defined(Q_OS_IOS) |
3172 | // Scenegraph render cycle in iOS sometimes misses update after beforeSynchronizing signal. |
3173 | // This ensures we don't end up displaying the graph without any data, in case update is |
3174 | // skipped after synchDataToRenderer. |
3175 | QTimer::singleShot(0, window, SLOT(update())); |
3176 | #endif |
3177 | } |
3178 | |
3179 | void QQuickGraphsItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
3180 | { |
3181 | QQuickItem::geometryChange(newGeometry, oldGeometry); |
3182 | |
3183 | m_cachedGeometry = newGeometry; |
3184 | updateWindowParameters(); |
3185 | } |
3186 | |
3187 | void QQuickGraphsItem::itemChange(ItemChange change, const ItemChangeData &value) |
3188 | { |
3189 | QQuick3DViewport::itemChange(change, value); |
3190 | updateWindowParameters(); |
3191 | } |
3192 | |
3193 | void QQuickGraphsItem::updateWindowParameters() |
3194 | { |
3195 | const QMutexLocker locker(&m_mutex); |
3196 | // Update the device pixel ratio, window size and bounding box |
3197 | QQuickWindow *win = window(); |
3198 | if (win && !m_controller.isNull()) { |
3199 | Q3DScene *scene = m_controller->scene(); |
3200 | if (win->devicePixelRatio() != scene->devicePixelRatio()) { |
3201 | scene->setDevicePixelRatio(win->devicePixelRatio()); |
3202 | win->update(); |
3203 | } |
3204 | |
3205 | bool directRender = m_renderMode == QAbstract3DGraph::RenderDirectToBackground; |
3206 | QSize windowSize; |
3207 | |
3208 | if (directRender) |
3209 | windowSize = win->size(); |
3210 | else |
3211 | windowSize = m_cachedGeometry.size().toSize(); |
3212 | |
3213 | if (windowSize != scene->d_func()->windowSize()) { |
3214 | scene->d_func()->setWindowSize(windowSize); |
3215 | win->update(); |
3216 | } |
3217 | |
3218 | if (directRender) { |
3219 | // Origin mapping is needed when rendering directly to background |
3220 | QPointF point = QQuickItem::mapToScene(point: QPointF(0.0, 0.0)); |
3221 | scene->d_func()->setViewport(QRect(point.x() + 0.5f, point.y() + 0.5f, |
3222 | m_cachedGeometry.width() + 0.5f, |
3223 | m_cachedGeometry.height() + 0.5f)); |
3224 | } else { |
3225 | // No translation needed when rendering to FBO |
3226 | scene->d_func()->setViewport(QRect(0.0, 0.0, m_cachedGeometry.width() + 0.5f, |
3227 | m_cachedGeometry.height() + 0.5f)); |
3228 | } |
3229 | } |
3230 | |
3231 | if (m_sliceView && m_sliceView->isVisible()) { |
3232 | const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height()); |
3233 | QQuick3DOrthographicCamera *camera = static_cast<QQuick3DOrthographicCamera *>(m_sliceView->camera()); |
3234 | const float magnificationScaleFactor = .16f; // this controls the size of the slice view |
3235 | const float magnification = scale * magnificationScaleFactor; |
3236 | camera->setHorizontalMagnification(magnification); |
3237 | camera->setVerticalMagnification(magnification); |
3238 | } |
3239 | } |
3240 | |
3241 | void QQuickGraphsItem::handleSelectionModeChange(QAbstract3DGraph::SelectionFlags mode) |
3242 | { |
3243 | emit selectionModeChanged(mode); |
3244 | } |
3245 | |
3246 | void QQuickGraphsItem::handleShadowQualityChange(QAbstract3DGraph::ShadowQuality quality) |
3247 | { |
3248 | emit shadowQualityChanged(quality); |
3249 | } |
3250 | |
3251 | void QQuickGraphsItem::handleSelectedElementChange(QAbstract3DGraph::ElementType type) |
3252 | { |
3253 | m_controller->m_clickedType = type; |
3254 | emit selectedElementChanged(type); |
3255 | } |
3256 | |
3257 | void QQuickGraphsItem::handleOptimizationHintChange(QAbstract3DGraph::OptimizationHints hints) |
3258 | { |
3259 | emit optimizationHintsChanged(hints); |
3260 | } |
3261 | |
3262 | QAbstract3DInputHandler *QQuickGraphsItem::inputHandler() const |
3263 | { |
3264 | return m_activeInputHandler; |
3265 | } |
3266 | |
3267 | void QQuickGraphsItem::setInputHandler(QAbstract3DInputHandler *inputHandler) |
3268 | { |
3269 | setActiveInputHandler(inputHandler); |
3270 | } |
3271 | |
3272 | void QQuickGraphsItem::mouseDoubleClickEvent(QMouseEvent *event) |
3273 | { |
3274 | if (m_activeInputHandler) |
3275 | m_activeInputHandler->mouseDoubleClickEvent(event); |
3276 | } |
3277 | |
3278 | void QQuickGraphsItem::touchEvent(QTouchEvent *event) |
3279 | { |
3280 | if (m_activeInputHandler) |
3281 | m_activeInputHandler->touchEvent(event); |
3282 | handleTouchEvent(event); |
3283 | window()->update(); |
3284 | } |
3285 | |
3286 | void QQuickGraphsItem::mousePressEvent(QMouseEvent *event) |
3287 | { |
3288 | QPoint mousePos = event->pos(); |
3289 | handleMousePressedEvent(event); |
3290 | if (m_activeInputHandler) |
3291 | m_activeInputHandler->mousePressEvent(event, mousePos); |
3292 | } |
3293 | |
3294 | void QQuickGraphsItem::mouseReleaseEvent(QMouseEvent *event) |
3295 | { |
3296 | QPoint mousePos = event->pos(); |
3297 | if (m_activeInputHandler) |
3298 | m_activeInputHandler->mouseReleaseEvent(event, mousePos); |
3299 | } |
3300 | |
3301 | void QQuickGraphsItem::mouseMoveEvent(QMouseEvent *event) |
3302 | { |
3303 | QPoint mousePos = event->pos(); |
3304 | if (m_activeInputHandler) |
3305 | m_activeInputHandler->mouseMoveEvent(event, mousePos); |
3306 | } |
3307 | |
3308 | #if QT_CONFIG(wheelevent) |
3309 | void QQuickGraphsItem::wheelEvent(QWheelEvent *event) |
3310 | { |
3311 | if (m_activeInputHandler) |
3312 | m_activeInputHandler->wheelEvent(event); |
3313 | } |
3314 | #endif |
3315 | |
3316 | void QQuickGraphsItem::checkWindowList(QQuickWindow *window) |
3317 | { |
3318 | QQuickWindow *oldWindow = m_graphWindowList.value(key: this); |
3319 | m_graphWindowList[this] = window; |
3320 | |
3321 | if (oldWindow != window && oldWindow) { |
3322 | QObject::disconnect(sender: oldWindow, signal: &QObject::destroyed, receiver: this, |
3323 | slot: &QQuickGraphsItem::windowDestroyed); |
3324 | QObject::disconnect(sender: oldWindow, signal: &QQuickWindow::beforeSynchronizing, receiver: this, |
3325 | slot: &QQuickGraphsItem::synchData); |
3326 | if (!m_controller.isNull()) { |
3327 | QObject::disconnect(sender: m_controller.data(), signal: &Abstract3DController::needRender, |
3328 | receiver: oldWindow, slot: &QQuickWindow::update); |
3329 | } |
3330 | } |
3331 | |
3332 | QList<QQuickWindow *> windowList; |
3333 | |
3334 | foreach (QQuickGraphsItem *graph, m_graphWindowList.keys()) { |
3335 | if (graph->m_renderMode == QAbstract3DGraph::RenderDirectToBackground) |
3336 | windowList.append(t: m_graphWindowList.value(key: graph)); |
3337 | } |
3338 | |
3339 | if (!window) { |
3340 | m_graphWindowList.remove(key: this); |
3341 | return; |
3342 | } |
3343 | } |
3344 | |
3345 | void QQuickGraphsItem::setMeasureFps(bool enable) |
3346 | { |
3347 | if (m_measureFps != enable) { |
3348 | m_measureFps = enable; |
3349 | if (enable) { |
3350 | QObject::connect(sender: renderStats(), signal: &QQuick3DRenderStats::fpsChanged, |
3351 | context: this, slot: &QQuickGraphsItem::handleFpsChanged); |
3352 | } else { |
3353 | QObject::disconnect(sender: renderStats(), signal: 0, receiver: this, member: 0); |
3354 | } |
3355 | } |
3356 | } |
3357 | |
3358 | bool QQuickGraphsItem::measureFps() const |
3359 | { |
3360 | return m_measureFps; |
3361 | } |
3362 | |
3363 | int QQuickGraphsItem::currentFps() const |
3364 | { |
3365 | return m_currentFps; |
3366 | } |
3367 | |
3368 | // TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812 |
3369 | // begin.. |
3370 | void QQuickGraphsItem::setOrthoProjection(bool enable) |
3371 | { |
3372 | m_controller->setOrthoProjection(enable); |
3373 | } |
3374 | |
3375 | bool QQuickGraphsItem::isOrthoProjection() const |
3376 | { |
3377 | return m_controller->isOrthoProjection(); |
3378 | } |
3379 | |
3380 | QAbstract3DGraph::ElementType QQuickGraphsItem::selectedElement() const |
3381 | { |
3382 | return m_controller->selectedElement(); |
3383 | } |
3384 | |
3385 | void QQuickGraphsItem::setAspectRatio(qreal ratio) |
3386 | { |
3387 | m_controller->setAspectRatio(ratio); |
3388 | } |
3389 | |
3390 | qreal QQuickGraphsItem::aspectRatio() const |
3391 | { |
3392 | return m_controller->aspectRatio(); |
3393 | } |
3394 | |
3395 | void QQuickGraphsItem::setOptimizationHints(QAbstract3DGraph::OptimizationHints hints) |
3396 | { |
3397 | int intmode = int(hints); |
3398 | m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode)); |
3399 | } |
3400 | |
3401 | QAbstract3DGraph::OptimizationHints QQuickGraphsItem::optimizationHints() const |
3402 | { |
3403 | return m_controller->optimizationHints(); |
3404 | } |
3405 | |
3406 | void QQuickGraphsItem::setPolar(bool enable) |
3407 | { |
3408 | m_controller->setPolar(enable); |
3409 | } |
3410 | |
3411 | bool QQuickGraphsItem::isPolar() const |
3412 | { |
3413 | return m_controller->isPolar(); |
3414 | } |
3415 | |
3416 | void QQuickGraphsItem::setRadialLabelOffset(float offset) |
3417 | { |
3418 | m_controller->setRadialLabelOffset(offset); |
3419 | } |
3420 | |
3421 | float QQuickGraphsItem::radialLabelOffset() const |
3422 | { |
3423 | return m_controller->radialLabelOffset(); |
3424 | } |
3425 | |
3426 | void QQuickGraphsItem::setHorizontalAspectRatio(qreal ratio) |
3427 | { |
3428 | m_controller->setHorizontalAspectRatio(ratio); |
3429 | } |
3430 | |
3431 | qreal QQuickGraphsItem::horizontalAspectRatio() const |
3432 | { |
3433 | return m_controller->horizontalAspectRatio(); |
3434 | } |
3435 | |
3436 | void QQuickGraphsItem::setReflection(bool enable) |
3437 | { |
3438 | m_controller->setReflection(enable); |
3439 | } |
3440 | |
3441 | bool QQuickGraphsItem::isReflection() const |
3442 | { |
3443 | return m_controller->reflection(); |
3444 | } |
3445 | |
3446 | void QQuickGraphsItem::setReflectivity(qreal reflectivity) |
3447 | { |
3448 | m_controller->setReflectivity(reflectivity); |
3449 | } |
3450 | |
3451 | qreal QQuickGraphsItem::reflectivity() const |
3452 | { |
3453 | return m_controller->reflectivity(); |
3454 | } |
3455 | |
3456 | void QQuickGraphsItem::setLocale(const QLocale &locale) |
3457 | { |
3458 | m_controller->setLocale(locale); |
3459 | } |
3460 | |
3461 | QLocale QQuickGraphsItem::locale() const |
3462 | { |
3463 | return m_controller->locale(); |
3464 | } |
3465 | |
3466 | QVector3D QQuickGraphsItem::queriedGraphPosition() const |
3467 | { |
3468 | return m_controller->queriedGraphPosition(); |
3469 | } |
3470 | |
3471 | void QQuickGraphsItem::setMargin(qreal margin) |
3472 | { |
3473 | m_controller->setMargin(margin); |
3474 | } |
3475 | |
3476 | qreal QQuickGraphsItem::margin() const |
3477 | { |
3478 | return m_controller->margin(); |
3479 | } |
3480 | // ..end |
3481 | // TODO: Check if it would make sense to remove these from Abstract3DController - QTBUG-113812 |
3482 | |
3483 | QQuick3DNode *QQuickGraphsItem::rootNode() const |
3484 | { |
3485 | return QQuick3DViewport::scene(); |
3486 | } |
3487 | |
3488 | void QQuickGraphsItem::changeLabelBackgroundColor(QQuick3DRepeater *repeater, const QColor &color) |
3489 | { |
3490 | int count = repeater->count(); |
3491 | for (int i = 0; i < count; i++) { |
3492 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3493 | label->setProperty(name: "backgroundColor" , value: color); |
3494 | } |
3495 | } |
3496 | |
3497 | void QQuickGraphsItem::changeLabelBackgroundEnabled(QQuick3DRepeater *repeater, const bool &enabled) |
3498 | { |
3499 | int count = repeater->count(); |
3500 | for (int i = 0; i < count; i++) { |
3501 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3502 | label->setProperty(name: "backgroundEnabled" , value: enabled); |
3503 | } |
3504 | } |
3505 | |
3506 | void QQuickGraphsItem::changeLabelBorderEnabled(QQuick3DRepeater *repeater, const bool &enabled) |
3507 | { |
3508 | int count = repeater->count(); |
3509 | for (int i = 0; i < count; i++) { |
3510 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3511 | label->setProperty(name: "borderEnabled" , value: enabled); |
3512 | } |
3513 | } |
3514 | |
3515 | void QQuickGraphsItem::changeLabelTextColor(QQuick3DRepeater *repeater, const QColor &color) |
3516 | { |
3517 | int count = repeater->count(); |
3518 | for (int i = 0; i < count; i++) { |
3519 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3520 | label->setProperty(name: "labelTextColor" , value: color); |
3521 | } |
3522 | } |
3523 | |
3524 | void QQuickGraphsItem::changeLabelFont(QQuick3DRepeater *repeater, const QFont &font) |
3525 | { |
3526 | int count = repeater->count(); |
3527 | for (int i = 0; i < count; i++) { |
3528 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3529 | label->setProperty(name: "labelFont" , value: font); |
3530 | } |
3531 | } |
3532 | |
3533 | void QQuickGraphsItem::changeLabelsEnabled(QQuick3DRepeater *repeater, const bool &enabled) |
3534 | { |
3535 | int count = repeater->count(); |
3536 | for (int i = 0; i < count; i++) { |
3537 | auto label = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3538 | label->setProperty(name: "visible" , value: enabled); |
3539 | } |
3540 | } |
3541 | |
3542 | void QQuickGraphsItem::changeGridLineColor(QQuick3DRepeater *repeater, const QColor &color) |
3543 | { |
3544 | for (int i = 0; i < repeater->count(); i++) { |
3545 | auto lineNode = static_cast<QQuick3DNode *>(repeater->objectAt(index: i)); |
3546 | lineNode->setProperty(name: "lineColor" , value: color); |
3547 | } |
3548 | } |
3549 | |
3550 | void QQuickGraphsItem::updateTitleLabels() |
3551 | { |
3552 | if (m_controller->m_changeTracker.axisXTitleVisibilityChanged) { |
3553 | m_titleLabelX->setVisible(m_controller->axisX()->isTitleVisible()); |
3554 | m_controller->m_changeTracker.axisXTitleVisibilityChanged = false; |
3555 | } |
3556 | |
3557 | if (m_controller->m_changeTracker.axisYTitleVisibilityChanged) { |
3558 | m_titleLabelY->setVisible(m_controller->axisY()->isTitleVisible()); |
3559 | m_controller->m_changeTracker.axisYTitleVisibilityChanged = false; |
3560 | } |
3561 | |
3562 | if (m_controller->m_changeTracker.axisZTitleVisibilityChanged) { |
3563 | m_titleLabelZ->setVisible(m_controller->axisZ()->isTitleVisible()); |
3564 | m_controller->m_changeTracker.axisZTitleVisibilityChanged = false; |
3565 | } |
3566 | |
3567 | if (m_controller->m_changeTracker.axisXTitleChanged) { |
3568 | m_titleLabelX->setProperty(name: "labelText" , value: m_controller->axisX()->title()); |
3569 | m_controller->m_changeTracker.axisXTitleChanged = false; |
3570 | } |
3571 | |
3572 | if (m_controller->m_changeTracker.axisYTitleChanged) { |
3573 | m_titleLabelY->setProperty(name: "labelText" , value: m_controller->axisY()->title()); |
3574 | m_controller->m_changeTracker.axisYTitleChanged = false; |
3575 | } |
3576 | |
3577 | if (m_controller->m_changeTracker.axisZTitleChanged) { |
3578 | m_titleLabelZ->setProperty(name: "labelText" , value: m_controller->axisZ()->title()); |
3579 | m_controller->m_changeTracker.axisZTitleChanged = false; |
3580 | } |
3581 | } |
3582 | |
3583 | void QQuickGraphsItem::updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode) |
3584 | { |
3585 | Q_UNUSED(newMode); |
3586 | |
3587 | if (m_sliceView && m_sliceView->isVisible()) |
3588 | updateSliceGraph(); |
3589 | } |
3590 | |
3591 | bool QQuickGraphsItem::doPicking(const QPointF &point) |
3592 | { |
3593 | if (m_activeInputHandler->d_func()->m_inputState |
3594 | == QAbstract3DInputHandlerPrivate::InputStateSelecting) { |
3595 | QList<QQuick3DPickResult> results = pickAll(x: point.x(), y: point.y()); |
3596 | if (!m_customItemList.isEmpty()) { |
3597 | // Try to pick custom item only |
3598 | for (const auto &result : results) { |
3599 | QCustom3DItem *customItem = m_customItemList.key(value: result.objectHit(), defaultKey: nullptr); |
3600 | |
3601 | if (customItem) { |
3602 | int selectedIndex = m_controller->m_customItems.indexOf(t: customItem); |
3603 | m_controller->m_selectedCustomItemIndex = selectedIndex; |
3604 | handleSelectedElementChange(type: QAbstract3DGraph::ElementCustomItem); |
3605 | // Don't allow picking in subclasses if custom item is picked |
3606 | return false; |
3607 | } |
3608 | } |
3609 | } |
3610 | |
3611 | for (const auto &result : results) { |
3612 | QString objName = result.objectHit()->objectName(); |
3613 | if (objName.contains(QStringLiteral("ElementAxisXLabel" ))) |
3614 | handleSelectedElementChange(type: QAbstract3DGraph::ElementAxisXLabel); |
3615 | else if (objName.contains(QStringLiteral("ElementAxisYLabel" ))) |
3616 | handleSelectedElementChange(type: QAbstract3DGraph::ElementAxisYLabel); |
3617 | else if (objName.contains(QStringLiteral("ElementAxisZLabel" ))) |
3618 | handleSelectedElementChange(type: QAbstract3DGraph::ElementAxisZLabel); |
3619 | else |
3620 | continue; |
3621 | } |
3622 | return true; |
3623 | } |
3624 | |
3625 | return false; |
3626 | } |
3627 | |
3628 | void QQuickGraphsItem::minimizeMainGraph() |
3629 | { |
3630 | QQuickItem *anchor = QQuickItemPrivate::get(item: this)->anchors()->fill(); |
3631 | if (anchor) |
3632 | QQuickItemPrivate::get(item: this)->anchors()->resetFill(); |
3633 | |
3634 | const float minimizedSize = .2f; |
3635 | setWidth(parentItem()->width() * minimizedSize); |
3636 | setHeight(parentItem()->height() * minimizedSize); |
3637 | } |
3638 | |
3639 | void QQuickGraphsItem::updateSliceGraph() |
3640 | { |
3641 | if (!m_sliceView || !m_sliceActivatedChanged) |
3642 | return; |
3643 | |
3644 | if (m_sliceView->isVisible()) { |
3645 | setWidth(parentItem()->width()); |
3646 | setHeight(parentItem()->height()); |
3647 | |
3648 | m_sliceView->setVisible(false); |
3649 | m_controller->setSlicingActive(false); |
3650 | } else { |
3651 | minimizeMainGraph(); |
3652 | m_sliceView->setVisible(true); |
3653 | updateSliceGrid(); |
3654 | updateSliceLabels(); |
3655 | m_controller->setSlicingActive(true); |
3656 | } |
3657 | |
3658 | m_sliceActivatedChanged = false; |
3659 | } |
3660 | |
3661 | void QQuickGraphsItem::addInputHandler(QAbstract3DInputHandler *inputHandler) |
3662 | { |
3663 | Q_ASSERT(inputHandler); |
3664 | QQuickGraphsItem *owner = qobject_cast<QQuickGraphsItem *>(object: inputHandler->parent()); |
3665 | if (owner != this) { |
3666 | Q_ASSERT_X(!owner, "addInputHandler" , |
3667 | "Input handler already attached to another component." ); |
3668 | inputHandler->setParent(this); |
3669 | } |
3670 | |
3671 | if (!m_inputHandlers.contains(t: inputHandler)) |
3672 | m_inputHandlers.append(t: inputHandler); |
3673 | } |
3674 | |
3675 | void QQuickGraphsItem::releaseInputHandler(QAbstract3DInputHandler *inputHandler) |
3676 | { |
3677 | if (inputHandler && m_inputHandlers.contains(t: inputHandler)) { |
3678 | // Clear the default status from released default input handler |
3679 | if (inputHandler->d_func()->m_isDefaultHandler) |
3680 | inputHandler->d_func()->m_isDefaultHandler = false; |
3681 | |
3682 | // If the input handler is in use, remove it |
3683 | if (m_activeInputHandler == inputHandler) |
3684 | setActiveInputHandler(nullptr); |
3685 | |
3686 | m_inputHandlers.removeAll(t: inputHandler); |
3687 | inputHandler->setParent(nullptr); |
3688 | } |
3689 | } |
3690 | |
3691 | void QQuickGraphsItem::setActiveInputHandler(QAbstract3DInputHandler *inputHandler) |
3692 | { |
3693 | if (inputHandler == m_activeInputHandler) |
3694 | return; |
3695 | |
3696 | // If existing input handler is the default input handler, delete it |
3697 | if (m_activeInputHandler) { |
3698 | if (m_activeInputHandler->d_func()->m_isDefaultHandler) { |
3699 | m_inputHandlers.removeAll(t: m_activeInputHandler); |
3700 | delete m_activeInputHandler; |
3701 | } else { |
3702 | // Disconnect the old input handler |
3703 | m_activeInputHandler->setScene(nullptr); |
3704 | QObject::disconnect(sender: m_activeInputHandler, signal: nullptr, receiver: m_controller, member: nullptr); |
3705 | QObject::disconnect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged, |
3706 | receiver: this, slot: &QQuickGraphsItem::doPicking); |
3707 | } |
3708 | } |
3709 | |
3710 | // Assume ownership and connect to this controller's scene |
3711 | if (inputHandler) |
3712 | addInputHandler(inputHandler); |
3713 | |
3714 | m_activeInputHandler = inputHandler; |
3715 | |
3716 | if (m_activeInputHandler) { |
3717 | m_activeInputHandler->setScene(scene()); |
3718 | |
3719 | // Connect the input handler |
3720 | QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::inputViewChanged, |
3721 | context: m_controller, slot: &Abstract3DController::handleInputViewChanged); |
3722 | QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged, |
3723 | context: m_controller, slot: &Abstract3DController::handleInputPositionChanged); |
3724 | QObject::connect(sender: m_activeInputHandler, signal: &QAbstract3DInputHandler::positionChanged, |
3725 | context: this, slot: &QQuickGraphsItem::doPicking); |
3726 | } |
3727 | |
3728 | // Notify change of input handler |
3729 | emit inputHandlerChanged(inputHandler: m_activeInputHandler); |
3730 | } |
3731 | |
3732 | void QQuickGraphsItem::windowDestroyed(QObject *obj) |
3733 | { |
3734 | // Remove destroyed window from window lists |
3735 | QQuickWindow *win = static_cast<QQuickWindow *>(obj); |
3736 | QQuickWindow *oldWindow = m_graphWindowList.value(key: this); |
3737 | |
3738 | if (win == oldWindow) |
3739 | m_graphWindowList.remove(key: this); |
3740 | } |
3741 | |
3742 | QQmlComponent *QQuickGraphsItem::createRepeaterDelegateComponent(const QString &fileName) |
3743 | { |
3744 | QQmlComponent component(qmlEngine(this), fileName); |
3745 | return qobject_cast<QQmlComponent *>(object: component.create()); |
3746 | } |
3747 | |
3748 | QQuick3DRepeater *QQuickGraphsItem::createRepeater(QQuick3DNode *parent) |
3749 | { |
3750 | auto engine = qmlEngine(this); |
3751 | QQmlComponent repeaterComponent(engine); |
3752 | repeaterComponent.setData("import QtQuick3D; Repeater3D{}" , baseUrl: QUrl()); |
3753 | auto repeater = qobject_cast<QQuick3DRepeater *>(object: repeaterComponent.create()); |
3754 | repeater->setParent(parent ? parent : graphNode()); |
3755 | repeater->setParentItem(parent ? parent : graphNode()); |
3756 | return repeater; |
3757 | } |
3758 | |
3759 | QQuick3DNode *QQuickGraphsItem::createTitleLabel(QQuick3DNode *parent) |
3760 | { |
3761 | auto engine = qmlEngine(this); |
3762 | QQmlComponent comp(engine, QStringLiteral(":/axis/TitleLabel" )); |
3763 | auto titleLabel = qobject_cast<QQuick3DNode *>(object: comp.create()); |
3764 | titleLabel->setParent(parent ? parent : graphNode()); |
3765 | titleLabel->setParentItem(parent ? parent : graphNode()); |
3766 | titleLabel->setVisible(false); |
3767 | titleLabel->setScale(m_labelScale); |
3768 | return titleLabel; |
3769 | } |
3770 | |
3771 | void QQuickGraphsItem::createItemLabel() |
3772 | { |
3773 | auto engine = qmlEngine(this); |
3774 | QQmlComponent comp(engine, QStringLiteral(":/axis/ItemLabel" )); |
3775 | m_itemLabel = qobject_cast<QQuickItem *>(o: comp.create()); |
3776 | m_itemLabel->setParent(this); |
3777 | m_itemLabel->setParentItem(this); |
3778 | m_itemLabel->setVisible(false); |
3779 | } |
3780 | |
3781 | QQuick3DCustomMaterial *QQuickGraphsItem::createQmlCustomMaterial(const QString &fileName) |
3782 | { |
3783 | QQmlComponent component(qmlEngine(this), fileName); |
3784 | QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(object: component.create()); |
3785 | return material; |
3786 | } |
3787 | |
3788 | QQuick3DPrincipledMaterial *QQuickGraphsItem::createPrincipledMaterial() |
3789 | { |
3790 | QQmlComponent component(qmlEngine(this)); |
3791 | component.setData("import QtQuick3D; PrincipledMaterial{}" , baseUrl: QUrl()); |
3792 | return qobject_cast<QQuick3DPrincipledMaterial *>(object: component.create()); |
3793 | } |
3794 | |
3795 | bool QQuickGraphsItem::event(QEvent *event) |
3796 | { |
3797 | return QQuickItem::event(event); |
3798 | } |
3799 | |
3800 | void QQuickGraphsItem::createSliceView() |
3801 | { |
3802 | if (m_sliceView) |
3803 | return; |
3804 | |
3805 | connect(sender: parentItem(), signal: &QQuickItem::widthChanged , context: this, slot: &QQuickGraphsItem::handleParentWidthChange); |
3806 | connect(sender: parentItem(), signal: &QQuickItem::heightChanged , context: this, slot: &QQuickGraphsItem::handleParentHeightChange); |
3807 | |
3808 | m_sliceView = new QQuick3DViewport(); |
3809 | m_sliceView->setParent(parent()); |
3810 | m_sliceView->setParentItem(parentItem()); |
3811 | m_sliceView->setVisible(false); |
3812 | |
3813 | m_sliceView->bindableHeight().setBinding(f: [&] { return parentItem()->height(); }); |
3814 | m_sliceView->bindableWidth().setBinding(f: [&] { return parentItem()->width(); }); |
3815 | |
3816 | auto scene = m_sliceView->scene(); |
3817 | |
3818 | auto camera = new QQuick3DOrthographicCamera(scene); |
3819 | camera->setPosition(QVector3D(.0f, .0f, 20.0f)); |
3820 | const float scale = qMin(a: m_sliceView->width(), b: m_sliceView->height()); |
3821 | const float magnificationScaleFactor = 2 * window()->devicePixelRatio() * .08f; // this controls the size of the slice view |
3822 | const float magnification = scale * magnificationScaleFactor; |
3823 | camera->setHorizontalMagnification(magnification); |
3824 | camera->setVerticalMagnification(magnification); |
3825 | m_sliceView->setCamera(camera); |
3826 | |
3827 | auto light = new QQuick3DDirectionalLight(scene); |
3828 | light->setParent(camera); |
3829 | light->setParentItem(camera); |
3830 | |
3831 | auto gridDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/GridLine" )); |
3832 | auto labelDelegate = createRepeaterDelegateComponent(QStringLiteral(":/axis/AxisLabel" )); |
3833 | |
3834 | m_sliceHorizontalGridRepeater = createRepeater(parent: scene); |
3835 | m_sliceHorizontalGridRepeater->setDelegate(gridDelegate); |
3836 | |
3837 | m_sliceVerticalGridRepeater = createRepeater(parent: scene); |
3838 | m_sliceVerticalGridRepeater->setDelegate(gridDelegate); |
3839 | |
3840 | m_sliceHorizontalLabelRepeater = createRepeater(parent: scene); |
3841 | m_sliceHorizontalLabelRepeater->setDelegate(labelDelegate); |
3842 | |
3843 | m_sliceVerticalLabelRepeater = createRepeater(parent: scene); |
3844 | m_sliceVerticalLabelRepeater->setDelegate(labelDelegate); |
3845 | |
3846 | m_sliceHorizontalTitleLabel = createTitleLabel(parent: scene); |
3847 | m_sliceHorizontalTitleLabel->setVisible(true); |
3848 | |
3849 | m_sliceVerticalTitleLabel = createTitleLabel(parent: scene); |
3850 | m_sliceVerticalTitleLabel->setVisible(true); |
3851 | |
3852 | m_sliceItemLabel = createTitleLabel(parent: scene); |
3853 | m_sliceItemLabel->setVisible(false); |
3854 | } |
3855 | |
3856 | void QQuickGraphsItem::updateSliceGrid() |
3857 | { |
3858 | QAbstract3DAxis *horizontalAxis = nullptr; |
3859 | QAbstract3DAxis *verticalAxis = m_controller->axisY(); |
3860 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
3861 | float scale; |
3862 | float translate; |
3863 | |
3864 | QVector3D horizontalScale = QVector3D(.0f, .0f, .0f); |
3865 | QVector3D verticalScale = QVector3D(lineWidthScaleFactor(), |
3866 | backgroundScale.y() * lineLengthScaleFactor(), |
3867 | lineWidthScaleFactor()); |
3868 | auto selectionMode = m_controller->selectionMode(); |
3869 | if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) { |
3870 | horizontalAxis = m_controller->axisX(); |
3871 | horizontalScale = QVector3D(backgroundScale.x() * lineLengthScaleFactor(), |
3872 | lineWidthScaleFactor(), |
3873 | lineWidthScaleFactor()); |
3874 | scale = m_scaleWithBackground.x(); |
3875 | translate = m_scaleWithBackground.x(); |
3876 | } else if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) { |
3877 | horizontalAxis = m_controller->axisZ(); |
3878 | horizontalScale = QVector3D(backgroundScale.z() * lineLengthScaleFactor(), |
3879 | lineWidthScaleFactor(), |
3880 | lineWidthScaleFactor()); |
3881 | scale = m_scaleWithBackground.z(); |
3882 | translate = m_scaleWithBackground.z(); |
3883 | } |
3884 | |
3885 | if (horizontalAxis == nullptr) { |
3886 | qWarning(msg: "Invalid axis type" ); |
3887 | return; |
3888 | } |
3889 | |
3890 | if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeValue) { |
3891 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis); |
3892 | m_sliceVerticalGridRepeater->model().clear(); |
3893 | m_sliceVerticalGridRepeater->setModel(valueAxis->gridSize() |
3894 | + valueAxis->subGridSize()); |
3895 | } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) { |
3896 | m_sliceVerticalGridRepeater->model().clear(); |
3897 | m_sliceVerticalGridRepeater->setModel(horizontalAxis->labels().size()); |
3898 | } |
3899 | |
3900 | if (verticalAxis->type() & QAbstract3DAxis::AxisTypeValue) { |
3901 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis); |
3902 | m_sliceHorizontalGridRepeater->model().clear(); |
3903 | m_sliceHorizontalGridRepeater->setModel(valueAxis->gridSize() |
3904 | + valueAxis->subGridSize()); |
3905 | } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) { |
3906 | m_sliceHorizontalGridRepeater->model().clear(); |
3907 | m_sliceHorizontalGridRepeater->setModel(verticalAxis->labels().size()); |
3908 | } |
3909 | |
3910 | float linePosX = .0f; |
3911 | float linePosY = .0f; |
3912 | float linePosZ = -1.f; // Draw grid lines behind slice (especially for surface) |
3913 | |
3914 | if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeCategory) { |
3915 | m_sliceVerticalGridRepeater->setVisible(false); |
3916 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeValue) { |
3917 | for (int i = 0; i < m_sliceVerticalGridRepeater->count(); i++) { |
3918 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(m_sliceVerticalGridRepeater->objectAt(index: i)); |
3919 | auto axis = static_cast<QValue3DAxis *>(horizontalAxis); |
3920 | if (i < axis->gridSize()) |
3921 | linePosX = axis->gridPositionAt(gridLine: i) * scale * 2.0f - translate; |
3922 | else |
3923 | linePosX = axis->subGridPositionAt(gridLine: i - axis->gridSize()) * scale * 2.0f - translate; |
3924 | lineNode->setProperty(name: "lineColor" , value: QColor(0, 0, 0)); |
3925 | positionAndScaleLine(lineNode, scale: verticalScale, position: QVector3D(linePosX, linePosY, linePosZ)); |
3926 | } |
3927 | } |
3928 | |
3929 | linePosX = 0; |
3930 | scale = m_scaleWithBackground.y(); |
3931 | translate = m_scaleWithBackground.y(); |
3932 | |
3933 | for (int i = 0; i < m_sliceHorizontalGridRepeater->count(); i++) { |
3934 | QQuick3DNode *lineNode = static_cast<QQuick3DNode *>(m_sliceHorizontalGridRepeater->objectAt(index: i)); |
3935 | if (verticalAxis->type() == QAbstract3DAxis::AxisTypeValue) { |
3936 | auto axis = static_cast<QValue3DAxis *>(verticalAxis); |
3937 | if (i < axis->gridSize()) |
3938 | linePosY = axis->gridPositionAt(gridLine: i) * scale * 2.0f - translate; |
3939 | else |
3940 | linePosY = axis->subGridPositionAt(gridLine: i - axis->gridSize()) * scale * 2.0f - translate; |
3941 | } else if (verticalAxis->type() == QAbstract3DAxis::AxisTypeCategory) { |
3942 | linePosY = calculateCategoryGridLinePosition(axis: verticalAxis, index: i); |
3943 | } |
3944 | lineNode->setProperty(name: "lineColor" , value: QColor(0, 0, 0)); |
3945 | positionAndScaleLine(lineNode, scale: horizontalScale, position: QVector3D(linePosX, linePosY, linePosZ)); |
3946 | } |
3947 | } |
3948 | |
3949 | void QQuickGraphsItem::updateSliceLabels() |
3950 | { |
3951 | QAbstract3DAxis *horizontalAxis = nullptr; |
3952 | QAbstract3DAxis *verticalAxis = m_controller->axisY(); |
3953 | auto backgroundScale = m_scaleWithBackground + m_backgroundScaleMargin; |
3954 | float scale; |
3955 | float translate; |
3956 | auto selectionMode = m_controller->selectionMode(); |
3957 | |
3958 | if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) |
3959 | horizontalAxis = m_controller->axisX(); |
3960 | else if (selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) |
3961 | horizontalAxis = m_controller->axisZ(); |
3962 | |
3963 | scale = backgroundScale.x() - m_backgroundScaleMargin.x(); |
3964 | translate = backgroundScale.x() - m_backgroundScaleMargin.x(); |
3965 | |
3966 | if (horizontalAxis == nullptr) { |
3967 | qWarning(msg: "Invalid selection mode" ); |
3968 | return; |
3969 | } |
3970 | |
3971 | if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeValue) { |
3972 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(horizontalAxis); |
3973 | m_sliceHorizontalLabelRepeater->model().clear(); |
3974 | m_sliceHorizontalLabelRepeater->setModel(valueAxis->labels().size()); |
3975 | } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) { |
3976 | m_sliceHorizontalLabelRepeater->model().clear(); |
3977 | m_sliceHorizontalLabelRepeater->setModel(horizontalAxis->labels().size()); |
3978 | } |
3979 | |
3980 | if (verticalAxis->type() & QAbstract3DAxis::AxisTypeValue) { |
3981 | QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(verticalAxis); |
3982 | m_sliceVerticalLabelRepeater->model().clear(); |
3983 | m_sliceVerticalLabelRepeater->setModel(valueAxis->labels().size()); |
3984 | } else if (horizontalAxis->type() & QAbstract3DAxis::AxisTypeCategory) { |
3985 | m_sliceVerticalLabelRepeater->model().clear(); |
3986 | m_sliceVerticalLabelRepeater->setModel(verticalAxis->labels().size()); |
3987 | } |
3988 | |
3989 | float textPadding = 12.0f; |
3990 | |
3991 | float labelsMaxWidth = float(findLabelsMaxWidth(labels: horizontalAxis->labels())) + textPadding; |
3992 | QFontMetrics fm(m_controller->activeTheme()->font()); |
3993 | float labelHeight = fm.height() + textPadding; |
3994 | |
3995 | float pointSize = m_controller->activeTheme()->font().pointSizeF(); |
3996 | float scaleFactor = fontScaleFactor(pointSize) * pointSize; |
3997 | float fontRatio = labelsMaxWidth / labelHeight; |
3998 | QVector3D fontScaled = QVector3D(scaleFactor * fontRatio, scaleFactor, 0.00001f); |
3999 | |
4000 | float adjustment = labelsMaxWidth * scaleFactor; |
4001 | float yPos = backgroundScale.y() + adjustment; |
4002 | |
4003 | QVector3D labelTrans = QVector3D(0.0f, -yPos, 0.0f); |
4004 | QStringList labels = horizontalAxis->labels(); |
4005 | Q3DTheme *theme = m_controller->activeTheme(); |
4006 | QFont font = theme->font(); |
4007 | bool borderEnabled = theme->isLabelBorderEnabled(); |
4008 | QColor labelTextColor = theme->labelTextColor(); |
4009 | bool backgroundEnabled = theme->isLabelBackgroundEnabled(); |
4010 | QColor backgroundColor = theme->labelBackgroundColor(); |
4011 | |
4012 | if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeValue) { |
4013 | auto valueAxis = static_cast<QValue3DAxis *>(horizontalAxis); |
4014 | for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) { |
4015 | auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(index: i)); |
4016 | labelTrans.setX(valueAxis->labelPositionAt(index: i) * scale * 2.0f - translate); |
4017 | obj->setScale(fontScaled); |
4018 | obj->setPosition(labelTrans); |
4019 | obj->setProperty(name: "labelText" , value: labels[i]); |
4020 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
4021 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
4022 | obj->setProperty(name: "labelFont" , value: font); |
4023 | obj->setProperty(name: "borderEnabled" , value: borderEnabled); |
4024 | obj->setProperty(name: "labelTextColor" , value: labelTextColor); |
4025 | obj->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4026 | obj->setProperty(name: "backgroundColor" , value: backgroundColor); |
4027 | obj->setEulerRotation(QVector3D(.0f, .0f, -45.0f)); |
4028 | } |
4029 | } else if (horizontalAxis->type() == QAbstract3DAxis::AxisTypeCategory) { |
4030 | for (int i = 0; i < m_sliceHorizontalLabelRepeater->count(); i++) { |
4031 | labelTrans = calculateCategoryLabelPosition(axis: horizontalAxis, labelPosition: labelTrans, index: i); |
4032 | labelTrans.setY(labelTrans.y() - (adjustment / 1.5f)); |
4033 | if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn)) |
4034 | labelTrans.setX(labelTrans.z()); |
4035 | labelTrans.setZ(1.0f); // Bring the labels on top of bars and grid |
4036 | auto obj = static_cast<QQuick3DNode *>(m_sliceHorizontalLabelRepeater->objectAt(index: i)); |
4037 | obj->setScale(fontScaled); |
4038 | obj->setPosition(labelTrans); |
4039 | obj->setProperty(name: "labelText" , value: labels[i]); |
4040 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
4041 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
4042 | obj->setProperty(name: "labelFont" , value: font); |
4043 | obj->setProperty(name: "borderEnabled" , value: borderEnabled); |
4044 | obj->setProperty(name: "labelTextColor" , value: labelTextColor); |
4045 | obj->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4046 | obj->setProperty(name: "backgroundColor" , value: backgroundColor); |
4047 | obj->setEulerRotation(QVector3D(0.0f, 0.0f, -60.0f)); |
4048 | } |
4049 | } |
4050 | |
4051 | scale = backgroundScale.y() - m_backgroundScaleMargin.y(); |
4052 | translate = backgroundScale.y() - m_backgroundScaleMargin.y(); |
4053 | labels = verticalAxis->labels(); |
4054 | labelsMaxWidth = float(findLabelsMaxWidth(labels)) + textPadding; |
4055 | adjustment = labelsMaxWidth * scaleFactor; |
4056 | float xPos = 0.0f; |
4057 | if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionRow)) |
4058 | xPos = backgroundScale.x() + adjustment; |
4059 | else if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn)) |
4060 | xPos = backgroundScale.z() + adjustment; |
4061 | labelTrans = QVector3D(xPos, 0.0f, 0.0f); |
4062 | |
4063 | if (verticalAxis->type() == QAbstract3DAxis::AxisTypeValue) { |
4064 | auto valueAxis = static_cast<QValue3DAxis *>(verticalAxis); |
4065 | for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) { |
4066 | auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(index: i)); |
4067 | labelTrans.setY(valueAxis->labelPositionAt(index: i) * scale * 2.0f - translate); |
4068 | obj->setScale(fontScaled); |
4069 | obj->setPosition(labelTrans); |
4070 | obj->setProperty(name: "labelText" , value: labels[i]); |
4071 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
4072 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
4073 | obj->setProperty(name: "labelFont" , value: font); |
4074 | obj->setProperty(name: "borderEnabled" , value: borderEnabled); |
4075 | obj->setProperty(name: "labelTextColor" , value: labelTextColor); |
4076 | obj->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4077 | obj->setProperty(name: "backgroundColor" , value: backgroundColor); |
4078 | } |
4079 | } else if (verticalAxis->type() == QAbstract3DAxis::AxisTypeCategory) { |
4080 | for (int i = 0; i < m_sliceVerticalLabelRepeater->count(); i++) { |
4081 | labelTrans = calculateCategoryLabelPosition(axis: verticalAxis, labelPosition: labelTrans, index: i); |
4082 | auto obj = static_cast<QQuick3DNode *>(m_sliceVerticalLabelRepeater->objectAt(index: i)); |
4083 | obj->setScale(fontScaled); |
4084 | obj->setPosition(labelTrans); |
4085 | obj->setProperty(name: "labelText" , value: labels[i]); |
4086 | obj->setProperty(name: "labelWidth" , value: labelsMaxWidth); |
4087 | obj->setProperty(name: "labelHeight" , value: labelHeight); |
4088 | obj->setProperty(name: "labelFont" , value: font); |
4089 | obj->setProperty(name: "borderEnabled" , value: borderEnabled); |
4090 | obj->setProperty(name: "labelTextColor" , value: labelTextColor); |
4091 | obj->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4092 | obj->setProperty(name: "backgroundColor" , value: backgroundColor); |
4093 | } |
4094 | } |
4095 | |
4096 | labelHeight = fm.height() + textPadding; |
4097 | float labelWidth = fm.horizontalAdvance(verticalAxis->title()) + textPadding; |
4098 | QVector3D vTitleScale = fontScaled; |
4099 | vTitleScale.setX(fontScaled.y() * labelWidth / labelHeight); |
4100 | adjustment = labelHeight * scaleFactor; |
4101 | if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionRow)) |
4102 | xPos = backgroundScale.x() + adjustment; |
4103 | else if (m_controller->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn)) |
4104 | xPos = backgroundScale.z() + adjustment; |
4105 | labelTrans = QVector3D(-xPos, 0.0f, 0.0f); |
4106 | |
4107 | if (!verticalAxis->title().isEmpty()) { |
4108 | m_sliceVerticalTitleLabel->setScale(vTitleScale); |
4109 | m_sliceVerticalTitleLabel->setPosition(labelTrans); |
4110 | m_sliceVerticalTitleLabel->setProperty(name: "labelWidth" , value: labelWidth); |
4111 | m_sliceVerticalTitleLabel->setProperty(name: "labelHeight" , value: labelHeight); |
4112 | m_sliceVerticalTitleLabel->setProperty(name: "labelText" , value: verticalAxis->title()); |
4113 | m_sliceVerticalTitleLabel->setProperty(name: "labelFont" , value: font); |
4114 | m_sliceVerticalTitleLabel->setProperty(name: "borderEnabled" , value: borderEnabled); |
4115 | m_sliceVerticalTitleLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
4116 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4117 | m_sliceVerticalTitleLabel->setProperty(name: "backgroundColor" , value: backgroundColor); |
4118 | m_sliceVerticalTitleLabel->setEulerRotation(QVector3D(.0f, .0f, 90.0f)); |
4119 | } else { |
4120 | m_sliceVerticalTitleLabel->setVisible(false); |
4121 | } |
4122 | |
4123 | labelHeight = fm.height() + textPadding; |
4124 | labelWidth = fm.horizontalAdvance(horizontalAxis->title()) + textPadding; |
4125 | QVector3D hTitleScale = fontScaled; |
4126 | hTitleScale.setX(fontScaled.y() * labelWidth / labelHeight); |
4127 | adjustment = labelHeight * scaleFactor; |
4128 | yPos = backgroundScale.y() * 1.5f + adjustment; |
4129 | labelTrans = QVector3D(0.0f, -yPos, 0.0f); |
4130 | |
4131 | if (!horizontalAxis->title().isEmpty()) { |
4132 | m_sliceHorizontalTitleLabel->setScale(hTitleScale); |
4133 | m_sliceHorizontalTitleLabel->setPosition(labelTrans); |
4134 | m_sliceHorizontalTitleLabel->setProperty(name: "labelWidth" , value: labelWidth); |
4135 | m_sliceHorizontalTitleLabel->setProperty(name: "labelHeight" , value: labelHeight); |
4136 | m_sliceHorizontalTitleLabel->setProperty(name: "labelText" , value: horizontalAxis->title()); |
4137 | m_sliceHorizontalTitleLabel->setProperty(name: "labelFont" , value: font); |
4138 | m_sliceHorizontalTitleLabel->setProperty(name: "borderEnabled" , value: borderEnabled); |
4139 | m_sliceHorizontalTitleLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
4140 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4141 | m_sliceHorizontalTitleLabel->setProperty(name: "backgroundColor" , value: backgroundColor); |
4142 | } else { |
4143 | m_sliceHorizontalTitleLabel->setVisible(false); |
4144 | } |
4145 | |
4146 | m_sliceItemLabel->setScale(fontScaled); |
4147 | m_sliceItemLabel->setProperty(name: "labelFont" , value: font); |
4148 | m_sliceItemLabel->setProperty(name: "borderEnabled" , value: borderEnabled); |
4149 | m_sliceItemLabel->setProperty(name: "labelTextColor" , value: labelTextColor); |
4150 | m_sliceItemLabel->setProperty(name: "backgroundEnabled" , value: backgroundEnabled); |
4151 | m_sliceItemLabel->setProperty(name: "backgroundColor" , value: backgroundColor); |
4152 | } |
4153 | |
4154 | void QQuickGraphsItem::setUpCamera() |
4155 | { |
4156 | m_pCamera = new QQuick3DPerspectiveCamera(rootNode()); |
4157 | m_pCamera->setClipNear(0.001f); |
4158 | m_pCamera->setFieldOfView(45.0f); |
4159 | m_pCamera->setPosition(QVector3D(.0f, .0f, 5.f)); |
4160 | |
4161 | auto cameraTarget = new QQuick3DNode(rootNode()); |
4162 | cameraTarget->setParentItem(rootNode()); |
4163 | |
4164 | setCameraTarget(cameraTarget); |
4165 | cameraTarget->setPosition(QVector3D(0, 0, 0)); |
4166 | QQuick3DObjectPrivate::get(item: cameraTarget)->refSceneManager( |
4167 | *QQuick3DObjectPrivate::get(item: rootNode())->sceneManager); |
4168 | |
4169 | m_pCamera->lookAt(node: cameraTarget); |
4170 | m_pCamera->setParent(cameraTarget); |
4171 | m_pCamera->setParentItem(cameraTarget); |
4172 | |
4173 | m_oCamera = new QQuick3DOrthographicCamera(rootNode()); |
4174 | // Set clip near 0.0001f so that it can be set correct value to workaround |
4175 | // a Quick3D device pixel ratio bug |
4176 | m_oCamera->setClipNear(0.0001f); |
4177 | m_oCamera->setPosition(QVector3D(0.f, 0.f, 5.f)); |
4178 | m_oCamera->setParent(cameraTarget); |
4179 | m_oCamera->setParentItem(cameraTarget); |
4180 | m_oCamera->lookAt(node: cameraTarget); |
4181 | |
4182 | auto useOrtho = m_controller->isOrthoProjection(); |
4183 | if (useOrtho) |
4184 | setCamera(m_oCamera); |
4185 | else |
4186 | setCamera(m_pCamera); |
4187 | } |
4188 | |
4189 | void QQuickGraphsItem::setUpLight() |
4190 | { |
4191 | auto light = new QQuick3DDirectionalLight(rootNode()); |
4192 | QQuick3DObjectPrivate::get(item: light)->refSceneManager( |
4193 | *QQuick3DObjectPrivate::get(item: rootNode())->sceneManager); |
4194 | light->setParent(camera()); |
4195 | light->setParentItem(camera()); |
4196 | m_light = light; |
4197 | } |
4198 | |
4199 | QT_END_NAMESPACE |
4200 | |