1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquickgraphsbars_p.h"
5#include "quickgraphstexturedata_p.h"
6#include "bars3dcontroller_p.h"
7#include "declarativescene_p.h"
8#include "qbar3dseries_p.h"
9#include "qvalue3daxis_p.h"
10#include "qcategory3daxis_p.h"
11
12#include "q3dcamera_p.h"
13#include <QtCore/QMutexLocker>
14#include <QColor>
15#include "q3dtheme_p.h"
16
17#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
18#include "quickgraphstexturedata_p.h"
19#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
20
21QQuickGraphsBars::QQuickGraphsBars(QQuickItem *parent)
22 : QQuickGraphsItem(parent),
23 m_barsController(0),
24 m_cachedRowCount(0),
25 m_cachedColumnCount(0),
26 m_minRow(0),
27 m_maxRow(0),
28 m_minCol(0),
29 m_maxCol(0),
30 m_newRows(0),
31 m_newCols(0),
32 m_maxSceneSize(40.0f),
33 m_rowWidth(0),
34 m_columnDepth(0),
35 m_maxDimension(0),
36 m_scaleFactor(0),
37 m_xScaleFactor(1.0f),
38 m_zScaleFactor(1.0f),
39 m_cachedBarSeriesMargin(0.0f, 0.0f),
40 m_hasNegativeValues(false),
41 m_noZeroInRange(false),
42 m_actualFloorLevel(0.0f),
43 m_heightNormalizer(1.0f),
44 m_backgroundAdjustment(0.0f),
45 m_selectedBarSeries(0),
46 m_selectedBarCoord(Bars3DController::invalidSelectionPosition()),
47 m_selectedBarPos(0.0f, 0.0f, 0.0f),
48 m_keepSeriesUniform(false),
49 m_seriesScaleX(0.0f),
50 m_seriesScaleZ(0.0f),
51 m_seriesStep(0.0f),
52 m_seriesStart(0.0f),
53 m_zeroPosition(0.0f),
54 m_visibleSeriesCount(0)
55{
56 setAcceptedMouseButtons(Qt::AllButtons);
57 setFlags(ItemHasContents);
58 // Create the shared component on the main GUI thread.
59 m_barsController = new Bars3DController(boundingRect().toRect(), new Declarative3DScene);
60 setSharedController(m_barsController);
61
62 QObject::connect(sender: m_barsController, signal: &Bars3DController::primarySeriesChanged,
63 context: this, slot: &QQuickGraphsBars::primarySeriesChanged);
64 QObject::connect(sender: m_barsController, signal: &Bars3DController::selectedSeriesChanged,
65 context: this, slot: &QQuickGraphsBars::selectedSeriesChanged);
66}
67
68QQuickGraphsBars::~QQuickGraphsBars()
69{
70 QMutexLocker locker(m_nodeMutex.data());
71 const QMutexLocker locker2(mutex());
72 removeBarModels();
73 removeSelectedModels();
74 removeSlicedBarModels();
75 delete m_barsController;
76}
77
78QCategory3DAxis *QQuickGraphsBars::rowAxis() const
79{
80 return static_cast<QCategory3DAxis *>(m_barsController->axisZ());
81}
82
83void QQuickGraphsBars::setRowAxis(QCategory3DAxis *axis)
84{
85 m_barsController->setAxisZ(axis);
86 // labelsChanged and rangeChanged signals are required to update the row and column numbers.
87 // The same situation exists in the barscontroller. (see setAxisZ and setAxisHelper)
88 // A better implementation may apply once controllers are removed
89 QObject::connect(sender: axis, signal: &QAbstract3DAxis::labelsChanged, context: this,
90 slot: &QQuickGraphsBars::handleRowCountChanged);
91 QObject::connect(sender: axis, signal: &QAbstract3DAxis::rangeChanged,
92 context: this, slot: &QQuickGraphsBars::handleRowCountChanged);
93 handleRowCountChanged();
94}
95
96QValue3DAxis *QQuickGraphsBars::valueAxis() const
97{
98 return static_cast<QValue3DAxis *>(m_barsController->axisY());
99}
100
101void QQuickGraphsBars::setValueAxis(QValue3DAxis *axis)
102{
103 m_barsController->setAxisY(axis);
104 if (segmentLineRepeaterY()) {
105 int segmentCount = 0;
106 int subSegmentCount = 0;
107 int gridLineCount = 0;
108 int subGridLineCount = 0;
109 if (axis->type() & QAbstract3DAxis::AxisTypeValue) {
110 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis);
111 segmentCount = valueAxis->segmentCount();
112 subSegmentCount = valueAxis->subSegmentCount();
113 gridLineCount = 2 * (segmentCount + 1);
114 subGridLineCount = 2 * (segmentCount * (subSegmentCount - 1));
115 } else if (axis->type() & QAbstract3DAxis::AxisTypeCategory) {
116 gridLineCount = axis->labels().size();
117 }
118 segmentLineRepeaterY()->setModel(gridLineCount);
119 subsegmentLineRepeaterY()->setModel(subGridLineCount);
120 repeaterY()->setModel(2 * axis->labels().size());
121 }
122}
123
124QCategory3DAxis *QQuickGraphsBars::columnAxis() const
125{
126 return static_cast<QCategory3DAxis *>(m_barsController->axisX());
127}
128
129void QQuickGraphsBars::setColumnAxis(QCategory3DAxis *axis)
130{
131 m_barsController->setAxisX(axis);
132 QObject::connect(sender: axis, signal: &QAbstract3DAxis::labelsChanged, context: this,
133 slot: &QQuickGraphsBars::handleColCountChanged);
134 QObject::connect(sender: axis, signal: &QAbstract3DAxis::rangeChanged, context: this,
135 slot: &QQuickGraphsBars::handleColCountChanged);
136 handleColCountChanged();
137}
138
139void QQuickGraphsBars::setMultiSeriesUniform(bool uniform)
140{
141 if (uniform != isMultiSeriesUniform()) {
142 m_barsController->setMultiSeriesScaling(uniform);
143 emit multiSeriesUniformChanged(uniform);
144 }
145}
146
147bool QQuickGraphsBars::isMultiSeriesUniform() const
148{
149 return m_barsController->multiSeriesScaling();
150}
151
152void QQuickGraphsBars::setBarThickness(float thicknessRatio)
153{
154 if (thicknessRatio != barThickness()) {
155 m_barsController->setBarSpecs(thicknessRatio, spacing: barSpacing(),
156 relative: isBarSpacingRelative());
157 emit barThicknessChanged(thicknessRatio);
158 }
159}
160
161float QQuickGraphsBars::barThickness() const
162{
163 return m_barsController->barThickness();
164}
165
166void QQuickGraphsBars::setBarSpacing(const QSizeF &spacing)
167{
168 if (spacing != barSpacing()) {
169 m_barsController->setBarSpecs(thicknessRatio: barThickness(), spacing, relative: isBarSpacingRelative());
170 emit barSpacingChanged(spacing);
171 }
172}
173
174QSizeF QQuickGraphsBars::barSpacing() const
175{
176 return m_barsController->barSpacing();
177}
178
179void QQuickGraphsBars::setBarSpacingRelative(bool relative)
180{
181 if (relative != isBarSpacingRelative()) {
182 m_barsController->setBarSpecs(thicknessRatio: barThickness(), spacing: barSpacing(), relative);
183 emit barSpacingRelativeChanged(relative);
184 }
185}
186
187bool QQuickGraphsBars::isBarSpacingRelative() const
188{
189 return m_barsController->isBarSpecRelative();
190}
191
192void QQuickGraphsBars::setBarSeriesMargin(const QSizeF &margin)
193{
194 if (margin != barSeriesMargin()) {
195 m_barsController->setBarSeriesMargin(margin);
196 emit barSeriesMarginChanged(margin: barSeriesMargin());
197 }
198}
199
200QSizeF QQuickGraphsBars::barSeriesMargin() const
201{
202 return m_barsController->barSeriesMargin();
203}
204
205QQmlListProperty<QBar3DSeries> QQuickGraphsBars::seriesList()
206{
207 return QQmlListProperty<QBar3DSeries>(this, this,
208 &QQuickGraphsBars::appendSeriesFunc,
209 &QQuickGraphsBars::countSeriesFunc,
210 &QQuickGraphsBars::atSeriesFunc,
211 &QQuickGraphsBars::clearSeriesFunc);
212}
213
214void QQuickGraphsBars::appendSeriesFunc(QQmlListProperty<QBar3DSeries> *list, QBar3DSeries *series)
215{
216 reinterpret_cast<QQuickGraphsBars *>(list->data)->addSeries(series);
217}
218
219qsizetype QQuickGraphsBars::countSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
220{
221 return reinterpret_cast<QQuickGraphsBars *>(list->data)->m_barsController->barSeriesList().size();
222}
223
224QBar3DSeries *QQuickGraphsBars::atSeriesFunc(QQmlListProperty<QBar3DSeries> *list, qsizetype index)
225{
226 return reinterpret_cast<QQuickGraphsBars *>(list->data)->m_barsController->barSeriesList().at(i: index);
227}
228
229void QQuickGraphsBars::clearSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
230{
231 QQuickGraphsBars *declBars = reinterpret_cast<QQuickGraphsBars *>(list->data);
232 QList<QBar3DSeries *> realList = declBars->m_barsController->barSeriesList();
233 int count = realList.size();
234 for (int i = 0; i < count; i++)
235 declBars->removeSeries(series: realList.at(i));
236}
237
238void QQuickGraphsBars::addSeries(QBar3DSeries *series)
239{
240 m_barsController->addSeries(series);
241 connectSeries(series);
242 if (series->selectedBar() != invalidSelectionPosition())
243 updateSelectedBar();
244}
245
246void QQuickGraphsBars::removeSeries(QBar3DSeries *series)
247{
248 m_barsController->removeSeries(series);
249 removeBarModels();
250 series->setParent(this); // Reparent as removing will leave series parentless
251 disconnectSeries(series);
252 handleRowCountChanged();
253 handleColCountChanged();
254}
255
256void QQuickGraphsBars::insertSeries(int index, QBar3DSeries *series)
257{
258 m_barsController->insertSeries(index, series);
259 handleRowCountChanged();
260 handleColCountChanged();
261}
262
263void QQuickGraphsBars::setPrimarySeries(QBar3DSeries *series)
264{
265 m_barsController->setPrimarySeries(series);
266 handleRowCountChanged();
267 handleColCountChanged();
268}
269
270QBar3DSeries *QQuickGraphsBars::primarySeries() const
271{
272 return m_barsController->primarySeries();
273}
274
275QBar3DSeries *QQuickGraphsBars::selectedSeries() const
276{
277 return m_barsController->selectedSeries();
278}
279
280void QQuickGraphsBars::setFloorLevel(float level)
281{
282 if (level != floorLevel()) {
283 m_barsController->setFloorLevel(level);
284 emit floorLevelChanged(level);
285 }
286}
287
288float QQuickGraphsBars::floorLevel() const
289{
290 return m_barsController->floorLevel();
291}
292
293void QQuickGraphsBars::componentComplete()
294{
295 QQuickGraphsItem::componentComplete();
296
297 auto wallBackground = background();
298 QUrl wallUrl = QUrl(QStringLiteral("defaultMeshes/backgroundNoFloorMesh"));
299 wallBackground->setSource(wallUrl);
300 setBackground(wallBackground);
301
302 QUrl floorUrl = QUrl(QStringLiteral(":/defaultMeshes/planeMesh"));
303 m_floorBackground = new QQuick3DModel();
304 m_floorBackgroundScale = new QQuick3DNode();
305 m_floorBackgroundRotation = new QQuick3DNode();
306
307 m_floorBackgroundScale->setParent(rootNode());
308 m_floorBackgroundScale->setParentItem(rootNode());
309
310 m_floorBackgroundRotation->setParent(m_floorBackgroundScale);
311 m_floorBackgroundRotation->setParentItem(m_floorBackgroundScale);
312
313 m_floorBackground->setObjectName("Floor Background");
314 m_floorBackground->setParent(m_floorBackgroundRotation);
315 m_floorBackground->setParentItem(m_floorBackgroundRotation);
316
317 m_floorBackground->setSource(floorUrl);
318
319 QValue3DAxis *axisY = static_cast<QValue3DAxis *>(m_barsController->axisY());
320 m_helperAxisY.setFormatter(axisY->formatter());
321
322 setFloorGridInRange(true);
323 setVerticalSegmentLine(false);
324
325 QObject::connect(sender: cameraTarget(), signal: &QQuick3DNode::rotationChanged, context: this,
326 slot: &QQuickGraphsBars::handleCameraRotationChanged);
327}
328
329void QQuickGraphsBars::synchData()
330{
331 Q3DCamera *camera = m_barsController->m_scene->activeCamera();
332 Q3DTheme *theme = m_barsController->activeTheme();
333
334 if (!m_noZeroInRange) {
335 camera->d_func()->setMinYRotation(-90.0f);
336 camera->d_func()->setMaxYRotation(90.0f);
337 } else {
338 if ((m_hasNegativeValues && !m_helperAxisY.isReversed())
339 || (!m_hasNegativeValues && m_helperAxisY.isReversed())) {
340 camera->d_func()->setMinYRotation(-90.0f);
341 camera->d_func()->setMaxYRotation(0.0f);
342 } else {
343 camera->d_func()->setMinYRotation(0.0f);
344 camera->d_func()->setMaxYRotation(90.0f);
345 }
346 }
347 if (m_barsController->m_changeTracker.barSpecsChanged || !m_cachedBarThickness.isValid()) {
348 updateBarSpecs(thicknessRatio: m_barsController->m_barThicknessRatio, spacing: m_barsController->m_barSpacing,
349 relative: m_barsController->m_isBarSpecRelative);
350 m_barsController->m_changeTracker.barSpecsChanged = false;
351 }
352
353 // Floor level update requires data update, so do before qquickgraphicsitem sync
354 if (m_barsController->m_changeTracker.floorLevelChanged) {
355 updateFloorLevel(level: m_barsController->m_floorLevel);
356 m_barsController->m_changeTracker.floorLevelChanged = false;
357 }
358
359 // Do not clear dirty flag, we need to react to it in qquickgraphicsitem as well
360 if (theme->d_func()->m_dirtyBits.backgroundEnabledDirty) {
361 m_floorBackground->setVisible(theme->isBackgroundEnabled());
362 m_barsController->setSeriesVisualsDirty(true);
363 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++)
364 it.key()->d_func()->m_changeTracker.meshChanged = true;
365 }
366
367 if (m_barsController->m_changeTracker.barSeriesMarginChanged) {
368 updateBarSeriesMargin(margin: barSeriesMargin());
369 m_barsController->m_changeTracker.barSeriesMarginChanged = false;
370 }
371
372 auto axisY = static_cast<QValue3DAxis *>(m_barsController->axisY());
373 axisY->formatter()->d_func()->recalculate();
374 m_helperAxisY.setFormatter(axisY->formatter());
375
376 if (m_axisRangeChanged) {
377 theme->d_func()->resetDirtyBits();
378 updateGrid();
379 updateLabels();
380 m_axisRangeChanged = false;
381 }
382
383 QQuickGraphsItem::synchData();
384
385 QMatrix4x4 modelMatrix;
386
387 // Draw floor
388 m_floorBackground->setPickable(false);
389 m_floorBackgroundScale->setScale(scaleWithBackground());
390 modelMatrix.scale(vector: scaleWithBackground());
391 m_floorBackgroundScale->setPosition(QVector3D(0.0f, -m_backgroundAdjustment, 0.0f));
392
393 QQuaternion m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: 90.0f));
394 QQuaternion m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: -90.0f));
395
396 if (isYFlipped()) {
397 m_floorBackgroundRotation->setRotation(m_xRightAngleRotation);
398 modelMatrix.rotate(quaternion: m_xRightAngleRotation);
399 } else {
400 m_floorBackgroundRotation->setRotation(m_xRightAngleRotationNeg);
401 modelMatrix.rotate(quaternion: m_xRightAngleRotationNeg);
402 }
403
404 auto bgFloor = m_floorBackground;
405 bgFloor->setPickable(false);
406 QQmlListReference materialsRefF(bgFloor, "materials");
407 QQuick3DPrincipledMaterial * bgMatFloor;
408
409 if (!materialsRefF.size()) {
410 bgMatFloor = new QQuick3DPrincipledMaterial();
411 bgMatFloor->setParent(bgFloor);
412 bgMatFloor->setMetalness(0.f);
413 bgMatFloor->setRoughness(.3f);
414 bgMatFloor->setEmissiveFactor(QVector3D(.001f, .001f, .001f));
415 materialsRefF.append(bgMatFloor);
416 } else {
417 bgMatFloor = static_cast<QQuick3DPrincipledMaterial *>(materialsRefF.at(0));
418 }
419 bgMatFloor->setBaseColor(theme->backgroundColor());
420
421 if (m_selectedBarPos.isNull())
422 itemLabel()->setVisible(false);
423}
424
425void QQuickGraphsBars::updateParameters() {
426 int cachedMinRow = m_minRow;
427 int cachedMinCol = m_minCol;
428 m_minRow = m_barsController->m_axisZ->min();
429 m_maxRow = m_barsController->m_axisZ->max();
430 m_minCol = m_barsController->m_axisX->min();
431 m_maxCol = m_barsController->m_axisX->max();
432 m_newRows = m_maxRow - m_minRow + 1;
433 m_newCols = m_maxCol - m_minCol + 1;
434
435 QList<QBar3DSeries *> barSeriesList = m_barsController->barSeriesList();
436 if (m_cachedRowCount!= m_newRows || m_cachedColumnCount != m_newCols) {
437 m_barsController->m_changeTracker.selectedBarChanged = true;
438 m_cachedColumnCount = m_newCols;
439 m_cachedRowCount = m_newRows;
440
441 // Calculate max scene size
442 float sceneRatio = qMin(a: float(m_newCols) / float(m_newRows),
443 b: float(m_newRows) / float(m_newCols));
444 m_maxSceneSize = 2.0f * qSqrt(v: sceneRatio * m_newCols * m_newRows);
445
446 if (m_cachedBarThickness.isValid())
447 calculateSceneScalingFactors();
448
449 removeBarModels();
450 removeSelectedModels();
451 }
452
453 if (cachedMinRow != m_minRow || cachedMinCol != m_minCol)
454 removeBarModels();
455
456 m_axisRangeChanged = true;
457 m_barsController->setDataDirty(true);
458}
459
460void QQuickGraphsBars::updateFloorLevel(float level)
461{
462 setFloorLevel(level);
463 calculateHeightAdjustment();
464}
465
466void QQuickGraphsBars::updateGraph()
467{
468 QList<QBar3DSeries *> barSeriesList = m_barsController->barSeriesList();
469 calculateSceneScalingFactors();
470
471 bool isEmpty = m_barsController->m_changedSeriesList.isEmpty();
472
473 for (const auto &series : std::as_const(t&: barSeriesList)) {
474 if (series->d_func()->m_changeTracker.meshChanged) {
475 removeBarModels();
476 removeSelectedModels();
477 series->d_func()->m_changeTracker.meshChanged = false;
478 m_barsController->setDataDirty(true);
479 }
480 }
481
482 if (m_barsController->isDataDirty())
483 generateBars(barSeriesList);
484
485 if (m_barsController->isSeriesVisualsDirty()) {
486 if (isSliceEnabled()) {
487 removeSlicedBarModels();
488 createSliceView();
489 if (!isEmpty) {
490 updateSliceGrid();
491 updateSliceLabels();
492 }
493 if (!isSliceActivatedChanged() && m_selectionDirty) {
494 setSliceActivatedChanged(true);
495 m_selectionDirty = false;
496 }
497 }
498 int visualIndex = 0;
499 for (const auto &barSeries : std::as_const(t&: barSeriesList)) {
500 if (barSeries->isVisible()) {
501 updateBarVisuality(series: barSeries, visualIndex);
502 updateBarPositions(series: barSeries);
503 updateBarVisuals(series: barSeries);
504 ++visualIndex;
505 } else {
506 updateBarVisuality(series: barSeries, visualIndex: -1);
507 }
508 }
509
510 // Needs to be done after data is set, as it needs to know the visual array.
511 if (m_barsController->m_changeTracker.selectedBarChanged) {
512 updateSelectedBar();
513 m_barsController->m_changeTracker.selectedBarChanged = false;
514 }
515 }
516
517 m_barsController->setDataDirty(false);
518 m_barsController->setSeriesVisualsDirty(false);
519}
520
521void QQuickGraphsBars::updateAxisRange(float min, float max)
522{
523 QQuickGraphsItem::updateAxisRange(min, max);
524
525 m_helperAxisY.setMin(min);
526 m_helperAxisY.setMax(max);
527
528 calculateHeightAdjustment();
529}
530
531void QQuickGraphsBars::updateAxisReversed(bool enable)
532{
533 m_barsController->setSeriesVisualsDirty(true);
534 m_helperAxisY.setReversed(enable);
535 calculateHeightAdjustment();
536}
537
538void QQuickGraphsBars::calculateSceneScalingFactors()
539{
540 m_rowWidth = (m_cachedColumnCount * m_cachedBarSpacing.width()) * 0.5f;
541 m_columnDepth = (m_cachedRowCount * m_cachedBarSpacing.height()) * 0.5f;
542 m_maxDimension = qMax(a: m_rowWidth, b: m_columnDepth);
543 m_scaleFactor = qMin(a: (m_cachedColumnCount *(m_maxDimension / m_maxSceneSize)),
544 b: (m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
545
546 // Single bar scaling
547 m_xScale = m_cachedBarThickness.width() / m_scaleFactor;
548 m_zScale = m_cachedBarThickness.height() / m_scaleFactor;
549
550 // Adjust scaling according to margin
551 m_xScale = m_xScale - m_xScale * m_cachedBarSeriesMargin.width();
552 m_zScale = m_zScale - m_zScale * m_cachedBarSeriesMargin.height();
553
554 // Whole graph scale factors
555 m_xScaleFactor = m_rowWidth / m_scaleFactor;
556 m_zScaleFactor = m_columnDepth / m_scaleFactor;
557
558 if (m_requestedMargin < 0.0f) {
559 m_hBackgroundMargin = 0.0f;
560 m_vBackgroundMargin = 0.0f;
561 } else {
562 m_hBackgroundMargin = m_requestedMargin;
563 m_vBackgroundMargin = m_requestedMargin;
564 }
565
566 m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin;
567 m_scaleYWithBackground = 1.0f + m_vBackgroundMargin;
568 m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin;
569
570 auto scale = QVector3D(m_xScaleFactor, 1.0f, m_zScaleFactor);
571 setScaleWithBackground(scale);
572 setBackgroundScaleMargin({m_hBackgroundMargin, m_vBackgroundMargin, m_hBackgroundMargin});
573 setScale(scale);
574
575 m_helperAxisX.setScale(m_scaleXWithBackground * 2);
576 m_helperAxisY.setScale(m_yScale);
577 m_helperAxisZ.setScale(-m_scaleZWithBackground * 2);
578 m_helperAxisX.setTranslate(-m_xScale);
579 m_helperAxisY.setTranslate(0.0f);
580}
581
582void QQuickGraphsBars::calculateHeightAdjustment()
583{
584 m_minHeight = m_helperAxisY.min();
585 m_maxHeight = m_helperAxisY.max();
586 float newAdjustment = 1.0f;
587 m_actualFloorLevel = qBound(min: m_minHeight, val: floorLevel(), max: m_maxHeight);
588 float maxAbs = qFabs(v: m_maxHeight - m_actualFloorLevel);
589
590 // Check if we have negative values
591 if (m_minHeight < m_actualFloorLevel)
592 m_hasNegativeValues = true;
593 else if (m_minHeight >= m_actualFloorLevel)
594 m_hasNegativeValues = false;
595
596 if (m_maxHeight < m_actualFloorLevel) {
597 m_heightNormalizer = float(qFabs(v: m_minHeight) - qFabs(v: m_maxHeight));
598 maxAbs = qFabs(v: m_maxHeight) - qFabs(v: m_minHeight);
599 } else {
600 m_heightNormalizer = float(m_maxHeight - m_minHeight);
601 }
602
603 // Height fractions are used in gradient calculations and are therefore doubled
604 // Note that if max or min is exactly zero, we still consider it outside the range
605 if (m_maxHeight <= m_actualFloorLevel || m_minHeight >= m_actualFloorLevel) {
606 m_noZeroInRange = true;
607 m_gradientFraction = 2.0f;
608 } else {
609 m_noZeroInRange = false;
610 float minAbs = qFabs(v: m_minHeight - m_actualFloorLevel);
611 m_gradientFraction = qMax(a: minAbs, b: maxAbs) / m_heightNormalizer * 2.0f;
612 }
613
614 // Calculate translation adjustment for background floor
615 newAdjustment = (qBound(min: 0.0f, val: (maxAbs / m_heightNormalizer), max: 1.0f) - 0.5f) * 2.0f;
616 if (m_helperAxisY.isReversed())
617 newAdjustment = -newAdjustment;
618
619 if (newAdjustment != m_backgroundAdjustment)
620 m_backgroundAdjustment = newAdjustment;
621}
622
623void QQuickGraphsBars::calculateSeriesStartPosition()
624{
625 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) * 0.5f)
626 * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
627}
628
629QVector3D QQuickGraphsBars::calculateCategoryLabelPosition(QAbstract3DAxis *axis,
630 QVector3D labelPosition, int index)
631{
632 QVector3D ret = labelPosition;
633 if (axis->orientation() == QAbstract3DAxis::AxisOrientationX) {
634 float xPos = (index + 0.5f) * m_cachedBarSpacing.width();
635 ret.setX((xPos - m_rowWidth) / m_scaleFactor);
636 }
637 if (axis->orientation() == QAbstract3DAxis::AxisOrientationZ) {
638 float zPos = (index + 0.5f) * m_cachedBarSpacing.height();
639 ret.setZ((m_columnDepth - zPos) / m_scaleFactor);
640 }
641 ret.setY(-m_backgroundAdjustment);
642 return ret;
643}
644
645float QQuickGraphsBars::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index)
646{
647 float ret = 0.0f;
648 if (axis->orientation() == QAbstract3DAxis::AxisOrientationZ) {
649 float colPos = index * -(m_cachedBarSpacing.height() / m_scaleFactor);
650 ret = colPos + scale().z();
651 }
652 if (axis->orientation() == QAbstract3DAxis::AxisOrientationX) {
653 float rowPos = index * (m_cachedBarSpacing.width() / m_scaleFactor);
654 ret = rowPos - scale().x();
655 }
656 if (axis->orientation() == QAbstract3DAxis::AxisOrientationY)
657 ret = -m_backgroundAdjustment;
658 return ret;
659}
660
661void QQuickGraphsBars::handleAxisXChanged(QAbstract3DAxis *axis)
662{
663 emit columnAxisChanged(axis: static_cast<QCategory3DAxis *>(axis));
664}
665
666void QQuickGraphsBars::handleAxisYChanged(QAbstract3DAxis *axis)
667{
668 emit valueAxisChanged(axis: static_cast<QValue3DAxis *>(axis));
669}
670
671void QQuickGraphsBars::handleAxisZChanged(QAbstract3DAxis *axis)
672{
673 emit rowAxisChanged(axis: static_cast<QCategory3DAxis *>(axis));
674}
675
676void QQuickGraphsBars::handleSeriesMeshChanged(QAbstract3DSeries::Mesh mesh)
677{
678 m_meshType = mesh;
679 removeBarModels();
680}
681
682void QQuickGraphsBars::handleMeshSmoothChanged(bool enable)
683{
684 m_smooth = enable;
685 removeBarModels();
686}
687
688void QQuickGraphsBars::handleRowCountChanged()
689{
690 QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_barsController->axisZ());
691 if (repeaterZ()) {
692 updateParameters();
693 segmentLineRepeaterZ()->model().clear();
694 segmentLineRepeaterZ()->setModel(m_cachedRowCount);
695 repeaterZ()->model().clear();
696 repeaterZ()->setModel(categoryAxisZ->labels().size());
697 }
698}
699
700void QQuickGraphsBars::handleColCountChanged()
701{
702 QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_barsController->axisX());
703 if (repeaterX()) {
704 updateParameters();
705 segmentLineRepeaterX()->model().clear();
706 segmentLineRepeaterX()->setModel(m_cachedColumnCount);
707 repeaterX()->model().clear();
708 repeaterX()->setModel(categoryAxisX->labels().size());
709 }
710}
711
712void QQuickGraphsBars::handleRowColorsChanged()
713{
714 m_barsController->setSeriesVisualsDirty(true);
715}
716
717void QQuickGraphsBars::handleCameraRotationChanged()
718{
719 updateLabels();
720}
721
722void QQuickGraphsBars::connectSeries(QBar3DSeries *series)
723{
724 m_meshType = series->mesh();
725 m_smooth = series->isMeshSmooth();
726
727 QObject::connect(sender: series, signal: &QBar3DSeries::meshChanged, context: this,
728 slot: &QQuickGraphsBars::handleSeriesMeshChanged);
729 QObject::connect(sender: series, signal: &QBar3DSeries::meshSmoothChanged, context: this,
730 slot: &QQuickGraphsBars::handleMeshSmoothChanged);
731 QObject::connect(sender: series->dataProxy(), signal: &QBarDataProxy::colCountChanged, context: this,
732 slot: &QQuickGraphsBars::handleColCountChanged);
733 QObject::connect(sender: series->dataProxy(), signal: &QBarDataProxy::rowCountChanged, context: this,
734 slot: &QQuickGraphsBars::handleRowCountChanged);
735 QObject::connect(sender: series, signal: &QBar3DSeries::rowColorsChanged, context: this,
736 slot: &QQuickGraphsBars::handleRowColorsChanged);
737}
738
739void QQuickGraphsBars::disconnectSeries(QBar3DSeries *series)
740{
741 QObject::disconnect(sender: series, signal: 0, receiver: this, member: 0);
742}
743
744void QQuickGraphsBars::generateBars(QList<QBar3DSeries *> &barSeriesList)
745{
746 m_visibleSeriesCount = 0;
747 for (const auto &barSeries : std::as_const(t&: barSeriesList)) {
748 QQuick3DTexture *texture = createTexture();
749 texture->setParent(this);
750 auto gradient = barSeries->baseGradient();
751 auto textureData = static_cast<QuickGraphsTextureData *>(texture->textureData());
752 textureData->createGradient(gradient);
753
754 bool visible = barSeries->isVisible();
755
756 QList<BarModel *> *barList = m_barModelsMap.value(key: barSeries);
757
758 if (!barList) {
759 barList = new QList<BarModel *>;
760 m_barModelsMap[barSeries] = barList;
761 }
762
763 if (barList->isEmpty()) {
764 if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
765
766 QBarDataProxy *dataProxy = barSeries->dataProxy();
767 int dataRowIndex = m_minRow;
768 int newRowSize = qMin(a: dataProxy->rowCount() - dataRowIndex, b: m_newRows);
769
770 for (int row = 0; row < newRowSize; ++row) {
771 const QBarDataRow *dataRow = dataProxy->rowAt(rowIndex: dataRowIndex);
772 if (dataRow) {
773 int dataColIndex = m_minCol;
774 int newColSize = qMin(a: dataRow->size() - dataColIndex, b: m_newCols);
775 for (int col = 0; col < newColSize; ++col) {
776 QBarDataItem *dataItem = const_cast<QBarDataItem *> (&(dataRow->at(i: dataColIndex)));
777 auto scene = QQuick3DViewport::scene();
778 QQuick3DModel *model = createDataItem(scene, series: barSeries);
779 model->setVisible(visible);
780
781 BarModel *barModel = new BarModel();
782 barModel->model = model;
783 barModel->barItem = dataItem;
784 barModel->coord = QPoint(dataRowIndex, col);
785 barModel->texture = texture;
786
787 if (!barList->contains(t: barModel)) {
788 barList->append(t: barModel);
789 } else {
790 delete barModel->model;
791 delete barModel;
792 }
793 ++dataColIndex;
794 }
795 ++dataRowIndex;
796 }
797 }
798 } else if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
799 auto scene = QQuick3DViewport::scene();
800 BarModel *barInstancing = new BarModel();
801 barInstancing->texture = texture;
802
803 if (barInstancing->instancing == nullptr) {
804 barInstancing->instancing = new BarInstancing;
805 barInstancing->instancing->setParent(barSeries);
806 }
807
808 if (barInstancing->model == nullptr) {
809 barInstancing->model = createDataItem(scene, series: barSeries);
810 barInstancing->model->setInstancing(barInstancing->instancing);
811 barInstancing->model->setVisible(visible);
812 barInstancing->model->setPickable(true);
813 }
814
815 if (!barList->contains(t: barInstancing)) {
816 barList->append(t: barInstancing);
817 } else {
818 delete barInstancing->instancing;
819 delete barInstancing->model;
820 delete barInstancing;
821 }
822 }
823 createSelectedModels(series: barSeries);
824 }
825
826 if (barSeries->isVisible())
827 m_visibleSeriesCount++;
828 }
829}
830
831QQuick3DModel *QQuickGraphsBars::createDataItem(QQuick3DNode *scene, QAbstract3DSeries *series)
832{
833 auto model = new QQuick3DModel();
834 model->setParent(scene);
835 model->setParentItem(scene);
836 model->setObjectName(QStringLiteral("BarModel"));
837 QString fileName = getMeshFileName();
838 if (fileName.isEmpty())
839 fileName = series->userDefinedMesh();
840
841 model->setSource(QUrl(fileName));
842 return model;
843}
844
845QString QQuickGraphsBars::getMeshFileName()
846{
847 QString fileName = {};
848 switch (m_meshType) {
849 case QAbstract3DSeries::MeshSphere:
850 fileName = QStringLiteral("defaultMeshes/sphereMesh");
851 break;
852 case QAbstract3DSeries::MeshBar:
853 case QAbstract3DSeries::MeshCube:
854 fileName = QStringLiteral("defaultMeshes/barMesh");
855 break;
856 case QAbstract3DSeries::MeshPyramid:
857 fileName = QStringLiteral("defaultMeshes/pyramidMesh");
858 break;
859 case QAbstract3DSeries::MeshCone:
860 fileName = QStringLiteral("defaultMeshes/coneMesh");
861 break;
862 case QAbstract3DSeries::MeshCylinder:
863 fileName = QStringLiteral("defaultMeshes/cylinderMesh");
864 break;
865 case QAbstract3DSeries::MeshBevelBar:
866 case QAbstract3DSeries::MeshBevelCube:
867 fileName = QStringLiteral("defaultMeshes/bevelBarMesh");
868 break;
869 case QAbstract3DSeries::MeshUserDefined:
870 break;
871 default:
872 fileName = QStringLiteral("defaultMeshes/sphereMesh");
873 }
874
875 fixMeshFileName(fileName, meshType: m_meshType);
876
877 return fileName;
878}
879
880void QQuickGraphsBars::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh meshType)
881{
882 // Should it be smooth?
883 if (m_smooth && meshType != QAbstract3DSeries::MeshPoint
884 && meshType != QAbstract3DSeries::MeshUserDefined) {
885 fileName += QStringLiteral("Smooth");
886 }
887
888 // Should it be filled?
889 if (!m_barsController->activeTheme()->isBackgroundEnabled()
890 && meshType != QAbstract3DSeries::MeshSphere
891 && meshType != QAbstract3DSeries::MeshPoint
892 && meshType != QAbstract3DSeries::MeshUserDefined) {
893 fileName.append(QStringLiteral("Full"));
894 }
895}
896
897void QQuickGraphsBars::updateBarVisuality(QBar3DSeries *series, int visualIndex)
898{
899 QList<BarModel *> barList = *m_barModelsMap.value(key: series);
900 for (int i = 0; i < barList.count(); i++) {
901 m_barsController->m_changeTracker.selectedBarChanged = true;
902 if (barList.at(i)->model->visible() != series->isVisible() && isSliceEnabled()) {
903 if (m_selectedBarSeries == series && !series->isVisible()) {
904 setSliceEnabled(false);
905 setSliceActivatedChanged(true);
906 m_selectionDirty = true;
907 } else {
908 setSliceActivatedChanged(true);
909 m_selectionDirty = false;
910 }
911 }
912 if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
913 barList.at(i)->visualIndex = visualIndex;
914 barList.at(i)->model->setVisible(series->isVisible());
915 } else if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
916 barList.at(i)->visualIndex = visualIndex;
917 barList.at(i)->model->setVisible(series->isVisible());
918 if (m_selectedBarSeries == series && !series->isVisible()) {
919 for (const auto list : std::as_const(t&: m_selectedModels)) {
920 for (auto selectedModel : *list)
921 selectedModel->setVisible(false);
922 }
923 }
924 }
925 }
926
927 itemLabel()->setVisible(false);
928}
929
930void QQuickGraphsBars::updateBarPositions(QBar3DSeries *series)
931{
932 QBarDataProxy *dataProxy = series->dataProxy();
933
934 m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
935 m_seriesStep = 1.0f / float(m_visibleSeriesCount);
936 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) * 0.5f)
937 * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
938
939 if (m_keepSeriesUniform)
940 m_seriesScaleZ = m_seriesScaleX;
941 else
942 m_seriesScaleZ = 1.0f;
943
944 m_meshRotation = dataProxy->series()->meshRotation();
945 m_zeroPosition = m_helperAxisY.itemPositionAt(value: m_actualFloorLevel);
946
947 QList<BarModel *> barList = *m_barModelsMap.value(key: series);
948
949 int dataRowIndex = m_minRow;
950 int newRowSize = qMin(a: dataProxy->rowCount() - dataRowIndex, b: m_newRows);
951 int row = 0;
952 int dataColIndex = m_minCol;
953 int newColSize = qMin(a: dataProxy->colCount() - dataColIndex, b: m_newCols);
954 int col = 0;
955 for (int i = 0; i < barList.count(); i++) {
956 if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
957 QBarDataItem *item = barList.at(i)->barItem;
958 QQuick3DModel *model = barList.at(i)->model;
959 float heightValue = updateBarHeightParameters(item);
960 float angle = item->rotation();
961
962 if (angle)
963 model->setRotation(QQuaternion::fromAxisAndAngle(axis: upVector, angle));
964 else
965 model->setRotation(QQuaternion());
966
967 if (heightValue < 0.f) {
968 const QVector3D rot = model->eulerRotation();
969 model->setEulerRotation(QVector3D(-180.f, rot.y(), rot.z()));
970 }
971
972 float seriesPos = m_seriesStart + m_seriesStep * (barList.at(i)->visualIndex
973 - (barList.at(i)->visualIndex
974 * m_cachedBarSeriesMargin.width())) + 0.5f;
975
976
977 float colPos = (col + seriesPos) * m_cachedBarSpacing.width();
978 float xPos = (colPos - m_rowWidth) / m_scaleFactor;
979 float rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
980 float zPos = (m_columnDepth - rowPos) / m_scaleFactor;
981
982 barList.at(i)->heightValue = heightValue;
983 model->setPosition(QVector3D(xPos, heightValue - m_backgroundAdjustment, zPos));
984 model->setScale(QVector3D(m_xScale * m_seriesScaleX, qAbs(t: heightValue),
985 m_zScale * m_seriesScaleZ));
986
987 if (heightValue == 0)
988 model->setPickable(false);
989 else
990 model->setPickable(true);
991
992 if (col < newColSize - 1) {
993 ++col;
994 } else {
995 col = 0;
996 if (row < newRowSize - 1)
997 ++row;
998 else
999 row = 0;
1000 }
1001 } else if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1002 auto barItemList = barList.at(i)->instancing->dataArray();
1003 for (auto bih : barItemList)
1004 delete bih;
1005 barList.at(i)->instancing->clearDataArray();
1006
1007 QList<BarItemHolder *> positions;
1008 for (int row = 0; row < newRowSize; ++row) {
1009 const QBarDataRow *dataRow = dataProxy->rowAt(rowIndex: dataRowIndex);
1010 if (dataRow) {
1011 dataColIndex = m_minCol;
1012 for (int col = 0; col < newColSize; col++) {
1013 const QBarDataItem *item = const_cast<QBarDataItem *> (&(dataRow->at(i: dataColIndex)));
1014 float heightValue = updateBarHeightParameters(item);
1015
1016 float angle = item->rotation();
1017 BarItemHolder *bih = new BarItemHolder();
1018 if (angle)
1019 bih->rotation = QQuaternion::fromAxisAndAngle(axis: upVector, angle);
1020 else
1021 bih->rotation = QQuaternion();
1022
1023 if (heightValue < 0.f) {
1024 const QVector3D eulerRot = barList.at(i)->model->eulerRotation();
1025 bih->eulerRotation = QVector3D(-180.f, eulerRot.y(), eulerRot.z());
1026 }
1027
1028 float seriesPos = m_seriesStart + m_seriesStep * (barList.at(i)->visualIndex
1029 - (barList.at(i)->visualIndex
1030 * m_cachedBarSeriesMargin.width())) + 0.5f;
1031
1032
1033 float colPos = (col + seriesPos) * m_cachedBarSpacing.width();
1034 float xPos = (colPos - m_rowWidth) / m_scaleFactor;
1035 float rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1036 float zPos = (m_columnDepth - rowPos) / m_scaleFactor;
1037
1038 bih->position = {xPos, (heightValue - m_backgroundAdjustment), zPos};
1039 bih->coord = QPoint(row, col);
1040
1041 if (heightValue == 0) {
1042 bih->scale = {.0f, .0f, .0f};
1043 } else {
1044 bih->scale = {m_xScale * m_seriesScaleX, qAbs(t: heightValue),
1045 m_zScale * m_seriesScaleZ};
1046 }
1047
1048 bih->heightValue = heightValue;
1049 bih->selectedBar = false;
1050
1051 bool colorStyleIsUniform = (series->colorStyle() == Q3DTheme::ColorStyleUniform);
1052 if (colorStyleIsUniform) {
1053 QList<QColor> rowColors = series->rowColors();
1054 if (rowColors.size() == 0) {
1055 bih->color = series->baseColor();
1056 } else {
1057 int rowColorIndex = bih->coord.x() % rowColors.size();
1058 bih->color = rowColors[rowColorIndex];
1059 }
1060 }
1061
1062 positions.push_back(t: bih);
1063 dataColIndex++;
1064 }
1065 }
1066 dataRowIndex++;
1067 }
1068 barList.at(i)->instancing->setDataArray(positions);
1069 }
1070 }
1071}
1072
1073float QQuickGraphsBars::updateBarHeightParameters(const QBarDataItem *item)
1074{
1075 float value = item->value();
1076 float heightValue = m_helperAxisY.itemPositionAt(value);
1077
1078 if (m_noZeroInRange) {
1079 if (m_hasNegativeValues) {
1080 heightValue = -1.0f + heightValue;
1081 if (heightValue > 0.0f)
1082 heightValue = 0.0f;
1083 } else {
1084 if (heightValue < 0.0f)
1085 heightValue = 0.0f;
1086 }
1087 } else {
1088 heightValue -= m_zeroPosition;
1089 }
1090
1091 if (m_helperAxisY.isReversed())
1092 heightValue = -heightValue;
1093
1094 return heightValue;
1095}
1096
1097void QQuickGraphsBars::updateBarVisuals(QBar3DSeries *series)
1098{
1099 QList<BarModel *> barList = *m_barModelsMap.value(key: series);
1100 bool useGradient = series->d_func()->isUsingGradient();
1101
1102 if (useGradient) {
1103 if (!m_hasHighlightTexture) {
1104 m_highlightTexture = createTexture();
1105 m_highlightTexture->setParent(this);
1106 m_multiHighlightTexture = createTexture();
1107 m_multiHighlightTexture->setParent(this);
1108 m_hasHighlightTexture = true;
1109 }
1110 auto highlightGradient = series->singleHighlightGradient();
1111 auto highlightTextureData
1112 = static_cast<QuickGraphsTextureData *>(m_highlightTexture->textureData());
1113 highlightTextureData->createGradient(gradient: highlightGradient);
1114 auto multiHighlightGradient = series->multiHighlightGradient();
1115 auto multiHighlightTextureData
1116 = static_cast<QuickGraphsTextureData *>(m_multiHighlightTexture->textureData());
1117 multiHighlightTextureData->createGradient(gradient: multiHighlightGradient);
1118 } else {
1119 if (m_hasHighlightTexture) {
1120 m_highlightTexture->deleteLater();
1121 m_multiHighlightTexture->deleteLater();
1122 m_hasHighlightTexture = false;
1123 }
1124 }
1125
1126 bool rangeGradient = (useGradient && series->d_func()->m_colorStyle
1127 == Q3DTheme::ColorStyleRangeGradient);
1128 QColor baseColor = series->baseColor();
1129 QColor barColor;
1130
1131 if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
1132 if (!rangeGradient) {
1133 for (int i = 0; i < barList.count(); i++) {
1134 QQuick3DModel *model = barList.at(i)->model;
1135 updateItemMaterial(item: model, useGradient, rangeGradient,
1136 QStringLiteral(":/materials/ObjectGradientMaterial"));
1137 if (useGradient) {
1138 updateCustomMaterial(item: model, isHighlight: false, isMultiHighlight: false, texture: barList.at(i)->texture);
1139 } else {
1140 QList<QColor> rowColors = series->rowColors();
1141 if (rowColors.size() == 0) {
1142 barColor = baseColor;
1143 } else {
1144 int rowColorIndex = barList.at(i)->coord.x() % rowColors.size();
1145 barColor = rowColors[rowColorIndex];
1146 }
1147 updatePrincipledMaterial(model, color: barColor, useGradient, isHighlight: false,
1148 texture: barList.at(i)->texture);
1149 }
1150 }
1151 } else {
1152 for (int i = 0; i < barList.count(); i++) {
1153 QQuick3DModel *model = barList.at(i)->model;
1154 updateItemMaterial(item: model, useGradient, rangeGradient,
1155 QStringLiteral(":/materials/RangeGradientMaterial"));
1156 if (useGradient) {
1157 updateCustomMaterial(item: model, isHighlight: false, isMultiHighlight: false, texture: barList.at(i)->texture);
1158 } else {
1159 updatePrincipledMaterial(model, color: baseColor, useGradient, isHighlight: false,
1160 texture: barList.at(i)->texture);
1161 }
1162 }
1163 }
1164 } else if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1165 for (int i = 0; i < barList.count(); i++) {
1166 barList.at(i)->instancing->setRangeGradient(rangeGradient);
1167 if (!rangeGradient) {
1168 updateItemMaterial(item: barList.at(i)->model, useGradient, rangeGradient,
1169 QStringLiteral(":/materials/ObjectGradientMaterialInstancing"));
1170 if (useGradient) {
1171 updateCustomMaterial(item: barList.at(i)->model, isHighlight: false, isMultiHighlight: false, texture: barList.at(i)->texture);
1172 } else {
1173 updatePrincipledMaterial(model: barList.at(i)->model, color: QColor(Qt::white),
1174 useGradient, isHighlight: false, texture: barList.at(i)->texture);
1175 }
1176 } else {
1177 updateItemMaterial(item: barList.at(i)->model, useGradient, rangeGradient,
1178 QStringLiteral(":/materials/RangeGradientMaterialInstancing"));
1179 if (useGradient) {
1180 updateCustomMaterial(item: barList.at(i)->model, isHighlight: false, isMultiHighlight: false, texture: barList.at(i)->texture);
1181 } else {
1182 updatePrincipledMaterial(model: barList.at(i)->model, color: baseColor,
1183 useGradient, isHighlight: false, texture: barList.at(i)->texture);
1184 }
1185 }
1186 }
1187 }
1188}
1189
1190void QQuickGraphsBars::updateItemMaterial(QQuick3DModel *item, bool useGradient, bool rangeGradient, const QString &materialName)
1191{
1192 QQmlListReference materialsRef(item, "materials");
1193 if (!rangeGradient) {
1194 if (materialsRef.size()) {
1195 QObject *material = materialsRef.at(0);
1196 if (useGradient && !material->objectName().contains(QStringLiteral("objectgradient"))) {
1197 // The item has an existing material which is principled or range gradient.
1198 // The item needs an object gradient material.
1199 QQuick3DCustomMaterial *objectGradientMaterial = createQmlCustomMaterial(
1200 fileName: materialName);
1201 objectGradientMaterial->setParent(item);
1202 QObject *oldMaterial = materialsRef.at(0);
1203 materialsRef.replace(0, objectGradientMaterial);
1204 objectGradientMaterial->setObjectName("objectgradient");
1205 delete oldMaterial;
1206 } else if (!useGradient && !qobject_cast<QQuick3DPrincipledMaterial *>(object: material)) {
1207 // The item has an existing material which is object gradient or range gradient.
1208 // The item needs a principled material for uniform color.
1209 auto principledMaterial = new QQuick3DPrincipledMaterial();
1210 principledMaterial->setParent(item);
1211 QObject *oldCustomMaterial = materialsRef.at(0);
1212 materialsRef.replace(0, principledMaterial);
1213 delete oldCustomMaterial;
1214 }
1215 } else {
1216 if (useGradient) {
1217 // The item needs object gradient material.
1218 QQuick3DCustomMaterial *objectGradientMaterial = createQmlCustomMaterial(
1219 fileName: materialName);
1220 objectGradientMaterial->setParent(item);
1221 materialsRef.append(objectGradientMaterial);
1222 objectGradientMaterial->setObjectName("objectgradient");
1223 } else {
1224 // The item needs a principled material.
1225 auto principledMaterial = new QQuick3DPrincipledMaterial();
1226 principledMaterial->setParent(item);
1227 materialsRef.append(principledMaterial);
1228 }
1229 }
1230 } else {
1231 if (materialsRef.size()) {
1232 QObject *material = materialsRef.at(0);
1233 if (!qobject_cast<QQuick3DCustomMaterial *>(object: material)
1234 || material->objectName().contains(QStringLiteral("objectgradient"))) {
1235 // The item has an existing material which is principled or object gradient.
1236 // The item needs a range gradient material.
1237 if (useGradient) {
1238 QQuick3DCustomMaterial *customMaterial = createQmlCustomMaterial(
1239 fileName: materialName);
1240 customMaterial->setParent(item);
1241 QObject *oldPrincipledMaterial = materialsRef.at(0);
1242 materialsRef.replace(0, customMaterial);
1243 delete oldPrincipledMaterial;
1244 } else {
1245 // The item needs a principled material for uniform color.
1246 auto principledMaterial = new QQuick3DPrincipledMaterial();
1247 principledMaterial->setParent(item);
1248 QObject *oldCustomMaterial = materialsRef.at(0);
1249 materialsRef.replace(0, principledMaterial);
1250 delete oldCustomMaterial;
1251 }
1252 }
1253 } else {
1254 if (useGradient) {
1255 // The item needs a range gradient material.
1256 QQuick3DCustomMaterial *customMaterial = createQmlCustomMaterial(
1257 fileName: materialName);
1258 customMaterial->setParent(item);
1259 materialsRef.append(customMaterial);
1260 } else {
1261 // The item needs a principled material for uniform color.
1262 auto principledMaterial = new QQuick3DPrincipledMaterial();
1263 principledMaterial->setParent(item);
1264 materialsRef.append(principledMaterial);
1265 }
1266 }
1267 }
1268}
1269
1270void QQuickGraphsBars::updateCustomMaterial(QQuick3DModel *item, bool isHighlight,
1271 bool isMultiHighlight, QQuick3DTexture *texture)
1272{
1273 QQmlListReference materialsRef(item, "materials");
1274 auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(object: materialsRef.at(0));
1275 if (!customMaterial)
1276 return;
1277 QVariant textureInputAsVariant = customMaterial->property(name: "custex");
1278 QQuick3DShaderUtilsTextureInput *textureInput
1279 = textureInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
1280
1281 if (!isHighlight && !isMultiHighlight)
1282 textureInput->setTexture(texture);
1283 else
1284 textureInput->setTexture(isHighlight ? m_highlightTexture : m_multiHighlightTexture);
1285}
1286
1287void QQuickGraphsBars::updatePrincipledMaterial(QQuick3DModel *model, const QColor &color,
1288 bool useGradient, bool isHighlight,
1289 QQuick3DTexture *texture)
1290{
1291 QQmlListReference materialsRef(model, "materials");
1292 auto principledMaterial = qobject_cast<QQuick3DPrincipledMaterial *>(object: materialsRef.at(0));
1293 if (!principledMaterial)
1294 return;
1295 principledMaterial->setParent(this);
1296
1297 if (useGradient) {
1298 principledMaterial->setBaseColor(QColor(Qt::white));
1299 if (!isHighlight)
1300 principledMaterial->setBaseColorMap(texture);
1301 else
1302 principledMaterial->setBaseColorMap(m_highlightTexture);
1303 } else {
1304 principledMaterial->setBaseColor(color);
1305 }
1306}
1307
1308void QQuickGraphsBars::removeBarModels()
1309{
1310 deleteBarItemHolders();
1311 for (const auto list : std::as_const(t&: m_barModelsMap)) {
1312 for (auto barModel : *list) {
1313 deleteBarModels(barModel);
1314 }
1315 delete list;
1316 }
1317
1318 m_barModelsMap.clear();
1319}
1320
1321void QQuickGraphsBars::deleteBarModels(BarModel *barModel)
1322{
1323 barModel->model->setPickable(false);
1324 barModel->model->setVisible(false);
1325 QQmlListReference materialsRef(barModel->model, "materials");
1326 if (materialsRef.size()) {
1327 auto material = materialsRef.at(0);
1328 delete material;
1329 }
1330 delete barModel->model;
1331 delete barModel;
1332}
1333
1334void QQuickGraphsBars::deleteBarItemHolders()
1335{
1336 for (const auto list : std::as_const(t&: m_barModelsMap)) {
1337 for (auto barModel : *list) {
1338 QList<BarItemHolder *> barItemList = barModel->instancing->dataArray();
1339 for (auto bih : barItemList)
1340 delete bih;
1341 }
1342 }
1343}
1344
1345QQuick3DTexture *QQuickGraphsBars::createTexture()
1346{
1347 QQuick3DTexture *texture = new QQuick3DTexture();
1348 texture->setParent(this);
1349 texture->setRotationUV(-90.0f);
1350 texture->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
1351 texture->setVerticalTiling(QQuick3DTexture::ClampToEdge);
1352 QuickGraphsTextureData *textureData = new QuickGraphsTextureData();
1353 textureData->setParent(texture);
1354 textureData->setParentItem(texture);
1355 texture->setTextureData(textureData);
1356
1357 return texture;
1358}
1359
1360bool QQuickGraphsBars::handleMousePressedEvent(QMouseEvent *event)
1361{
1362 m_selectionDirty = true;
1363
1364 if (!QQuickGraphsItem::handleMousePressedEvent(event))
1365 return true;
1366
1367 createSliceView();
1368
1369 if (Qt::LeftButton == event->button())
1370 doPicking(position: event->pos());
1371
1372 return true;
1373}
1374
1375bool QQuickGraphsBars::handleTouchEvent(QTouchEvent *event)
1376{
1377 m_selectionDirty = true;
1378
1379 if (!QQuickGraphsItem::handleTouchEvent(event))
1380 return true;
1381
1382 createSliceView();
1383
1384 if (scene()->selectionQueryPosition() != scene()->invalidSelectionPoint()
1385 && !event->isUpdateEvent()) {
1386 doPicking(position: event->point(i: 0).position());
1387 scene()->setSelectionQueryPosition(scene()->invalidSelectionPoint());
1388 }
1389 return true;
1390}
1391
1392bool QQuickGraphsBars::doPicking(const QPointF &position)
1393{
1394 if (!QQuickGraphsItem::doPicking(point: position))
1395 return false;
1396
1397 QList<QQuick3DPickResult> pickResults = pickAll(x: position.x(), y: position.y());
1398 QQuick3DModel *selectedModel = nullptr;
1399 int instanceInd = 0;
1400 if (!m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionNone)) {
1401 for (const auto &picked : std::as_const(t&: pickResults)) {
1402 if (picked.objectHit()->visible()) {
1403 if (picked.objectHit() == backgroundBB() || picked.objectHit() == background()) {
1404 resetClickedStatus();
1405 continue;
1406 } else if (picked.objectHit()->objectName().contains(QStringLiteral("BarModel"))) {
1407 if (optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
1408 selectedModel = picked.objectHit();
1409 break;
1410 } else if (optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1411 selectedModel = picked.objectHit();
1412 // Prevents to select bars with a height of 0 which affect picking.
1413 if (selectedModel->instancing()->instancePosition(index: picked.instanceIndex()).y()
1414 != 0) {
1415 instanceInd = picked.instanceIndex();
1416 break;
1417 }
1418 }
1419 }
1420 }
1421 }
1422
1423 if (selectedModel) {
1424 QBar3DSeries *series = 0;
1425 QPoint coord = m_barsController->invalidSelectionPosition();
1426 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
1427 QList<BarModel *> barList = *it.value();
1428 if (!it.key()->isVisible())
1429 continue;
1430 for (int i = 0; i < barList.size(); i++) {
1431 if (optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
1432 QQuick3DModel *model = barList.at(i)->model;
1433 if (model == selectedModel) {
1434 series = it.key();
1435 coord = barList.at(i)->coord;
1436 }
1437 } else if (optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1438 QList<BarItemHolder *> barItemList = barList.at(i)->instancing->dataArray();
1439 auto itemPos = barItemList.at(i: instanceInd)->position;
1440 auto selected = selectedModel->instancing()->instancePosition(index: instanceInd);
1441 if (itemPos == selected) {
1442 series = it.key();
1443 coord = barItemList.at(i: instanceInd)->coord;
1444 m_selectedBarPos = barItemList.at(i: instanceInd)->position;
1445 }
1446 }
1447 }
1448 }
1449 setSelectedBar(series, coord);
1450 } else {
1451 resetClickedStatus();
1452 }
1453 }
1454 return true;
1455}
1456
1457void QQuickGraphsBars::setSelectedBar(QBar3DSeries *series, const QPoint &coord)
1458{
1459 if (!m_barModelsMap.contains(key: series))
1460 series = 0;
1461
1462 if (coord != m_selectedBarCoord || series != m_selectedBarSeries) {
1463 m_selectedBarSeries = series;
1464 m_selectedBarCoord = coord;
1465 if (isSliceEnabled())
1466 setSliceActivatedChanged(true);
1467
1468 // Clear selection from other series and finally set new selection to the specified series
1469 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
1470 if (it.key() != m_selectedBarSeries)
1471 it.key()->d_func()->setSelectedBar(invalidSelectionPosition());
1472 }
1473 if (m_selectedBarSeries) {
1474 m_selectedBarSeries->d_func()->setSelectedBar(m_selectedBarCoord);
1475 m_barsController->setSelectedBar(position: m_selectedBarCoord, series: m_selectedBarSeries, enterSlice: false);
1476 }
1477 m_barsController->setSeriesVisualsDirty(true);
1478 }
1479}
1480
1481void QQuickGraphsBars::updateSelectedBar()
1482{
1483 bool visible = false;
1484 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
1485 if (m_selectedBarSeries && it.key()->isVisible()) {
1486 bool useGradient = m_selectedBarSeries->d_func()->isUsingGradient();
1487 QString label = m_selectedBarSeries->itemLabel();
1488 if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
1489 for (auto barList : *it.value()) {
1490 Bars3DController::SelectionType selectionType =
1491 isSelected(row: barList->coord.x(), bar: barList->coord.y(), series: it.key());
1492 switch (selectionType) {
1493 case Bars3DController::SelectionItem: {
1494 if (useGradient) {
1495 updateCustomMaterial(item: barList->model, isHighlight: true, isMultiHighlight: false,
1496 texture: barList->texture);
1497 } else {
1498 updatePrincipledMaterial(model: barList->model,
1499 color: it.key()->singleHighlightColor(), useGradient,
1500 isHighlight: true, texture: barList->texture);
1501 }
1502
1503 m_selectedBarPos = barList->model->position();
1504 visible = m_selectedBarSeries->isVisible() && !m_selectedBarPos.isNull();
1505 QString label = (m_selectedBarSeries->d_func()->itemLabel());
1506
1507 if (barList->heightValue >= 0.0f) {
1508 m_selectedBarPos.setY(m_selectedBarPos.y() + barList->heightValue
1509 + 0.2f);
1510 } else {
1511 m_selectedBarPos.setY(m_selectedBarPos.y() + barList->heightValue
1512 - 0.2f);
1513 }
1514
1515 updateItemLabel(position: m_selectedBarPos);
1516 itemLabel()->setProperty(name: "labelText", value: label);
1517
1518 if (isSliceEnabled()) {
1519 QFontMetrics fm(m_barsController->activeTheme()->font());
1520 float textPadding = m_barsController->activeTheme()->font().pointSizeF() * .5f;
1521 float labelHeight = fm.height() + textPadding;
1522 float labelWidth = fm.horizontalAdvance(label) + textPadding;
1523 QVector3D scale = sliceItemLabel()->scale();
1524 scale.setX(scale.y() * labelWidth / labelHeight);
1525 sliceItemLabel()->setProperty(name: "labelWidth", value: labelWidth);
1526 sliceItemLabel()->setProperty(name: "labelHeight", value: labelHeight);
1527 QVector3D slicePos = barList->model->position();
1528 if (m_barsController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn))
1529 slicePos.setX(slicePos.z() - .1f);
1530 else if (m_barsController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionRow))
1531 slicePos.setX(slicePos.x() - .1f);
1532 slicePos.setZ(.0f);
1533 slicePos.setY(slicePos.y() + 1.5f);
1534 sliceItemLabel()->setPosition(slicePos);
1535 sliceItemLabel()->setProperty(name: "labelText", value: label);
1536 sliceItemLabel()->setEulerRotation(QVector3D(0.0f, 0.0f, 90.0f));
1537 sliceItemLabel()->setVisible(true);
1538 }
1539 break;
1540 }
1541 case Bars3DController::SelectionRow:
1542 case Bars3DController::SelectionColumn: {
1543 if (useGradient) {
1544 updateCustomMaterial(item: barList->model, isHighlight: false, isMultiHighlight: true,
1545 texture: barList->texture);
1546 } else {
1547 updatePrincipledMaterial(model: barList->model,
1548 color: it.key()->multiHighlightColor(), useGradient,
1549 isHighlight: true, texture: barList->texture);
1550 }
1551 break;
1552 }
1553 default:
1554 break;
1555 }
1556 }
1557 } else if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1558 bool rangeGradient = (useGradient && it.key()->d_func()->m_colorStyle
1559 == Q3DTheme::ColorStyleRangeGradient);
1560 int index = 0;
1561 QList<BarModel *> barList = *m_barModelsMap.value(key: it.key());
1562 QList<BarItemHolder *> barItemList = barList.at(i: 0)->instancing->dataArray();
1563 for (auto bih : barItemList) {
1564 Bars3DController::SelectionType selectionType =
1565 isSelected(row: bih->coord.x(), bar: bih->coord.y(), series: it.key());
1566 switch (selectionType) {
1567 case Bars3DController::SelectionItem: {
1568 if (index <= m_selectedModels.value(key: it.key())->size()) {
1569 visible = m_selectedBarSeries->isVisible() && !m_selectedBarPos.isNull();
1570 bih->selectedBar = true;
1571 QQuick3DModel *selectedModel = m_selectedModels.value(key: it.key())->at(i: index);
1572 selectedModel->setVisible(true);
1573 selectedModel->setPosition(bih->position);
1574 selectedModel->setScale(bih->scale);
1575
1576 if (!rangeGradient) {
1577 updateItemMaterial(item: selectedModel, useGradient, rangeGradient,
1578 QStringLiteral(":/materials/ObjectGradientMaterial"));
1579 if (useGradient) {
1580 updateCustomMaterial(item: selectedModel, isHighlight: true, isMultiHighlight: false,
1581 texture: barList.at(i: 0)->texture);
1582 } else {
1583 updatePrincipledMaterial(model: selectedModel,
1584 color: it.key()->singleHighlightColor(), useGradient,
1585 isHighlight: true, texture: barList.at(i: 0)->texture);
1586 }
1587 } else {
1588 updateItemMaterial(item: selectedModel, useGradient, rangeGradient,
1589 QStringLiteral(":/materials/RangeGradientMaterial"));
1590 if (useGradient) {
1591 updateCustomMaterial(item: selectedModel, isHighlight: true, isMultiHighlight: false,
1592 texture: barList.at(i: 0)->texture);
1593 } else {
1594 updatePrincipledMaterial(model: selectedModel,
1595 color: it.key()->singleHighlightColor(), useGradient,
1596 isHighlight: true, texture: barList.at(i: 0)->texture);
1597 }
1598 }
1599
1600 m_selectedBarPos = bih->position;
1601 QString label = (m_selectedBarSeries->d_func()->itemLabel());
1602
1603 if (bih->heightValue >= 0.0f)
1604 m_selectedBarPos.setY(m_selectedBarPos.y() + bih->heightValue + 0.2f);
1605 else
1606 m_selectedBarPos.setY(m_selectedBarPos.y() + bih->heightValue - 0.2f);
1607
1608 updateItemLabel(position: m_selectedBarPos);
1609 itemLabel()->setProperty(name: "labelText", value: label);
1610
1611 if (isSliceEnabled()) {
1612 QFontMetrics fm(m_barsController->activeTheme()->font());
1613 float textPadding = m_barsController->activeTheme()->font().pointSizeF() * .5f;
1614 float labelHeight = fm.height() + textPadding;
1615 float labelWidth = fm.horizontalAdvance(label) + textPadding;
1616 QVector3D scale = sliceItemLabel()->scale();
1617 scale.setX(scale.y() * labelWidth / labelHeight);
1618 sliceItemLabel()->setProperty(name: "labelWidth", value: labelWidth);
1619 sliceItemLabel()->setProperty(name: "labelHeight", value: labelHeight);
1620 QVector3D slicePos = bih->position;
1621 if (m_barsController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionColumn))
1622 slicePos.setX(slicePos.z() - .1f);
1623 else if (m_barsController->selectionMode().testFlag(flag: QAbstract3DGraph::SelectionRow))
1624 slicePos.setX(slicePos.x() - .1f);
1625 slicePos.setZ(.0f);
1626 slicePos.setY(slicePos.y() + 1.5f);
1627 sliceItemLabel()->setPosition(slicePos);
1628 sliceItemLabel()->setProperty(name: "labelText", value: label);
1629 sliceItemLabel()->setEulerRotation(QVector3D(0.0f, 0.0f, 90.0f));
1630 sliceItemLabel()->setVisible(true);
1631 }
1632 ++index;
1633 }
1634 break;
1635 }
1636 case Bars3DController::SelectionRow:
1637 case Bars3DController::SelectionColumn: {
1638 if (index <= m_selectedModels.value(key: it.key())->size()) {
1639 bih->selectedBar = true;
1640 QQuick3DModel *selectedModel = m_selectedModels.value(key: it.key())->at(i: index);
1641 selectedModel->setVisible(true);
1642 selectedModel->setPosition(bih->position);
1643 selectedModel->setScale(bih->scale);
1644
1645 if (!rangeGradient) {
1646 updateItemMaterial(item: selectedModel, useGradient, rangeGradient,
1647 QStringLiteral(":/materials/ObjectGradientMaterial"));
1648 if (useGradient) {
1649 updateCustomMaterial(item: selectedModel, isHighlight: false, isMultiHighlight: true,
1650 texture: barList.at(i: 0)->texture);
1651 } else {
1652 updatePrincipledMaterial(model: selectedModel,
1653 color: it.key()->multiHighlightColor(), useGradient,
1654 isHighlight: true, texture: barList.at(i: 0)->texture);
1655 }
1656 } else {
1657 updateItemMaterial(item: selectedModel, useGradient, rangeGradient,
1658 QStringLiteral(":/materials/RangeGradientMaterial"));
1659 if (useGradient) {
1660 updateCustomMaterial(item: selectedModel, isHighlight: false, isMultiHighlight: true,
1661 texture: barList.at(i: 0)->texture);
1662 } else {
1663 updatePrincipledMaterial(model: selectedModel,
1664 color: it.key()->multiHighlightColor(), useGradient,
1665 isHighlight: true, texture: barList.at(i: 0)->texture);
1666 }
1667 }
1668 ++index;
1669 }
1670 break;
1671 }
1672 default:
1673 break;
1674 }
1675
1676 }
1677 }
1678 }
1679 }
1680 itemLabel()->setVisible(visible);
1681}
1682
1683void QQuickGraphsBars::createSelectedModels(QBar3DSeries *series)
1684{
1685 QList<QQuick3DModel *> *selectedModelsList = m_selectedModels.value(key: series);
1686 if (!selectedModelsList) {
1687 selectedModelsList = new QList<QQuick3DModel *>;
1688 m_selectedModels[series] = selectedModelsList;
1689 }
1690 int selectedModelsListSize = 1;
1691 int rowCount = series->dataProxy()->rowCount();
1692 int colCount = series->dataProxy()->colCount();
1693 if (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))
1694 selectedModelsListSize = colCount;
1695 else if (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn))
1696 selectedModelsListSize = rowCount;
1697
1698 if (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow) &&
1699 m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
1700 selectedModelsListSize = rowCount + colCount - 1;
1701 }
1702
1703 for (int ind = 0; ind < selectedModelsListSize; ++ind) {
1704 QQuick3DModel *model = createDataItem(scene: QQuick3DViewport::scene(), series);
1705 model->setVisible(false);
1706
1707 if (!selectedModelsList->contains(t: model))
1708 selectedModelsList->append(t: model);
1709 }
1710}
1711
1712Abstract3DController::SelectionType QQuickGraphsBars::isSelected(int row, int bar,
1713 QBar3DSeries *series)
1714{
1715 Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone;
1716 if ((m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries)
1717 && m_selectedBarSeries) || series == m_selectedBarSeries) {
1718 if (row == m_selectedBarCoord.x() && bar == m_selectedBarCoord.y()
1719 && (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem))) {
1720 isSelectedType = Bars3DController::SelectionItem;
1721 } else if (row == m_selectedBarCoord.x()
1722 && (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))) {
1723 isSelectedType = Bars3DController::SelectionRow;
1724 } else if (bar == m_selectedBarCoord.y()
1725 && (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn))) {
1726 isSelectedType = Bars3DController::SelectionColumn;
1727 }
1728 }
1729
1730 return isSelectedType;
1731}
1732
1733void QQuickGraphsBars::resetClickedStatus()
1734{
1735 m_barsController->setSeriesVisualsDirty(true);
1736 m_selectedBarPos = QVector3D(0.0f, 0.0f, 0.0f);
1737 m_selectedBarCoord = Bars3DController::invalidSelectionPosition();
1738 m_selectedBarSeries = 0;
1739 m_barsController->clearSelection();
1740
1741 if (optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1742 for (const auto list : std::as_const(t&: m_selectedModels)) {
1743 for (auto selectedModel : *list)
1744 selectedModel->setVisible(false);
1745 }
1746 for (const auto list : std::as_const(t&: m_barModelsMap)) {
1747 QList<BarItemHolder *> barItemList = list->at(i: 0)->instancing->dataArray();
1748 for (auto bih : barItemList)
1749 bih->selectedBar = false;
1750 }
1751 }
1752 m_barsController->setSeriesVisualsDirty(true);
1753}
1754
1755void QQuickGraphsBars::createSliceView()
1756{
1757 QQuickGraphsItem::createSliceView();
1758
1759 QQuick3DViewport *sliceParent = sliceView();
1760
1761 QList<QBar3DSeries *> barSeriesList = m_barsController->barSeriesList();
1762 for (const auto &barSeries : std::as_const(t&: barSeriesList)) {
1763 bool useGradient = barSeries->d_func()->isUsingGradient();
1764 bool rangeGradient = (useGradient && barSeries->d_func()->m_colorStyle
1765 == Q3DTheme::ColorStyleRangeGradient);
1766 QList<QQuick3DModel *> *slicedBarList = m_slicedBarModels.value(key: barSeries);
1767 if (!slicedBarList) {
1768 slicedBarList = new QList<QQuick3DModel *>;
1769 m_slicedBarModels[barSeries] = slicedBarList;
1770 }
1771 if (slicedBarList->isEmpty()) {
1772 int slicedBarListSize = 0;
1773 if (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))
1774 slicedBarListSize = barSeries->dataProxy()->colCount();
1775 else if (m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn))
1776 slicedBarListSize = barSeries->dataProxy()->rowCount();
1777
1778 for (int ind = 0; ind < slicedBarListSize; ++ind) {
1779 QQuick3DModel *model = createDataItem(scene: sliceParent->scene(), series: barSeries);
1780 model->setVisible(false);
1781 if (!rangeGradient) {
1782 updateItemMaterial(item: model, useGradient, rangeGradient,
1783 QStringLiteral(":/materials/ObjectGradientMaterial"));
1784 } else {
1785 updateItemMaterial(item: model, useGradient, rangeGradient,
1786 QStringLiteral(":/materials/RangeGradientMaterial"));
1787 }
1788
1789 if (!slicedBarList->contains(t: model))
1790 slicedBarList->append(t: model);
1791 }
1792 }
1793 }
1794}
1795
1796void QQuickGraphsBars::updateSliceGraph()
1797{
1798 if (m_selectionDirty)
1799 QQuickGraphsItem::updateSliceGraph();
1800
1801 if (!sliceView()->isVisible()) {
1802 removeSlicedBarModels();
1803 m_barsController->m_changeTracker.selectedBarChanged = false;
1804 return;
1805 }
1806
1807 int index = 0;
1808 bool rowMode = m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow);
1809 for (auto it = m_slicedBarModels.begin(); it != m_slicedBarModels.end(); it++) {
1810 QList<BarModel *> barList = *m_barModelsMap.value(key: it.key());
1811 bool useGradient = it.key()->d_func()->isUsingGradient();
1812 if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationLegacy) {
1813 for (int ind = 0; ind < it.value()->count(); ++ind) {
1814 if (rowMode)
1815 index = (m_selectedBarCoord.x() * it.key()->dataProxy()->colCount()) + ind;
1816 else
1817 index = m_selectedBarCoord.y() + (ind * it.key()->dataProxy()->colCount());
1818 bool visible = ((m_selectedBarSeries == it.key()
1819 || m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries))
1820 && it.key()->isVisible());
1821
1822 if (index < barList.size() && m_selectedBarCoord != invalidSelectionPosition()) {
1823 QQuick3DModel *sliceBarModel = it.value()->at(i: ind);
1824 BarModel *barModel = barList.at(i: index);
1825
1826 sliceBarModel->setVisible(visible);
1827 if (rowMode) {
1828 sliceBarModel->setPosition(QVector3D(barModel->model->x(),
1829 barModel->model->y(), 0.0f));
1830 } else {
1831 sliceBarModel->setX(barModel->model->z()
1832 + (barModel->visualIndex * .2f));
1833 sliceBarModel->setY(barModel->model->y());
1834 sliceBarModel->setZ(0.0f);
1835 }
1836 sliceBarModel->setScale(barModel->model->scale());
1837 bool highlightBar = (ind == (rowMode ? m_selectedBarCoord.y()
1838 : m_selectedBarCoord.x()));
1839 if (useGradient) {
1840 updateCustomMaterial(item: sliceBarModel, isHighlight: highlightBar, isMultiHighlight: false, texture: barList.at(i: index)->texture);
1841 } else {
1842 updatePrincipledMaterial(model: sliceBarModel,
1843 color: highlightBar ? m_selectedBarSeries->singleHighlightColor()
1844 : m_selectedBarSeries->baseColor(),
1845 useGradient, isHighlight: highlightBar, texture: barList.at(i: index)->texture);
1846 }
1847 } else {
1848 setSliceEnabled(false);
1849 QQuickGraphsItem::updateSliceGraph();
1850 return;
1851 }
1852 }
1853 } else if (m_barsController->optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1854 QList<BarItemHolder *> barItemList = barList.at(i: 0)->instancing->dataArray();
1855 if (!barItemList.isEmpty()) {
1856 for (int ind = 0; ind < it.value()->size(); ++ind) {
1857 if (rowMode)
1858 index = (m_selectedBarCoord.x() * it.key()->dataProxy()->colCount()) + ind;
1859 else
1860 index = m_selectedBarCoord.y() + (ind * it.key()->dataProxy()->colCount());
1861 bool visible = ((m_selectedBarSeries == it.key()
1862 || m_selectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries))
1863 && it.key()->isVisible());
1864
1865 if (index < barItemList.size() && m_selectedBarCoord != invalidSelectionPosition()) {
1866 QQuick3DModel *sliceBarModel = it.value()->at(i: ind);
1867 BarItemHolder *bih = barItemList.at(i: index);
1868
1869 sliceBarModel->setVisible(visible);
1870
1871 if (rowMode) {
1872 sliceBarModel->setPosition(QVector3D(bih->position.x(),
1873 bih->position.y(), 0.0f));
1874 } else {
1875 sliceBarModel->setX(bih->position.z()
1876 + (barList.at(i: 0)->visualIndex * .2f));
1877 sliceBarModel->setY(bih->position.y());
1878 sliceBarModel->setZ(0.0f);
1879 }
1880 sliceBarModel->setScale(bih->scale);
1881 bool highlightBar = (ind == (rowMode ? m_selectedBarCoord.y()
1882 : m_selectedBarCoord.x()));
1883 if (useGradient) {
1884 updateCustomMaterial(item: sliceBarModel, isHighlight: highlightBar, isMultiHighlight: false, texture: barList.at(i: 0)->texture);
1885 } else {
1886 updatePrincipledMaterial(model: sliceBarModel,
1887 color: highlightBar ? m_selectedBarSeries->singleHighlightColor()
1888 : m_selectedBarSeries->baseColor(),
1889 useGradient, isHighlight: highlightBar, texture: barList.at(i: 0)->texture);
1890 }
1891 } else {
1892 setSliceEnabled(false);
1893 QQuickGraphsItem::updateSliceGraph();
1894 return;
1895 }
1896 }
1897 }
1898 }
1899 }
1900}
1901
1902void QQuickGraphsBars::handleLabelCountChanged(QQuick3DRepeater *repeater)
1903{
1904 QQuickGraphsItem::handleLabelCountChanged(repeater);
1905
1906 if (repeater == repeaterX())
1907 handleColCountChanged();
1908 if (repeater == repeaterZ())
1909 handleRowCountChanged();
1910}
1911
1912void QQuickGraphsBars::removeSlicedBarModels()
1913{
1914 for (const auto list : std::as_const(t&: m_slicedBarModels)) {
1915 for (auto model : *list) {
1916 model->setPickable(false);
1917 model->setVisible(false);
1918 QQmlListReference materialsRef(model, "materials");
1919 if (materialsRef.size()) {
1920 auto material = materialsRef.at(0);
1921 delete material;
1922 }
1923 delete model;
1924 }
1925 delete list;
1926 }
1927 m_slicedBarModels.clear();
1928}
1929
1930void QQuickGraphsBars::removeSelectedModels()
1931{
1932 for (const auto list : std::as_const(t&: m_selectedModels)) {
1933 for (auto selectedModel : *list) {
1934 selectedModel->setPickable(false);
1935 selectedModel->setVisible(false);
1936 QQmlListReference materialsRef(selectedModel, "materials");
1937 if (materialsRef.size()) {
1938 auto material = materialsRef.at(0);
1939 delete material;
1940 }
1941 delete selectedModel;
1942 }
1943 delete list;
1944 }
1945 m_selectedModels.clear();
1946 m_barsController->setSelectedBar(position: m_selectedBarCoord, series: m_selectedBarSeries, enterSlice: false);
1947}
1948
1949void QQuickGraphsBars::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
1950{
1951 if (mode.testFlag(flag: QAbstract3DGraph::SelectionSlice) && m_selectedBarSeries) {
1952 setSliceActivatedChanged(true);
1953 if (sliceView() && isSliceEnabled()) {
1954 m_selectionDirty = false;
1955 } else {
1956 setSliceEnabled(true);
1957 m_selectionDirty = true;
1958 }
1959 }
1960
1961 m_selectionMode = mode;
1962
1963 if (optimizationHints() == QAbstract3DGraph::OptimizationDefault) {
1964 for (const auto list : std::as_const(t&: m_barModelsMap)) {
1965 QList<BarItemHolder *> barItemList = list->at(i: 0)->instancing->dataArray();
1966 for (auto bih : barItemList)
1967 bih->selectedBar = false;
1968 }
1969 }
1970
1971 removeSelectedModels();
1972 QList<QBar3DSeries *> barSeriesList = m_barsController->barSeriesList();
1973 for (const auto &series : std::as_const(t&: barSeriesList)) {
1974 if (m_barModelsMap.contains(key: series))
1975 createSelectedModels(series);
1976 }
1977
1978 m_barsController->setSeriesVisualsDirty(true);
1979 itemLabel()->setVisible(false);
1980}
1981
1982void QQuickGraphsBars::updateBarSpecs(float thicknessRatio, const QSizeF &spacing, bool relative)
1983{
1984 // Convert ratio to QSizeF, as we need it in that format for autoscaling calculations
1985 m_cachedBarThickness.setWidth(1.0);
1986 m_cachedBarThickness.setHeight(1.0f / thicknessRatio);
1987
1988 if (relative) {
1989 m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2)
1990 * (spacing.width() + 1.0f));
1991 m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2)
1992 * (spacing.height() + 1.0f));
1993 } else {
1994 m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2;
1995 }
1996
1997 m_axisRangeChanged = true;
1998 m_barsController->m_changeTracker.selectedBarChanged = true;
1999
2000 // Calculate here and at setting sample space
2001 calculateSceneScalingFactors();
2002}
2003
2004void QQuickGraphsBars::updateBarSeriesMargin(const QSizeF &margin)
2005{
2006 m_cachedBarSeriesMargin = margin;
2007 calculateSeriesStartPosition();
2008 calculateSceneScalingFactors();
2009 m_barsController->setSeriesVisualsDirty(true);
2010}
2011

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