Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include "graphs3d/utils/qgraphs3dlogging_p.h"
++ #include "q3dscene.h"
++ #include "qbar3dseries.h"
++ #include "qbar3dseries_p.h"
++ #include "qbardataproxy_p.h"
++ #include "qcategory3daxis_p.h"
++ #include "qgraphsinputhandler_p.h"
++ #include "qgraphstheme.h"
++ #include "qquickgraphsbars_p.h"
++ #include "qquickgraphstexturedata_p.h"
++ #include "qvalue3daxis_p.h"
++ #include "qgraphs3dlogging_p.h"
++
++ #include <QColor>
++ #include <QtQuick3D/private/qquick3dcustommaterial_p.h>
++ #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
++ #include <QtQuick3D/private/qquick3drepeater_p.h>
++ #include <QtQuick/qquickitemgrabresult.h>
++
++ #include <QtGui/qquaternion.h>
++
++ QT_BEGIN_NAMESPACE
++
++ #include <qtgraphs_tracepoints_p.h>
++
++ Q_TRACE_PREFIX(qtgraphs,
++ "QT_BEGIN_NAMESPACE" \
++ "#include <graphs3d/utils/qgraphs3dnamespace.h>" \
++ "class qquickgraphsbars;" \
++ "QT_END_NAMESPACE"
++ )
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsSynch_entry_beforeGraphsItem);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsSynch_exit_beforeGraphsItem);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsSynch_entry_afterGraphsItem);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsSynch_exit_afterGraphsItem);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBarsUpdateGraph_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBarsUpdateGraph_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBars_entry, int seriesCount);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBars_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBarsLegacy_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBarsLegacy_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBarsDefault_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsGenerateBarsDefault_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsUpdateBarPositionsLegacy_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsUpdateBarPositionsLegacy_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsDoPicking_entry, float posX, float posY);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsDoPicking_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsDoRayPicking_entry, float originX, float originY,
++ float originZ, float directionX, float directionY, float directionZ);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsDoRayPicking_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsCreateSliceView_entry);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsCreateSliceView_exit);
++
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsCreateOffscreenSliceView_entry, int requestedIndex, int sliceType);
++ Q_TRACE_POINT(qtgraphs, QGraphs3DBarsCreateOffscreenSliceView_exit);
++
++ /*!
++ * \qmltype Bars3D
++ * \inherits GraphsItem3D
++ * \inqmlmodule QtGraphs
++ * \ingroup graphs_qml_3D
++ * \brief 3D bar graph.
++ *
++ * This type enables developers to render bar graphs in 3D with Qt Quick.
++ *
++ * You will need to import Qt Graphs module to use this type:
++ *
++ * \snippet doc_src_qmlgraphs.cpp 0
++ *
++ * After that you can use Bars3D in your qml files:
++ *
++ * \snippet doc_src_qmlgraphs.cpp 1
++ *
++ * See \l{Simple Bar Graph} for more thorough usage example.
++ *
++ * \sa Bar3DSeries, ItemModelBarDataProxy, Scatter3D, Surface3D, {Qt Graphs C++ Classes for 3D}
++ */
++
++ /*!
++ * \qmlproperty Category3DAxis Bars3D::rowAxis
++ * The active row axis.
++ *
++ * If an axis is not given, a temporary default axis with no labels is created.
++ * This temporary axis is destroyed if another axis is explicitly set to the
++ * same orientation.
++ */
++
++ /*!
++ * \qmlproperty ValueAxis3D Bars3D::valueAxis
++ * The active value axis.
++ *
++ * If an axis is not given, a temporary default axis with no labels and an
++ * automatically adjusting range is created.
++ * This temporary axis is destroyed if another axis is explicitly set to the
++ * same orientation.
++ */
++
++ /*!
++ * \qmlproperty Category3DAxis Bars3D::columnAxis
++ * The active column axis.
++ *
++ * If an axis is not given, a temporary default axis with no labels is created.
++ * This temporary axis is destroyed if another axis is explicitly set to the
++ * same orientation.
++ */
++
++ /*!
++ * \qmlproperty bool Bars3D::multiSeriesUniform
++ * Defines whether bars are to be scaled with proportions set to a single series bar even
++ * if there are multiple series displayed. If set to \c {true}, \l{barSpacing}{bar spacing} will
++ * be correctly applied only to the X-axis. Preset to \c false by default.
++ */
++
++ /*!
++ * \qmlproperty real Bars3D::barThickness
++ * The bar thickness ratio between the X and Z dimensions. The value \c 1.0
++ * means that the bars are as wide as they are deep, whereas \c 0.5
++ * makes them twice as deep as they are wide.
++ */
++
++ /*!
++ * \qmlproperty size Bars3D::barSpacing
++ * Bar spacing in X and Z dimensions.
++ *
++ * Preset to \c {(1.0, 1.0)} by default. Spacing is affected by the
++ * barSpacingRelative property.
++ */
++
++ /*!
++ * \qmlproperty bool Bars3D::barSpacingRelative
++ * Whether spacing is absolute or relative to bar thickness.
++ *
++ * If \c true, the value of \c 0.0 means that the bars are placed
++ * side-to-side, \c 1.0 means that a space as wide as the thickness of one bar
++ * is left between the bars, and so on. Preset to \c true.
++ */
++
++ /*!
++ * \qmlproperty size Bars3D::barSeriesMargin
++ *
++ * Margin between series columns in X and Z dimensions. Preset to \c {(0.0, 0.0)} by default.
++ * Sensible values are on the range [0,1).
++ */
++
++ /*!
++ * \qmlproperty Bar3DSeries Bars3D::selectedSeries
++ * \readonly
++ *
++ * The selected series or \c null. If \l {GraphsItem3D::selectionMode}{selectionMode} has
++ * the \c SelectionMultiSeries flag set, this property holds the series that
++ * owns the selected bar.
++ */
++
++ /*!
++ * \qmlproperty list<Bar3DSeries> Bars3D::seriesList
++ * \qmldefault
++ * The series of the graph.
++ * By default, this property contains an empty list.
++ * To set the series, either use the addSeries() function or define them as children of the graph.
++ */
++
++ /*!
++ * \qmlproperty Bar3DSeries Bars3D::primarySeries
++ * The primary series of the graph. It
++ * is used to determine the row and column axis labels when the labels are not explicitly
++ * set to the axes.
++ *
++ * If the specified series is not yet added to the graph, setting it as the
++ * primary series will also implicitly add it to the graph.
++ *
++ * If the primary series itself is removed from the graph, this property
++ * resets to default.
++ *
++ * If the series is null, this property resets to default.
++ * Defaults to the first added series or zero if no series are added to the graph.
++ */
++
++ /*!
++ * \qmlproperty real Bars3D::floorLevel
++ *
++ * The floor level for the bar graph in Y-axis data coordinates.
++ *
++ * The actual floor level will be restricted by the Y-axis minimum and maximum
++ * values.
++ * Defaults to zero.
++ */
++
++ /*!
++ * \qmlmethod void Bars3D::addSeries(Bar3DSeries series)
++ * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes,
++ * so the rows and columns of all series must match for the visualized data to be meaningful.
++ * If the graph has multiple visible series, only the first one added will
++ * generate the row or column labels on the axes in cases where the labels are not explicitly set
++ * to the axes. If the newly added series has specified a selected bar, it will be highlighted and
++ * any existing selection will be cleared. Only one added series can have an active selection.
++ * \sa GraphsItem3D::hasSeries()
++ */
++
++ /*!
++ * \qmlmethod void Bars3D::removeSeries(Bar3DSeries series)
++ * Remove the \a series from the graph.
++ * \sa GraphsItem3D::hasSeries()
++ */
++
++ /*!
++ * \qmlmethod void Bars3D::insertSeries(int index, Bar3DSeries series)
++ * Inserts the \a series into the position \a index in the series list.
++ * If the \a series has already been added to the list, it is moved to the
++ * new \a index.
++ * \note When moving a series to a new \a index that is after its old index,
++ * the new position in list is calculated as if the series was still in its old
++ * index, so the final index is actually the \a index decremented by one.
++ * \sa GraphsItem3D::hasSeries()
++ */
++
++ /*!
++ * \qmlmethod void Bars3D::renderSliceToImage(int requestedIndex, QtGraphs3D::SliceCaptureType sliceType, QUrl filePath)
++ * \since 6.10
++ *
++ * Exports a 2d slice from series at \a requestedIndex and saves the result to an image
++ * at a specified \a filePath.
++ * The exported slice includes bars of row or column, which is defined by
++ * \a sliceType.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::multiSeriesUniformChanged(bool uniform)
++ *
++ * This signal is emitted when multiSeriesUniform changes to \a uniform.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::barThicknessChanged(real thicknessRatio)
++ *
++ * This signal is emitted when barThickness changes to \a thicknessRatio.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::barSpacingChanged(size spacing)
++ *
++ * This signal is emitted when barSpacing changes to \a spacing.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::barSpacingRelativeChanged(bool relative)
++ *
++ * This signal is emitted when barSpacingRelative changes to \a relative.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::barSeriesMarginChanged(size margin)
++ *
++ * This signal is emitted when barSeriesMargin changes to \a margin.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::rowAxisChanged(Category3DAxis axis)
++ *
++ * This signal is emitted when rowAxis changes to \a axis.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::columnAxisChanged(Category3DAxis axis)
++ *
++ * This signal is emitted when columnAxis changes to \a axis.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::valueAxisChanged(ValueAxis3D axis)
++ *
++ * This signal is emitted when valueAxis changes to \a axis.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::primarySeriesChanged(Bar3DSeries series)
++ *
++ * This signal is emitted when primarySeries changes to \a series.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::selectedSeriesChanged(Bar3DSeries series)
++ *
++ * This signal is emitted when selectedSeries changes to \a series.
++ */
++
++ /*!
++ * \qmlsignal Bars3D::floorLevelChanged(real level)
++ *
++ * This signal is emitted when floorLevel changes to \a level.
++ */
++
++ QQuickGraphsBars::QQuickGraphsBars(QQuickItem *parent)
++ : QQuickGraphsItem(parent)
++ {
++ m_graphType = QAbstract3DSeries::SeriesType::Bar;
++ setAxisX(0);
++ setAxisY(0);
++ setAxisZ(0);
++ setAcceptedMouseButtons(Qt::AllButtons);
++ setFlags(ItemHasContents);
++ clearSelection();
++ }
++
++ QQuickGraphsBars::~QQuickGraphsBars()
++ {
++ removeBarModels();
++ removeSlicedBarModels();
++ if (m_grabresult)
++ delete m_grabresult;
++ }
++
++ QCategory3DAxis *QQuickGraphsBars::rowAxis() const
++ {
++ return static_cast<QCategory3DAxis *>(axisZ());
++ }
++
++ void QQuickGraphsBars::setRowAxis(QCategory3DAxis *axis)
++ {
++ QQuickGraphsItem::setAxisZ(axis);
++ }
++
++ QValue3DAxis *QQuickGraphsBars::valueAxis() const
++ {
++ return static_cast<QValue3DAxis *>(axisY());
++ }
++
++ void QQuickGraphsBars::setValueAxis(QValue3DAxis *axis)
++ {
++ QQuickGraphsItem::setAxisY(axis);
++ }
++
++ QCategory3DAxis *QQuickGraphsBars::columnAxis() const
++ {
++ return static_cast<QCategory3DAxis *>(axisX());
++ }
++
++ void QQuickGraphsBars::setColumnAxis(QCategory3DAxis *axis)
++ {
++ QQuickGraphsItem::setAxisX(axis);
++ }
++
++ void QQuickGraphsBars::setMultiSeriesScaling(bool uniform)
++ {
++ m_isMultiSeriesUniform = uniform;
++
++ m_changeTracker.multiSeriesScalingChanged = true;
++ emitNeedRender();
++ }
++
++ bool QQuickGraphsBars::multiSeriesScaling() const
++ {
++ return m_isMultiSeriesUniform;
++ }
++
++ void QQuickGraphsBars::setMultiSeriesUniform(bool uniform)
++ {
++ if (uniform == isMultiSeriesUniform()) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << uniform;
++ return;
++ }
++ setMultiSeriesScaling(uniform);
++ emit multiSeriesUniformChanged(uniform);
++ }
++
++ bool QQuickGraphsBars::isMultiSeriesUniform() const
++ {
++ return multiSeriesScaling();
++ }
++
++ void QQuickGraphsBars::setBarSpecs(float thicknessRatio, QSizeF spacing, bool relative)
++ {
++ m_barThicknessRatio = thicknessRatio;
++ m_barSpacing = spacing;
++ m_isBarSpecRelative = relative;
++
++ m_changeTracker.barSpecsChanged = true;
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::setBarThickness(float thicknessRatio)
++ {
++ if (thicknessRatio == barThickness() || thicknessRatio <= 0.f) {
++ qCDebug(lcProperties3D, "%s value is already set to: %.1f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), thicknessRatio);
++ return;
++ }
++ setBarSpecs(thicknessRatio, barSpacing(), isBarSpacingRelative());
++ emit barThicknessChanged(thicknessRatio);
++ }
++
++ float QQuickGraphsBars::barThickness() const
++ {
++ return m_barThicknessRatio;
++ }
++
++ void QQuickGraphsBars::setBarSpacing(QSizeF spacing)
++ {
++ if (spacing == barSpacing()) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << spacing;
++ return;
++ }
++ setBarSpecs(barThickness(), spacing, isBarSpacingRelative());
++ emit barSpacingChanged(spacing);
++ }
++
++ QSizeF QQuickGraphsBars::barSpacing() const
++ {
++ return m_barSpacing;
++ }
++
++ void QQuickGraphsBars::setBarSpacingRelative(bool relative)
++ {
++ if (relative == isBarSpacingRelative()) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << relative;
++ return;
++ }
++ setBarSpecs(barThickness(), barSpacing(), relative);
++ emit barSpacingRelativeChanged(relative);
++ }
++
++ bool QQuickGraphsBars::isBarSpacingRelative() const
++ {
++ return m_isBarSpecRelative;
++ }
++
++ void QQuickGraphsBars::setBarSeriesMargin(QSizeF margin)
++ {
++ if (margin == barSeriesMargin()) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << margin;
++ return;
++ }
++ m_barSeriesMargin = margin;
++ m_changeTracker.barSeriesMarginChanged = true;
++ emitNeedRender();
++ emit barSeriesMarginChanged(barSeriesMargin());
++ }
++
++ QSizeF QQuickGraphsBars::barSeriesMargin() const
++ {
++ return m_barSeriesMargin;
++ }
++
++ QList<QBar3DSeries *> QQuickGraphsBars::barSeriesList()
++ {
++ QList<QBar3DSeries *> barSeriesList;
++ for (QAbstract3DSeries *abstractSeries : std::as_const(m_seriesList)) {
++ QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(abstractSeries);
++ if (barSeries)
++ barSeriesList.append(barSeries);
++ }
++
++ return barSeriesList;
++ }
++
++ QQmlListProperty<QBar3DSeries> QQuickGraphsBars::seriesList()
++ {
++ return QQmlListProperty<QBar3DSeries>(this,
++ this,
++ &QQuickGraphsBars::appendSeriesFunc,
++ &QQuickGraphsBars::countSeriesFunc,
++ &QQuickGraphsBars::atSeriesFunc,
++ &QQuickGraphsBars::clearSeriesFunc);
++ }
++
++ void QQuickGraphsBars::appendSeriesFunc(QQmlListProperty<QBar3DSeries> *list, QBar3DSeries *series)
++ {
++ reinterpret_cast<QQuickGraphsBars *>(list->data)->addSeries(series);
++ }
++
++ qsizetype QQuickGraphsBars::countSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
++ {
++ return reinterpret_cast<QQuickGraphsBars *>(list->data)->barSeriesList().size();
++ }
++
++ QBar3DSeries *QQuickGraphsBars::atSeriesFunc(QQmlListProperty<QBar3DSeries> *list, qsizetype index)
++ {
++ return reinterpret_cast<QQuickGraphsBars *>(list->data)->barSeriesList().at(index);
++ }
++
++ void QQuickGraphsBars::clearSeriesFunc(QQmlListProperty<QBar3DSeries> *list)
++ {
++ QQuickGraphsBars *declBars = reinterpret_cast<QQuickGraphsBars *>(list->data);
++ QList<QBar3DSeries *> realList = declBars->barSeriesList();
++ qsizetype count = realList.size();
++ for (int i = 0; i < count; i++)
++ declBars->removeSeries(realList.at(i));
++ }
++
++ void QQuickGraphsBars::addSeries(QBar3DSeries *series)
++ {
++ insertSeries(m_seriesList.size(), series);
++ connectSeries(series);
++ if (series->selectedBar() != invalidSelectionPosition())
++ updateSelectedBar();
++
++ if (series->rowAxis())
++ handleMultiAxisChanged(series->rowAxis());
++ if (series->valueAxis())
++ handleMultiAxisChanged(series->valueAxis());
++ if (series->columnAxis())
++ handleMultiAxisChanged(series->columnAxis());
++ }
++
++ void QQuickGraphsBars::removeSeries(QBar3DSeries *series)
++ {
++ if (!series)
++ return;
++
++ bool wasVisible = (series && series->d_func()->m_graph == this && series->isVisible());
++
++ QQuickGraphsItem::removeSeriesInternal(series);
++
++ if (m_selectedBarSeries == series)
++ setSelectedBar(invalidSelectionPosition(), 0, false);
++
++ if (wasVisible)
++ adjustAxisRanges();
++
++ // If primary series is removed, reset it to default
++ if (series == m_primarySeries) {
++ if (m_seriesList.size())
++ m_primarySeries = static_cast<QBar3DSeries *>(m_seriesList.at(0));
++ else
++ m_primarySeries = 0;
++
++ handleDataRowLabelsChanged();
++ handleDataColumnLabelsChanged();
++
++ emit primarySeriesChanged(m_primarySeries);
++ }
++
++ for (auto it = m_slicedBarModels.begin(); it != m_slicedBarModels.end(); it++) {
++ // Remove series also from slice bar list
++ if (it.key() == series)
++ m_slicedBarModels.remove(it.key());
++ }
++
++ removeBarModels();
++ if (m_selectedBarSeries == series)
++ resetClickedStatus();
++ series->setParent(this); // Reparent as removing will leave series parentless
++ disconnectSeries(series);
++ }
++
++ void QQuickGraphsBars::insertSeries(qsizetype index, QBar3DSeries *series)
++ {
++ Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesType::Bar);
++
++ qsizetype oldSize = m_seriesList.size();
++
++ QQuickGraphsItem::insertSeries(index, series);
++
++ if (oldSize != m_seriesList.size()) {
++ QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(series);
++ if (!oldSize) {
++ m_primarySeries = barSeries;
++ handleDataRowLabelsChanged();
++ handleDataColumnLabelsChanged();
++ }
++
++ if (barSeries->selectedBar() != invalidSelectionPosition())
++ setSelectedBar(barSeries->selectedBar(), barSeries, false);
++
++ if (!oldSize)
++ emit primarySeriesChanged(m_primarySeries);
++ }
++ }
++
++ void QQuickGraphsBars::clearSelection()
++ {
++ setSelectedBar(invalidSelectionPosition(), 0, false);
++ }
++
++ void QQuickGraphsBars::setPrimarySeries(QBar3DSeries *series)
++ {
++ if (!series) {
++ if (m_seriesList.size())
++ series = static_cast<QBar3DSeries *>(m_seriesList.at(0));
++ } else if (!m_seriesList.contains(series)) {
++ // Add nonexistent series.
++ addSeries(series);
++ }
++
++ if (m_primarySeries != series) {
++ m_primarySeries = series;
++ handleDataRowLabelsChanged();
++ handleDataColumnLabelsChanged();
++ emit primarySeriesChanged(m_primarySeries);
++ }
++ }
++
++ QBar3DSeries *QQuickGraphsBars::primarySeries() const
++ {
++ return m_primarySeries;
++ }
++
++ QBar3DSeries *QQuickGraphsBars::selectedSeries() const
++ {
++ return m_selectedBarSeries;
++ }
++
++ void QQuickGraphsBars::setSelectionMode(QtGraphs3D::SelectionFlags mode)
++ {
++ if (mode.testFlag(QtGraphs3D::SelectionFlag::Slice)
++ && (mode.testFlag(QtGraphs3D::SelectionFlag::Row)
++ == mode.testFlag(QtGraphs3D::SelectionFlag::Column))) {
++ qCWarning(lcProperties3D, "%s must specify one of either row or column selection mode"
++ "in conjunction with slicing mode.",
++ qUtf8Printable(QLatin1String(__FUNCTION__)));
++ } else {
++ QtGraphs3D::SelectionFlags oldMode = selectionMode();
++
++ QQuickGraphsItem::setSelectionMode(mode);
++
++ if (mode != oldMode) {
++ // Refresh selection upon mode change to ensure slicing is correctly
++ // updated according to series the visibility.
++ setSelectedBar(m_selectedBar, m_selectedBarSeries, true);
++
++ // Special case: Always deactivate slicing when changing away from slice
++ // automanagement, as this can't be handled in setSelectedBar.
++ if (!mode.testFlag(QtGraphs3D::SelectionFlag::Slice)
++ && oldMode.testFlag(QtGraphs3D::SelectionFlag::Slice)) {
++ scene()->setSlicingActive(false);
++ }
++ }
++ }
++ }
++
++ void QQuickGraphsBars::handleAxisAutoAdjustRangeChangedInOrientation(
++ QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust)
++ {
++ Q_UNUSED(orientation);
++ Q_UNUSED(autoAdjust);
++ adjustAxisRanges();
++ }
++
++ void QQuickGraphsBars::handleSeriesVisibilityChangedBySender(QObject *sender)
++ {
++ QQuickGraphsItem::handleSeriesVisibilityChangedBySender(sender);
++
++ // Visibility changes may require disabling slicing,
++ // so just reset selection to ensure everything is still valid.
++ setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
++ }
++
++ void QQuickGraphsBars::handleItemLabelVisibleChangedBySender(bool visible, QObject *sender)
++ {
++ auto series = static_cast<QBar3DSeries *>(sender);
++ if (series == m_selectedBarSeries)
++ {
++ itemLabel()->setVisible(visible);
++ if (auto label = sliceItemLabel(); label && isSlicingActive())
++ label->setVisible(visible);
++ }
++ }
++
++ void QQuickGraphsBars::handleAxisRangeChangedBySender(QObject *sender)
++ {
++ // Data window changed
++ if (sender == m_axisX || sender == m_axisZ) {
++ if (sender == m_axisX)
++ handleDataColumnLabelsChanged();
++ if (sender == m_axisZ)
++ handleDataRowLabelsChanged();
++ }
++
++ QQuickGraphsItem::handleAxisRangeChangedBySender(sender);
++
++ setDataDirty(true);
++
++ // Update selected bar - may be moved offscreen
++ setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
++ }
++
++ void QQuickGraphsBars::adjustAxisRanges()
++ {
++ QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_axisZ);
++ QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_axisX);
++ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_axisY);
++
++ bool adjustZ = (categoryAxisZ && categoryAxisZ->isAutoAdjustRange());
++ bool adjustX = (categoryAxisX && categoryAxisX->isAutoAdjustRange());
++ bool adjustY = (valueAxis && categoryAxisX && categoryAxisZ && valueAxis->isAutoAdjustRange());
++
++ if (adjustZ || adjustX || adjustY) {
++ qsizetype maxRowCount = 0;
++ qsizetype maxColumnCount = 0;
++ float minValue = 0.0f;
++ float maxValue = 0.0f;
++
++ // First figure out row and column counts
++ qsizetype seriesCount = m_seriesList.size();
++ if (adjustZ || adjustX) {
++ for (qsizetype series = 0; series < seriesCount; series++) {
++ const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series));
++ if (barSeries->isVisible()) {
++ const QBarDataProxy *proxy = barSeries->dataProxy();
++
++ if (adjustZ && proxy) {
++ qsizetype rowCount = proxy->rowCount();
++ if (rowCount)
++ rowCount--;
++
++ maxRowCount = qMax(maxRowCount, rowCount);
++ }
++
++ if (adjustX && proxy) {
++ const QBarDataArray &array = barSeries->dataArray();
++ qsizetype columnCount = 0;
++ for (int i = 0; i < array.size(); i++) {
++ if (columnCount < array.at(i).size())
++ columnCount = array.at(i).size();
++ }
++ if (columnCount)
++ columnCount--;
++
++ maxColumnCount = qMax(maxColumnCount, columnCount);
++ }
++ }
++ }
++ // Call private implementations of setRange to avoid unsetting auto adjust
++ // flag
++ if (adjustZ)
++ categoryAxisZ->d_func()->setRange(0.0f, float(maxRowCount), true);
++ if (adjustX)
++ categoryAxisX->d_func()->setRange(0.0f, float(maxColumnCount), true);
++ }
++
++ // Now that we know the row and column ranges, figure out the value axis
++ // range
++ if (adjustY) {
++ for (int series = 0; series < seriesCount; series++) {
++ const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series));
++ if (barSeries->isVisible()) {
++ const QBarDataProxy *proxy = barSeries->dataProxy();
++ if (adjustY && proxy) {
++ QPair<float, float> limits = proxy->d_func()
++ ->limitValues(categoryAxisZ->min(),
++ categoryAxisZ->max(),
++ categoryAxisX->min(),
++ categoryAxisX->max());
++ if (!series) {
++ // First series initializes the values
++ minValue = limits.first;
++ maxValue = limits.second;
++ } else {
++ minValue = qMin(minValue, limits.first);
++ maxValue = qMax(maxValue, limits.second);
++ }
++ }
++ }
++ }
++
++ if (maxValue < 0.0f)
++ maxValue = 0.0f;
++ if (minValue > 0.0f)
++ minValue = 0.0f;
++ if (minValue == 0.0f && maxValue == 0.0f) {
++ // Only zero value values in data set, set range to something.
++ minValue = 0.0f;
++ maxValue = 1.0f;
++ }
++ valueAxis->d_func()->setRange(minValue, maxValue, true);
++ }
++ }
++ }
++
++ void QQuickGraphsBars::setFloorLevel(float level)
++ {
++ if (level == floorLevel()) {
++ qCDebug(lcProperties3D, "%s value is already set to: %.f",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), level);
++ return;
++ }
++ m_floorLevel = level;
++ setDataDirty(true);
++ m_changeTracker.floorLevelChanged = true;
++ emitNeedRender();
++ emit floorLevelChanged(level);
++ }
++
++ float QQuickGraphsBars::floorLevel() const
++ {
++ return m_floorLevel;
++ }
++
++ QQuick3DViewport *QQuickGraphsBars::createOffscreenSliceView(int requestedIndex,
++ QtGraphs3D::SliceCaptureType sliceType)
++ {
++ QQuick3DViewport *sliceView = QQuickGraphsItem::createOffscreenSliceView(sliceType);
++
++ Q_TRACE_SCOPE(QGraphs3DBarsCreateOffscreenSliceView, requestedIndex,
++ static_cast<int>(sliceType));
++
++ const bool isRow = (sliceType == QtGraphs3D::SliceCaptureType::RowImage);
++ const bool isColumn = (sliceType == QtGraphs3D::SliceCaptureType::ColumnImage);
++
++ QList<QBar3DSeries *> barSeriesList = this->barSeriesList();
++ for (const auto &barSeries : std::as_const(barSeriesList)) {
++ qsizetype newRowSize = qMin(barSeries->dataProxy()->rowCount() - m_minRow, m_newRows);
++ qsizetype newColSize = 0;
++ if (newRowSize) {
++ const QBarDataRow *dataRow = &barSeries->dataProxy()->rowAt(m_minRow);
++ if (dataRow) {
++ qsizetype dataColIndex = m_minCol;
++ newColSize = qMin(dataRow->size() - dataColIndex, m_newCols);
++ }
++ }
++
++ if (!barSeries->isVisible())
++ continue;
++
++ if (requestedIndex < 0 || requestedIndex >= newRowSize || requestedIndex >= newColSize) {
++ qCWarning(lcEvents3D, "%s the index is out of range. The render stops.",
++ qUtf8Printable(QLatin1String(__FUNCTION__)));
++ sliceView->setVisible(false);
++ sliceView->deleteLater();
++ return nullptr;
++ }
++
++ qsizetype slicedBarListSize = -1;
++
++ if (isRow)
++ slicedBarListSize = newColSize;
++ else if (isColumn)
++ slicedBarListSize = newRowSize;
++
++ if (slicedBarListSize < 0)
++ return nullptr;
++
++ QList<BarModel *> barList = *m_barModelsMap.value(barSeries);
++ bool useGradient = barSeries->d_func()->isUsingGradient();
++ bool rangeGradient =
++ (useGradient
++ && barSeries->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
++
++ QQuick3DModel *model = nullptr;
++ QList<BarItemHolder *> barItemHolderList;
++ QList<BarItemHolder *> barItemList;
++ QList<BarItemHolder *> selectedItems;
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ model = new QQuick3DModel();
++ model->setParent(sliceView->scene());
++ model->setParentItem(sliceView->scene());
++ model->setObjectName(QStringLiteral("BarModel"));
++ QString fileName = getMeshFileName();
++ if (fileName.isEmpty())
++ fileName = barSeries->userDefinedMesh();
++
++ model->setSource(QUrl(fileName));
++
++ auto barInstancing = new BarInstancing;
++ barInstancing->setParent(barSeries);
++ model->setInstancing(barInstancing);
++
++ BarModel *barListItem = barList.at(0);
++ updateItemMaterial(model, useGradient, rangeGradient,
++ QStringLiteral(":/materials/BarsMaterialInstancing"));
++ updateMaterialProperties(model, false, false, barListItem->texture,
++ barSeries->baseColor());
++
++ barItemList = barListItem->instancing->dataArray();
++ for (const auto bih : std::as_const(barItemList)) {
++ if (!((isRow && bih->coord.x() == requestedIndex)
++ || (isColumn && bih->coord.y() == requestedIndex)))
++ continue;
++
++ BarItemHolder *selectedBih = new BarItemHolder();
++ selectedBih->selectedBar = false;
++ selectedBih->coord = bih->coord;
++ selectedBih->rotation = bih->rotation;
++ selectedBih->heightValue = bih->heightValue;
++ selectedBih->position = bih->position;
++ selectedBih->scale = bih->scale;
++
++ selectedItems.push_back(selectedBih);
++ }
++ if (selectedItems.size() == 0)
++ continue;
++ }
++
++ qsizetype index = 0;
++ for (int ind = 0; ind < slicedBarListSize; ++ind) {
++ if (isRow)
++ index = (requestedIndex * barSeries->dataProxy()->colCount()) + ind;
++ else
++ index = requestedIndex + (ind * barSeries->dataProxy()->colCount());
++
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ if (index > barList.size())
++ return nullptr;
++ model = createDataItem(sliceView->scene(), barSeries);
++ BarModel *barModel = barList.at(index);
++ if (isRow) {
++ model->setPosition(QVector3D(barModel->model->x(), barModel->model->y(), 0.0f));
++ } else {
++ model->setX(barModel->model->z() - (barList.at(0)->visualIndex * .1f));
++ model->setY(barModel->model->y());
++ model->setZ(0.0f);
++ }
++ model->setScale(barModel->model->scale());
++
++ updateItemMaterial(model, useGradient, rangeGradient,
++ QStringLiteral(":/materials/BarsMaterial"));
++
++ updateMaterialProperties(model, false, false, barList.at(index)->texture,
++ barSeries->baseColor());
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ BarModel *barModel = barList.at(0);
++ BarItemHolder *itemHolder = new BarItemHolder();
++ itemHolder->selectedBar = false;
++ itemHolder->color = barSeries->baseColor();
++ itemHolder->coord = barModel->coord;
++ itemHolder->rotation = selectedItems.at(ind)->rotation;
++ itemHolder->heightValue = barModel->heightValue;
++ itemHolder->position = selectedItems.at(ind)->position;
++ itemHolder->scale = selectedItems.at(ind)->scale;
++
++ if (isRow) {
++ itemHolder->position.setZ(.0f);
++ } else {
++ itemHolder->position.setX(itemHolder->position.z()
++ - (barModel->visualIndex * .1f));
++ itemHolder->position.setZ(.0f);
++ }
++
++ barItemHolderList.push_back(itemHolder);
++ }
++ }
++
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ BarInstancing *instancing = static_cast<BarInstancing *>(model->instancing());
++ instancing->setDataArray(barItemHolderList);
++ }
++ }
++
++ return sliceView;
++ }
++
++ void QQuickGraphsBars::renderSliceToImage(int requestedIndex,
++ QtGraphs3D::SliceCaptureType sliceType)
++ {
++ QQuick3DViewport *sliceView = createOffscreenSliceView(requestedIndex, sliceType);
++
++ if (!m_grabresult)
++ m_grabresult = new QImage();
++
++ if (sliceView) {
++ QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
++ connect(grabbed.data(), &QQuickItemGrabResult::ready, this, [&, grabbed, sliceView]() {
++ sliceView->setVisible(false);
++ sliceView->deleteLater();
++ *m_grabresult = grabbed.data()->image();
++ emit sliceImageChanged(*m_grabresult);
++ });
++ }
++ }
++
++ void QQuickGraphsBars::renderSliceToImage(int requestedIndex,
++ QtGraphs3D::SliceCaptureType sliceType,
++ const QUrl &filePath)
++ {
++ QQuick3DViewport *sliceView = createOffscreenSliceView(requestedIndex, sliceType);
++
++ if (!sliceView)
++ return;
++
++ if (filePath.isEmpty()) {
++ qCWarning(lcGraphsBars3D, "Save path is not defined.");
++ sliceView->setVisible(false);
++ sliceView->deleteLater();
++ return;
++ }
++
++ QSharedPointer<QQuickItemGrabResult> grabbed = sliceView->grabToImage();
++ connect(grabbed.data(), &QQuickItemGrabResult::ready, this, [grabbed, sliceView, filePath]() {
++ if (!grabbed.data()->saveToFile(filePath))
++ qCWarning(lcGraphsBars3D, "Saving requested slice view to image failed");
++ sliceView->setVisible(false);
++ sliceView->deleteLater();
++ });
++
++ return;
++ }
++
++ void QQuickGraphsBars::componentComplete()
++ {
++ QQuickGraphsItem::componentComplete();
++ if (!isReady())
++ return;
++
++ auto wallBackground = background();
++ QUrl wallUrl = QUrl(QStringLiteral("defaultMeshes/backgroundNoFloorMesh"));
++ wallBackground->setSource(wallUrl);
++ setBackground(wallBackground);
++
++ QUrl floorUrl = QUrl(QStringLiteral(":/defaultMeshes/barMeshFull"));
++ m_floorBackground = new QQuick3DModel();
++ m_floorBackgroundScale = new QQuick3DNode();
++ m_floorBackgroundRotation = new QQuick3DNode();
++
++ m_floorBackgroundScale->setParent(graphNode());
++ m_floorBackgroundScale->setParentItem(graphNode());
++
++ m_floorBackgroundRotation->setParent(m_floorBackgroundScale);
++ m_floorBackgroundRotation->setParentItem(m_floorBackgroundScale);
++
++ m_floorBackground->setObjectName("Floor Background");
++ m_floorBackground->setParent(m_floorBackgroundRotation);
++ m_floorBackground->setParentItem(m_floorBackgroundRotation);
++
++ m_floorBackground->setSource(floorUrl);
++ m_floorBackground->setPickable(false);
++
++ setFloorGridInRange(true);
++ setVerticalSegmentLine(false);
++
++ QObject::connect(cameraTarget(),
++ &QQuick3DNode::rotationChanged,
++ this,
++ &QQuickGraphsBars::handleCameraRotationChanged);
++
++ graphsInputHandler()->setGraphsItem(this);
++ }
++
++ void QQuickGraphsBars::synchData()
++ {
++ qCDebug(lcGraphs3D, "%s start of sync", qUtf8Printable(QLatin1String(__FUNCTION__)));
++ if (!isReady())
++ return;
++
++ Q_TRACE(QGraphs3DBarsSynch_entry_beforeGraphsItem);
++
++ if (m_changeTracker.barSpecsChanged || !m_cachedBarThickness.isValid()) {
++ updateBarSpecs(m_barThicknessRatio, m_barSpacing, m_isBarSpecRelative);
++ m_changeTracker.barSpecsChanged = false;
++ }
++
++ // Floor level update requires data update, so do before qquickgraphicsitem sync
++ if (m_changeTracker.floorLevelChanged) {
++ updateFloorLevel(m_floorLevel);
++ // Update labels and grid as well, unless we are in shader grid mode
++ updateLabels();
++ if (gridLineType() != QtGraphs3D::GridLineType::Shader)
++ updateGrid();
++ m_changeTracker.floorLevelChanged = false;
++ }
++
++ if (m_changeTracker.multiSeriesScalingChanged) {
++ m_keepSeriesUniform = m_isMultiSeriesUniform;
++ setSeriesVisualsDirty(true);
++ m_changeTracker.multiSeriesScalingChanged = false;
++ }
++
++ // Do not clear dirty flag, we need to react to it in qquickgraphicsitem as well
++ if (theme()->dirtyBits()->plotAreaBackgroundVisibilityDirty) {
++ setSeriesVisualsDirty(true);
++ for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++)
++ it.key()->d_func()->m_changeTracker.meshChanged = true;
++ }
++
++ if (m_changeTracker.barSeriesMarginChanged) {
++ updateBarSeriesMargin(barSeriesMargin());
++ m_changeTracker.barSeriesMarginChanged = false;
++ }
++
++ if (m_changeTracker.axisRangeChanged) {
++ adjustAxisRanges();
++ m_changeTracker.axisRangeChanged = false;
++ }
++ Q_TRACE(QGraphs3DBarsSynch_exit_beforeGraphsItem);
++
++ QQuickGraphsItem::synchData();
++
++ Q_TRACE(QGraphs3DBarsSynch_entry_afterGraphsItem);
++
++ if (m_selectedBarPos.isNull())
++ itemLabel()->setVisible(false);
++
++ // Draw floor
++ if (m_changeTracker.floorChanged) {
++ updateFloor();
++ m_changeTracker.floorChanged = false;
++ }
++
++ Q_TRACE(QGraphs3DBarsSynch_exit_afterGraphsItem);
++ qCDebug(lcGraphs3D, "%s end syncing", qUtf8Printable(QLatin1String(__FUNCTION__)));
++ }
++
++ void QQuickGraphsBars::updateFloor()
++ {
++ auto min = qMin(scaleWithBackground().x(), scaleWithBackground().z());
++ m_floorBackgroundScale->setScale(QVector3D(
++ scaleWithBackground().x() + m_hBackgroundMargin,
++ min * gridOffset(),
++ scaleWithBackground().z() + m_hBackgroundMargin));
++ m_floorBackgroundScale->setPosition(QVector3D(0.0f, -m_backgroundAdjustment, 0.0f));
++
++ QQuaternion m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f));
++ QQuaternion m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f));
++
++ if (isYFlipped())
++ m_floorBackgroundRotation->setRotation(m_xRightAngleRotation);
++ else
++ m_floorBackgroundRotation->setRotation(m_xRightAngleRotationNeg);
++
++ QQmlListReference materialsRefF(m_floorBackground, "materials");
++ QQmlListReference bbRef(background(), "materials");
++ QQuick3DCustomMaterial *bgMatFloor;
++ if (!materialsRefF.size() && bbRef.size()) {
++ bgMatFloor = static_cast<QQuick3DCustomMaterial *>(bbRef.at(0));
++ materialsRefF.append(bgMatFloor);
++ }
++ }
++
++ void QQuickGraphsBars::updateParameters()
++ {
++ m_minRow = m_axisZ->min();
++ m_maxRow = m_axisZ->max();
++ m_minCol = m_axisX->min();
++ m_maxCol = m_axisX->max();
++ m_newRows = m_maxRow - m_minRow + 1;
++ m_newCols = m_maxCol - m_minCol + 1;
++
++ if (m_cachedRowCount != m_newRows || m_cachedColumnCount != m_newCols) {
++ m_changeTracker.selectedBarChanged = true;
++ m_cachedColumnCount = m_newCols;
++ m_cachedRowCount = m_newRows;
++
++ // Calculate max scene size
++ float sceneRatio = qMin(float(m_newCols) / float(m_newRows),
++ float(m_newRows) / float(m_newCols));
++ m_maxSceneSize = 2.0f * qSqrt(sceneRatio * m_newCols * m_newRows);
++
++ if (m_cachedBarThickness.isValid())
++ calculateSceneScalingFactors();
++ }
++ m_changeTracker.axisRangeChanged = true;
++ setDataDirty(true);
++ }
++
++ void QQuickGraphsBars::updateFloorLevel(float level)
++ {
++ setFloorLevel(level);
++ calculateHeightAdjustment();
++ }
++
++ void QQuickGraphsBars::updateGraph()
++ {
++ Q_TRACE_SCOPE(QGraphs3DBarsGenerateBarsUpdateGraph);
++
++ QList<QBar3DSeries *> barSeriesAsList = barSeriesList();
++ calculateSceneScalingFactors();
++
++ for (const auto &series : std::as_const(barSeriesAsList)) {
++ QAbstract3DSeriesChangeBitField &changeTracker = series->d_func()->m_changeTracker;
++ if (changeTracker.meshChanged || changeTracker.meshSmoothChanged) {
++ changeTracker.meshChanged = false;
++ changeTracker.meshSmoothChanged = false;
++ setDataDirty(true);
++ }
++ }
++
++ if (isDataDirty()) {
++ removeBarModels();
++ generateBars(barSeriesAsList);
++ }
++
++ if (isSeriesVisualsDirty()) {
++ int visualIndex = 0;
++ for (const auto &barSeries : std::as_const(barSeriesAsList)) {
++ if (barSeries->isVisible()) {
++ updateBarVisuality(barSeries, visualIndex);
++ updateBarPositions(barSeries);
++ updateBarVisuals(barSeries);
++ ++visualIndex;
++ } else {
++ updateBarVisuality(barSeries, -1);
++ }
++ }
++ }
++
++ // Needs to be done after data is set, as it needs to know the visual array.
++ if (m_changeTracker.selectedBarChanged) {
++ updateSelectedBar();
++ setItemSelected(m_selectedBar != invalidSelectionPosition());
++ if (isSliceEnabled()) {
++ createSliceView();
++ for (const auto &series : std::as_const(barSeriesAsList)) {
++ bool visible = (m_selectedBarSeries == series) && series->isVisible();
++ if (sliceView()->isVisible()) {
++ if (visible) {
++ removeSlicedBarModels();
++ createSliceView();
++ setSliceActivatedChanged(false);
++ toggleSliceGraph();
++ break;
++ } else {
++ setSliceActivatedChanged(true);
++ }
++ } else {
++ if (visible) {
++ m_selectionDirty = true;
++ setSliceActivatedChanged(true);
++ }
++ }
++ }
++ }
++ m_changeTracker.selectedBarChanged = false;
++ }
++
++ setDataDirty(false);
++ setSeriesVisualsDirty(false);
++ }
++
++ void QQuickGraphsBars::updateAxisRange(float min, float max)
++ {
++ QQuickGraphsItem::updateAxisRange(min, max);
++ calculateHeightAdjustment();
++ }
++
++ void QQuickGraphsBars::updateAxisReversed(bool enable)
++ {
++ Q_UNUSED(enable);
++ setSeriesVisualsDirty(true);
++ calculateHeightAdjustment();
++ }
++
++ void QQuickGraphsBars::updateLightStrength()
++ {
++ for (const auto list : std::as_const(m_barModelsMap)) {
++ for (auto barModel : std::as_const(*list)) {
++ QQmlListReference materialRef(barModel->model, "materials");
++ if (materialRef.size()) {
++ QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(
++ materialRef.at(0));
++ material->setProperty("specularBrightness", lightStrength() * 0.05);
++ }
++ }
++ }
++ }
++
++ void QQuickGraphsBars::calculateSceneScalingFactors()
++ {
++ m_rowWidth = (m_cachedColumnCount * m_cachedBarSpacing.width()) * 0.5f;
++ m_columnDepth = (m_cachedRowCount * m_cachedBarSpacing.height()) * 0.5f;
++ m_maxDimension = qMax(m_rowWidth, m_columnDepth);
++ m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
++ (m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
++
++ // Single bar scaling
++ m_xScale = m_cachedBarThickness.width() / m_scaleFactor;
++ m_zScale = m_cachedBarThickness.height() / m_scaleFactor;
++
++ // Adjust scaling according to margin
++ m_xScale = m_xScale - m_xScale * m_cachedBarSeriesMargin.width();
++ m_zScale = m_zScale - m_zScale * m_cachedBarSeriesMargin.height();
++
++ // Whole graph scale factors
++ m_xScaleFactor = m_rowWidth / m_scaleFactor;
++ m_zScaleFactor = m_columnDepth / m_scaleFactor;
++
++ if (margin() < 0.0f) {
++ m_hBackgroundMargin = 0.0f;
++ m_vBackgroundMargin = 0.0f;
++ } else {
++ m_hBackgroundMargin = margin();
++ m_vBackgroundMargin = margin();
++ }
++
++ // Margin for a line to be fully visible on the edge in the grid shader
++ const float halfLineWidth = 50.0;
++ const float gridTextureSize = 4096.0;
++ const float gridMargin = halfLineWidth / gridTextureSize;
++ m_hBackgroundMargin += gridMargin;
++ m_vBackgroundMargin += gridMargin;
++
++ auto scale = QVector3D(m_xScaleFactor, 1.0f, m_zScaleFactor);
++ setScaleWithBackground(scale);
++ setBackgroundScaleMargin({m_hBackgroundMargin, m_vBackgroundMargin, m_hBackgroundMargin});
++ setScale(scale);
++ m_changeTracker.floorChanged = true;
++ }
++
++ void QQuickGraphsBars::calculateHeightAdjustment()
++ {
++ float newAdjustment = 1.0f;
++ m_actualFloorLevel = qBound(valueAxis()->min(), floorLevel(), valueAxis()->max());
++ float maxAbs = qFabs(valueAxis()->max() - m_actualFloorLevel);
++
++ // Check if we have negative values
++ if (valueAxis()->min() < m_actualFloorLevel)
++ m_hasNegativeValues = true;
++ else if (valueAxis()->min() >= m_actualFloorLevel)
++ m_hasNegativeValues = false;
++
++ // Check first if user has set the ranges
++ if (!isUserCameraRotationRange()) {
++ // If not, adjust them automatically based on possibly existing negative values
++ if (!m_noZeroInRange) {
++ setMinCameraYRotation(-90.0f);
++ setMaxCameraYRotation(90.0f);
++ } else {
++ if ((m_hasNegativeValues && !valueAxis()->reversed())
++ || (!m_hasNegativeValues && valueAxis()->reversed())) {
++ setMinCameraYRotation(-90.0f);
++ setMaxCameraYRotation(0.0f);
++ } else {
++ setMinCameraYRotation(0.0f);
++ setMaxCameraYRotation(90.0f);
++ }
++ }
++ setUserCameraRotationRange(false);
++ }
++
++ if (valueAxis()->max() < m_actualFloorLevel) {
++ m_heightNormalizer = float(qFabs(valueAxis()->min()) - qFabs(valueAxis()->max()));
++ maxAbs = qFabs(valueAxis()->max()) - qFabs(valueAxis()->min());
++ } else {
++ m_heightNormalizer = float(valueAxis()->max() - valueAxis()->min());
++ }
++
++ // Height fractions are used in gradient calculations and are therefore
++ // doubled Note that if max or min is exactly zero, we still consider it
++ // outside the range
++ if (valueAxis()->max() <= m_actualFloorLevel || valueAxis()->min() >= m_actualFloorLevel)
++ m_noZeroInRange = true;
++ else
++ m_noZeroInRange = false;
++
++ // Calculate translation adjustment for background floor
++ newAdjustment = (qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) - 0.5f) * 2.0f;
++ if (valueAxis()->reversed())
++ newAdjustment = -newAdjustment;
++
++ if (newAdjustment != m_backgroundAdjustment)
++ m_backgroundAdjustment = newAdjustment;
++ }
++
++ void QQuickGraphsBars::calculateSeriesStartPosition()
++ {
++ m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) * 0.5f)
++ * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
++ }
++
++ QVector3D QQuickGraphsBars::calculateCategoryLabelPosition(QAbstract3DAxis *axis,
++ QVector3D labelPosition,
++ int index)
++ {
++ QVector3D ret = labelPosition;
++ if (axis->orientation() == QAbstract3DAxis::AxisOrientation::X) {
++ float xPos = (index + 0.5f) * m_cachedBarSpacing.width();
++ ret.setX((xPos - m_rowWidth) / m_scaleFactor);
++ }
++ if (axis->orientation() == QAbstract3DAxis::AxisOrientation::Z) {
++ float zPos = (index + 0.5f) * m_cachedBarSpacing.height();
++ ret.setZ((m_columnDepth - zPos) / m_scaleFactor);
++ }
++ ret.setY(-m_backgroundAdjustment);
++ return ret;
++ }
++
++ float QQuickGraphsBars::calculateCategoryGridLinePosition(QAbstract3DAxis *axis, int index)
++ {
++ float ret = 0.0f;
++ if (axis->orientation() == QAbstract3DAxis::AxisOrientation::Z) {
++ float colPos = index * -(m_cachedBarSpacing.height() / m_scaleFactor);
++ ret = colPos + scale().z();
++ }
++ if (axis->orientation() == QAbstract3DAxis::AxisOrientation::X) {
++ float rowPos = index * (m_cachedBarSpacing.width() / m_scaleFactor);
++ ret = rowPos - scale().x();
++ }
++ if (axis->orientation() == QAbstract3DAxis::AxisOrientation::Y)
++ ret = -m_backgroundAdjustment;
++ return ret;
++ }
++
++ void QQuickGraphsBars::handleAxisXChanged(QAbstract3DAxis *axis)
++ {
++ emit columnAxisChanged(static_cast<QCategory3DAxis *>(axis));
++ }
++
++ void QQuickGraphsBars::handleAxisYChanged(QAbstract3DAxis *axis)
++ {
++ emit valueAxisChanged(static_cast<QValue3DAxis *>(axis));
++ }
++
++ void QQuickGraphsBars::handleAxisZChanged(QAbstract3DAxis *axis)
++ {
++ emit rowAxisChanged(static_cast<QCategory3DAxis *>(axis));
++ }
++
++ void QQuickGraphsBars::handleMultiAxisChanged(QAbstract3DAxis *axis)
++ {
++ QQuickGraphsItem::handleMultiAxisChanged(axis);
++ }
++
++ QAbstract3DAxis *QQuickGraphsBars::getSeriesMultiAxis(QAbstract3DSeries *series,
++ QAbstract3DAxis::AxisOrientation orientation)
++ {
++ QBar3DSeries *barSeries = qobject_cast<QBar3DSeries *>(series);
++
++ if (!barSeries)
++ return nullptr;
++
++ QAbstract3DAxis *axis = nullptr;
++ switch (orientation) {
++ case QAbstract3DAxis::AxisOrientation::X:
++ axis = barSeries->rowAxis();
++ break;
++ case QAbstract3DAxis::AxisOrientation::Y:
++ axis = barSeries->valueAxis();
++ break;
++ case QAbstract3DAxis::AxisOrientation::Z:
++ axis = barSeries->columnAxis();
++ break;
++ case QAbstract3DAxis::AxisOrientation::None:
++ break;
++ }
++
++ if (axis)
++ axis->d_func()->setOrientation(orientation);
++
++ return axis;
++ }
++
++ void QQuickGraphsBars::handleSeriesMeshChanged(QAbstract3DSeries::Mesh mesh)
++ {
++ m_meshType = mesh;
++ }
++
++ void QQuickGraphsBars::handleMeshSmoothChanged(bool enable)
++ {
++ m_smooth = enable;
++ }
++
++ void QQuickGraphsBars::handleCameraRotationChanged()
++ {
++ updateLabels();
++ }
++
++ void QQuickGraphsBars::handleArrayReset()
++ {
++ QBar3DSeries *series;
++ if (qobject_cast<QBarDataProxy *>(sender()))
++ series = static_cast<QBarDataProxy *>(sender())->series();
++ else
++ series = static_cast<QBar3DSeries *>(sender());
++
++ if (series->isVisible()) {
++ adjustAxisRanges();
++ setDataDirty(true);
++ series->d_func()->markItemLabelDirty();
++ }
++ if (!m_changedSeriesList.contains(series))
++ m_changedSeriesList.append(series);
++ // Clear selection unless still valid
++ setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
++ series->d_func()->markItemLabelDirty();
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleRowsAdded(qsizetype startIndex, qsizetype count)
++ {
++ Q_UNUSED(startIndex);
++ Q_UNUSED(count);
++ QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
++ if (series->isVisible()) {
++ adjustAxisRanges();
++ setDataDirty(true);
++ }
++ if (!m_changedSeriesList.contains(series))
++ m_changedSeriesList.append(series);
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleRowsChanged(qsizetype startIndex, qsizetype count)
++ {
++ QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
++ qsizetype oldChangeCount = m_changedRows.size();
++ if (!oldChangeCount)
++ m_changedRows.reserve(count);
++
++ for (int i = 0; i < count; i++) {
++ bool newItem = true;
++ qsizetype candidate = startIndex + i;
++ for (qsizetype j = 0; j < oldChangeCount; j++) {
++ const ChangeRow &oldChangeItem = m_changedRows.at(j);
++ if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
++ newItem = false;
++ break;
++ }
++ }
++ if (newItem) {
++ ChangeRow newChangeItem = {series, candidate};
++ m_changedRows.append(newChangeItem);
++ if (series == m_selectedBarSeries && m_selectedBar.x() == candidate)
++ series->d_func()->markItemLabelDirty();
++ }
++ }
++ if (count) {
++ m_changeTracker.rowsChanged = true;
++
++ if (series->isVisible())
++ adjustAxisRanges();
++
++ // Clear selection unless still valid (row length might have changed)
++ setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
++ emitNeedRender();
++ }
++ }
++
++ void QQuickGraphsBars::handleRowsRemoved(qsizetype startIndex, qsizetype count)
++ {
++ Q_UNUSED(startIndex);
++ Q_UNUSED(count);
++
++ QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
++ if (series == m_selectedBarSeries) {
++ // If rows removed from selected series before the selection, adjust the
++ // selection
++ int selectedRow = m_selectedBar.x();
++ if (startIndex <= selectedRow) {
++ if ((startIndex + count) > selectedRow)
++ selectedRow = -1; // Selected row removed
++ else
++ selectedRow -= count; // Move selected row down by amount of rows removed
++
++ setSelectedBar(QPoint(selectedRow, m_selectedBar.y()), m_selectedBarSeries, false);
++ }
++ }
++
++ if (series->isVisible()) {
++ adjustAxisRanges();
++ setDataDirty(true);
++ }
++ if (!m_changedSeriesList.contains(series))
++ m_changedSeriesList.append(series);
++
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleRowsInserted(qsizetype startIndex, qsizetype count)
++ {
++ Q_UNUSED(startIndex);
++ Q_UNUSED(count);
++ QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
++ if (series == m_selectedBarSeries) {
++ // If rows inserted to selected series before the selection, adjust the
++ // selection
++ int selectedRow = m_selectedBar.x();
++ if (startIndex <= selectedRow) {
++ selectedRow += count;
++ setSelectedBar(QPoint(selectedRow, m_selectedBar.y()), m_selectedBarSeries, false);
++ }
++ }
++
++ if (series->isVisible()) {
++ adjustAxisRanges();
++ setDataDirty(true);
++ }
++ if (!m_changedSeriesList.contains(series))
++ m_changedSeriesList.append(series);
++
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleItemChanged(qsizetype rowIndex, qsizetype columnIndex)
++ {
++ QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
++
++ bool newItem = true;
++ QPoint candidate((int(rowIndex)), (int(columnIndex)));
++ for (ChangeItem item : std::as_const(m_changedItems)) {
++ if (item.point == candidate && item.series == series) {
++ newItem = false;
++ setDataDirty(true);
++ break;
++ }
++ }
++
++ if (newItem) {
++ ChangeItem newItem = {series, candidate};
++ m_changedItems.append(newItem);
++ m_changeTracker.itemChanged = true;
++
++ if (series == m_selectedBarSeries && m_selectedBar == candidate)
++ series->d_func()->markItemLabelDirty();
++ if (series->isVisible())
++ adjustAxisRanges();
++ }
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleDataRowLabelsChanged()
++ {
++ if (m_axisZ) {
++ // Grab a sublist equal to data window (no need to have more labels in axis)
++ int min = int(m_axisZ->min());
++ int count = int(m_axisZ->max()) - min + 1;
++ QStringList subList;
++ if (m_primarySeries && m_primarySeries->dataProxy())
++ subList = m_primarySeries->rowLabels().mid(min, count);
++ static_cast<QCategory3DAxis *>(m_axisZ)->d_func()->setDataLabels(subList);
++ }
++
++ if (repeaterZ()) {
++ updateParameters();
++ repeaterZ()->setModel(m_axisZ->labels().size());
++ }
++ }
++
++ void QQuickGraphsBars::handleDataColumnLabelsChanged()
++ {
++ if (m_axisX) {
++ // Grab a sublist equal to data window (no need to have more labels in axis)
++ int min = int(m_axisX->min());
++ int count = int(m_axisX->max()) - min + 1;
++ QStringList subList;
++ if (m_primarySeries && m_primarySeries->dataProxy())
++ subList = m_primarySeries->columnLabels().mid(min, count);
++ static_cast<QCategory3DAxis *>(m_axisX)->d_func()->setDataLabels(subList);
++ }
++
++ if (repeaterX()) {
++ updateParameters();
++ repeaterX()->setModel(m_axisX->labels().size());
++ }
++ }
++
++ void QQuickGraphsBars::handleRowColorsChanged()
++ {
++ setSeriesVisualsDirty(true);
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleValueColoringChanged()
++ {
++ setSeriesVisualsDirty(true);
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::handleLightingModeChanged()
++ {
++ setSeriesVisualsDirty(true);
++ emitNeedRender();
++ }
++
++ void QQuickGraphsBars::connectSeries(QBar3DSeries *series)
++ {
++ m_meshType = series->mesh();
++ m_smooth = series->isMeshSmooth();
++
++ QObject::connect(series,
++ &QBar3DSeries::meshChanged,
++ this,
++ &QQuickGraphsBars::handleSeriesMeshChanged);
++ QObject::connect(series,
++ &QBar3DSeries::meshSmoothChanged,
++ this,
++ &QQuickGraphsBars::handleMeshSmoothChanged);
++ QObject::connect(series->dataProxy(),
++ &QBarDataProxy::colCountChanged,
++ this,
++ &QQuickGraphsBars::handleDataColumnLabelsChanged);
++ QObject::connect(series->dataProxy(),
++ &QBarDataProxy::rowCountChanged,
++ this,
++ &QQuickGraphsBars::handleDataRowLabelsChanged);
++ QObject::connect(series,
++ &QBar3DSeries::rowColorsChanged,
++ this,
++ &QQuickGraphsBars::handleRowColorsChanged);
++ QObject::connect(series,
++ &QBar3DSeries::valueColoringEnabledChanged,
++ this,
++ &QQuickGraphsBars::handleValueColoringChanged);
++ QObject::connect(series,
++ &QBar3DSeries::rowAxisChanged,
++ this,
++ &QQuickGraphsBars::handleMultiAxisChanged);
++ QObject::connect(series,
++ &QBar3DSeries::valueAxisChanged,
++ this,
++ &QQuickGraphsBars::handleMultiAxisChanged);
++ QObject::connect(series,
++ &QBar3DSeries::columnAxisChanged,
++ this,
++ &QQuickGraphsBars::handleMultiAxisChanged);
++
++ }
++
++ void QQuickGraphsBars::disconnectSeries(QBar3DSeries *series)
++ {
++ QObject::disconnect(series, 0, this, 0);
++ }
++
++ void QQuickGraphsBars::generateBars(QList<QBar3DSeries *> &barSeriesList)
++ {
++ m_visibleSeriesCount = 0;
++ Q_TRACE_SCOPE(QGraphs3DBarsGenerateBars, barSeriesList.size());
++
++ for (const auto &barSeries : std::as_const(barSeriesList)) {
++ QQuick3DTexture *texture = createTexture();
++ texture->setParent(this);
++ auto gradient = barSeries->baseGradient();
++ auto textureData = static_cast<QQuickGraphsTextureData *>(texture->textureData());
++ textureData->createGradient(gradient);
++
++ bool visible = barSeries->isVisible();
++
++ QList<BarModel *> *barList = m_barModelsMap.value(barSeries);
++
++ if (!barList) {
++ barList = new QList<BarModel *>;
++ m_barModelsMap[barSeries] = barList;
++ }
++
++ if (barList->isEmpty()) {
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ QBarDataProxy *dataProxy = barSeries->dataProxy();
++ qsizetype dataRowIndex = m_minRow;
++ qsizetype newRowSize = qMin(dataProxy->rowCount() - dataRowIndex, m_newRows);
++ Q_TRACE_SCOPE(QGraphs3DBarsGenerateBarsLegacy);
++
++ for (int row = 0; row < newRowSize; ++row) {
++ const QBarDataRow &dataRow = dataProxy->rowAt(dataRowIndex);
++ if (!dataRow.isEmpty()) {
++ qsizetype dataColIndex = m_minCol;
++ qsizetype newColSize = qMin(dataRow.size() - dataColIndex, m_newCols);
++ for (int col = 0; col < newColSize; ++col) {
++ QBarDataItem &dataItem = const_cast<QBarDataItem &>(
++ dataRow.at(dataColIndex));
++ auto scene = graphNode();
++ QQuick3DModel *model = createDataItem(scene, barSeries);
++ model->setVisible(visible);
++
++ BarModel *barModel = new BarModel();
++ barModel->model = model;
++ barModel->barItem = &dataItem;
++ barModel->coord = QPoint(int(dataRowIndex), col);
++ barModel->texture = texture;
++
++ if (!barList->contains(barModel))
++ barList->append(barModel);
++
++ ++dataColIndex;
++ }
++ ++dataRowIndex;
++ }
++ }
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ Q_TRACE_SCOPE(QGraphs3DBarsGenerateBarsDefault);
++
++ auto scene = graphNode();
++ BarModel *barInstancing = new BarModel();
++ barInstancing->texture = texture;
++
++ if (barInstancing->instancing == nullptr) {
++ barInstancing->instancing = new BarInstancing;
++ barInstancing->instancing->setParent(barSeries);
++ barInstancing->selectionInstancing = new BarInstancing;
++ barInstancing->selectionInstancing->setParent(barSeries);
++ barInstancing->multiSelectionInstancing = new BarInstancing;
++ barInstancing->multiSelectionInstancing->setParent(barSeries);
++ }
++
++ if (barInstancing->model == nullptr) {
++ barInstancing->model = createDataItem(scene, barSeries);
++ barInstancing->model->setInstancing(barInstancing->instancing);
++ barInstancing->model->setVisible(visible);
++ barInstancing->model->setPickable(true);
++ barInstancing->selectedModel = createDataItem(scene, barSeries);
++ barInstancing->selectedModel->setInstancing(barInstancing->selectionInstancing);
++ barInstancing->selectedModel->setVisible(visible);
++ barInstancing->selectedModel->setPickable(true);
++ barInstancing->multiSelectedModel = createDataItem(scene, barSeries);
++ barInstancing->multiSelectedModel->setInstancing(
++ barInstancing->multiSelectionInstancing);
++ barInstancing->multiSelectedModel->setVisible(visible);
++ barInstancing->multiSelectedModel->setPickable(true);
++ }
++
++ if (!barList->contains(barInstancing)) {
++ barList->append(barInstancing);
++ qCDebug(lcGraphs3D) << "bar info:"
++ << "\n model:" << barInstancing->model
++ << "\n series:" << barSeries
++ << "\n textureSize:" << barInstancing->texture->textureData()->size()
++ << "\n rowCount: " << barSeries->dataProxy()->rowCount()
++ << "\n columnCount:" << barSeries->dataProxy()->colCount()
++ << "\n baseColor" << barSeries->baseColor()
++ << "\n mesh:" << barSeries->mesh();
++ }
++ }
++
++ markSeriesVisualsDirty();
++ }
++
++ if (barSeries->isVisible())
++ m_visibleSeriesCount++;
++ }
++ }
++
++ QQuick3DModel *QQuickGraphsBars::createDataItem(QQuick3DNode *scene, QAbstract3DSeries *series)
++ {
++ auto model = new QQuick3DModel();
++ model->setParent(scene);
++ model->setParentItem(scene);
++ model->setObjectName(QStringLiteral("BarModel"));
++ QString fileName = getMeshFileName();
++ if (fileName.isEmpty())
++ fileName = series->userDefinedMesh();
++
++ model->setSource(QUrl(fileName));
++ return model;
++ }
++
++ QString QQuickGraphsBars::getMeshFileName()
++ {
++ QString fileName = {};
++ switch (m_meshType) {
++ case QAbstract3DSeries::Mesh::Sphere:
++ fileName = QStringLiteral("defaultMeshes/sphereMesh");
++ break;
++ case QAbstract3DSeries::Mesh::Bar:
++ case QAbstract3DSeries::Mesh::Cube:
++ fileName = QStringLiteral("defaultMeshes/barMesh");
++ break;
++ case QAbstract3DSeries::Mesh::Pyramid:
++ fileName = QStringLiteral("defaultMeshes/pyramidMesh");
++ break;
++ case QAbstract3DSeries::Mesh::Cone:
++ fileName = QStringLiteral("defaultMeshes/coneMesh");
++ break;
++ case QAbstract3DSeries::Mesh::Cylinder:
++ fileName = QStringLiteral("defaultMeshes/cylinderMesh");
++ break;
++ case QAbstract3DSeries::Mesh::BevelBar:
++ case QAbstract3DSeries::Mesh::BevelCube:
++ fileName = QStringLiteral("defaultMeshes/bevelBarMesh");
++ break;
++ case QAbstract3DSeries::Mesh::UserDefined:
++ break;
++ default:
++ fileName = QStringLiteral("defaultMeshes/sphereMesh");
++ }
++
++ fixMeshFileName(fileName, m_meshType);
++
++ return fileName;
++ }
++
++ void QQuickGraphsBars::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh meshType)
++ {
++ // Should it be smooth?
++ if (m_smooth && meshType != QAbstract3DSeries::Mesh::Point
++ && meshType != QAbstract3DSeries::Mesh::UserDefined) {
++ fileName += QStringLiteral("Smooth");
++ }
++
++ // Should it be filled?
++ if (!theme()->isPlotAreaBackgroundVisible() && meshType != QAbstract3DSeries::Mesh::Sphere
++ && meshType != QAbstract3DSeries::Mesh::Point
++ && meshType != QAbstract3DSeries::Mesh::UserDefined) {
++ fileName.append(QStringLiteral("Full"));
++ }
++ }
++
++ void QQuickGraphsBars::updateBarVisuality(QBar3DSeries *series, int visualIndex)
++ {
++ QList<BarModel *> barList = *m_barModelsMap.value(series);
++ for (int i = 0; i < barList.count(); i++) {
++ barList.at(i)->visualIndex = visualIndex;
++ barList.at(i)->model->setVisible(series->isVisible());
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ barList.at(0)->selectedModel->setVisible(false);
++ barList.at(0)->multiSelectedModel->setVisible(false);
++ }
++ }
++
++ m_changeTracker.selectedBarChanged = true;
++ itemLabel()->setVisible(false);
++ }
++
++ void QQuickGraphsBars::updateBarPositions(QBar3DSeries *series)
++ {
++ QBarDataProxy *dataProxy = series->dataProxy();
++
++ m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
++ m_seriesStep = 1.0f / float(m_visibleSeriesCount);
++ m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) * 0.5f)
++ * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
++
++ if (m_keepSeriesUniform)
++ m_seriesScaleZ = m_seriesScaleX;
++ else
++ m_seriesScaleZ = 1.0f;
++
++ m_zeroPosition = valueAxis()->positionAt(m_actualFloorLevel);
++
++ QList<BarModel *> barList = *m_barModelsMap.value(series);
++
++ qsizetype dataRowIndex = m_minRow;
++ qsizetype newRowSize = qMin(dataProxy->rowCount() - dataRowIndex, m_newRows);
++ qsizetype row = 0;
++ qsizetype dataColIndex = m_minCol;
++ qsizetype newColSize = qMin(dataProxy->colCount() - dataColIndex, m_newCols);
++ qsizetype col = 0;
++ for (int i = 0; i < barList.count(); i++) {
++ float seriesPos = m_seriesStart + 0.5f
++ + (m_seriesStep
++ * (barList.at(i)->visualIndex
++ - (barList.at(i)->visualIndex * m_cachedBarSeriesMargin.width())));
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ Q_TRACE_SCOPE(QGraphs3DBarsUpdateBarPositionsLegacy);
++
++ QBarDataItem *item = barList.at(i)->barItem;
++ QQuick3DModel *model = barList.at(i)->model;
++ float heightValue = updateBarHeightParameters(item, series->valueAxis());
++ float angle = item->rotation();
++
++ if (angle) {
++ model->setRotation(QQuaternion::fromAxisAndAngle(upVector, angle)
++ + series->meshRotation());
++ } else {
++ model->setRotation(QQuaternion() + series->meshRotation());
++ }
++
++ if (heightValue < 0.f) {
++ const QVector3D rot = model->eulerRotation();
++ model->setEulerRotation(QVector3D(-180.f, rot.y(), rot.z()));
++ }
++
++ float colPos = (col + seriesPos) * m_cachedBarSpacing.width();
++ float xPos = (colPos - m_rowWidth) / m_scaleFactor;
++ float rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
++ float zPos = (m_columnDepth - rowPos) / m_scaleFactor;
++ float yPos;
++
++ if (heightValue < 0.f)
++ yPos = heightValue - m_backgroundAdjustment - 0.015f;
++ else
++ yPos = heightValue - m_backgroundAdjustment + 0.015f;
++
++ barList.at(i)->heightValue = heightValue;
++ model->setPosition(QVector3D(xPos, yPos, zPos));
++
++ if (heightValue == 0) {
++ model->setScale(QVector3D(.0f, .0f, .0f));
++ model->setPickable(false);
++ } else {
++ model->setScale(QVector3D(m_xScale * m_seriesScaleX,
++ qAbs(heightValue),
++ m_zScale * m_seriesScaleZ));
++ model->setPickable(true);
++ }
++
++ if (col < newColSize - 1) {
++ ++col;
++ } else {
++ col = 0;
++ if (row < newRowSize - 1)
++ ++row;
++ else
++ row = 0;
++ }
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ Q_TRACE_SCOPE(QGraphs3DBarsGenerateBarsDefault);
++
++ deleteBarItemHolders(barList.at(i)->instancing);
++ QList<BarItemHolder *> positions;
++ for (int row = 0; row < newRowSize; ++row) {
++ const QBarDataRow &dataRow = dataProxy->rowAt(dataRowIndex);
++ if (!dataRow.isEmpty()) {
++ dataColIndex = m_minCol;
++ newColSize = qMin(dataRow.size() - dataColIndex, m_newCols);
++ for (int col = 0; col < newColSize; col++) {
++ const QBarDataItem &item = dataRow.at(dataColIndex);
++ float heightValue = updateBarHeightParameters(&item, series->valueAxis());
++ BarItemHolder *bih = new BarItemHolder();
++
++ if (barList.at(i)->model->eulerRotation() != QVector3D()
++ || series->meshRotation() != QQuaternion()) {
++ const QQuaternion rotation =
++ QQuaternion(barList.at(i)->model->eulerRotation().toVector4D())
++ + series->meshRotation();
++
++ bih->rotation = rotation;
++ } else {
++ bih->rotation = QQuaternion::fromEulerAngles(
++ QVector3D(.0f, item.rotation(), .0f));
++ }
++ if (heightValue < 0.f) {
++ // if bars need to be rendered negative(under the floor)
++ // construct a temporary quaternion(normalized) that rotates through
++ // angle (in degrees) around x axis and multiply rotation with it.
++ QQuaternion rot = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 180.0f);
++ bih->rotation *= rot;
++ }
++
++ float colPos = (col + seriesPos) * m_cachedBarSpacing.width();
++ float xPos = (colPos - m_rowWidth) / m_scaleFactor;
++ float rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
++ float zPos = (m_columnDepth - rowPos) / m_scaleFactor;
++ float yPos;
++
++ if (heightValue < 0.f)
++ yPos = heightValue - m_backgroundAdjustment - 0.015f;
++ else
++ yPos = heightValue - m_backgroundAdjustment + 0.015f;
++
++ bih->position = {xPos, yPos, zPos};
++ bih->coord = QPoint(row, col);
++
++ if (heightValue == 0) {
++ bih->scale = {.0f, .0f, .0f};
++ } else {
++ bih->scale = {m_xScale * m_seriesScaleX,
++ qAbs(heightValue),
++ m_zScale * m_seriesScaleZ};
++ }
++
++ bih->heightValue = heightValue;
++ bih->selectedBar = false;
++
++ bool colorStyleIsUniform = (series->colorStyle()
++ == QGraphsTheme::ColorStyle::Uniform);
++ if (colorStyleIsUniform) {
++ QList<QColor> rowColors = series->rowColors();
++ if (rowColors.size() == 0) {
++ bih->color = series->baseColor();
++ } else {
++ int rowColorIndex = bih->coord.x() % rowColors.size();
++ bih->color = rowColors[rowColorIndex];
++ }
++ }
++
++ positions.push_back(bih);
++ dataColIndex++;
++ }
++ }
++ dataRowIndex++;
++ }
++ barList.at(i)->instancing->setDataArray(positions);
++ }
++ }
++ }
++
++ float QQuickGraphsBars::updateBarHeightParameters(const QBarDataItem *item, QValue3DAxis *axis)
++ {
++
++ if (!axis)
++ axis = valueAxis();
++ float value = item->value();
++ float heightValue = axis->positionAt(value);
++
++ if (qIsNaN(heightValue))
++ return 0.0f;
++
++ if (m_noZeroInRange) {
++ if (m_hasNegativeValues) {
++ heightValue = -1.0f + heightValue;
++ if (heightValue > 0.0f)
++ heightValue = 0.0f;
++ } else {
++ if (heightValue < 0.0f)
++ heightValue = 0.0f;
++ }
++ } else {
++ heightValue -= m_zeroPosition;
++ }
++
++ if (axis->reversed())
++ heightValue = -heightValue;
++
++ return heightValue;
++ }
++
++ void QQuickGraphsBars::updateBarVisuals(QBar3DSeries *series)
++ {
++ QList<BarModel *> barList = *m_barModelsMap.value(series);
++ bool useGradient = series->d_func()->isUsingGradient();
++
++ if (useGradient) {
++ if (!m_hasHighlightTexture) {
++ m_highlightTexture = createTexture();
++ m_highlightTexture->setParent(this);
++ m_multiHighlightTexture = createTexture();
++ m_multiHighlightTexture->setParent(this);
++ m_hasHighlightTexture = true;
++ }
++ auto highlightGradient = series->singleHighlightGradient();
++ auto highlightTextureData = static_cast<QQuickGraphsTextureData *>(
++ m_highlightTexture->textureData());
++ highlightTextureData->createGradient(highlightGradient);
++ auto multiHighlightGradient = series->multiHighlightGradient();
++ auto multiHighlightTextureData = static_cast<QQuickGraphsTextureData *>(
++ m_multiHighlightTexture->textureData());
++ multiHighlightTextureData->createGradient(multiHighlightGradient);
++ } else {
++ if (m_hasHighlightTexture) {
++ m_highlightTexture->deleteLater();
++ m_multiHighlightTexture->deleteLater();
++ m_hasHighlightTexture = false;
++ }
++ }
++
++ bool rangeGradient = (useGradient
++ && series->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
++ QColor baseColor = series->baseColor();
++ QColor barColor;
++ QLinearGradient gradient = series->baseGradient();
++
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ // Release resources that might not have been deleted even though deleteLater had been set
++
++ if (m_customView)
++ m_customView->window()->releaseResources();
++ else
++ window()->releaseResources();
++
++ for (int i = 0; i < barList.count(); i++) {
++ QQuick3DModel *model = barList.at(i)->model;
++ auto textureData = static_cast<QQuickGraphsTextureData *>(
++ barList.at(i)->texture->textureData());
++ textureData->createGradient(series->baseGradient());
++ const bool transparency = textureData->hasTransparency();
++ updateItemMaterial(model,
++ useGradient,
++ rangeGradient,
++ QStringLiteral(":/materials/BarsMaterial"));
++ if (useGradient) {
++ updateMaterialProperties(model,
++ false,
++ false,
++ barList.at(i)->texture,
++ QColor(Qt::white),
++ transparency);
++ } else {
++ QList<QColor> rowColors = series->rowColors();
++ if (rowColors.size() == 0) {
++ barColor = baseColor;
++ } else {
++ int rowColorIndex = barList.at(i)->coord.x() % rowColors.size();
++ barColor = rowColors[rowColorIndex];
++ }
++ const bool transparency = barColor.alphaF() < 1.0;
++ updateMaterialProperties(model,
++ false,
++ false,
++ barList.at(i)->texture,
++ barColor,
++ transparency);
++ }
++ QQmlListReference materialsRef(model, "materials");
++ auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(materialsRef.at(0));
++ customMaterial->setProperty("valueColoring", series->isValueColoringEnabled());
++ customMaterial->setProperty("heightValue", barList.at(i)->heightValue);
++ customMaterial->setProperty("shaded",
++ series->lightingMode()
++ == QAbstract3DSeries::LightingMode::Shaded);
++ }
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ for (int i = 0; i < barList.count(); i++) {
++ auto textureData = static_cast<QQuickGraphsTextureData *>(
++ barList.at(i)->texture->textureData());
++ textureData->createGradient(series->baseGradient());
++ barList.at(i)->instancing->setTransparency(textureData->hasTransparency());
++ updateItemMaterial(barList.at(i)->model,
++ useGradient,
++ rangeGradient,
++ QStringLiteral(":/materials/BarsMaterialInstancing"));
++ if (useGradient) {
++ updateMaterialProperties(barList.at(i)->model,
++ false,
++ false,
++ barList.at(i)->texture,
++ QColor(Qt::white),
++ textureData->hasTransparency());
++ } else {
++ if (!barList.at(i)->instancing->dataArray().isEmpty()) {
++ const bool transparency
++ = barList.at(i)->instancing->dataArray().at(0)->color.alphaF() < 1.0;
++ updateMaterialProperties(barList.at(i)->model,
++ false,
++ false,
++ barList.at(i)->texture,
++ QColor(Qt::white),
++ transparency);
++ }
++ }
++ QQmlListReference materialsRef(barList.at(i)->model, "materials");
++ auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(materialsRef.at(0));
++ customMaterial->setProperty("valueColoring", series->isValueColoringEnabled());
++ customMaterial->setProperty("rootScale", rootNode()->scale().y());
++ customMaterial->setProperty("shaded",
++ series->lightingMode()
++ == QAbstract3DSeries::LightingMode::Shaded);
++ }
++ }
++ }
++
++ void QQuickGraphsBars::updateItemMaterial(QQuick3DModel *item,
++ bool useGradient,
++ bool rangeGradient,
++ const QString &materialName)
++ {
++ QQmlListReference materialsRef(item, "materials");
++
++ bool needNewMaterial = false;
++ if (!materialsRef.size())
++ needNewMaterial = true;
++ else if (materialsRef.at(0)->objectName().contains(QStringLiteral("Instancing"))
++ == materialName.contains(QStringLiteral("Instancing"))) {
++ needNewMaterial = true;
++ }
++
++ if (needNewMaterial) {
++ materialsRef.clear();
++ auto *material = createQmlCustomMaterial(materialName);
++ material->setObjectName(materialName);
++ material->setParent(item);
++ materialsRef.append(material);
++ }
++ int colorStyle;
++ if (!useGradient)
++ colorStyle = 0; // style is uniform
++ else if (!rangeGradient)
++ colorStyle = 1; // style is objectGradient
++ else
++ colorStyle = 2; // style is rangeGradient
++
++ auto *material = qobject_cast<QQuick3DCustomMaterial *>(materialsRef.at(0));
++ material->setProperty("colorStyle", colorStyle);
++ }
++
++ void QQuickGraphsBars::updateMaterialProperties(QQuick3DModel *item,
++ const bool isHighlight,
++ const bool isMultiHighlight,
++ QQuick3DTexture *texture,
++ QColor color,
++ const bool transparency)
++ {
++ QQmlListReference materialsRef(item, "materials");
++ auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(materialsRef.at(0));
++ if (!customMaterial)
++ return;
++ customMaterial->setProperty("transparency", transparency);
++ QVariant textureInputAsVariant = customMaterial->property("custex");
++ QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant
++ .value<QQuick3DShaderUtilsTextureInput *>();
++
++ int colorStyle = customMaterial->property("colorStyle").value<int>();
++ if (colorStyle == 0) {
++ customMaterial->setProperty("uniformColor", color);
++ } else {
++ if (!isHighlight && !isMultiHighlight)
++ textureInput->setTexture(texture);
++ else
++ textureInput->setTexture(isHighlight ? m_highlightTexture : m_multiHighlightTexture);
++ customMaterial->setProperty("isHighlight", isHighlight || isMultiHighlight);
++ }
++ customMaterial->setProperty("specularBrightness", lightStrength() * 0.05);
++ customMaterial->setProperty("rootScale", rootNode()->scale().y());
++ }
++
++ void QQuickGraphsBars::removeBarModels()
++ {
++ for (const auto list : std::as_const(m_barModelsMap)) {
++ for (auto barModel : std::as_const(*list)) {
++ deleteBarModels(barModel->model);
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ deleteBarItemHolders(barModel->instancing);
++ deleteBarItemHolders(barModel->selectionInstancing);
++ deleteBarItemHolders(barModel->multiSelectionInstancing);
++ deleteBarModels(barModel->selectedModel);
++ deleteBarModels(barModel->multiSelectedModel);
++ }
++ delete barModel;
++ }
++ delete list;
++ }
++
++ m_barModelsMap.clear();
++ setSelectedBar(m_selectedBar, m_selectedBarSeries, false);
++ }
++
++ void QQuickGraphsBars::deleteBarModels(QQuick3DModel *model)
++ {
++ if (model) {
++ model->setPickable(false);
++ model->setVisible(false);
++ QQmlListReference materialsRef(model, "materials");
++ if (materialsRef.size()) {
++ auto material = materialsRef.at(0);
++ delete material;
++ }
++ delete model;
++ }
++ }
++
++ void QQuickGraphsBars::deleteBarItemHolders(BarInstancing *instancing)
++ {
++ if (instancing) {
++ const QList<BarItemHolder *> barItemList = instancing->dataArray();
++ for (const auto bih : barItemList)
++ delete bih;
++ instancing->clearDataArray();
++ }
++ }
++
++ QQuick3DTexture *QQuickGraphsBars::createTexture()
++ {
++ QQuick3DTexture *texture = new QQuick3DTexture();
++ texture->setParent(this);
++ texture->setRotationUV(-90.0f);
++ texture->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
++ texture->setVerticalTiling(QQuick3DTexture::ClampToEdge);
++ QQuickGraphsTextureData *textureData = new QQuickGraphsTextureData();
++ textureData->setParent(texture);
++ textureData->setParentItem(texture);
++ texture->setTextureData(textureData);
++
++ return texture;
++ }
++
++ bool QQuickGraphsBars::doPicking(QPointF position)
++ {
++ if (!QQuickGraphsItem::doPicking(position))
++ return false;
++
++ Q_TRACE_SCOPE(QGraphs3DBarsDoPicking, position.x(), position.y());
++
++ m_selectionDirty = true;
++ QList<QQuick3DPickResult> pickResults;
++ if (m_customView)
++ pickResults = m_customView->pickAll(position.x(), position.y());
++ else
++ pickResults = pickAll(position.x(), position.y());
++ QQuick3DModel *selectedModel = nullptr;
++ QVector3D instancePos = {.0f, .0f, .0f};
++ if (!selectionMode().testFlag(QtGraphs3D::SelectionFlag::None)) {
++ if (!pickResults.isEmpty()) {
++ for (const auto &picked : std::as_const(pickResults)) {
++ if (const auto &hit = picked.objectHit()) {
++ if (hit == backgroundBB() || hit == background()) {
++ resetClickedStatus();
++ continue;
++ } else if (hit->objectName().contains(QStringLiteral("BarModel"))) {
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ selectedModel = hit;
++ for (const auto barlist : std::as_const(m_barModelsMap)) {
++ for (const auto barModel : std::as_const(*barlist)) {
++ if (barModel->model == selectedModel) {
++ setSelectedBar(barModel->coord,
++ m_barModelsMap.key(barlist),
++ false);
++ }
++ }
++ }
++ break;
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ BarInstancing *barIns = static_cast<BarInstancing *>(hit->instancing());
++ // Prevents to select bars with a height of 0 which affect picking.
++ if (!barIns->dataArray().isEmpty()
++ && barIns->dataArray().at(picked.instanceIndex())->heightValue
++ != 0) {
++ selectedModel = hit;
++ instancePos = selectedModel->instancing()->instancePosition(
++ picked.instanceIndex());
++ for (const auto barlist : std::as_const(m_barModelsMap)) {
++ for (const auto barModel : std::as_const(*barlist)) {
++ QList<BarItemHolder *> barItemList = barModel->instancing
++ ->dataArray();
++ for (const auto bih : std::as_const(barItemList)) {
++ if (bih->position == instancePos) {
++ setSelectedBar(bih->coord,
++ m_barModelsMap.key(barlist),
++ false);
++ qCDebug(lcInput3D) << "picking result:"
++ << "\n instance position:" << bih->position
++ << "\n picked item column and row position:"
++ << bih->coord
++ << "\n picked item value:"
++ << m_barModelsMap.key(barlist)->dataProxy()
++ ->itemAt(bih->coord).value();
++ if (isSliceEnabled())
++ setSliceActivatedChanged(true);
++ }
++ }
++ }
++ }
++ break;
++ }
++ }
++ } else if (hit->objectName().contains(QStringLiteral("ElementAxis"))) {
++ QPoint coord = invalidSelectionPosition();
++ if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
++ && selectedAxis() == axisX()) {
++ // Use row from previous selection in case of row + column mode
++ int previousRow = qMax(0, m_selectedBar.x());
++ coord = QPoint(previousRow, selectedLabelIndex());
++ } else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
++ && selectedAxis() == axisZ()) {
++ // Use column from previous selection in case of row + column mode
++ int previousCol = qMax(0, m_selectedBar.y());
++ coord = QPoint(selectedLabelIndex(), previousCol);
++ }
++ for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
++ if (it.key()->isVisible())
++ setSelectedBar(coord, it.key(), false);
++ }
++ break;
++ }
++ }
++ }
++ } else {
++ resetClickedStatus();
++ }
++ }
++ return true;
++ }
++
++ bool QQuickGraphsBars::doRayPicking(QVector3D origin, QVector3D direction)
++ {
++ if (!QQuickGraphsItem::doRayPicking(origin, direction))
++ return false;
++
++ Q_TRACE_SCOPE(QGraphs3DBarsDoRayPicking, origin.x(), origin.y(), origin.z(), direction.x(),
++ direction.y(), direction.z());
++
++ m_selectionDirty = true;
++ QList<QQuick3DPickResult> pickResults;
++ if (m_customView)
++ pickResults = m_customView->rayPickAll(origin, direction);
++ else
++ pickResults = rayPickAll(origin, direction);
++ QQuick3DModel *selectedModel = nullptr;
++ QVector3D instancePos = {.0f, .0f, .0f};
++ if (!selectionMode().testFlag(QtGraphs3D::SelectionFlag::None)) {
++ if (!pickResults.isEmpty()) {
++ for (const auto &picked : std::as_const(pickResults)) {
++ if (const auto &hit = picked.objectHit()) {
++ if (hit == backgroundBB() || hit == background()) {
++ resetClickedStatus();
++ continue;
++ } else if (hit->objectName().contains(QStringLiteral("BarModel"))) {
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ selectedModel = hit;
++ for (const auto barlist : std::as_const(m_barModelsMap)) {
++ for (const auto barModel : std::as_const(*barlist)) {
++ if (barModel->model == selectedModel) {
++ setSelectedBar(barModel->coord,
++ m_barModelsMap.key(barlist),
++ false);
++ }
++ }
++ }
++ break;
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ BarInstancing *barIns = static_cast<BarInstancing *>(hit->instancing());
++ // Prevents to select bars with a height of 0 which affect picking.
++ if (!barIns->dataArray().isEmpty()
++ && barIns->dataArray().at(picked.instanceIndex())->heightValue
++ != 0) {
++ selectedModel = hit;
++ instancePos = selectedModel->instancing()->instancePosition(
++ picked.instanceIndex());
++ for (const auto barlist : std::as_const(m_barModelsMap)) {
++ for (const auto barModel : std::as_const(*barlist)) {
++ QList<BarItemHolder *> barItemList = barModel->instancing
++ ->dataArray();
++ for (const auto bih : std::as_const(barItemList)) {
++ if (bih->position == instancePos) {
++ setSelectedBar(bih->coord,
++ m_barModelsMap.key(barlist),
++ false);
++ qCDebug(lcInput3D) << "picking result:"
++ << "\n instance position:" << bih->position
++ << "\n picked item column and row position:"
++ << bih->coord
++ << "\n picked item value:"
++ << m_barModelsMap.key(barlist)->dataProxy()
++ ->itemAt(bih->coord).value();
++ if (isSliceEnabled())
++ setSliceActivatedChanged(true);
++ }
++ }
++ }
++ }
++ break;
++ }
++ }
++ } else if (hit->objectName().contains(QStringLiteral("ElementAxis"))) {
++ QPoint coord = invalidSelectionPosition();
++ if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column)
++ && selectedAxis() == axisX()) {
++ // Use row from previous selection in case of row + column mode
++ int previousRow = qMax(0, m_selectedBar.x());
++ coord = QPoint(previousRow, selectedLabelIndex());
++ } else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)
++ && selectedAxis() == axisZ()) {
++ // Use column from previous selection in case of row + column mode
++ int previousCol = qMax(0, m_selectedBar.y());
++ coord = QPoint(selectedLabelIndex(), previousCol);
++ }
++ for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
++ if (it.key()->isVisible())
++ setSelectedBar(coord, it.key(), false);
++ }
++ break;
++ }
++ }
++ }
++ } else {
++ resetClickedStatus();
++ }
++ }
++ return true;
++ }
++
++ QAbstract3DAxis *QQuickGraphsBars::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation)
++ {
++ QAbstract3DAxis *defaultAxis = 0;
++
++ if (orientation == QAbstract3DAxis::AxisOrientation::Y)
++ defaultAxis = createDefaultValueAxis();
++ else
++ defaultAxis = createDefaultCategoryAxis();
++
++ return defaultAxis;
++ }
++
++ void QQuickGraphsBars::adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series)
++ {
++ const QBarDataProxy *proxy = 0;
++ if (series)
++ proxy = series->dataProxy();
++
++ if (!proxy)
++ pos = invalidSelectionPosition();
++
++ if (pos != invalidSelectionPosition()) {
++ qsizetype maxRow = proxy->rowCount() - 1;
++ qsizetype maxCol = (pos.x() <= maxRow && pos.x() >= 0 && !proxy->rowAt(pos.x()).isEmpty())
++ ? proxy->rowAt(pos.x()).size() - 1
++ : -1;
++
++ if (pos.x() < 0 || pos.x() > maxRow || pos.y() < 0 || pos.y() > maxCol)
++ pos = invalidSelectionPosition();
++ }
++ }
++
++ void QQuickGraphsBars::setSelectedBar(QPoint coord, QBar3DSeries *series, bool enterSlice)
++ {
++ // If the selection targets non-existent bar, clear selection instead.
++ QPoint pos = coord;
++
++ // Series may already have been removed, so check it before setting the
++ // selection.
++ if (!m_seriesList.contains(series))
++ series = nullptr;
++
++ adjustSelectionPosition(pos, series);
++
++ if (series && selectionMode().testFlag(QtGraphs3D::SelectionFlag::Slice)) {
++ // If the selected bar is outside data window, or there is no visible
++ // selected bar, disable slicing.
++ if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max() || pos.y() < m_axisX->min()
++ || pos.y() > m_axisX->max() || !series->isVisible()) {
++ scene()->setSlicingActive(false);
++ } else if (enterSlice) {
++ scene()->setSlicingActive(true);
++ }
++ emitNeedRender();
++ }
++
++ if (pos != m_selectedBar || series != m_selectedBarSeries) {
++ bool seriesChanged = (series != m_selectedBarSeries);
++ m_selectedBar = pos;
++ m_selectedBarSeries = series;
++ m_changeTracker.selectedBarChanged = true;
++ checkSliceEnabled();
++
++ // Clear selection from other series and finally set new selection to the
++ // specified series
++ for (QAbstract3DSeries *otherSeries : std::as_const(m_seriesList)) {
++ QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(otherSeries);
++ if (barSeries != m_selectedBarSeries)
++ barSeries->d_func()->setSelectedBar(invalidSelectionPosition());
++ }
++ if (m_selectedBarSeries)
++ m_selectedBarSeries->d_func()->setSelectedBar(m_selectedBar);
++
++ if (seriesChanged)
++ emit selectedSeriesChanged(m_selectedBarSeries);
++
++ if (pos == invalidSelectionPosition())
++ resetClickedStatus();
++
++ setSeriesVisualsDirty(true);
++ emitNeedRender();
++ }
++ }
++
++ void QQuickGraphsBars::updateSelectedBar()
++ {
++ for (auto it = m_barModelsMap.begin(); it != m_barModelsMap.end(); it++) {
++ if (m_selectedBarSeries && it.key()->isVisible()) {
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ for (auto barList : std::as_const(*it.value())) {
++ QQuickGraphsBars::SelectionType selectionType = isSelected(barList->coord.x(),
++ barList->coord.y(),
++ it.key());
++ switch (selectionType) {
++ case QQuickGraphsBars::SelectionItem: {
++ updateMaterialProperties(barList->model,
++ true,
++ false,
++ barList->texture,
++ it.key()->singleHighlightColor());
++
++ m_selectedBarPos = barList->model->position();
++ QString label = (m_selectedBarSeries->d_func()->itemLabel());
++
++ if (barList->heightValue >= 0.0f) {
++ m_selectedBarPos.setY(m_selectedBarPos.y() + barList->heightValue
++ + 0.2f);
++ } else {
++ m_selectedBarPos.setY(m_selectedBarPos.y() + barList->heightValue
++ - 0.2f);
++ }
++
++ updateItemLabel(m_selectedBarPos);
++ itemLabel()->setVisible(m_selectedBarSeries->isItemLabelVisible()
++ && theme()->labelsVisible());
++ itemLabel()->setProperty("labelText", label);
++ if (!label.compare(QString(hiddenLabelTag)))
++ itemLabel()->setVisible(false);
++ if (isSliceEnabled())
++ updateSliceItemLabel(label, m_selectedBarPos);
++ break;
++ }
++ case QQuickGraphsBars::SelectionRow:
++ case QQuickGraphsBars::SelectionColumn: {
++ updateMaterialProperties(barList->model,
++ false,
++ true,
++ barList->texture,
++ it.key()->multiHighlightColor());
++ break;
++ }
++ default:
++ break;
++ }
++ }
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ QList<BarModel *> barList = *m_barModelsMap.value(it.key());
++ deleteBarItemHolders(barList.at(0)->selectionInstancing);
++ deleteBarItemHolders(barList.at(0)->multiSelectionInstancing);
++ createBarItemHolders(it.key(), barList, false);
++ }
++ }
++ }
++ }
++
++ QQuickGraphsBars::SelectionType QQuickGraphsBars::isSelected(int row, int bar, QBar3DSeries *series)
++ {
++ QQuickGraphsBars::SelectionType isSelectedType = QQuickGraphsBars::SelectionNone;
++ if ((selectionMode().testFlag(QtGraphs3D::SelectionFlag::MultiSeries) && m_selectedBarSeries)
++ || series == m_selectedBarSeries) {
++ if (row == m_selectedBar.x() && bar == m_selectedBar.y()
++ && (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Item))) {
++ isSelectedType = QQuickGraphsBars::SelectionItem;
++ } else if (row == m_selectedBar.x()
++ && (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row))) {
++ isSelectedType = QQuickGraphsBars::SelectionRow;
++ } else if (bar == m_selectedBar.y()
++ && (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column))) {
++ isSelectedType = QQuickGraphsBars::SelectionColumn;
++ }
++ }
++
++ return isSelectedType;
++ }
++
++ void QQuickGraphsBars::updateSliceItemLabel(const QString &label, QVector3D position)
++ {
++ QQuickGraphsItem::updateSliceItemLabel(label, position);
++
++ QFontMetrics fm(theme()->labelFont());
++ float textPadding = theme()->labelFont().pointSizeF() * .7f;
++ float labelHeight = fm.height() + textPadding;
++ float labelWidth = fm.horizontalAdvance(label) + textPadding;
++ sliceItemLabel()->setProperty("labelWidth", labelWidth);
++ sliceItemLabel()->setProperty("labelHeight", labelHeight);
++ QVector3D slicePos = position;
++ if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column))
++ slicePos.setX(slicePos.z());
++ else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row))
++ slicePos.setX(slicePos.x());
++ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axisY());
++ if (valueAxis->reversed())
++ slicePos.setY(slicePos.y() - (textPadding * .06f));
++ else
++ slicePos.setY(slicePos.y() + (textPadding * .06f));
++ slicePos.setZ(.1f);
++ sliceItemLabel()->setPosition(slicePos);
++ sliceItemLabel()->setProperty("labelText", label);
++ if (!label.compare(QString(hiddenLabelTag)))
++ sliceItemLabel()->setVisible(false);
++ sliceItemLabel()->setEulerRotation(QVector3D(0.0f, 0.0f, 90.0f));
++ sliceItemLabel()->setVisible(m_selectedBarSeries->isItemLabelVisible()
++ && theme()->labelsVisible());
++ }
++
++ void QQuickGraphsBars::resetClickedStatus()
++ {
++ m_selectedBarPos = QVector3D(0.0f, 0.0f, 0.0f);
++ m_selectedBar = QQuickGraphsBars::invalidSelectionPosition();
++ m_selectedBarSeries = 0;
++ clearSelection();
++
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ for (const auto barList : std::as_const(m_barModelsMap)) {
++ QList<BarItemHolder *> barItemList = barList->at(0)->instancing->dataArray();
++ for (auto bih : std::as_const(barItemList))
++ bih->selectedBar = false;
++ }
++ }
++
++ if (sliceView() && sliceView()->isVisible()) {
++ setSliceActivatedChanged(true);
++ m_selectionDirty = true;
++ }
++ setSeriesVisualsDirty(true);
++ }
++
++ void QQuickGraphsBars::createSliceView()
++ {
++ setSliceOrthoProjection(false);
++ QQuickGraphsItem::createSliceView();
++
++ Q_TRACE_SCOPE(QGraphs3DBarsCreateSliceView);
++
++ QList<QBar3DSeries *> barSeries = barSeriesList();
++ for (const auto &barSeries : std::as_const(barSeries)) {
++ QList<BarModel *> &slicedBarList = m_slicedBarModels[barSeries];
++
++ if (slicedBarList.isEmpty()) {
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ qsizetype dataRowIndex = m_minRow;
++ qsizetype newRowSize = qMin(barSeries->dataProxy()->rowCount() - dataRowIndex,
++ m_newRows);
++ qsizetype newColSize = 0;
++ if (newRowSize) {
++ const QBarDataRow *dataRow = &barSeries->dataProxy()->rowAt(dataRowIndex);
++ if (dataRow) {
++ qsizetype dataColIndex = m_minCol;
++ newColSize = qMin(dataRow->size() - dataColIndex, m_newCols);
++ }
++ }
++ qsizetype slicedBarListSize = 0;
++
++ if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row))
++ slicedBarListSize = newColSize;
++ else if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Column))
++ slicedBarListSize = newRowSize;
++
++ for (int ind = 0; ind < slicedBarListSize; ++ind) {
++ QQuick3DModel *model = createDataItem(sliceView()->scene(), barSeries);
++ model->setVisible(false);
++ BarModel *barModel = new BarModel();
++ barModel->model = model;
++
++ if (!slicedBarList.contains(barModel))
++ slicedBarList.append(barModel);
++ }
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ BarModel *barInstancing = new BarModel();
++
++ if (barInstancing->selectionInstancing == nullptr) {
++ barInstancing->selectionInstancing = new BarInstancing;
++ barInstancing->selectionInstancing->setParent(barSeries);
++ barInstancing->multiSelectionInstancing = new BarInstancing;
++ barInstancing->multiSelectionInstancing->setParent(barSeries);
++ }
++
++ if (barInstancing->selectedModel == nullptr) {
++ barInstancing->selectedModel = createDataItem(sliceView()->scene(), barSeries);
++ barInstancing->selectedModel->setInstancing(barInstancing->selectionInstancing);
++ barInstancing->selectedModel->setVisible(false);
++ barInstancing->multiSelectedModel = createDataItem(sliceView()->scene(),
++ barSeries);
++ barInstancing->multiSelectedModel->setInstancing(
++ barInstancing->multiSelectionInstancing);
++ barInstancing->multiSelectedModel->setVisible(false);
++ }
++
++ if (!slicedBarList.contains(barInstancing))
++ slicedBarList.append(barInstancing);
++ }
++ }
++ }
++ }
++
++ void QQuickGraphsBars::toggleSliceGraph()
++ {
++ if (m_selectionDirty)
++ QQuickGraphsItem::toggleSliceGraph();
++
++ if (sliceView() && !sliceView()->isVisible()) {
++ removeSlicedBarModels();
++ m_changeTracker.selectedBarChanged = false;
++ return;
++ }
++
++ qsizetype index = 0;
++ bool rowMode = selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row);
++ for (auto it = m_slicedBarModels.begin(); it != m_slicedBarModels.end(); it++) {
++ bool useGradient = it.key()->d_func()->isUsingGradient();
++ bool rangeGradient = (useGradient
++ && it.key()->d_func()->m_colorStyle
++ == QGraphsTheme::ColorStyle::RangeGradient);
++ QList<BarModel *> barList = *m_barModelsMap.value(it.key());
++ QList<BarModel *> sliceList = it.value();
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ for (int ind = 0; ind < it.value().count(); ++ind) {
++ if (rowMode)
++ index = (m_selectedBar.x() * it.key()->dataProxy()->colCount()) + ind;
++ else
++ index = m_selectedBar.y() + (ind * it.key()->dataProxy()->colCount());
++ bool visible = ((m_selectedBarSeries == it.key()
++ || selectionMode().testFlag(QtGraphs3D::SelectionFlag::MultiSeries))
++ && it.key()->isVisible());
++ if (index < barList.size() && m_selectedBar != invalidSelectionPosition()) {
++ BarModel *sliceBarModel = sliceList.at(ind);
++ BarModel *barModel = barList.at(index);
++
++ sliceBarModel->model->setVisible(visible);
++ if (rowMode) {
++ sliceBarModel->model->setPosition(
++ QVector3D(barModel->model->x(), barModel->model->y(), 0.0f));
++ } else {
++ sliceBarModel->model->setX(barModel->model->z()
++ - (barList.at(0)->visualIndex * .1f));
++ sliceBarModel->model->setY(barModel->model->y());
++ sliceBarModel->model->setZ(0.0f);
++ }
++ sliceBarModel->model->setScale(barModel->model->scale());
++
++ updateItemMaterial(sliceBarModel->model,
++ useGradient,
++ rangeGradient,
++ QStringLiteral(":/materials/BarsMaterial"));
++
++ if (barModel->coord == m_selectedBar
++ && selectionMode().testFlag(QtGraphs3D::SelectionFlag::Item)) {
++ updateMaterialProperties(sliceBarModel->model,
++ true,
++ false,
++ barList.at(index)->texture,
++ it.key()->singleHighlightColor());
++ updateSliceItemLabel(m_selectedBarSeries->itemLabel(), m_selectedBarPos);
++ } else {
++ updateMaterialProperties(sliceBarModel->model,
++ false,
++ false,
++ barList.at(index)->texture,
++ it.key()->baseColor());
++ }
++ } else {
++ setSliceActivatedChanged(true);
++ QQuickGraphsItem::toggleSliceGraph();
++ setSliceEnabled(false);
++ return;
++ }
++ }
++ setSliceActivatedChanged(false);
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ deleteBarItemHolders(sliceList.at(0)->selectionInstancing);
++ deleteBarItemHolders(sliceList.at(0)->multiSelectionInstancing);
++ createBarItemHolders(it.key(), barList, true);
++ setSliceActivatedChanged(false);
++ }
++ }
++ }
++
++ void QQuickGraphsBars::handleLabelCountChanged(QQuick3DRepeater *repeater, QColor axisLabelColor)
++ {
++ QQuickGraphsItem::handleLabelCountChanged(repeater, axisLabelColor);
++
++ if (repeater == repeaterX())
++ handleDataColumnLabelsChanged();
++ if (repeater == repeaterZ())
++ handleDataRowLabelsChanged();
++ }
++
++ void QQuickGraphsBars::removeSlicedBarModels()
++ {
++ for (auto &list : std::as_const(m_slicedBarModels)) {
++ for (auto barModel : list) {
++ if (optimizationHint() == QtGraphs3D::OptimizationHint::Legacy) {
++ deleteBarModels(barModel->model);
++ } else if (optimizationHint() == QtGraphs3D::OptimizationHint::Default) {
++ deleteBarItemHolders(barModel->selectionInstancing);
++ deleteBarItemHolders(barModel->multiSelectionInstancing);
++ deleteBarModels(barModel->selectedModel);
++ deleteBarModels(barModel->multiSelectedModel);
++ }
++ delete barModel;
++ }
++ }
++
++ m_slicedBarModels.clear();
++ }
++
++ void QQuickGraphsBars::createBarItemHolders(QBar3DSeries *series,
++ QList<BarModel *> barList,
++ bool slice)
++ {
++ bool useGradient = series->d_func()->isUsingGradient();
++ bool rangeGradient = (useGradient
++ && series->d_func()->m_colorStyle == QGraphsTheme::ColorStyle::RangeGradient);
++ bool visible = ((m_selectedBarSeries == series
++ || selectionMode().testFlag(QtGraphs3D::SelectionFlag::MultiSeries))
++ && series->isVisible());
++ QList<BarItemHolder *> barItemList;
++ QList<BarItemHolder *> multiBarItemList;
++ QQuick3DModel *selectedModel = nullptr;
++ QQuick3DModel *multiSelectedModel = nullptr;
++ QList<BarItemHolder *> selectedItem;
++ QList<BarItemHolder *> multiSelectedItems;
++ BarInstancing *instancing;
++ BarInstancing *multiInstancing;
++
++ if (slice) {
++ QList<BarModel *> sliceBarList = m_slicedBarModels.value(series);
++ barItemList = barList.at(0)->selectionInstancing->dataArray();
++ multiBarItemList = barList.at(0)->multiSelectionInstancing->dataArray();
++ selectedModel = sliceBarList.at(0)->selectedModel;
++ multiSelectedModel = sliceBarList.at(0)->multiSelectedModel;
++ instancing = sliceBarList.at(0)->selectionInstancing;
++ multiInstancing = sliceBarList.at(0)->multiSelectionInstancing;
++ } else {
++ barItemList = barList.at(0)->instancing->dataArray();
++ selectedModel = barList.at(0)->selectedModel;
++ multiSelectedModel = barList.at(0)->multiSelectedModel;
++ instancing = barList.at(0)->selectionInstancing;
++ multiInstancing = barList.at(0)->multiSelectionInstancing;
++ }
++
++ for (const auto bih : std::as_const(barItemList)) {
++ QQuickGraphsBars::SelectionType selectionType = isSelected(bih->coord.x(),
++ bih->coord.y(),
++ series);
++ switch (selectionType) {
++ case QQuickGraphsBars::SelectionItem: {
++ updateItemMaterial(selectedModel,
++ useGradient,
++ rangeGradient,
++ QStringLiteral(":/materials/BarsMaterialInstancing"));
++ updateMaterialProperties(selectedModel,
++ true,
++ false,
++ barList.at(0)->texture,
++ QColor(Qt::white));
++ if (!slice)
++ bih->selectedBar = true;
++ selectedModel->setVisible(visible);
++ BarItemHolder *selectedBih = new BarItemHolder();
++ selectedBih->selectedBar = false;
++ selectedBih->color = series->singleHighlightColor();
++ selectedBih->coord = bih->coord;
++ selectedBih->rotation = bih->rotation;
++ selectedBih->heightValue = bih->heightValue;
++ selectedBih->position = bih->position;
++ selectedBih->scale = bih->scale;
++
++ QString label = m_selectedBarSeries->itemLabel();
++ if (slice) {
++ if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)) {
++ selectedBih->position.setZ(.0f);
++ } else {
++ selectedBih->position.setX(selectedBih->position.z()
++ - (barList.at(0)->visualIndex * .1f));
++ selectedBih->position.setZ(.0f);
++ }
++ updateSliceItemLabel(label, m_selectedBarPos);
++ }
++
++ selectedItem.push_back(selectedBih);
++ instancing->setDataArray(selectedItem);
++
++ m_selectedBarPos = bih->position;
++
++ if (bih->heightValue >= 0.0f)
++ m_selectedBarPos.setY(m_selectedBarPos.y() + bih->heightValue + 0.2f);
++ else
++ m_selectedBarPos.setY(m_selectedBarPos.y() + bih->heightValue - 0.2f);
++
++ updateItemLabel(m_selectedBarPos);
++ itemLabel()->setVisible(m_selectedBarSeries->isItemLabelVisible()
++ && theme()->labelsVisible());
++ itemLabel()->setProperty("labelText", label);
++ if (!label.compare(QString(hiddenLabelTag)))
++ itemLabel()->setVisible(false);
++ if (isSliceEnabled())
++ updateSliceItemLabel(label, m_selectedBarPos);
++ break;
++ }
++ case QQuickGraphsBars::SelectionRow:
++ case QQuickGraphsBars::SelectionColumn: {
++ updateItemMaterial(multiSelectedModel,
++ useGradient,
++ rangeGradient,
++ QStringLiteral(":/materials/BarsMaterialInstancing"));
++ updateMaterialProperties(multiSelectedModel,
++ false,
++ true,
++ barList.at(0)->texture,
++ QColor(Qt::white));
++ if (!slice)
++ bih->selectedBar = true;
++ multiSelectedModel->setVisible(visible);
++ BarItemHolder *selectedBih = new BarItemHolder();
++ selectedBih->selectedBar = false;
++ selectedBih->color = series->multiHighlightColor();
++ selectedBih->coord = bih->coord;
++ selectedBih->rotation = bih->rotation;
++ selectedBih->heightValue = bih->heightValue;
++ selectedBih->position = bih->position;
++ selectedBih->scale = bih->scale;
++
++ multiSelectedItems.push_back(selectedBih);
++ multiInstancing->setDataArray(multiSelectedItems);
++ break;
++ }
++ default:
++ break;
++ }
++ }
++
++ if (slice) {
++ for (const auto bih : std::as_const(multiBarItemList)) {
++ updateItemMaterial(multiSelectedModel,
++ useGradient,
++ rangeGradient,
++ QStringLiteral(":/materials/BarsMaterialInstancing"));
++ updateMaterialProperties(multiSelectedModel,
++ false,
++ false,
++ barList.at(0)->texture,
++ QColor(Qt::white));
++
++ multiSelectedModel->setVisible(visible);
++ BarItemHolder *selectedBih = new BarItemHolder();
++ selectedBih->selectedBar = false;
++ selectedBih->color = series->baseColor();
++ selectedBih->coord = bih->coord;
++ selectedBih->rotation = bih->rotation;
++ selectedBih->heightValue = bih->heightValue;
++ selectedBih->position = bih->position;
++ selectedBih->scale = bih->scale;
++
++ if (selectionMode().testFlag(QtGraphs3D::SelectionFlag::Row)) {
++ selectedBih->position.setZ(.0f);
++ } else {
++ selectedBih->position.setX(selectedBih->position.z()
++ - (barList.at(0)->visualIndex * .1f));
++ selectedBih->position.setZ(.0f);
++ }
++
++ multiSelectedItems.push_back(selectedBih);
++ multiInstancing->setDataArray(multiSelectedItems);
++ }
++ }
++ }
++
++ void QQuickGraphsBars::updateSelectionMode(QtGraphs3D::SelectionFlags mode)
++ {
++ checkSliceEnabled();
++ if (!sliceView())
++ createSliceView();
++
++ bool validSlice = mode.testFlag(QtGraphs3D::SelectionFlag::Slice) && m_selectedBarSeries;
++ if (sliceView() && sliceView()->isVisible()) {
++ if (validSlice) {
++ removeSlicedBarModels();
++ createSliceView();
++ toggleSliceGraph();
++ } else {
++ m_selectionDirty = true;
++ setSliceActivatedChanged(true);
++ }
++ } else if (validSlice) {
++ m_selectionDirty = true;
++ setSliceActivatedChanged(true);
++ }
++
++ setSeriesVisualsDirty(true);
++ itemLabel()->setVisible(false);
++ if (sliceView() && !mode.testFlag(QtGraphs3D::SelectionFlag::Item))
++ sliceItemLabel()->setVisible(false);
++ }
++
++ void QQuickGraphsBars::updateBarSpecs(float thicknessRatio, QSizeF spacing, bool relative)
++ {
++ // Convert ratio to QSizeF, as we need it in that format for autoscaling
++ // calculations
++ m_cachedBarThickness.setWidth(1.0);
++ m_cachedBarThickness.setHeight(1.0f / thicknessRatio);
++
++ if (relative) {
++ m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2) * (spacing.width() + 1.0f));
++ m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2)
++ * (spacing.height() + 1.0f));
++ } else {
++ m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2;
++ }
++
++ m_changeTracker.axisRangeChanged = true;
++ m_changeTracker.selectedBarChanged = true;
++
++ // Calculate here and at setting sample space
++ calculateSceneScalingFactors();
++ setSeriesVisualsDirty(true);
++ }
++
++ void QQuickGraphsBars::updateBarSeriesMargin(QSizeF margin)
++ {
++ m_cachedBarSeriesMargin = margin;
++ calculateSeriesStartPosition();
++ calculateSceneScalingFactors();
++ setSeriesVisualsDirty(true);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qquickgraphsbars_p.cpp"