1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "q3dscene.h"
5#include "qbar3dseries_p.h"
6#include "qbardataproxy_p.h"
7#include "qcategory3daxis_p.h"
8#include "qgraphsinputhandler_p.h"
9#include "qgraphstheme.h"
10#include "qquickgraphsbars_p.h"
11#include "qquickgraphstexturedata_p.h"
12#include "qvalue3daxis_p.h"
13#include "qgraphs3dlogging_p.h"
14
15#include <QColor>
16#include <QtCore/QMutexLocker>
17#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
18#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
19#include <QtQuick3D/private/qquick3drepeater_p.h>
20#include <QtQuick/qquickitemgrabresult.h>
21
22#include <QtGui/qquaternion.h>
23
24/*!
25 * \qmltype Bars3D
26 * \inherits GraphsItem3D
27 * \inqmlmodule QtGraphs
28 * \ingroup graphs_qml_3D
29 * \brief 3D bar graph.
30 *
31 * This type enables developers to render bar graphs in 3D with Qt Quick.
32 *
33 * You will need to import Qt Graphs module to use this type:
34 *
35 * \snippet doc_src_qmlgraphs.cpp 0
36 *
37 * After that you can use Bars3D in your qml files:
38 *
39 * \snippet doc_src_qmlgraphs.cpp 1
40 *
41 * See \l{Simple Bar Graph} for more thorough usage example.
42 *
43 * \sa Bar3DSeries, ItemModelBarDataProxy, Scatter3D, Surface3D, {Qt Graphs C++ Classes for 3D}
44 */
45
46/*!
47 * \qmlproperty Category3DAxis Bars3D::rowAxis
48 * The active row axis.
49 *
50 * If an axis is not given, a temporary default axis with no labels is created.
51 * This temporary axis is destroyed if another axis is explicitly set to the
52 * same orientation.
53 */
54
55/*!
56 * \qmlproperty ValueAxis3D Bars3D::valueAxis
57 * The active value axis.
58 *
59 * If an axis is not given, a temporary default axis with no labels and an
60 * automatically adjusting range is created.
61 * This temporary axis is destroyed if another axis is explicitly set to the
62 * same orientation.
63 */
64
65/*!
66 * \qmlproperty Category3DAxis Bars3D::columnAxis
67 * The active column axis.
68 *
69 * If an axis is not given, a temporary default axis with no labels is created.
70 * This temporary axis is destroyed if another axis is explicitly set to the
71 * same orientation.
72 */
73
74/*!
75 * \qmlproperty bool Bars3D::multiSeriesUniform
76 * Defines whether bars are to be scaled with proportions set to a single series bar even
77 * if there are multiple series displayed. If set to \c {true}, \l{barSpacing}{bar spacing} will
78 * be correctly applied only to the X-axis. Preset to \c false by default.
79 */
80
81/*!
82 * \qmlproperty real Bars3D::barThickness
83 * The bar thickness ratio between the X and Z dimensions. The value \c 1.0
84 * means that the bars are as wide as they are deep, whereas \c 0.5
85 * makes them twice as deep as they are wide.
86 */
87
88/*!
89 * \qmlproperty size Bars3D::barSpacing
90 * Bar spacing in X and Z dimensions.
91 *
92 * Preset to \c {(1.0, 1.0)} by default. Spacing is affected by the
93 * barSpacingRelative property.
94 */
95
96/*!
97 * \qmlproperty bool Bars3D::barSpacingRelative
98 * Whether spacing is absolute or relative to bar thickness.
99 *
100 * If \c true, the value of \c 0.0 means that the bars are placed
101 * side-to-side, \c 1.0 means that a space as wide as the thickness of one bar
102 * is left between the bars, and so on. Preset to \c true.
103 */
104
105/*!
106 * \qmlproperty size Bars3D::barSeriesMargin
107 *
108 * Margin between series columns in X and Z dimensions. Preset to \c {(0.0, 0.0)} by default.
109 * Sensible values are on the range [0,1).
110 */
111
112/*!
113 * \qmlproperty Bar3DSeries Bars3D::selectedSeries
114 * \readonly
115 *
116 * The selected series or \c null. If \l {GraphsItem3D::selectionMode}{selectionMode} has
117 * the \c SelectionMultiSeries flag set, this property holds the series that
118 * owns the selected bar.
119 */
120
121/*!
122 * \qmlproperty list<Bar3DSeries> Bars3D::seriesList
123 * \qmldefault
124 * The series of the graph.
125 * By default, this property contains an empty list.
126 * To set the series, either use the addSeries() function or define them as children of the graph.
127 */
128
129/*!
130 * \qmlproperty Bar3DSeries Bars3D::primarySeries
131 * The primary series of the graph. It
132 * is used to determine the row and column axis labels when the labels are not explicitly
133 * set to the axes.
134 *
135 * If the specified series is not yet added to the graph, setting it as the
136 * primary series will also implicitly add it to the graph.
137 *
138 * If the primary series itself is removed from the graph, this property
139 * resets to default.
140 *
141 * If the series is null, this property resets to default.
142 * Defaults to the first added series or zero if no series are added to the graph.
143 */
144
145/*!
146 * \qmlproperty real Bars3D::floorLevel
147 *
148 * The floor level for the bar graph in Y-axis data coordinates.
149 *
150 * The actual floor level will be restricted by the Y-axis minimum and maximum
151 * values.
152 * Defaults to zero.
153 */
154
155/*!
156 * \qmlmethod void Bars3D::addSeries(Bar3DSeries series)
157 * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes,
158 * so the rows and columns of all series must match for the visualized data to be meaningful.
159 * If the graph has multiple visible series, only the first one added will
160 * generate the row or column labels on the axes in cases where the labels are not explicitly set
161 * to the axes. If the newly added series has specified a selected bar, it will be highlighted and
162 * any existing selection will be cleared. Only one added series can have an active selection.
163 * \sa GraphsItem3D::hasSeries()
164 */
165
166/*!
167 * \qmlmethod void Bars3D::removeSeries(Bar3DSeries series)
168 * Remove the \a series from the graph.
169 * \sa GraphsItem3D::hasSeries()
170 */
171
172/*!
173 * \qmlmethod void Bars3D::insertSeries(int index, Bar3DSeries series)
174 * Inserts the \a series into the position \a index in the series list.
175 * If the \a series has already been added to the list, it is moved to the
176 * new \a index.
177 * \note When moving a series to a new \a index that is after its old index,
178 * the new position in list is calculated as if the series was still in its old
179 * index, so the final index is actually the \a index decremented by one.
180 * \sa GraphsItem3D::hasSeries()
181 */
182
183/*!
184 * \qmlmethod void Bars3D::renderSliceToImage(int requestedIndex, QtGraphs3D::SliceCaptureType sliceType, QUrl filePath)
185 * \since 6.10
186 *
187 * Exports a 2d slice from series at \a requestedIndex and saves the result to an image
188 * at a specified \a filePath.
189 * The exported slice includes bars of row or column, which is defined by
190 * \a sliceType.
191 */
192
193/*!
194 * \qmlsignal Bars3D::multiSeriesUniformChanged(bool uniform)
195 *
196 * This signal is emitted when multiSeriesUniform changes to \a uniform.
197 */
198
199/*!
200 * \qmlsignal Bars3D::barThicknessChanged(real thicknessRatio)
201 *
202 * This signal is emitted when barThickness changes to \a thicknessRatio.
203 */
204
205/*!
206 * \qmlsignal Bars3D::barSpacingChanged(size spacing)
207 *
208 * This signal is emitted when barSpacing changes to \a spacing.
209 */
210
211/*!
212 * \qmlsignal Bars3D::barSpacingRelativeChanged(bool relative)
213 *
214 * This signal is emitted when barSpacingRelative changes to \a relative.
215 */
216
217/*!
218 * \qmlsignal Bars3D::barSeriesMarginChanged(size margin)
219 *
220 * This signal is emitted when barSeriesMargin changes to \a margin.
221 */
222
223/*!
224 * \qmlsignal Bars3D::rowAxisChanged(Category3DAxis axis)
225 *
226 * This signal is emitted when rowAxis changes to \a axis.
227 */
228
229/*!
230 * \qmlsignal Bars3D::columnAxisChanged(Category3DAxis axis)
231 *
232 * This signal is emitted when columnAxis changes to \a axis.
233 */
234
235/*!
236 * \qmlsignal Bars3D::valueAxisChanged(ValueAxis3D axis)
237 *
238 * This signal is emitted when valueAxis changes to \a axis.
239 */
240
241/*!
242 * \qmlsignal Bars3D::primarySeriesChanged(Bar3DSeries series)
243 *
244 * This signal is emitted when primarySeries changes to \a series.
245 */
246
247/*!
248 * \qmlsignal Bars3D::selectedSeriesChanged(Bar3DSeries series)
249 *
250 * This signal is emitted when selectedSeries changes to \a series.
251 */
252
253/*!
254 * \qmlsignal Bars3D::floorLevelChanged(real level)
255 *
256 * This signal is emitted when floorLevel changes to \a level.
257 */
258
259QQuickGraphsBars::QQuickGraphsBars(QQuickItem *parent)
260 : QQuickGraphsItem(parent)
261{
262 m_graphType = QAbstract3DSeries::SeriesType::Bar;
263 setAxisX(0);
264 setAxisY(0);
265 setAxisZ(0);
266 setAcceptedMouseButtons(Qt::AllButtons);
267 setFlags(ItemHasContents);
268 clearSelection();
269}
270
271QQuickGraphsBars::~QQuickGraphsBars()
272{
273 QMutexLocker locker(m_nodeMutex.data());
274 const QMutexLocker locker2(mutex());
275 removeBarModels();
276 removeSlicedBarModels();
277 if (m_grabresult)
278 delete m_grabresult;
279}
280
281QCategory3DAxis *QQuickGraphsBars::rowAxis() const
282{
283 return static_cast<QCategory3DAxis *>(axisZ());
284}
285
286void QQuickGraphsBars::setRowAxis(QCategory3DAxis *axis)
287{
288 QQuickGraphsItem::setAxisZ(axis);
289}
290
291QValue3DAxis *QQuickGraphsBars::valueAxis() const
292{
293 return static_cast<QValue3DAxis *>(axisY());
294}
295
296void QQuickGraphsBars::setValueAxis(QValue3DAxis *axis)
297{
298 QQuickGraphsItem::setAxisY(axis);
299}
300
301QCategory3DAxis *QQuickGraphsBars::columnAxis() const
302{
303 return static_cast<QCategory3DAxis *>(axisX());
304}
305
306void QQuickGraphsBars::setColumnAxis(QCategory3DAxis *axis)
307{
308 QQuickGraphsItem::setAxisX(axis);
309}
310
311void QQuickGraphsBars::setMultiSeriesScaling(bool uniform)
312{
313 m_isMultiSeriesUniform = uniform;
314
315 m_changeTracker.multiSeriesScalingChanged = true;
316 emitNeedRender();
317}
318
319bool QQuickGraphsBars::multiSeriesScaling() const
320{
321 return m_isMultiSeriesUniform;
322}
323
324void QQuickGraphsBars::setMultiSeriesUniform(bool uniform)
325{
326 if (uniform == isMultiSeriesUniform()) {
327 qCDebug(lcProperties3D) << __FUNCTION__
328 << "value is already set to:" << uniform;
329 return;
330 }
331 setMultiSeriesScaling(uniform);
332 emit multiSeriesUniformChanged(uniform);
333}
334
335bool QQuickGraphsBars::isMultiSeriesUniform() const
336{
337 return multiSeriesScaling();
338}
339
340void QQuickGraphsBars::setBarSpecs(float thicknessRatio, QSizeF spacing, bool relative)
341{
342 m_barThicknessRatio = thicknessRatio;
343 m_barSpacing = spacing;
344 m_isBarSpecRelative = relative;
345
346 m_changeTracker.barSpecsChanged = true;
347 emitNeedRender();
348}
349
350void QQuickGraphsBars::setBarThickness(float thicknessRatio)
351{
352 if (thicknessRatio == barThickness() || thicknessRatio <= 0.f) {
353 qCDebug(lcProperties3D, "%s value is already set to: %.1f",
354 qUtf8Printable(QLatin1String(__FUNCTION__)), thicknessRatio);
355 return;
356 }
357 setBarSpecs(thicknessRatio, spacing: barSpacing(), relative: isBarSpacingRelative());
358 emit barThicknessChanged(thicknessRatio);
359}
360
361float QQuickGraphsBars::barThickness() const
362{
363 return m_barThicknessRatio;
364}
365
366void QQuickGraphsBars::setBarSpacing(QSizeF spacing)
367{
368 if (spacing == barSpacing()) {
369 qCDebug(lcProperties3D) << __FUNCTION__
370 << "value is already set to:" << spacing;
371 return;
372 }
373 setBarSpecs(thicknessRatio: barThickness(), spacing, relative: isBarSpacingRelative());
374 emit barSpacingChanged(spacing);
375}
376
377QSizeF QQuickGraphsBars::barSpacing() const
378{
379 return m_barSpacing;
380}
381
382void QQuickGraphsBars::setBarSpacingRelative(bool relative)
383{
384 if (relative == isBarSpacingRelative()) {
385 qCDebug(lcProperties3D) << __FUNCTION__
386 << "value is already set to:" << relative;
387 return;
388 }
389 setBarSpecs(thicknessRatio: barThickness(), spacing: barSpacing(), relative);
390 emit barSpacingRelativeChanged(relative);
391}
392
393bool QQuickGraphsBars::isBarSpacingRelative() const
394{
395 return m_isBarSpecRelative;
396}
397
398void QQuickGraphsBars::setBarSeriesMargin(QSizeF margin)
399{
400 if (margin == barSeriesMargin()) {
401 qCDebug(lcProperties3D) << __FUNCTION__
402 << "value is already set to:" << margin;
403 return;
404 }
405 m_barSeriesMargin = margin;
406 m_changeTracker.barSeriesMarginChanged = true;
407 emitNeedRender();
408 emit barSeriesMarginChanged(margin: barSeriesMargin());
409}
410
411QSizeF QQuickGraphsBars::barSeriesMargin() const
412{
413 return m_barSeriesMargin;
414}
415
416QList<QBar3DSeries *> QQuickGraphsBars::barSeriesList()
417{
418 QList<QBar3DSeries *> barSeriesList;
419 for (QAbstract3DSeries *abstractSeries : std::as_const(t&: m_seriesList)) {
420 QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(object: abstractSeries);
421 if (barSeries)
422 barSeriesList.append(t: barSeries);
423 }
424
425 return barSeriesList;
426}
427
428QQmlListProperty<QBar3DSeries> QQuickGraphsBars::seriesList()
429{
430 return QQmlListProperty<QBar3DSeries>(this,
431 this,
432 &QQuickGraphsBars::appendSeriesFunc,
433 &QQuickGraphsBars::countSeriesFunc,
434 &QQuickGraphsBars::atSeriesFunc,
435 &QQuickGraphsBars::clearSeriesFunc);
436}
437
438void QQuickGraphsBars::appendSeriesFunc(QQmlListProperty<QBar3DSeries> *list, QBar3DSeries *series)
439{
440 reinterpret_cast<QQuickGraphsBars *>(list->data)->addSeries(series);
441}
442
443qsizetype QQuickGraphsBars::countSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
444{
445 return reinterpret_cast<QQuickGraphsBars *>(list->data)->barSeriesList().size();
446}
447
448QBar3DSeries *QQuickGraphsBars::atSeriesFunc(QQmlListProperty<QBar3DSeries> *list, qsizetype index)
449{
450 return reinterpret_cast<QQuickGraphsBars *>(list->data)->barSeriesList().at(i: index);
451}
452
453void QQuickGraphsBars::clearSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
454{
455 QQuickGraphsBars *declBars = reinterpret_cast<QQuickGraphsBars *>(list->data);
456 QList<QBar3DSeries *> realList = declBars->barSeriesList();
457 qsizetype count = realList.size();
458 for (int i = 0; i < count; i++)
459 declBars->removeSeries(series: realList.at(i));
460}
461
462void QQuickGraphsBars::addSeries(QBar3DSeries *series)
463{
464 insertSeries(index: m_seriesList.size(), series);
465 connectSeries(series);
466 if (series->selectedBar() != invalidSelectionPosition())
467 updateSelectedBar();
468}
469
470void QQuickGraphsBars::removeSeries(QBar3DSeries *series)
471{
472 if (!series)
473 return;
474
475 bool wasVisible = (series && series->d_func()->m_graph == this && series->isVisible());
476
477 QQuickGraphsItem::removeSeriesInternal(series);
478
479 if (m_selectedBarSeries == series)
480 setSelectedBar(coord: invalidSelectionPosition(), series: 0, enterSlice: false);
481
482 if (wasVisible)
483 adjustAxisRanges();
484
485 // If primary series is removed, reset it to default
486 if (series == m_primarySeries) {
487 if (m_seriesList.size())
488 m_primarySeries = static_cast<QBar3DSeries *>(m_seriesList.at(i: 0));
489 else
490 m_primarySeries = 0;
491
492 handleDataRowLabelsChanged();
493 handleDataColumnLabelsChanged();
494
495 emit primarySeriesChanged(series: m_primarySeries);
496 }
497
498 for (auto it = m_slicedBarModels.begin(); it != m_slicedBarModels.end(); it++) {
499 // Remove series also from slice bar list
500 if (it.key() == series)
501 m_slicedBarModels.remove(key: it.key());
502 }
503
504 removeBarModels();
505 if (m_selectedBarSeries == series)
506 resetClickedStatus();
507 series->setParent(this); // Reparent as removing will leave series parentless
508 disconnectSeries(series);
509}
510
511void QQuickGraphsBars::insertSeries(qsizetype index, QBar3DSeries *series)
512{
513 Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesType::Bar);
514
515 qsizetype oldSize = m_seriesList.size();
516
517 QQuickGraphsItem::insertSeries(index, series);
518
519 if (oldSize != m_seriesList.size()) {
520 QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(series);
521 if (!oldSize) {
522 m_primarySeries = barSeries;
523 handleDataRowLabelsChanged();
524 handleDataColumnLabelsChanged();
525 }
526
527 if (barSeries->selectedBar() != invalidSelectionPosition())
528 setSelectedBar(coord: barSeries->selectedBar(), series: barSeries, enterSlice: false);
529
530 if (!oldSize)
531 emit primarySeriesChanged(series: m_primarySeries);
532 }
533}
534
535void QQuickGraphsBars::clearSelection()
536{
537 setSelectedBar(coord: invalidSelectionPosition(), series: 0, enterSlice: false);
538}
539
540void QQuickGraphsBars::setPrimarySeries(QBar3DSeries *series)
541{
542 if (!series) {
543 if (m_seriesList.size())
544 series = static_cast<QBar3DSeries *>(m_seriesList.at(i: 0));
545 } else if (!m_seriesList.contains(t: series)) {
546 // Add nonexistent series.
547 addSeries(series);
548 }
549
550 if (m_primarySeries != series) {
551 m_primarySeries = series;
552 handleDataRowLabelsChanged();
553 handleDataColumnLabelsChanged();
554 emit primarySeriesChanged(series: m_primarySeries);
555 }
556}
557
558QBar3DSeries *QQuickGraphsBars::primarySeries() const
559{
560 return m_primarySeries;
561}
562
563QBar3DSeries *QQuickGraphsBars::selectedSeries() const
564{
565 return m_selectedBarSeries;
566}
567
568void QQuickGraphsBars::setSelectionMode(QtGraphs3D::SelectionFlags mode)
569{
570 if (mode.testFlag(flag: QtGraphs3D::SelectionFlag::Slice)
571 && (mode.testFlag(flag: QtGraphs3D::SelectionFlag::Row)
572 == mode.testFlag(flag: QtGraphs3D::SelectionFlag::Column))) {
573 qCWarning(lcProperties3D, "%s must specify one of either row or column selection mode"
574 "in conjunction with slicing mode.",
575 qUtf8Printable(QLatin1String(__FUNCTION__)));
576 } else {
577 QtGraphs3D::SelectionFlags oldMode = selectionMode();
578
579 QQuickGraphsItem::setSelectionMode(mode);
580
581 if (mode != oldMode) {
582 // Refresh selection upon mode change to ensure slicing is correctly
583 // updated according to series the visibility.
584 setSelectedBar(coord: m_selectedBar, series: m_selectedBarSeries, enterSlice: true);
585
586 // Special case: Always deactivate slicing when changing away from slice
587 // automanagement, as this can't be handled in setSelectedBar.
588 if (!mode.testFlag(flag: QtGraphs3D::SelectionFlag::Slice)
589 && oldMode.testFlag(flag: QtGraphs3D::SelectionFlag::Slice)) {
590 scene()->setSlicingActive(false);
591 }
592 }
593 }
594}
595
596void QQuickGraphsBars::handleAxisAutoAdjustRangeChangedInOrientation(
597 QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust)
598{
599 Q_UNUSED(orientation);
600 Q_UNUSED(autoAdjust);
601 adjustAxisRanges();
602}
603
604void QQuickGraphsBars::handleSeriesVisibilityChangedBySender(QObject *sender)
605{
606 QQuickGraphsItem::handleSeriesVisibilityChangedBySender(sender);
607
608 // Visibility changes may require disabling slicing,
609 // so just reset selection to ensure everything is still valid.
610 setSelectedBar(coord: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
611}
612
613void QQuickGraphsBars::handleAxisRangeChangedBySender(QObject *sender)
614{
615 // Data window changed
616 if (sender == m_axisX || sender == m_axisZ) {
617 if (sender == m_axisX)
618 handleDataColumnLabelsChanged();
619 if (sender == m_axisZ)
620 handleDataRowLabelsChanged();
621 }
622
623 QQuickGraphsItem::handleAxisRangeChangedBySender(sender);
624
625 setDataDirty(true);
626
627 // Update selected bar - may be moved offscreen
628 setSelectedBar(coord: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
629}
630
631void QQuickGraphsBars::adjustAxisRanges()
632{
633 QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_axisZ);
634 QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_axisX);
635 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_axisY);
636
637 bool adjustZ = (categoryAxisZ && categoryAxisZ->isAutoAdjustRange());
638 bool adjustX = (categoryAxisX && categoryAxisX->isAutoAdjustRange());
639 bool adjustY = (valueAxis && categoryAxisX && categoryAxisZ && valueAxis->isAutoAdjustRange());
640
641 if (adjustZ || adjustX || adjustY) {
642 qsizetype maxRowCount = 0;
643 qsizetype maxColumnCount = 0;
644 float minValue = 0.0f;
645 float maxValue = 0.0f;
646
647 // First figure out row and column counts
648 qsizetype seriesCount = m_seriesList.size();
649 if (adjustZ || adjustX) {
650 for (qsizetype series = 0; series < seriesCount; series++) {
651 const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(i: series));
652 if (barSeries->isVisible()) {
653 const QBarDataProxy *proxy = barSeries->dataProxy();
654
655 if (adjustZ && proxy) {
656 qsizetype rowCount = proxy->rowCount();
657 if (rowCount)
658 rowCount--;
659
660 maxRowCount = qMax(a: maxRowCount, b: rowCount);
661 }
662
663 if (adjustX && proxy) {
664 const QBarDataArray &array = barSeries->dataArray();
665 qsizetype columnCount = 0;
666 for (int i = 0; i < array.size(); i++) {
667 if (columnCount < array.at(i).size())
668 columnCount = array.at(i).size();
669 }
670 if (columnCount)
671 columnCount--;
672
673 maxColumnCount = qMax(a: maxColumnCount, b: columnCount);
674 }
675 }
676 }
677 // Call private implementations of setRange to avoid unsetting auto adjust
678 // flag
679 if (adjustZ)
680 categoryAxisZ->d_func()->setRange(min: 0.0f, max: float(maxRowCount), suppressWarnings: true);
681 if (adjustX)
682 categoryAxisX->d_func()->setRange(min: 0.0f, max: float(maxColumnCount), suppressWarnings: true);
683 }
684
685 // Now that we know the row and column ranges, figure out the value axis
686 // range
687 if (adjustY) {
688 for (int series = 0; series < seriesCount; series++) {
689 const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(i: series));
690 if (barSeries->isVisible()) {
691 const QBarDataProxy *proxy = barSeries->dataProxy();
692 if (adjustY && proxy) {
693 QPair<float, float> limits = proxy->d_func()
694 ->limitValues(startRow: categoryAxisZ->min(),
695 startColumn: categoryAxisZ->max(),
696 rowCount: categoryAxisX->min(),
697 columnCount: categoryAxisX->max());
698 if (!series) {
699 // First series initializes the values
700 minValue = limits.first;
701 maxValue = limits.second;
702 } else {
703 minValue = qMin(a: minValue, b: limits.first);
704 maxValue = qMax(a: maxValue, b: limits.second);
705 }
706 }
707 }
708 }
709
710 if (maxValue < 0.0f)
711 maxValue = 0.0f;
712 if (minValue > 0.0f)
713 minValue = 0.0f;
714 if (minValue == 0.0f && maxValue == 0.0f) {
715 // Only zero value values in data set, set range to something.
716 minValue = 0.0f;
717 maxValue = 1.0f;
718 }
719 valueAxis->d_func()->setRange(min: minValue, max: maxValue, suppressWarnings: true);
720 }
721 }
722}
723
724void QQuickGraphsBars::setFloorLevel(float level)
725{
726 if (level == floorLevel()) {
727 qCDebug(lcProperties3D, "%s value is already set to: %.f",
728 qUtf8Printable(QLatin1String(__FUNCTION__)), level);
729 return;
730 }
731 m_floorLevel = level;
732 setDataDirty(true);
733 m_changeTracker.floorLevelChanged = true;
734 emitNeedRender();
735 emit floorLevelChanged(level);
736}
737
738float QQuickGraphsBars::floorLevel() const
739{
740 return m_floorLevel;
741}
742
743QQuick3DViewport *QQuickGraphsBars::createOffscreenSliceView(int requestedIndex,
744 QtGraphs3D::SliceCaptureType sliceType)
745{
746 QQuick3DViewport *sliceView = QQuickGraphsItem::createOffscreenSliceView(sliceType);
747
748 bool isRow = (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)
749 || sliceType == QtGraphs3D::SliceCaptureType::RowImage);
750 bool isColumn = (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)
751 || sliceType == QtGraphs3D::SliceCaptureType::ColumnImage);
752
753 QList<QBar3DSeries *> barSeriesList = this->barSeriesList();
754 for (const auto &barSeries : std::as_const(t&: barSeriesList)) {
755 qsizetype newRowSize = qMin(a: barSeries->dataProxy()->rowCount() - m_minRow, b: m_newRows);
756 qsizetype newColSize = 0;
757 if (newRowSize) {
758 const QBarDataRow *dataRow = &barSeries->dataProxy()->rowAt(rowIndex: m_minRow);
759 if (dataRow) {
760 qsizetype dataColIndex = m_minCol;
761 newColSize = qMin(a: dataRow->size() - dataColIndex, b: m_newCols);
762 }
763 }
764
765 if (!barSeries->isVisible())
766 continue;
767
768 if (requestedIndex < 0 || requestedIndex >= newRowSize || requestedIndex >= newColSize) {
769 qCWarning(lcEvents3D, "%s the index is out of range. The render stops.",
770 qUtf8Printable(QLatin1String(__FUNCTION__)));
771 sliceView->setVisible(false);
772 sliceView->deleteLater();
773 return nullptr;
774 }
775
776 qsizetype slicedBarListSize = -1;
777
778 if (isRow)
779 slicedBarListSize = newColSize;
780 else if (isColumn)
781 slicedBarListSize = newRowSize;
782
783 if (slicedBarListSize < 0)
784 return nullptr;
785
786 QList<BarModel *> barList = *m_barModelsMap.value(key: barSeries);
787 bool useGradient = barSeries->d_func()->isUsingGradient();
788 bool rangeGradient =
789 (useGradient
790 && barSeries->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
791
792 QQuick3DModel *model = nullptr;
793 QList<BarItemHolder *> barItemHolderList;
794 QList<BarItemHolder *> barItemList;
795 QList<BarItemHolder *> selectedItems;
796 if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
797 model = new QQuick3DModel();
798 model->setParent(sliceView->scene());
799 model->setParentItem(sliceView->scene());
800 model->setObjectName(QStringLiteral("BarModel"));
801 QString fileName = getMeshFileName();
802 if (fileName.isEmpty())
803 fileName = barSeries->userDefinedMesh();
804
805 model->setSource(QUrl(fileName));
806
807 auto barInstancing = new BarInstancing;
808 barInstancing->setParent(barSeries);
809 model->setInstancing(barInstancing);
810
811 BarModel *barListItem = barList.at(i: 0);
812 updateItemMaterial(item: model, useGradient, rangeGradient,
813 QStringLiteral(":/materials/BarsMaterialInstancing"));
814 updateMaterialProperties(item: model, isHighlight: false, isMultiHighlight: false, texture: barListItem->texture,
815 color: barSeries->baseColor());
816
817 barItemList = barListItem->instancing->dataArray();
818 for (const auto bih : std::as_const(t&: barItemList)) {
819 if (!((isRow && bih->coord.x() == requestedIndex)
820 || (isColumn && bih->coord.y() == requestedIndex)))
821 continue;
822
823 BarItemHolder *selectedBih = new BarItemHolder();
824 selectedBih->selectedBar = false;
825 selectedBih->coord = bih->coord;
826 selectedBih->rotation = bih->rotation;
827 selectedBih->heightValue = bih->heightValue;
828 selectedBih->position = bih->position;
829 selectedBih->scale = bih->scale;
830
831 selectedItems.push_back(t: selectedBih);
832 }
833 if (selectedItems.size() == 0)
834 continue;
835 }
836
837 qsizetype index = 0;
838 for (int ind = 0; ind < slicedBarListSize; ++ind) {
839 if (isRow)
840 index = (requestedIndex * barSeries->dataProxy()->colCount()) + ind;
841 else
842 index = requestedIndex + (ind * barSeries->dataProxy()->colCount());
843
844 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
845 if (index > barList.size())
846 return nullptr;
847 model = createDataItem(scene: sliceView->scene(), series: barSeries);
848 BarModel *barModel = barList.at(i: index);
849 if (isRow) {
850 model->setPosition(QVector3D(barModel->model->x(), barModel->model->y(), 0.0f));
851 } else {
852 model->setX(barModel->model->z() - (barList.at(i: 0)->visualIndex * .1f));
853 model->setY(barModel->model->y());
854 model->setZ(0.0f);
855 }
856 model->setScale(barModel->model->scale());
857
858 updateItemMaterial(item: model, useGradient, rangeGradient,
859 QStringLiteral(":/materials/BarsMaterial"));
860
861 updateMaterialProperties(item: model, isHighlight: false, isMultiHighlight: false, texture: barList.at(i: index)->texture,
862 color: barSeries->baseColor());
863 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
864 BarModel *barModel = barList.at(i: 0);
865 BarItemHolder *itemHolder = new BarItemHolder();
866 itemHolder->selectedBar = false;
867 itemHolder->color = barSeries->baseColor();
868 itemHolder->coord = barModel->coord;
869 itemHolder->rotation = selectedItems.at(i: ind)->rotation;
870 itemHolder->heightValue = barModel->heightValue;
871 itemHolder->position = selectedItems.at(i: ind)->position;
872 itemHolder->scale = selectedItems.at(i: ind)->scale;
873
874 if (isRow) {
875 itemHolder->position.setZ(.0f);
876 } else {
877 itemHolder->position.setX(itemHolder->position.z()
878 - (barModel->visualIndex * .1f));
879 itemHolder->position.setZ(.0f);
880 }
881
882 barItemHolderList.push_back(t: itemHolder);
883 }
884 }
885
886 if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
887 BarInstancing *instancing = static_cast<BarInstancing *>(model->instancing());
888 instancing->setDataArray(barItemHolderList);
889 }
890 }
891
892 return sliceView;
893}
894
895void QQuickGraphsBars::renderSliceToImage(int requestedIndex,
896 QtGraphs3D::SliceCaptureType sliceType)
897{
898 QQuick3DViewport *sliceView = createOffscreenSliceView(requestedIndex, sliceType);
899
900 if (!m_grabresult)
901 m_grabresult = new QImage();
902
903 if (sliceView) {
904 QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
905 connect(sender: grabbed.data(), signal: &QQuickItemGrabResult::ready, context: this, slot: [&, grabbed, sliceView]() {
906 sliceView->setVisible(false);
907 sliceView->deleteLater();
908 *m_grabresult = grabbed.data()->image();
909 emit sliceImageChanged(image: *m_grabresult);
910 });
911 }
912}
913
914void QQuickGraphsBars::renderSliceToImage(int requestedIndex,
915 QtGraphs3D::SliceCaptureType sliceType,
916 const QUrl &filePath)
917{
918 QQuick3DViewport *sliceView = createOffscreenSliceView(requestedIndex, sliceType);
919
920 if (!sliceView)
921 return;
922
923 if (filePath.isEmpty()) {
924 qWarning(msg: "Save path is not defined.");
925 sliceView->setVisible(false);
926 sliceView->deleteLater();
927 return;
928 }
929
930 QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
931 connect(sender: grabbed.data(), signal: &QQuickItemGrabResult::ready, context: this, slot: [grabbed, sliceView, filePath]() {
932 if (!grabbed.data()->saveToFile(fileName: filePath))
933 qWarning(msg: "Saving requested slice view to image failed");
934 sliceView->setVisible(false);
935 sliceView->deleteLater();
936 });
937
938 return;
939}
940
941void QQuickGraphsBars::componentComplete()
942{
943 QQuickGraphsItem::componentComplete();
944
945 auto wallBackground = background();
946 QUrl wallUrl = QUrl(QStringLiteral("defaultMeshes/backgroundNoFloorMesh"));
947 wallBackground->setSource(wallUrl);
948 setBackground(wallBackground);
949
950 QUrl floorUrl = QUrl(QStringLiteral(":/defaultMeshes/barMeshFull"));
951 m_floorBackground = new QQuick3DModel();
952 m_floorBackgroundScale = new QQuick3DNode();
953 m_floorBackgroundRotation = new QQuick3DNode();
954
955 m_floorBackgroundScale->setParent(graphNode());
956 m_floorBackgroundScale->setParentItem(graphNode());
957
958 m_floorBackgroundRotation->setParent(m_floorBackgroundScale);
959 m_floorBackgroundRotation->setParentItem(m_floorBackgroundScale);
960
961 m_floorBackground->setObjectName("Floor Background");
962 m_floorBackground->setParent(m_floorBackgroundRotation);
963 m_floorBackground->setParentItem(m_floorBackgroundRotation);
964
965 m_floorBackground->setSource(floorUrl);
966 m_floorBackground->setPickable(false);
967
968 setFloorGridInRange(true);
969 setVerticalSegmentLine(false);
970
971 QObject::connect(sender: cameraTarget(),
972 signal: &QQuick3DNode::rotationChanged,
973 context: this,
974 slot: &QQuickGraphsBars::handleCameraRotationChanged);
975
976 graphsInputHandler()->setGraphsItem(this);
977}
978
979void QQuickGraphsBars::synchData()
980{
981 qCDebug(lcGraphs3D, "%s start of sync", qUtf8Printable(QLatin1String(__FUNCTION__)));
982 if (m_changeTracker.barSpecsChanged || !m_cachedBarThickness.isValid()) {
983 updateBarSpecs(thicknessRatio: m_barThicknessRatio, spacing: m_barSpacing, relative: m_isBarSpecRelative);
984 m_changeTracker.barSpecsChanged = false;
985 }
986
987 // Floor level update requires data update, so do before qquickgraphicsitem sync
988 if (m_changeTracker.floorLevelChanged) {
989 updateFloorLevel(level: m_floorLevel);
990 m_changeTracker.floorLevelChanged = false;
991 }
992
993 if (m_changeTracker.multiSeriesScalingChanged) {
994 m_keepSeriesUniform = m_isMultiSeriesUniform;
995 setSeriesVisualsDirty(true);
996 m_changeTracker.multiSeriesScalingChanged = false;
997 }
998
999 // Do not clear dirty flag, we need to react to it in qquickgraphicsitem as well
1000 if (theme()->dirtyBits()->plotAreaBackgroundVisibilityDirty) {
1001 setSeriesVisualsDirty(true);
1002 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++)
1003 it.key()->d_func()->m_changeTracker.meshChanged = true;
1004 }
1005
1006 if (m_changeTracker.barSeriesMarginChanged) {
1007 updateBarSeriesMargin(margin: barSeriesMargin());
1008 m_changeTracker.barSeriesMarginChanged = false;
1009 }
1010
1011 if (m_changeTracker.axisRangeChanged) {
1012 adjustAxisRanges();
1013 m_changeTracker.axisRangeChanged = false;
1014 }
1015
1016 QQuickGraphsItem::synchData();
1017
1018 if (m_selectedBarPos.isNull())
1019 itemLabel()->setVisible(false);
1020
1021 // Draw floor
1022 if (m_changeTracker.floorChanged) {
1023 updateFloor();
1024 m_changeTracker.floorChanged = false;
1025 }
1026
1027 qCDebug(lcGraphs3D, "%s end syncing", qUtf8Printable(QLatin1String(__FUNCTION__)));
1028}
1029
1030void QQuickGraphsBars::updateFloor()
1031{
1032 // Margin for a line to be fully visible on the edge in the grid shader
1033 const float halfLineWidth = 50.0;
1034 const float gridTextureSize = 4096.0;
1035 const float gridMargin = halfLineWidth / gridTextureSize;
1036 auto min = qMin(a: scaleWithBackground().x(), b: scaleWithBackground().z());
1037 m_floorBackgroundScale->setScale(QVector3D(scaleWithBackground().x() + gridMargin,
1038 min * gridOffset(),
1039 scaleWithBackground().z() + gridMargin));
1040 m_floorBackgroundScale->setPosition(QVector3D(0.0f, -m_backgroundAdjustment, 0.0f));
1041
1042 QQuaternion m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: 90.0f));
1043 QQuaternion m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: -90.0f));
1044
1045 if (isYFlipped())
1046 m_floorBackgroundRotation->setRotation(m_xRightAngleRotation);
1047 else
1048 m_floorBackgroundRotation->setRotation(m_xRightAngleRotationNeg);
1049
1050 QQmlListReference materialsRefF(m_floorBackground, "materials");
1051 QQmlListReference bbRef(background(), "materials");
1052 QQuick3DCustomMaterial *bgMatFloor;
1053 if (!materialsRefF.size() && bbRef.size()) {
1054 bgMatFloor = static_cast<QQuick3DCustomMaterial *>(bbRef.at(0));
1055 materialsRefF.append(bgMatFloor);
1056 }
1057}
1058
1059void QQuickGraphsBars::updateParameters()
1060{
1061 m_minRow = m_axisZ->min();
1062 m_maxRow = m_axisZ->max();
1063 m_minCol = m_axisX->min();
1064 m_maxCol = m_axisX->max();
1065 m_newRows = m_maxRow - m_minRow + 1;
1066 m_newCols = m_maxCol - m_minCol + 1;
1067
1068 if (m_cachedRowCount != m_newRows || m_cachedColumnCount != m_newCols) {
1069 m_changeTracker.selectedBarChanged = true;
1070 m_cachedColumnCount = m_newCols;
1071 m_cachedRowCount = m_newRows;
1072
1073 // Calculate max scene size
1074 float sceneRatio = qMin(a: float(m_newCols) / float(m_newRows),
1075 b: float(m_newRows) / float(m_newCols));
1076 m_maxSceneSize = 2.0f * qSqrt(v: sceneRatio * m_newCols * m_newRows);
1077
1078 if (m_cachedBarThickness.isValid())
1079 calculateSceneScalingFactors();
1080 }
1081 m_changeTracker.axisRangeChanged = true;
1082 setDataDirty(true);
1083}
1084
1085void QQuickGraphsBars::updateFloorLevel(float level)
1086{
1087 setFloorLevel(level);
1088 calculateHeightAdjustment();
1089}
1090
1091void QQuickGraphsBars::updateGraph()
1092{
1093 QList<QBar3DSeries *> barSeriesAsList = barSeriesList();
1094 calculateSceneScalingFactors();
1095
1096 for (const auto &series : std::as_const(t&: barSeriesAsList)) {
1097 QAbstract3DSeriesChangeBitField &changeTracker = series->d_func()->m_changeTracker;
1098 if (changeTracker.meshChanged || changeTracker.meshSmoothChanged) {
1099 changeTracker.meshChanged = false;
1100 changeTracker.meshSmoothChanged = false;
1101 setDataDirty(true);
1102 }
1103 }
1104
1105 if (isDataDirty()) {
1106 removeBarModels();
1107 generateBars(barSeriesList&: barSeriesAsList);
1108 }
1109
1110 if (isSeriesVisualsDirty()) {
1111 int visualIndex = 0;
1112 for (const auto &barSeries : std::as_const(t&: barSeriesAsList)) {
1113 if (barSeries->isVisible()) {
1114 updateBarVisuality(series: barSeries, visualIndex);
1115 updateBarPositions(series: barSeries);
1116 updateBarVisuals(series: barSeries);
1117 ++visualIndex;
1118 } else {
1119 updateBarVisuality(series: barSeries, visualIndex: -1);
1120 }
1121 }
1122 }
1123
1124 // Needs to be done after data is set, as it needs to know the visual array.
1125 if (m_changeTracker.selectedBarChanged) {
1126 updateSelectedBar();
1127 setItemSelected(m_selectedBar != invalidSelectionPosition());
1128 if (isSliceEnabled()) {
1129 createSliceView();
1130 for (const auto &series : std::as_const(t&: barSeriesAsList)) {
1131 bool visible = (m_selectedBarSeries == series) && series->isVisible();
1132 if (sliceView()->isVisible()) {
1133 if (visible) {
1134 removeSlicedBarModels();
1135 createSliceView();
1136 setSliceActivatedChanged(false);
1137 toggleSliceGraph();
1138 break;
1139 } else {
1140 setSliceActivatedChanged(true);
1141 }
1142 } else {
1143 if (visible) {
1144 m_selectionDirty = true;
1145 setSliceActivatedChanged(true);
1146 }
1147 }
1148 }
1149 }
1150 m_changeTracker.selectedBarChanged = false;
1151 }
1152
1153 setDataDirty(false);
1154 setSeriesVisualsDirty(false);
1155}
1156
1157void QQuickGraphsBars::updateAxisRange(float min, float max)
1158{
1159 QQuickGraphsItem::updateAxisRange(min, max);
1160 calculateHeightAdjustment();
1161}
1162
1163void QQuickGraphsBars::updateAxisReversed(bool enable)
1164{
1165 Q_UNUSED(enable);
1166 setSeriesVisualsDirty(true);
1167 calculateHeightAdjustment();
1168}
1169
1170void QQuickGraphsBars::updateLightStrength()
1171{
1172 for (const auto list : std::as_const(t&: m_barModelsMap)) {
1173 for (auto barModel : std::as_const(t&: *list)) {
1174 QQmlListReference materialRef(barModel->model, "materials");
1175 if (materialRef.size()) {
1176 QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(
1177 object: materialRef.at(0));
1178 material->setProperty(name: "specularBrightness", value: lightStrength() * 0.05);
1179 }
1180 }
1181 }
1182}
1183
1184void QQuickGraphsBars::calculateSceneScalingFactors()
1185{
1186 m_rowWidth = (m_cachedColumnCount * m_cachedBarSpacing.width()) * 0.5f;
1187 m_columnDepth = (m_cachedRowCount * m_cachedBarSpacing.height()) * 0.5f;
1188 m_maxDimension = qMax(a: m_rowWidth, b: m_columnDepth);
1189 m_scaleFactor = qMin(a: (m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
1190 b: (m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
1191
1192 // Single bar scaling
1193 m_xScale = m_cachedBarThickness.width() / m_scaleFactor;
1194 m_zScale = m_cachedBarThickness.height() / m_scaleFactor;
1195
1196 // Adjust scaling according to margin
1197 m_xScale = m_xScale - m_xScale * m_cachedBarSeriesMargin.width();
1198 m_zScale = m_zScale - m_zScale * m_cachedBarSeriesMargin.height();
1199
1200 // Whole graph scale factors
1201 m_xScaleFactor = m_rowWidth / m_scaleFactor;
1202 m_zScaleFactor = m_columnDepth / m_scaleFactor;
1203
1204 if (margin() < 0.0f) {
1205 m_hBackgroundMargin = 0.0f;
1206 m_vBackgroundMargin = 0.0f;
1207 } else {
1208 m_hBackgroundMargin = margin();
1209 m_vBackgroundMargin = margin();
1210 }
1211
1212 auto scale = QVector3D(m_xScaleFactor, 1.0f, m_zScaleFactor);
1213 setScaleWithBackground(scale);
1214 setBackgroundScaleMargin({m_hBackgroundMargin, m_vBackgroundMargin, m_hBackgroundMargin});
1215 setScale(scale);
1216 m_changeTracker.floorChanged = true;
1217}
1218
1219void QQuickGraphsBars::calculateHeightAdjustment()
1220{
1221 float newAdjustment = 1.0f;
1222 m_actualFloorLevel = qBound(min: valueAxis()->min(), val: floorLevel(), max: valueAxis()->max());
1223 float maxAbs = qFabs(v: valueAxis()->max() - m_actualFloorLevel);
1224
1225 // Check if we have negative values
1226 if (valueAxis()->min() < m_actualFloorLevel)
1227 m_hasNegativeValues = true;
1228 else if (valueAxis()->min() >= m_actualFloorLevel)
1229 m_hasNegativeValues = false;
1230
1231 // Check first if user has set the ranges
1232 if (!isUserCameraRotationRange()) {
1233 // If not, adjust them automatically based on possibly existing negative values
1234 if (!m_noZeroInRange) {
1235 setMinCameraYRotation(-90.0f);
1236 setMaxCameraYRotation(90.0f);
1237 } else {
1238 if ((m_hasNegativeValues && !valueAxis()->reversed())
1239 || (!m_hasNegativeValues && valueAxis()->reversed())) {
1240 setMinCameraYRotation(-90.0f);
1241 setMaxCameraYRotation(0.0f);
1242 } else {
1243 setMinCameraYRotation(0.0f);
1244 setMaxCameraYRotation(90.0f);
1245 }
1246 }
1247 setUserCameraRotationRange(false);
1248 }
1249
1250 if (valueAxis()->max() < m_actualFloorLevel) {
1251 m_heightNormalizer = float(qFabs(v: valueAxis()->min()) - qFabs(v: valueAxis()->max()));
1252 maxAbs = qFabs(v: valueAxis()->max()) - qFabs(v: valueAxis()->min());
1253 } else {
1254 m_heightNormalizer = float(valueAxis()->max() - valueAxis()->min());
1255 }
1256
1257 // Height fractions are used in gradient calculations and are therefore
1258 // doubled Note that if max or min is exactly zero, we still consider it
1259 // outside the range
1260 if (valueAxis()->max() <= m_actualFloorLevel || valueAxis()->min() >= m_actualFloorLevel)
1261 m_noZeroInRange = true;
1262 else
1263 m_noZeroInRange = false;
1264
1265 // Calculate translation adjustment for background floor
1266 newAdjustment = (qBound(min: 0.0f, val: (maxAbs / m_heightNormalizer), max: 1.0f) - 0.5f) * 2.0f;
1267 if (valueAxis()->reversed())
1268 newAdjustment = -newAdjustment;
1269
1270 if (newAdjustment != m_backgroundAdjustment)
1271 m_backgroundAdjustment = newAdjustment;
1272}
1273
1274void QQuickGraphsBars::calculateSeriesStartPosition()
1275{
1276 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) * 0.5f)
1277 * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
1278}
1279
1280QVector3D QQuickGraphsBars::calculateCategoryLabelPosition(QAbstract3DAxis *axis,
1281 QVector3D labelPosition,
1282 int index)
1283{
1284 QVector3D ret = labelPosition;
1285 if (axis->orientation() == QAbstract3DAxis::AxisOrientation::X) {
1286 float xPos = (index + 0.5f) * m_cachedBarSpacing.width();
1287 ret.setX((xPos - m_rowWidth) / m_scaleFactor);
1288 }
1289 if (axis->orientation() == QAbstract3DAxis::AxisOrientation::Z) {
1290 float zPos = (index + 0.5f) * m_cachedBarSpacing.height();
1291 ret.setZ((m_columnDepth - zPos) / m_scaleFactor);
1292 }
1293 ret.setY(-m_backgroundAdjustment);
1294 return ret;
1295}
1296
1297float QQuickGraphsBars::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index)
1298{
1299 float ret = 0.0f;
1300 if (axis->orientation() == QAbstract3DAxis::AxisOrientation::Z) {
1301 float colPos = index * -(m_cachedBarSpacing.height() / m_scaleFactor);
1302 ret = colPos + scale().z();
1303 }
1304 if (axis->orientation() == QAbstract3DAxis::AxisOrientation::X) {
1305 float rowPos = index * (m_cachedBarSpacing.width() / m_scaleFactor);
1306 ret = rowPos - scale().x();
1307 }
1308 if (axis->orientation() == QAbstract3DAxis::AxisOrientation::Y)
1309 ret = -m_backgroundAdjustment;
1310 return ret;
1311}
1312
1313void QQuickGraphsBars::handleAxisXChanged(QAbstract3DAxis *axis)
1314{
1315 emit columnAxisChanged(axis: static_cast<QCategory3DAxis *>(axis));
1316}
1317
1318void QQuickGraphsBars::handleAxisYChanged(QAbstract3DAxis *axis)
1319{
1320 emit valueAxisChanged(axis: static_cast<QValue3DAxis *>(axis));
1321}
1322
1323void QQuickGraphsBars::handleAxisZChanged(QAbstract3DAxis *axis)
1324{
1325 emit rowAxisChanged(axis: static_cast<QCategory3DAxis *>(axis));
1326}
1327
1328void QQuickGraphsBars::handleSeriesMeshChanged(QAbstract3DSeries::Mesh mesh)
1329{
1330 m_meshType = mesh;
1331}
1332
1333void QQuickGraphsBars::handleMeshSmoothChanged(bool enable)
1334{
1335 m_smooth = enable;
1336}
1337
1338void QQuickGraphsBars::handleCameraRotationChanged()
1339{
1340 updateLabels();
1341}
1342
1343void QQuickGraphsBars::handleArrayReset()
1344{
1345 QBar3DSeries *series;
1346 if (qobject_cast<QBarDataProxy *>(object: sender()))
1347 series = static_cast<QBarDataProxy *>(sender())->series();
1348 else
1349 series = static_cast<QBar3DSeries *>(sender());
1350
1351 if (series->isVisible()) {
1352 adjustAxisRanges();
1353 setDataDirty(true);
1354 series->d_func()->markItemLabelDirty();
1355 }
1356 if (!m_changedSeriesList.contains(t: series))
1357 m_changedSeriesList.append(t: series);
1358 // Clear selection unless still valid
1359 setSelectedBar(coord: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
1360 series->d_func()->markItemLabelDirty();
1361 emitNeedRender();
1362}
1363
1364void QQuickGraphsBars::handleRowsAdded(qsizetype startIndex, qsizetype count)
1365{
1366 Q_UNUSED(startIndex);
1367 Q_UNUSED(count);
1368 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
1369 if (series->isVisible()) {
1370 adjustAxisRanges();
1371 setDataDirty(true);
1372 }
1373 if (!m_changedSeriesList.contains(t: series))
1374 m_changedSeriesList.append(t: series);
1375 emitNeedRender();
1376}
1377
1378void QQuickGraphsBars::handleRowsChanged(qsizetype startIndex, qsizetype count)
1379{
1380 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
1381 qsizetype oldChangeCount = m_changedRows.size();
1382 if (!oldChangeCount)
1383 m_changedRows.reserve(asize: count);
1384
1385 for (int i = 0; i < count; i++) {
1386 bool newItem = true;
1387 qsizetype candidate = startIndex + i;
1388 for (qsizetype j = 0; j < oldChangeCount; j++) {
1389 const ChangeRow &oldChangeItem = m_changedRows.at(i: j);
1390 if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
1391 newItem = false;
1392 break;
1393 }
1394 }
1395 if (newItem) {
1396 ChangeRow newChangeItem = {.series: series, .row: candidate};
1397 m_changedRows.append(t: newChangeItem);
1398 if (series == m_selectedBarSeries && m_selectedBar.x() == candidate)
1399 series->d_func()->markItemLabelDirty();
1400 }
1401 }
1402 if (count) {
1403 m_changeTracker.rowsChanged = true;
1404
1405 if (series->isVisible())
1406 adjustAxisRanges();
1407
1408 // Clear selection unless still valid (row length might have changed)
1409 setSelectedBar(coord: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
1410 emitNeedRender();
1411 }
1412}
1413
1414void QQuickGraphsBars::handleRowsRemoved(qsizetype startIndex, qsizetype count)
1415{
1416 Q_UNUSED(startIndex);
1417 Q_UNUSED(count);
1418
1419 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
1420 if (series == m_selectedBarSeries) {
1421 // If rows removed from selected series before the selection, adjust the
1422 // selection
1423 int selectedRow = m_selectedBar.x();
1424 if (startIndex <= selectedRow) {
1425 if ((startIndex + count) > selectedRow)
1426 selectedRow = -1; // Selected row removed
1427 else
1428 selectedRow -= count; // Move selected row down by amount of rows removed
1429
1430 setSelectedBar(coord: QPoint(selectedRow, m_selectedBar.y()), series: m_selectedBarSeries, enterSlice: false);
1431 }
1432 }
1433
1434 if (series->isVisible()) {
1435 adjustAxisRanges();
1436 setDataDirty(true);
1437 }
1438 if (!m_changedSeriesList.contains(t: series))
1439 m_changedSeriesList.append(t: series);
1440
1441 emitNeedRender();
1442}
1443
1444void QQuickGraphsBars::handleRowsInserted(qsizetype startIndex, qsizetype count)
1445{
1446 Q_UNUSED(startIndex);
1447 Q_UNUSED(count);
1448 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
1449 if (series == m_selectedBarSeries) {
1450 // If rows inserted to selected series before the selection, adjust the
1451 // selection
1452 int selectedRow = m_selectedBar.x();
1453 if (startIndex <= selectedRow) {
1454 selectedRow += count;
1455 setSelectedBar(coord: QPoint(selectedRow, m_selectedBar.y()), series: m_selectedBarSeries, enterSlice: false);
1456 }
1457 }
1458
1459 if (series->isVisible()) {
1460 adjustAxisRanges();
1461 setDataDirty(true);
1462 }
1463 if (!m_changedSeriesList.contains(t: series))
1464 m_changedSeriesList.append(t: series);
1465
1466 emitNeedRender();
1467}
1468
1469void QQuickGraphsBars::handleItemChanged(qsizetype rowIndex, qsizetype columnIndex)
1470{
1471 QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
1472
1473 bool newItem = true;
1474 QPoint candidate((int(rowIndex)), (int(columnIndex)));
1475 for (ChangeItem item : std::as_const(t&: m_changedItems)) {
1476 if (item.point == candidate && item.series == series) {
1477 newItem = false;
1478 setDataDirty(true);
1479 break;
1480 }
1481 }
1482
1483 if (newItem) {
1484 ChangeItem newItem = {.series: series, .point: candidate};
1485 m_changedItems.append(t: newItem);
1486 m_changeTracker.itemChanged = true;
1487
1488 if (series == m_selectedBarSeries && m_selectedBar == candidate)
1489 series->d_func()->markItemLabelDirty();
1490 if (series->isVisible())
1491 adjustAxisRanges();
1492 emitNeedRender();
1493 }
1494}
1495
1496void QQuickGraphsBars::handleDataRowLabelsChanged()
1497{
1498 if (m_axisZ) {
1499 // Grab a sublist equal to data window (no need to have more labels in axis)
1500 int min = int(m_axisZ->min());
1501 int count = int(m_axisZ->max()) - min + 1;
1502 QStringList subList;
1503 if (m_primarySeries && m_primarySeries->dataProxy())
1504 subList = m_primarySeries->rowLabels().mid(pos: min, len: count);
1505 static_cast<QCategory3DAxis *>(m_axisZ)->d_func()->setDataLabels(subList);
1506 }
1507
1508 if (repeaterZ()) {
1509 updateParameters();
1510 repeaterZ()->setModel(m_axisZ->labels().size());
1511 }
1512}
1513
1514void QQuickGraphsBars::handleDataColumnLabelsChanged()
1515{
1516 if (m_axisX) {
1517 // Grab a sublist equal to data window (no need to have more labels in axis)
1518 int min = int(m_axisX->min());
1519 int count = int(m_axisX->max()) - min + 1;
1520 QStringList subList;
1521 if (m_primarySeries && m_primarySeries->dataProxy())
1522 subList = m_primarySeries->columnLabels().mid(pos: min, len: count);
1523 static_cast<QCategory3DAxis *>(m_axisX)->d_func()->setDataLabels(subList);
1524 }
1525
1526 if (repeaterX()) {
1527 updateParameters();
1528 repeaterX()->setModel(m_axisX->labels().size());
1529 }
1530}
1531
1532void QQuickGraphsBars::handleRowColorsChanged()
1533{
1534 setSeriesVisualsDirty(true);
1535 emitNeedRender();
1536}
1537
1538void QQuickGraphsBars::handleValueColoringChanged()
1539{
1540 setSeriesVisualsDirty(true);
1541 emitNeedRender();
1542}
1543
1544void QQuickGraphsBars::handleLightingModeChanged()
1545{
1546 setSeriesVisualsDirty(true);
1547 emitNeedRender();
1548}
1549
1550void QQuickGraphsBars::connectSeries(QBar3DSeries *series)
1551{
1552 m_meshType = series->mesh();
1553 m_smooth = series->isMeshSmooth();
1554
1555 QObject::connect(sender: series,
1556 signal: &QBar3DSeries::meshChanged,
1557 context: this,
1558 slot: &QQuickGraphsBars::handleSeriesMeshChanged);
1559 QObject::connect(sender: series,
1560 signal: &QBar3DSeries::meshSmoothChanged,
1561 context: this,
1562 slot: &QQuickGraphsBars::handleMeshSmoothChanged);
1563 QObject::connect(sender: series->dataProxy(),
1564 signal: &QBarDataProxy::colCountChanged,
1565 context: this,
1566 slot: &QQuickGraphsBars::handleDataColumnLabelsChanged);
1567 QObject::connect(sender: series->dataProxy(),
1568 signal: &QBarDataProxy::rowCountChanged,
1569 context: this,
1570 slot: &QQuickGraphsBars::handleDataRowLabelsChanged);
1571 QObject::connect(sender: series,
1572 signal: &QBar3DSeries::rowColorsChanged,
1573 context: this,
1574 slot: &QQuickGraphsBars::handleRowColorsChanged);
1575 QObject::connect(sender: series,
1576 signal: &QBar3DSeries::valueColoringEnabledChanged,
1577 context: this,
1578 slot: &QQuickGraphsBars::handleValueColoringChanged);
1579}
1580
1581void QQuickGraphsBars::disconnectSeries(QBar3DSeries *series)
1582{
1583 QObject::disconnect(sender: series, signal: 0, receiver: this, member: 0);
1584}
1585
1586void QQuickGraphsBars::generateBars(QList<QBar3DSeries *> &barSeriesList)
1587{
1588 m_visibleSeriesCount = 0;
1589 for (const auto &barSeries : std::as_const(t&: barSeriesList)) {
1590 QQuick3DTexture *texture = createTexture();
1591 texture->setParent(this);
1592 auto gradient = barSeries->baseGradient();
1593 auto textureData = static_cast<QQuickGraphsTextureData *>(texture->textureData());
1594 textureData->createGradient(gradient);
1595
1596 bool visible = barSeries->isVisible();
1597
1598 QList<BarModel *> *barList = m_barModelsMap.value(key: barSeries);
1599
1600 if (!barList) {
1601 barList = new QList<BarModel *>;
1602 m_barModelsMap[barSeries] = barList;
1603 }
1604
1605 if (barList->isEmpty()) {
1606 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
1607 QBarDataProxy *dataProxy = barSeries->dataProxy();
1608 qsizetype dataRowIndex = m_minRow;
1609 qsizetype newRowSize = qMin(a: dataProxy->rowCount() - dataRowIndex, b: m_newRows);
1610
1611 for (int row = 0; row < newRowSize; ++row) {
1612 const QBarDataRow &dataRow = dataProxy->rowAt(rowIndex: dataRowIndex);
1613 if (!dataRow.isEmpty()) {
1614 qsizetype dataColIndex = m_minCol;
1615 qsizetype newColSize = qMin(a: dataRow.size() - dataColIndex, b: m_newCols);
1616 for (int col = 0; col < newColSize; ++col) {
1617 QBarDataItem &dataItem = const_cast<QBarDataItem &>(
1618 dataRow.at(i: dataColIndex));
1619 auto scene = graphNode();
1620 QQuick3DModel *model = createDataItem(scene, series: barSeries);
1621 model->setVisible(visible);
1622
1623 BarModel *barModel = new BarModel();
1624 barModel->model = model;
1625 barModel->barItem = &dataItem;
1626 barModel->coord = QPoint(int(dataRowIndex), col);
1627 barModel->texture = texture;
1628
1629 if (!barList->contains(t: barModel))
1630 barList->append(t: barModel);
1631
1632 ++dataColIndex;
1633 }
1634 ++dataRowIndex;
1635 }
1636 }
1637 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
1638 auto scene = graphNode();
1639 BarModel *barInstancing = new BarModel();
1640 barInstancing->texture = texture;
1641
1642 if (barInstancing->instancing == nullptr) {
1643 barInstancing->instancing = new BarInstancing;
1644 barInstancing->instancing->setParent(barSeries);
1645 barInstancing->selectionInstancing = new BarInstancing;
1646 barInstancing->selectionInstancing->setParent(barSeries);
1647 barInstancing->multiSelectionInstancing = new BarInstancing;
1648 barInstancing->multiSelectionInstancing->setParent(barSeries);
1649 }
1650
1651 if (barInstancing->model == nullptr) {
1652 barInstancing->model = createDataItem(scene, series: barSeries);
1653 barInstancing->model->setInstancing(barInstancing->instancing);
1654 barInstancing->model->setVisible(visible);
1655 barInstancing->model->setPickable(true);
1656 barInstancing->selectedModel = createDataItem(scene, series: barSeries);
1657 barInstancing->selectedModel->setInstancing(barInstancing->selectionInstancing);
1658 barInstancing->selectedModel->setVisible(visible);
1659 barInstancing->selectedModel->setPickable(true);
1660 barInstancing->multiSelectedModel = createDataItem(scene, series: barSeries);
1661 barInstancing->multiSelectedModel->setInstancing(
1662 barInstancing->multiSelectionInstancing);
1663 barInstancing->multiSelectedModel->setVisible(visible);
1664 barInstancing->multiSelectedModel->setPickable(true);
1665 }
1666
1667 if (!barList->contains(t: barInstancing)) {
1668 barList->append(t: barInstancing);
1669 qCDebug(lcGraphs3D) << "bar info:"
1670 << "\n model:" << barInstancing->model
1671 << "\n series:" << barSeries
1672 << "\n textureSize:" << barInstancing->texture->textureData()->size()
1673 << "\n rowCount: " << barSeries->dataProxy()->rowCount()
1674 << "\n columnCount:" << barSeries->dataProxy()->colCount()
1675 << "\n baseColor" << barSeries->baseColor()
1676 << "\n mesh:" << barSeries->mesh();
1677 }
1678 }
1679
1680 markSeriesVisualsDirty();
1681 }
1682
1683 if (barSeries->isVisible())
1684 m_visibleSeriesCount++;
1685 }
1686}
1687
1688QQuick3DModel *QQuickGraphsBars::createDataItem(QQuick3DNode *scene, QAbstract3DSeries *series)
1689{
1690 auto model = new QQuick3DModel();
1691 model->setParent(scene);
1692 model->setParentItem(scene);
1693 model->setObjectName(QStringLiteral("BarModel"));
1694 QString fileName = getMeshFileName();
1695 if (fileName.isEmpty())
1696 fileName = series->userDefinedMesh();
1697
1698 model->setSource(QUrl(fileName));
1699 return model;
1700}
1701
1702QString QQuickGraphsBars::getMeshFileName()
1703{
1704 QString fileName = {};
1705 switch (m_meshType) {
1706 case QAbstract3DSeries::Mesh::Sphere:
1707 fileName = QStringLiteral("defaultMeshes/sphereMesh");
1708 break;
1709 case QAbstract3DSeries::Mesh::Bar:
1710 case QAbstract3DSeries::Mesh::Cube:
1711 fileName = QStringLiteral("defaultMeshes/barMesh");
1712 break;
1713 case QAbstract3DSeries::Mesh::Pyramid:
1714 fileName = QStringLiteral("defaultMeshes/pyramidMesh");
1715 break;
1716 case QAbstract3DSeries::Mesh::Cone:
1717 fileName = QStringLiteral("defaultMeshes/coneMesh");
1718 break;
1719 case QAbstract3DSeries::Mesh::Cylinder:
1720 fileName = QStringLiteral("defaultMeshes/cylinderMesh");
1721 break;
1722 case QAbstract3DSeries::Mesh::BevelBar:
1723 case QAbstract3DSeries::Mesh::BevelCube:
1724 fileName = QStringLiteral("defaultMeshes/bevelBarMesh");
1725 break;
1726 case QAbstract3DSeries::Mesh::UserDefined:
1727 break;
1728 default:
1729 fileName = QStringLiteral("defaultMeshes/sphereMesh");
1730 }
1731
1732 fixMeshFileName(fileName, meshType: m_meshType);
1733
1734 return fileName;
1735}
1736
1737void QQuickGraphsBars::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh meshType)
1738{
1739 // Should it be smooth?
1740 if (m_smooth && meshType != QAbstract3DSeries::Mesh::Point
1741 && meshType != QAbstract3DSeries::Mesh::UserDefined) {
1742 fileName += QStringLiteral("Smooth");
1743 }
1744
1745 // Should it be filled?
1746 if (!theme()->isPlotAreaBackgroundVisible() && meshType != QAbstract3DSeries::Mesh::Sphere
1747 && meshType != QAbstract3DSeries::Mesh::Point
1748 && meshType != QAbstract3DSeries::Mesh::UserDefined) {
1749 fileName.append(QStringLiteral("Full"));
1750 }
1751}
1752
1753void QQuickGraphsBars::updateBarVisuality(QBar3DSeries *series, int visualIndex)
1754{
1755 QList<BarModel *> barList = *m_barModelsMap.value(key: series);
1756 for (int i = 0; i < barList.count(); i++) {
1757 barList.at(i)->visualIndex = visualIndex;
1758 barList.at(i)->model->setVisible(series->isVisible());
1759 if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
1760 barList.at(i: 0)->selectedModel->setVisible(false);
1761 barList.at(i: 0)->multiSelectedModel->setVisible(false);
1762 }
1763 }
1764
1765 m_changeTracker.selectedBarChanged = true;
1766 itemLabel()->setVisible(false);
1767}
1768
1769void QQuickGraphsBars::updateBarPositions(QBar3DSeries *series)
1770{
1771 QBarDataProxy *dataProxy = series->dataProxy();
1772
1773 m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
1774 m_seriesStep = 1.0f / float(m_visibleSeriesCount);
1775 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) * 0.5f)
1776 * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
1777
1778 if (m_keepSeriesUniform)
1779 m_seriesScaleZ = m_seriesScaleX;
1780 else
1781 m_seriesScaleZ = 1.0f;
1782
1783 m_zeroPosition = valueAxis()->positionAt(x: m_actualFloorLevel);
1784
1785 QList<BarModel *> barList = *m_barModelsMap.value(key: series);
1786
1787 qsizetype dataRowIndex = m_minRow;
1788 qsizetype newRowSize = qMin(a: dataProxy->rowCount() - dataRowIndex, b: m_newRows);
1789 qsizetype row = 0;
1790 qsizetype dataColIndex = m_minCol;
1791 qsizetype newColSize = qMin(a: dataProxy->colCount() - dataColIndex, b: m_newCols);
1792 qsizetype col = 0;
1793 for (int i = 0; i < barList.count(); i++) {
1794 float seriesPos = m_seriesStart + 0.5f
1795 + (m_seriesStep
1796 * (barList.at(i)->visualIndex
1797 - (barList.at(i)->visualIndex * m_cachedBarSeriesMargin.width())));
1798 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
1799 QBarDataItem *item = barList.at(i)->barItem;
1800 QQuick3DModel *model = barList.at(i)->model;
1801 float heightValue = updateBarHeightParameters(item);
1802 float angle = item->rotation();
1803
1804 if (angle) {
1805 model->setRotation(QQuaternion::fromAxisAndAngle(axis: upVector, angle)
1806 + series->meshRotation());
1807 } else {
1808 model->setRotation(QQuaternion() + series->meshRotation());
1809 }
1810
1811 if (heightValue < 0.f) {
1812 const QVector3D rot = model->eulerRotation();
1813 model->setEulerRotation(QVector3D(-180.f, rot.y(), rot.z()));
1814 }
1815
1816 float colPos = (col + seriesPos) * m_cachedBarSpacing.width();
1817 float xPos = (colPos - m_rowWidth) / m_scaleFactor;
1818 float rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1819 float zPos = (m_columnDepth - rowPos) / m_scaleFactor;
1820 float yPos;
1821
1822 if (heightValue < 0.f)
1823 yPos = heightValue - m_backgroundAdjustment - 0.015f;
1824 else
1825 yPos = heightValue - m_backgroundAdjustment + 0.015f;
1826
1827 barList.at(i)->heightValue = heightValue;
1828 model->setPosition(QVector3D(xPos, yPos, zPos));
1829
1830 if (heightValue == 0) {
1831 model->setScale(QVector3D(.0f, .0f, .0f));
1832 model->setPickable(false);
1833 } else {
1834 model->setScale(QVector3D(m_xScale * m_seriesScaleX,
1835 qAbs(t: heightValue),
1836 m_zScale * m_seriesScaleZ));
1837 model->setPickable(true);
1838 }
1839
1840 if (col < newColSize - 1) {
1841 ++col;
1842 } else {
1843 col = 0;
1844 if (row < newRowSize - 1)
1845 ++row;
1846 else
1847 row = 0;
1848 }
1849 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
1850 deleteBarItemHolders(instancing: barList.at(i)->instancing);
1851 QList<BarItemHolder *> positions;
1852 for (int row = 0; row < newRowSize; ++row) {
1853 const QBarDataRow &dataRow = dataProxy->rowAt(rowIndex: dataRowIndex);
1854 if (!dataRow.isEmpty()) {
1855 dataColIndex = m_minCol;
1856 newColSize = qMin(a: dataRow.size() - dataColIndex, b: m_newCols);
1857 for (int col = 0; col < newColSize; col++) {
1858 const QBarDataItem &item = dataRow.at(i: dataColIndex);
1859 float heightValue = updateBarHeightParameters(item: &item);
1860 BarItemHolder *bih = new BarItemHolder();
1861
1862 if (barList.at(i)->model->eulerRotation() != QVector3D()
1863 || series->meshRotation() != QQuaternion()) {
1864 const QQuaternion rotation =
1865 QQuaternion(barList.at(i)->model->eulerRotation().toVector4D())
1866 + series->meshRotation();
1867
1868 bih->rotation = rotation;
1869 if (heightValue < 0.f) {
1870 bih->rotation = QQuaternion(
1871 QVector3D(-180.f, rotation.y(), rotation.z()).toVector4D());
1872 }
1873 } else {
1874 bih->rotation = QQuaternion::fromEulerAngles(
1875 angles: QVector3D(.0f, item.rotation(), .0f));
1876 }
1877
1878 float colPos = (col + seriesPos) * m_cachedBarSpacing.width();
1879 float xPos = (colPos - m_rowWidth) / m_scaleFactor;
1880 float rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1881 float zPos = (m_columnDepth - rowPos) / m_scaleFactor;
1882 float yPos;
1883
1884 if (heightValue < 0.f)
1885 yPos = heightValue - m_backgroundAdjustment - 0.015f;
1886 else
1887 yPos = heightValue - m_backgroundAdjustment + 0.015f;
1888
1889 bih->position = {xPos, yPos, zPos};
1890 bih->coord = QPoint(row, col);
1891
1892 if (heightValue == 0) {
1893 bih->scale = {.0f, .0f, .0f};
1894 } else {
1895 bih->scale = {m_xScale * m_seriesScaleX,
1896 qAbs(t: heightValue),
1897 m_zScale * m_seriesScaleZ};
1898 }
1899
1900 bih->heightValue = heightValue;
1901 bih->selectedBar = false;
1902
1903 bool colorStyleIsUniform = (series->colorStyle()
1904 == QGraphsTheme::ColorStyle::Uniform);
1905 if (colorStyleIsUniform) {
1906 QList<QColor> rowColors = series->rowColors();
1907 if (rowColors.size() == 0) {
1908 bih->color = series->baseColor();
1909 } else {
1910 int rowColorIndex = bih->coord.x() % rowColors.size();
1911 bih->color = rowColors[rowColorIndex];
1912 }
1913 }
1914
1915 positions.push_back(t: bih);
1916 dataColIndex++;
1917 }
1918 }
1919 dataRowIndex++;
1920 }
1921 barList.at(i)->instancing->setDataArray(positions);
1922 }
1923 }
1924}
1925
1926float QQuickGraphsBars::updateBarHeightParameters(const QBarDataItem *item)
1927{
1928 float value = item->value();
1929 float heightValue = valueAxis()->positionAt(x: value);
1930
1931 if (m_noZeroInRange) {
1932 if (m_hasNegativeValues) {
1933 heightValue = -1.0f + heightValue;
1934 if (heightValue > 0.0f)
1935 heightValue = 0.0f;
1936 } else {
1937 if (heightValue < 0.0f)
1938 heightValue = 0.0f;
1939 }
1940 } else {
1941 heightValue -= m_zeroPosition;
1942 }
1943
1944 if (valueAxis()->reversed())
1945 heightValue = -heightValue;
1946
1947 return heightValue;
1948}
1949
1950void QQuickGraphsBars::updateBarVisuals(QBar3DSeries *series)
1951{
1952 QList<BarModel *> barList = *m_barModelsMap.value(key: series);
1953 bool useGradient = series->d_func()->isUsingGradient();
1954
1955 if (useGradient) {
1956 if (!m_hasHighlightTexture) {
1957 m_highlightTexture = createTexture();
1958 m_highlightTexture->setParent(this);
1959 m_multiHighlightTexture = createTexture();
1960 m_multiHighlightTexture->setParent(this);
1961 m_hasHighlightTexture = true;
1962 }
1963 auto highlightGradient = series->singleHighlightGradient();
1964 auto highlightTextureData = static_cast<QQuickGraphsTextureData *>(
1965 m_highlightTexture->textureData());
1966 highlightTextureData->createGradient(gradient: highlightGradient);
1967 auto multiHighlightGradient = series->multiHighlightGradient();
1968 auto multiHighlightTextureData = static_cast<QQuickGraphsTextureData *>(
1969 m_multiHighlightTexture->textureData());
1970 multiHighlightTextureData->createGradient(gradient: multiHighlightGradient);
1971 } else {
1972 if (m_hasHighlightTexture) {
1973 m_highlightTexture->deleteLater();
1974 m_multiHighlightTexture->deleteLater();
1975 m_hasHighlightTexture = false;
1976 }
1977 }
1978
1979 bool rangeGradient = (useGradient
1980 && series->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
1981 QColor baseColor = series->baseColor();
1982 QColor barColor;
1983 QLinearGradient gradient = series->baseGradient();
1984
1985 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
1986 // Release resources that might not have been deleted even though deleteLater had been set
1987
1988 if (m_customView)
1989 m_customView->window()->releaseResources();
1990 else
1991 window()->releaseResources();
1992
1993 for (int i = 0; i < barList.count(); i++) {
1994 QQuick3DModel *model = barList.at(i)->model;
1995 auto textureData = static_cast<QQuickGraphsTextureData *>(
1996 barList.at(i)->texture->textureData());
1997 textureData->createGradient(gradient: series->baseGradient());
1998 const bool transparency = textureData->hasTransparency();
1999 updateItemMaterial(item: model,
2000 useGradient,
2001 rangeGradient,
2002 QStringLiteral(":/materials/BarsMaterial"));
2003 if (useGradient) {
2004 updateMaterialProperties(item: model,
2005 isHighlight: false,
2006 isMultiHighlight: false,
2007 texture: barList.at(i)->texture,
2008 color: QColor(Qt::white),
2009 transparency);
2010 } else {
2011 QList<QColor> rowColors = series->rowColors();
2012 if (rowColors.size() == 0) {
2013 barColor = baseColor;
2014 } else {
2015 int rowColorIndex = barList.at(i)->coord.x() % rowColors.size();
2016 barColor = rowColors[rowColorIndex];
2017 }
2018 const bool transparency = barColor.alphaF() < 1.0;
2019 updateMaterialProperties(item: model,
2020 isHighlight: false,
2021 isMultiHighlight: false,
2022 texture: barList.at(i)->texture,
2023 color: barColor,
2024 transparency);
2025 }
2026 QQmlListReference materialsRef(model, "materials");
2027 auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(object: materialsRef.at(0));
2028 customMaterial->setProperty(name: "valueColoring", value: series->isValueColoringEnabled());
2029 customMaterial->setProperty(name: "heightValue", value: barList.at(i)->heightValue);
2030 customMaterial->setProperty(name: "shaded",
2031 value: series->lightingMode()
2032 == QAbstract3DSeries::LightingMode::Shaded);
2033 }
2034 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2035 for (int i = 0; i < barList.count(); i++) {
2036 auto textureData = static_cast<QQuickGraphsTextureData *>(
2037 barList.at(i)->texture->textureData());
2038 textureData->createGradient(gradient: series->baseGradient());
2039 barList.at(i)->instancing->setTransparency(textureData->hasTransparency());
2040 updateItemMaterial(item: barList.at(i)->model,
2041 useGradient,
2042 rangeGradient,
2043 QStringLiteral(":/materials/BarsMaterialInstancing"));
2044 if (useGradient) {
2045 updateMaterialProperties(item: barList.at(i)->model,
2046 isHighlight: false,
2047 isMultiHighlight: false,
2048 texture: barList.at(i)->texture,
2049 color: QColor(Qt::white),
2050 transparency: textureData->hasTransparency());
2051 } else {
2052 if (!barList.at(i)->instancing->dataArray().isEmpty()) {
2053 const bool transparency
2054 = barList.at(i)->instancing->dataArray().at(i: 0)->color.alphaF() < 1.0;
2055 updateMaterialProperties(item: barList.at(i)->model,
2056 isHighlight: false,
2057 isMultiHighlight: false,
2058 texture: barList.at(i)->texture,
2059 color: QColor(Qt::white),
2060 transparency);
2061 }
2062 }
2063 QQmlListReference materialsRef(barList.at(i)->model, "materials");
2064 auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(object: materialsRef.at(0));
2065 customMaterial->setProperty(name: "valueColoring", value: series->isValueColoringEnabled());
2066 customMaterial->setProperty(name: "rootScale", value: rootNode()->scale().y());
2067 customMaterial->setProperty(name: "shaded",
2068 value: series->lightingMode()
2069 == QAbstract3DSeries::LightingMode::Shaded);
2070 }
2071 }
2072}
2073
2074void QQuickGraphsBars::updateItemMaterial(QQuick3DModel *item,
2075 bool useGradient,
2076 bool rangeGradient,
2077 const QString &materialName)
2078{
2079 QQmlListReference materialsRef(item, "materials");
2080
2081 bool needNewMaterial = false;
2082 if (!materialsRef.size())
2083 needNewMaterial = true;
2084 else if (materialsRef.at(0)->objectName().contains(QStringLiteral("Instancing"))
2085 == materialName.contains(QStringLiteral("Instancing"))) {
2086 needNewMaterial = true;
2087 }
2088
2089 if (needNewMaterial) {
2090 materialsRef.clear();
2091 auto *material = createQmlCustomMaterial(fileName: materialName);
2092 material->setObjectName(materialName);
2093 material->setParent(item);
2094 materialsRef.append(material);
2095 }
2096 int colorStyle;
2097 if (!useGradient)
2098 colorStyle = 0; // style is uniform
2099 else if (!rangeGradient)
2100 colorStyle = 1; // style is objectGradient
2101 else
2102 colorStyle = 2; // style is rangeGradient
2103
2104 auto *material = qobject_cast<QQuick3DCustomMaterial *>(object: materialsRef.at(0));
2105 material->setProperty(name: "colorStyle", value: colorStyle);
2106}
2107
2108void QQuickGraphsBars::updateMaterialProperties(QQuick3DModel *item,
2109 const bool isHighlight,
2110 const bool isMultiHighlight,
2111 QQuick3DTexture *texture,
2112 QColor color,
2113 const bool transparency)
2114{
2115 QQmlListReference materialsRef(item, "materials");
2116 auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(object: materialsRef.at(0));
2117 if (!customMaterial)
2118 return;
2119 customMaterial->setProperty(name: "transparency", value: transparency);
2120 QVariant textureInputAsVariant = customMaterial->property(name: "custex");
2121 QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant
2122 .value<QQuick3DShaderUtilsTextureInput *>();
2123
2124 int colorStyle = customMaterial->property(name: "colorStyle").value<int>();
2125 if (colorStyle == 0) {
2126 customMaterial->setProperty(name: "uniformColor", value: color);
2127 } else {
2128 if (!isHighlight && !isMultiHighlight)
2129 textureInput->setTexture(texture);
2130 else
2131 textureInput->setTexture(isHighlight ? m_highlightTexture : m_multiHighlightTexture);
2132 customMaterial->setProperty(name: "isHighlight", value: isHighlight || isMultiHighlight);
2133 }
2134 customMaterial->setProperty(name: "specularBrightness", value: lightStrength() * 0.05);
2135 customMaterial->setProperty(name: "rootScale", value: rootNode()->scale().y());
2136}
2137
2138void QQuickGraphsBars::removeBarModels()
2139{
2140 for (const auto list : std::as_const(t&: m_barModelsMap)) {
2141 for (auto barModel : std::as_const(t&: *list)) {
2142 deleteBarModels(model: barModel->model);
2143 if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2144 deleteBarItemHolders(instancing: barModel->instancing);
2145 deleteBarItemHolders(instancing: barModel->selectionInstancing);
2146 deleteBarItemHolders(instancing: barModel->multiSelectionInstancing);
2147 deleteBarModels(model: barModel->selectedModel);
2148 deleteBarModels(model: barModel->multiSelectedModel);
2149 }
2150 delete barModel;
2151 }
2152 delete list;
2153 }
2154
2155 m_barModelsMap.clear();
2156 setSelectedBar(coord: m_selectedBar, series: m_selectedBarSeries, enterSlice: false);
2157}
2158
2159void QQuickGraphsBars::deleteBarModels(QQuick3DModel *model)
2160{
2161 if (model) {
2162 model->setPickable(false);
2163 model->setVisible(false);
2164 QQmlListReference materialsRef(model, "materials");
2165 if (materialsRef.size()) {
2166 auto material = materialsRef.at(0);
2167 delete material;
2168 }
2169 delete model;
2170 }
2171}
2172
2173void QQuickGraphsBars::deleteBarItemHolders(BarInstancing *instancing)
2174{
2175 if (instancing) {
2176 const QList<BarItemHolder *> barItemList = instancing->dataArray();
2177 for (const auto bih : barItemList)
2178 delete bih;
2179 instancing->clearDataArray();
2180 }
2181}
2182
2183QQuick3DTexture *QQuickGraphsBars::createTexture()
2184{
2185 QQuick3DTexture *texture = new QQuick3DTexture();
2186 texture->setParent(this);
2187 texture->setRotationUV(-90.0f);
2188 texture->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
2189 texture->setVerticalTiling(QQuick3DTexture::ClampToEdge);
2190 QQuickGraphsTextureData *textureData = new QQuickGraphsTextureData();
2191 textureData->setParent(texture);
2192 textureData->setParentItem(texture);
2193 texture->setTextureData(textureData);
2194
2195 return texture;
2196}
2197
2198bool QQuickGraphsBars::doPicking(QPointF position)
2199{
2200 if (!QQuickGraphsItem::doPicking(point: position))
2201 return false;
2202
2203 m_selectionDirty = true;
2204 QList<QQuick3DPickResult> pickResults;
2205 if (m_customView)
2206 pickResults = m_customView->pickAll(x: position.x(), y: position.y());
2207 else
2208 pickResults = pickAll(x: position.x(), y: position.y());
2209 QQuick3DModel *selectedModel = nullptr;
2210 QVector3D instancePos = {.0f, .0f, .0f};
2211 if (!selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::None)) {
2212 if (!pickResults.isEmpty()) {
2213 for (const auto &picked : std::as_const(t&: pickResults)) {
2214 if (const auto &hit = picked.objectHit()) {
2215 if (hit == backgroundBB() || hit == background()) {
2216 resetClickedStatus();
2217 continue;
2218 } else if (hit->objectName().contains(QStringLiteral("BarModel"))) {
2219 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
2220 selectedModel = hit;
2221 for (const auto barlist : std::as_const(t&: m_barModelsMap)) {
2222 for (const auto barModel : std::as_const(t&: *barlist)) {
2223 if (barModel->model == selectedModel) {
2224 setSelectedBar(coord: barModel->coord,
2225 series: m_barModelsMap.key(value: barlist),
2226 enterSlice: false);
2227 }
2228 }
2229 }
2230 break;
2231 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2232 BarInstancing *barIns = static_cast<BarInstancing *>(hit->instancing());
2233 // Prevents to select bars with a height of 0 which affect picking.
2234 if (!barIns->dataArray().isEmpty()
2235 && barIns->dataArray().at(i: picked.instanceIndex())->heightValue
2236 != 0) {
2237 selectedModel = hit;
2238 instancePos = selectedModel->instancing()->instancePosition(
2239 index: picked.instanceIndex());
2240 for (const auto barlist : std::as_const(t&: m_barModelsMap)) {
2241 for (const auto barModel : std::as_const(t&: *barlist)) {
2242 QList<BarItemHolder *> barItemList = barModel->instancing
2243 ->dataArray();
2244 for (const auto bih : std::as_const(t&: barItemList)) {
2245 if (bih->position == instancePos) {
2246 setSelectedBar(coord: bih->coord,
2247 series: m_barModelsMap.key(value: barlist),
2248 enterSlice: false);
2249 qCDebug(lcInput3D) << "picking result:"
2250 << "\n instance position:" << bih->position
2251 << "\n picked item column and row position:"
2252 << bih->coord
2253 << "\n picked item value:"
2254 << m_barModelsMap.key(value: barlist)->dataProxy()
2255 ->itemAt(position: bih->coord).value();
2256 if (isSliceEnabled())
2257 setSliceActivatedChanged(true);
2258 }
2259 }
2260 }
2261 }
2262 break;
2263 }
2264 }
2265 } else if (hit->objectName().contains(QStringLiteral("ElementAxis"))) {
2266 QPoint coord = invalidSelectionPosition();
2267 if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)
2268 && selectedAxis() == axisX()) {
2269 // Use row from previous selection in case of row + column mode
2270 int previousRow = qMax(a: 0, b: m_selectedBar.x());
2271 coord = QPoint(previousRow, selectedLabelIndex());
2272 } else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)
2273 && selectedAxis() == axisZ()) {
2274 // Use column from previous selection in case of row + column mode
2275 int previousCol = qMax(a: 0, b: m_selectedBar.y());
2276 coord = QPoint(selectedLabelIndex(), previousCol);
2277 }
2278 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
2279 if (it.key()->isVisible())
2280 setSelectedBar(coord, series: it.key(), enterSlice: false);
2281 }
2282 break;
2283 }
2284 }
2285 }
2286 } else {
2287 resetClickedStatus();
2288 }
2289 }
2290 return true;
2291}
2292
2293bool QQuickGraphsBars::doRayPicking(QVector3D origin, QVector3D direction)
2294{
2295 if (!QQuickGraphsItem::doRayPicking(origin, direction))
2296 return false;
2297
2298 m_selectionDirty = true;
2299 QList<QQuick3DPickResult> pickResults;
2300 if (m_customView)
2301 pickResults = m_customView->rayPickAll(origin, direction);
2302 else
2303 pickResults = rayPickAll(origin, direction);
2304 QQuick3DModel *selectedModel = nullptr;
2305 QVector3D instancePos = {.0f, .0f, .0f};
2306 if (!selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::None)) {
2307 if (!pickResults.isEmpty()) {
2308 for (const auto &picked : std::as_const(t&: pickResults)) {
2309 if (const auto &hit = picked.objectHit()) {
2310 if (hit == backgroundBB() || hit == background()) {
2311 resetClickedStatus();
2312 continue;
2313 } else if (hit->objectName().contains(QStringLiteral("BarModel"))) {
2314 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
2315 selectedModel = hit;
2316 for (const auto barlist : std::as_const(t&: m_barModelsMap)) {
2317 for (const auto barModel : std::as_const(t&: *barlist)) {
2318 if (barModel->model == selectedModel) {
2319 setSelectedBar(coord: barModel->coord,
2320 series: m_barModelsMap.key(value: barlist),
2321 enterSlice: false);
2322 }
2323 }
2324 }
2325 break;
2326 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2327 BarInstancing *barIns = static_cast<BarInstancing *>(hit->instancing());
2328 // Prevents to select bars with a height of 0 which affect picking.
2329 if (!barIns->dataArray().isEmpty()
2330 && barIns->dataArray().at(i: picked.instanceIndex())->heightValue
2331 != 0) {
2332 selectedModel = hit;
2333 instancePos = selectedModel->instancing()->instancePosition(
2334 index: picked.instanceIndex());
2335 for (const auto barlist : std::as_const(t&: m_barModelsMap)) {
2336 for (const auto barModel : std::as_const(t&: *barlist)) {
2337 QList<BarItemHolder *> barItemList = barModel->instancing
2338 ->dataArray();
2339 for (const auto bih : std::as_const(t&: barItemList)) {
2340 if (bih->position == instancePos) {
2341 setSelectedBar(coord: bih->coord,
2342 series: m_barModelsMap.key(value: barlist),
2343 enterSlice: false);
2344 qCDebug(lcInput3D) << "picking result:"
2345 << "\n instance position:" << bih->position
2346 << "\n picked item column and row position:"
2347 << bih->coord
2348 << "\n picked item value:"
2349 << m_barModelsMap.key(value: barlist)->dataProxy()
2350 ->itemAt(position: bih->coord).value();
2351 if (isSliceEnabled())
2352 setSliceActivatedChanged(true);
2353 }
2354 }
2355 }
2356 }
2357 break;
2358 }
2359 }
2360 } else if (hit->objectName().contains(QStringLiteral("ElementAxis"))) {
2361 QPoint coord = invalidSelectionPosition();
2362 if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column)
2363 && selectedAxis() == axisX()) {
2364 // Use row from previous selection in case of row + column mode
2365 int previousRow = qMax(a: 0, b: m_selectedBar.x());
2366 coord = QPoint(previousRow, selectedLabelIndex());
2367 } else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)
2368 && selectedAxis() == axisZ()) {
2369 // Use column from previous selection in case of row + column mode
2370 int previousCol = qMax(a: 0, b: m_selectedBar.y());
2371 coord = QPoint(selectedLabelIndex(), previousCol);
2372 }
2373 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
2374 if (it.key()->isVisible())
2375 setSelectedBar(coord, series: it.key(), enterSlice: false);
2376 }
2377 break;
2378 }
2379 }
2380 }
2381 } else {
2382 resetClickedStatus();
2383 }
2384 }
2385 return true;
2386}
2387
2388QAbstract3DAxis *QQuickGraphsBars::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)
2389{
2390 QAbstract3DAxis *defaultAxis = 0;
2391
2392 if (orientation == QAbstract3DAxis::AxisOrientation::Y)
2393 defaultAxis = createDefaultValueAxis();
2394 else
2395 defaultAxis = createDefaultCategoryAxis();
2396
2397 return defaultAxis;
2398}
2399
2400void QQuickGraphsBars::adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series)
2401{
2402 const QBarDataProxy *proxy = 0;
2403 if (series)
2404 proxy = series->dataProxy();
2405
2406 if (!proxy)
2407 pos = invalidSelectionPosition();
2408
2409 if (pos != invalidSelectionPosition()) {
2410 qsizetype maxRow = proxy->rowCount() - 1;
2411 qsizetype maxCol = (pos.x() <= maxRow && pos.x() >= 0 && !proxy->rowAt(rowIndex: pos.x()).isEmpty())
2412 ? proxy->rowAt(rowIndex: pos.x()).size() - 1
2413 : -1;
2414
2415 if (pos.x() < 0 || pos.x() > maxRow || pos.y() < 0 || pos.y() > maxCol)
2416 pos = invalidSelectionPosition();
2417 }
2418}
2419
2420void QQuickGraphsBars::setSelectedBar(QPoint coord, QBar3DSeries *series, bool enterSlice)
2421{
2422 // If the selection targets non-existent bar, clear selection instead.
2423 QPoint pos = coord;
2424
2425 // Series may already have been removed, so check it before setting the
2426 // selection.
2427 if (!m_seriesList.contains(t: series))
2428 series = nullptr;
2429
2430 adjustSelectionPosition(pos, series);
2431
2432 if (series && selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Slice)) {
2433 // If the selected bar is outside data window, or there is no visible
2434 // selected bar, disable slicing.
2435 if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max() || pos.y() < m_axisX->min()
2436 || pos.y() > m_axisX->max() || !series->isVisible()) {
2437 scene()->setSlicingActive(false);
2438 } else if (enterSlice) {
2439 scene()->setSlicingActive(true);
2440 }
2441 emitNeedRender();
2442 }
2443
2444 if (pos != m_selectedBar || series != m_selectedBarSeries) {
2445 bool seriesChanged = (series != m_selectedBarSeries);
2446 m_selectedBar = pos;
2447 m_selectedBarSeries = series;
2448 m_changeTracker.selectedBarChanged = true;
2449 checkSliceEnabled();
2450
2451 // Clear selection from other series and finally set new selection to the
2452 // specified series
2453 for (QAbstract3DSeries *otherSeries : std::as_const(t&: m_seriesList)) {
2454 QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(otherSeries);
2455 if (barSeries != m_selectedBarSeries)
2456 barSeries->d_func()->setSelectedBar(invalidSelectionPosition());
2457 }
2458 if (m_selectedBarSeries)
2459 m_selectedBarSeries->d_func()->setSelectedBar(m_selectedBar);
2460
2461 if (seriesChanged)
2462 emit selectedSeriesChanged(series: m_selectedBarSeries);
2463
2464 if (pos == invalidSelectionPosition())
2465 resetClickedStatus();
2466
2467 setSeriesVisualsDirty(true);
2468 emitNeedRender();
2469 }
2470}
2471
2472void QQuickGraphsBars::updateSelectedBar()
2473{
2474 for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
2475 if (m_selectedBarSeries && it.key()->isVisible()) {
2476 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
2477 for (auto barList : std::as_const(t&: *it.value())) {
2478 QQuickGraphsBars::SelectionType selectionType = isSelected(row: barList->coord.x(),
2479 bar: barList->coord.y(),
2480 series: it.key());
2481 switch (selectionType) {
2482 case QQuickGraphsBars::SelectionItem: {
2483 updateMaterialProperties(item: barList->model,
2484 isHighlight: true,
2485 isMultiHighlight: false,
2486 texture: barList->texture,
2487 color: it.key()->singleHighlightColor());
2488
2489 m_selectedBarPos = barList->model->position();
2490 QString label = (m_selectedBarSeries->d_func()->itemLabel());
2491
2492 if (barList->heightValue >= 0.0f) {
2493 m_selectedBarPos.setY(m_selectedBarPos.y() + barList->heightValue
2494 + 0.2f);
2495 } else {
2496 m_selectedBarPos.setY(m_selectedBarPos.y() + barList->heightValue
2497 - 0.2f);
2498 }
2499
2500 updateItemLabel(position: m_selectedBarPos);
2501 itemLabel()->setVisible(theme()->labelsVisible());
2502 itemLabel()->setProperty(name: "labelText", value: label);
2503 if (!label.compare(s: QString(hiddenLabelTag)))
2504 itemLabel()->setVisible(false);
2505 if (isSliceEnabled())
2506 updateSliceItemLabel(label, position: m_selectedBarPos);
2507 break;
2508 }
2509 case QQuickGraphsBars::SelectionRow:
2510 case QQuickGraphsBars::SelectionColumn: {
2511 updateMaterialProperties(item: barList->model,
2512 isHighlight: false,
2513 isMultiHighlight: true,
2514 texture: barList->texture,
2515 color: it.key()->multiHighlightColor());
2516 break;
2517 }
2518 default:
2519 break;
2520 }
2521 }
2522 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2523 QList<BarModel *> barList = *m_barModelsMap.value(key: it.key());
2524 deleteBarItemHolders(instancing: barList.at(i: 0)->selectionInstancing);
2525 deleteBarItemHolders(instancing: barList.at(i: 0)->multiSelectionInstancing);
2526 createBarItemHolders(series: it.key(), barList, slice: false);
2527 }
2528 }
2529 }
2530}
2531
2532QQuickGraphsBars::SelectionType QQuickGraphsBars::isSelected(int row, int bar, QBar3DSeries *series)
2533{
2534 QQuickGraphsBars::SelectionType isSelectedType = QQuickGraphsBars::SelectionNone;
2535 if ((selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::MultiSeries) && m_selectedBarSeries)
2536 || series == m_selectedBarSeries) {
2537 if (row == m_selectedBar.x() && bar == m_selectedBar.y()
2538 && (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Item))) {
2539 isSelectedType = QQuickGraphsBars::SelectionItem;
2540 } else if (row == m_selectedBar.x()
2541 && (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row))) {
2542 isSelectedType = QQuickGraphsBars::SelectionRow;
2543 } else if (bar == m_selectedBar.y()
2544 && (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column))) {
2545 isSelectedType = QQuickGraphsBars::SelectionColumn;
2546 }
2547 }
2548
2549 return isSelectedType;
2550}
2551
2552void QQuickGraphsBars::updateSliceItemLabel(const QString &label, QVector3D position)
2553{
2554 QQuickGraphsItem::updateSliceItemLabel(label, position);
2555
2556 QFontMetrics fm(theme()->labelFont());
2557 float textPadding = theme()->labelFont().pointSizeF() * .7f;
2558 float labelHeight = fm.height() + textPadding;
2559 float labelWidth = fm.horizontalAdvance(label) + textPadding;
2560 sliceItemLabel()->setProperty(name: "labelWidth", value: labelWidth);
2561 sliceItemLabel()->setProperty(name: "labelHeight", value: labelHeight);
2562 QVector3D slicePos = position;
2563 if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column))
2564 slicePos.setX(slicePos.z());
2565 else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row))
2566 slicePos.setX(slicePos.x());
2567 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axisY());
2568 if (valueAxis->reversed())
2569 slicePos.setY(slicePos.y() - (textPadding * .06f));
2570 else
2571 slicePos.setY(slicePos.y() + (textPadding * .06f));
2572 slicePos.setZ(.1f);
2573 sliceItemLabel()->setPosition(slicePos);
2574 sliceItemLabel()->setProperty(name: "labelText", value: label);
2575 if (!label.compare(s: QString(hiddenLabelTag)))
2576 sliceItemLabel()->setVisible(false);
2577 sliceItemLabel()->setEulerRotation(QVector3D(0.0f, 0.0f, 90.0f));
2578 sliceItemLabel()->setVisible(theme()->labelsVisible());
2579}
2580
2581void QQuickGraphsBars::resetClickedStatus()
2582{
2583 m_selectedBarPos = QVector3D(0.0f, 0.0f, 0.0f);
2584 m_selectedBar = QQuickGraphsBars::invalidSelectionPosition();
2585 m_selectedBarSeries = 0;
2586 clearSelection();
2587
2588 if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2589 for (const auto barList : std::as_const(t&: m_barModelsMap)) {
2590 QList<BarItemHolder *> barItemList = barList->at(i: 0)->instancing->dataArray();
2591 for (auto bih : std::as_const(t&: barItemList))
2592 bih->selectedBar = false;
2593 }
2594 }
2595
2596 if (sliceView() && sliceView()->isVisible()) {
2597 setSliceActivatedChanged(true);
2598 m_selectionDirty = true;
2599 }
2600 setSeriesVisualsDirty(true);
2601}
2602
2603void QQuickGraphsBars::createSliceView()
2604{
2605 setSliceOrthoProjection(false);
2606 QQuickGraphsItem::createSliceView();
2607
2608 QList<QBar3DSeries *> barSeries = barSeriesList();
2609 for (const auto &barSeries : std::as_const(t&: barSeries)) {
2610 QList<BarModel *> &slicedBarList = m_slicedBarModels[barSeries];
2611
2612 if (slicedBarList.isEmpty()) {
2613 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
2614 qsizetype dataRowIndex = m_minRow;
2615 qsizetype newRowSize = qMin(a: barSeries->dataProxy()->rowCount() - dataRowIndex,
2616 b: m_newRows);
2617 qsizetype newColSize = 0;
2618 if (newRowSize) {
2619 const QBarDataRow *dataRow = &barSeries->dataProxy()->rowAt(rowIndex: dataRowIndex);
2620 if (dataRow) {
2621 qsizetype dataColIndex = m_minCol;
2622 newColSize = qMin(a: dataRow->size() - dataColIndex, b: m_newCols);
2623 }
2624 }
2625 qsizetype slicedBarListSize = 0;
2626
2627 if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row))
2628 slicedBarListSize = newColSize;
2629 else if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Column))
2630 slicedBarListSize = newRowSize;
2631
2632 for (int ind = 0; ind < slicedBarListSize; ++ind) {
2633 QQuick3DModel *model = createDataItem(scene: sliceView()->scene(), series: barSeries);
2634 model->setVisible(false);
2635 BarModel *barModel = new BarModel();
2636 barModel->model = model;
2637
2638 if (!slicedBarList.contains(t: barModel))
2639 slicedBarList.append(t: barModel);
2640 }
2641 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2642 BarModel *barInstancing = new BarModel();
2643
2644 if (barInstancing->selectionInstancing == nullptr) {
2645 barInstancing->selectionInstancing = new BarInstancing;
2646 barInstancing->selectionInstancing->setParent(barSeries);
2647 barInstancing->multiSelectionInstancing = new BarInstancing;
2648 barInstancing->multiSelectionInstancing->setParent(barSeries);
2649 }
2650
2651 if (barInstancing->selectedModel == nullptr) {
2652 barInstancing->selectedModel = createDataItem(scene: sliceView()->scene(), series: barSeries);
2653 barInstancing->selectedModel->setInstancing(barInstancing->selectionInstancing);
2654 barInstancing->selectedModel->setVisible(false);
2655 barInstancing->multiSelectedModel = createDataItem(scene: sliceView()->scene(),
2656 series: barSeries);
2657 barInstancing->multiSelectedModel->setInstancing(
2658 barInstancing->multiSelectionInstancing);
2659 barInstancing->multiSelectedModel->setVisible(false);
2660 }
2661
2662 if (!slicedBarList.contains(t: barInstancing))
2663 slicedBarList.append(t: barInstancing);
2664 }
2665 }
2666 }
2667}
2668
2669void QQuickGraphsBars::toggleSliceGraph()
2670{
2671 if (m_selectionDirty)
2672 QQuickGraphsItem::toggleSliceGraph();
2673
2674 if (sliceView() && !sliceView()->isVisible()) {
2675 removeSlicedBarModels();
2676 m_changeTracker.selectedBarChanged = false;
2677 return;
2678 }
2679
2680 qsizetype index = 0;
2681 bool rowMode = selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row);
2682 for (auto it = m_slicedBarModels.begin(); it != m_slicedBarModels.end(); it++) {
2683 bool useGradient = it.key()->d_func()->isUsingGradient();
2684 bool rangeGradient = (useGradient
2685 && it.key()->d_func()->m_colorStyle
2686 == QGraphsTheme::ColorStyle::RangeGradient);
2687 QList<BarModel *> barList = *m_barModelsMap.value(key: it.key());
2688 QList<BarModel *> sliceList = it.value();
2689 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
2690 for (int ind = 0; ind < it.value().count(); ++ind) {
2691 if (rowMode)
2692 index = (m_selectedBar.x() * it.key()->dataProxy()->colCount()) + ind;
2693 else
2694 index = m_selectedBar.y() + (ind * it.key()->dataProxy()->colCount());
2695 bool visible = ((m_selectedBarSeries == it.key()
2696 || selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::MultiSeries))
2697 && it.key()->isVisible());
2698 if (index < barList.size() && m_selectedBar != invalidSelectionPosition()) {
2699 BarModel *sliceBarModel = sliceList.at(i: ind);
2700 BarModel *barModel = barList.at(i: index);
2701
2702 sliceBarModel->model->setVisible(visible);
2703 if (rowMode) {
2704 sliceBarModel->model->setPosition(
2705 QVector3D(barModel->model->x(), barModel->model->y(), 0.0f));
2706 } else {
2707 sliceBarModel->model->setX(barModel->model->z()
2708 - (barList.at(i: 0)->visualIndex * .1f));
2709 sliceBarModel->model->setY(barModel->model->y());
2710 sliceBarModel->model->setZ(0.0f);
2711 }
2712 sliceBarModel->model->setScale(barModel->model->scale());
2713
2714 updateItemMaterial(item: sliceBarModel->model,
2715 useGradient,
2716 rangeGradient,
2717 QStringLiteral(":/materials/BarsMaterial"));
2718
2719 if (barModel->coord == m_selectedBar
2720 && selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Item)) {
2721 updateMaterialProperties(item: sliceBarModel->model,
2722 isHighlight: true,
2723 isMultiHighlight: false,
2724 texture: barList.at(i: index)->texture,
2725 color: it.key()->singleHighlightColor());
2726 updateSliceItemLabel(label: m_selectedBarSeries->itemLabel(), position: m_selectedBarPos);
2727 } else {
2728 updateMaterialProperties(item: sliceBarModel->model,
2729 isHighlight: false,
2730 isMultiHighlight: false,
2731 texture: barList.at(i: index)->texture,
2732 color: it.key()->baseColor());
2733 }
2734 } else {
2735 setSliceActivatedChanged(true);
2736 QQuickGraphsItem::toggleSliceGraph();
2737 setSliceEnabled(false);
2738 return;
2739 }
2740 }
2741 setSliceActivatedChanged(false);
2742 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2743 deleteBarItemHolders(instancing: sliceList.at(i: 0)->selectionInstancing);
2744 deleteBarItemHolders(instancing: sliceList.at(i: 0)->multiSelectionInstancing);
2745 createBarItemHolders(series: it.key(), barList, slice: true);
2746 setSliceActivatedChanged(false);
2747 }
2748 }
2749}
2750
2751void QQuickGraphsBars::handleLabelCountChanged(QQuick3DRepeater *repeater, QColor axisLabelColor)
2752{
2753 QQuickGraphsItem::handleLabelCountChanged(repeater, axisLabelColor);
2754
2755 if (repeater == repeaterX())
2756 handleDataColumnLabelsChanged();
2757 if (repeater == repeaterZ())
2758 handleDataRowLabelsChanged();
2759}
2760
2761void QQuickGraphsBars::removeSlicedBarModels()
2762{
2763 for (auto &list : std::as_const(t&: m_slicedBarModels)) {
2764 for (auto barModel : list) {
2765 if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
2766 deleteBarModels(model: barModel->model);
2767 } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
2768 deleteBarItemHolders(instancing: barModel->selectionInstancing);
2769 deleteBarItemHolders(instancing: barModel->multiSelectionInstancing);
2770 deleteBarModels(model: barModel->selectedModel);
2771 deleteBarModels(model: barModel->multiSelectedModel);
2772 }
2773 delete barModel;
2774 }
2775 }
2776
2777 m_slicedBarModels.clear();
2778}
2779
2780void QQuickGraphsBars::createBarItemHolders(QBar3DSeries *series,
2781 QList<BarModel *> barList,
2782 bool slice)
2783{
2784 bool useGradient = series->d_func()->isUsingGradient();
2785 bool rangeGradient = (useGradient
2786 && series->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
2787 bool visible = ((m_selectedBarSeries == series
2788 || selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::MultiSeries))
2789 && series->isVisible());
2790 QList<BarItemHolder *> barItemList;
2791 QList<BarItemHolder *> multiBarItemList;
2792 QQuick3DModel *selectedModel = nullptr;
2793 QQuick3DModel *multiSelectedModel = nullptr;
2794 QList<BarItemHolder *> selectedItem;
2795 QList<BarItemHolder *> multiSelectedItems;
2796 BarInstancing *instancing;
2797 BarInstancing *multiInstancing;
2798
2799 if (slice) {
2800 QList<BarModel *> sliceBarList = m_slicedBarModels.value(key: series);
2801 barItemList = barList.at(i: 0)->selectionInstancing->dataArray();
2802 multiBarItemList = barList.at(i: 0)->multiSelectionInstancing->dataArray();
2803 selectedModel = sliceBarList.at(i: 0)->selectedModel;
2804 multiSelectedModel = sliceBarList.at(i: 0)->multiSelectedModel;
2805 instancing = sliceBarList.at(i: 0)->selectionInstancing;
2806 multiInstancing = sliceBarList.at(i: 0)->multiSelectionInstancing;
2807 } else {
2808 barItemList = barList.at(i: 0)->instancing->dataArray();
2809 selectedModel = barList.at(i: 0)->selectedModel;
2810 multiSelectedModel = barList.at(i: 0)->multiSelectedModel;
2811 instancing = barList.at(i: 0)->selectionInstancing;
2812 multiInstancing = barList.at(i: 0)->multiSelectionInstancing;
2813 }
2814
2815 for (const auto bih : std::as_const(t&: barItemList)) {
2816 QQuickGraphsBars::SelectionType selectionType = isSelected(row: bih->coord.x(),
2817 bar: bih->coord.y(),
2818 series);
2819 switch (selectionType) {
2820 case QQuickGraphsBars::SelectionItem: {
2821 updateItemMaterial(item: selectedModel,
2822 useGradient,
2823 rangeGradient,
2824 QStringLiteral(":/materials/BarsMaterialInstancing"));
2825 updateMaterialProperties(item: selectedModel,
2826 isHighlight: true,
2827 isMultiHighlight: false,
2828 texture: barList.at(i: 0)->texture,
2829 color: QColor(Qt::white));
2830 if (!slice)
2831 bih->selectedBar = true;
2832 selectedModel->setVisible(visible);
2833 BarItemHolder *selectedBih = new BarItemHolder();
2834 selectedBih->selectedBar = false;
2835 selectedBih->color = series->singleHighlightColor();
2836 selectedBih->coord = bih->coord;
2837 selectedBih->rotation = bih->rotation;
2838 selectedBih->heightValue = bih->heightValue;
2839 selectedBih->position = bih->position;
2840 selectedBih->scale = bih->scale;
2841
2842 QString label = m_selectedBarSeries->itemLabel();
2843 if (slice) {
2844 if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)) {
2845 selectedBih->position.setZ(.0f);
2846 } else {
2847 selectedBih->position.setX(selectedBih->position.z()
2848 - (barList.at(i: 0)->visualIndex * .1f));
2849 selectedBih->position.setZ(.0f);
2850 }
2851 updateSliceItemLabel(label, position: m_selectedBarPos);
2852 }
2853
2854 selectedItem.push_back(t: selectedBih);
2855 instancing->setDataArray(selectedItem);
2856
2857 m_selectedBarPos = bih->position;
2858
2859 if (bih->heightValue >= 0.0f)
2860 m_selectedBarPos.setY(m_selectedBarPos.y() + bih->heightValue + 0.2f);
2861 else
2862 m_selectedBarPos.setY(m_selectedBarPos.y() + bih->heightValue - 0.2f);
2863
2864 updateItemLabel(position: m_selectedBarPos);
2865 itemLabel()->setVisible(theme()->labelsVisible());
2866 itemLabel()->setProperty(name: "labelText", value: label);
2867 if (!label.compare(s: QString(hiddenLabelTag)))
2868 itemLabel()->setVisible(false);
2869 if (isSliceEnabled())
2870 updateSliceItemLabel(label, position: m_selectedBarPos);
2871 break;
2872 }
2873 case QQuickGraphsBars::SelectionRow:
2874 case QQuickGraphsBars::SelectionColumn: {
2875 updateItemMaterial(item: multiSelectedModel,
2876 useGradient,
2877 rangeGradient,
2878 QStringLiteral(":/materials/BarsMaterialInstancing"));
2879 updateMaterialProperties(item: multiSelectedModel,
2880 isHighlight: false,
2881 isMultiHighlight: true,
2882 texture: barList.at(i: 0)->texture,
2883 color: QColor(Qt::white));
2884 if (!slice)
2885 bih->selectedBar = true;
2886 multiSelectedModel->setVisible(visible);
2887 BarItemHolder *selectedBih = new BarItemHolder();
2888 selectedBih->selectedBar = false;
2889 selectedBih->color = series->multiHighlightColor();
2890 selectedBih->coord = bih->coord;
2891 selectedBih->rotation = bih->rotation;
2892 selectedBih->heightValue = bih->heightValue;
2893 selectedBih->position = bih->position;
2894 selectedBih->scale = bih->scale;
2895
2896 multiSelectedItems.push_back(t: selectedBih);
2897 multiInstancing->setDataArray(multiSelectedItems);
2898 break;
2899 }
2900 default:
2901 break;
2902 }
2903 }
2904
2905 if (slice) {
2906 for (const auto bih : std::as_const(t&: multiBarItemList)) {
2907 updateItemMaterial(item: multiSelectedModel,
2908 useGradient,
2909 rangeGradient,
2910 QStringLiteral(":/materials/BarsMaterialInstancing"));
2911 updateMaterialProperties(item: multiSelectedModel,
2912 isHighlight: false,
2913 isMultiHighlight: false,
2914 texture: barList.at(i: 0)->texture,
2915 color: QColor(Qt::white));
2916
2917 multiSelectedModel->setVisible(visible);
2918 BarItemHolder *selectedBih = new BarItemHolder();
2919 selectedBih->selectedBar = false;
2920 selectedBih->color = series->baseColor();
2921 selectedBih->coord = bih->coord;
2922 selectedBih->rotation = bih->rotation;
2923 selectedBih->heightValue = bih->heightValue;
2924 selectedBih->position = bih->position;
2925 selectedBih->scale = bih->scale;
2926
2927 if (selectionMode().testFlag(flag: QtGraphs3D::SelectionFlag::Row)) {
2928 selectedBih->position.setZ(.0f);
2929 } else {
2930 selectedBih->position.setX(selectedBih->position.z()
2931 - (barList.at(i: 0)->visualIndex * .1f));
2932 selectedBih->position.setZ(.0f);
2933 }
2934
2935 multiSelectedItems.push_back(t: selectedBih);
2936 multiInstancing->setDataArray(multiSelectedItems);
2937 }
2938 }
2939}
2940
2941void QQuickGraphsBars::updateSelectionMode(QtGraphs3D::SelectionFlags mode)
2942{
2943 checkSliceEnabled();
2944 if (!sliceView())
2945 createSliceView();
2946
2947 bool validSlice = mode.testFlag(flag: QtGraphs3D::SelectionFlag::Slice) && m_selectedBarSeries;
2948 if (sliceView() && sliceView()->isVisible()) {
2949 if (validSlice) {
2950 removeSlicedBarModels();
2951 createSliceView();
2952 toggleSliceGraph();
2953 } else {
2954 m_selectionDirty = true;
2955 setSliceActivatedChanged(true);
2956 }
2957 } else if (validSlice) {
2958 m_selectionDirty = true;
2959 setSliceActivatedChanged(true);
2960 }
2961
2962 setSeriesVisualsDirty(true);
2963 itemLabel()->setVisible(false);
2964 if (sliceView() && !mode.testFlag(flag: QtGraphs3D::SelectionFlag::Item))
2965 sliceItemLabel()->setVisible(false);
2966}
2967
2968void QQuickGraphsBars::updateBarSpecs(float thicknessRatio, QSizeF spacing, bool relative)
2969{
2970 // Convert ratio to QSizeF, as we need it in that format for autoscaling
2971 // calculations
2972 m_cachedBarThickness.setWidth(1.0);
2973 m_cachedBarThickness.setHeight(1.0f / thicknessRatio);
2974
2975 if (relative) {
2976 m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2) * (spacing.width() + 1.0f));
2977 m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2)
2978 * (spacing.height() + 1.0f));
2979 } else {
2980 m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2;
2981 }
2982
2983 m_changeTracker.axisRangeChanged = true;
2984 m_changeTracker.selectedBarChanged = true;
2985
2986 // Calculate here and at setting sample space
2987 calculateSceneScalingFactors();
2988}
2989
2990void QQuickGraphsBars::updateBarSeriesMargin(QSizeF margin)
2991{
2992 m_cachedBarSeriesMargin = margin;
2993 calculateSeriesStartPosition();
2994 calculateSceneScalingFactors();
2995 setSeriesVisualsDirty(true);
2996}
2997

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