// Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE Q_TRACE_PREFIX(qtgraphs, "QT_BEGIN_NAMESPACE" \ "class CustomRenderer;" \ "QT_END_NAMESPACE" ) Q_TRACE_POINT(qtgraphs, QGraphs2DCustomRendererHandlePolish_entry); Q_TRACE_POINT(qtgraphs, QGraphs2DCustomRendererHandlePolish_exit); Q_TRACE_POINT(qtgraphs, QGraphs2DCustomRendererAfterPolish_entry, int cleanupSeriesCount); Q_TRACE_POINT(qtgraphs, QGraphs2DCustomRendererAfterPolish_exit); CustomRenderer::CustomRenderer(QGraphsView *graph, bool clipPlotArea) : QQuickItem(graph) , m_graph(graph) { setFlag(QQuickItem::ItemHasContents); setClip(clipPlotArea); } CustomRenderer::~CustomRenderer() { qDeleteAll(m_groups); } qreal CustomRenderer::mapX(AxisRenderer *axisRenderer, QCustomSeries *series, qreal x) { auto &axisX = axisRenderer->getAxisX(series); auto flipX = axisX.maxValue < axisX.minValue ? -1 : 1; return m_areaWidth * flipX * x * m_maxHorizontal - m_horizontalOffset; } qreal CustomRenderer::mapY(AxisRenderer *axisRenderer, QCustomSeries *series, qreal y) { auto &axisY = axisRenderer->getAxisY(series); if (m_graph->orientation() != Qt::Vertical) y = axisY.maxValue - y; auto flipY = axisY.maxValue < axisY.minValue ? -1 : 1; return m_areaHeight - m_areaHeight * flipY * y * m_maxVertical + m_verticalOffset; } void CustomRenderer::calculateRenderCoordinates(AxisRenderer *axisRenderer, QAbstractSeries *series, qreal origX, qreal origY, qreal *renderX, qreal *renderY) { auto &axisX = axisRenderer->getAxisX(series); auto &axisY = axisRenderer->getAxisY(series); auto flipX = axisX.maxValue < axisX.minValue ? -1 : 1; auto flipY = axisY.maxValue < axisY.minValue ? -1 : 1; *renderX = m_areaWidth * flipX * origX * m_maxHorizontal - m_horizontalOffset; *renderY = m_areaHeight - m_areaHeight * flipY * origY * m_maxVertical + m_verticalOffset; } void CustomRenderer::hideDelegates(QCustomSeries *series) { auto group = m_groups.value(series); if (group && group->currentDataItem) { for (int i = 0; i < group->dataItems.size(); ++i) { auto *dataItem = group->dataItems[i]; dataItem->setVisible(false); } } } void recursivelySetParents(QQuickItem *item) { for (auto &&child : item->children()) { auto childItem = qobject_cast(child); if (childItem) { childItem->setParentItem(item); recursivelySetParents(childItem); } } } void CustomRenderer::handlePolish(QCustomSeries *series) { Q_TRACE(QGraphs2DCustomRendererHandlePolish_entry); auto theme = m_graph->theme(); if (!theme) { qCCritical(lcCritical2D, "Theme not found"); return; } if (!m_graph->m_axisRenderer) { qCCritical(lcCritical2D, "Axis renderer not found."); return; } if (series->dataItems().isEmpty()) { auto group = m_groups.value(series); if (group) { for (auto m : std::as_const(group->dataItems)) m->deleteLater(); group->dataItems.clear(); } return; } if (width() <= 0 || height() <= 0) return; m_areaWidth = width(); m_areaHeight = height(); auto &axisX = m_graph->m_axisRenderer->getAxisX(series); auto &axisY = m_graph->m_axisRenderer->getAxisY(series); m_maxVertical = axisY.valueRange > 0 ? 1.0 / axisY.valueRange : 100.0; m_maxHorizontal = axisX.valueRange > 0 ? 1.0 / axisX.valueRange : 100.0; auto vmin = axisY.minValue > axisY.maxValue ? std::abs(axisY.minValue) : axisY.minValue; m_verticalOffset = (vmin / axisY.valueRange) * m_areaHeight; auto hmin = axisX.minValue > axisX.maxValue ? std::abs(axisX.minValue) : axisX.minValue; m_horizontalOffset = (hmin / axisX.valueRange) * m_areaWidth; if (!m_groups.contains(series)) { DataGroup *group = new DataGroup(); group->series = series; m_groups.insert(series, group); } auto group = m_groups.value(series); qsizetype dataItemCount = series->dataItems().size(); if (series->delegate()) group->currentDataItem = series->delegate(); if (group->currentDataItem != group->previousDataItem) { for (auto &&dataItem : group->dataItems) dataItem->deleteLater(); group->dataItems.clear(); } group->previousDataItem = group->currentDataItem; if (group->currentDataItem) { qsizetype dataItemVisualCount = group->dataItems.size(); if (dataItemVisualCount < dataItemCount) { for (qsizetype i = dataItemVisualCount; i < dataItemCount; ++i) { QQuickItem *item = qobject_cast( group->currentDataItem->create(group->currentDataItem->creationContext())); item->setParent(this); item->setParentItem(this); recursivelySetParents(item); group->dataItems << item; } } else if (dataItemVisualCount > dataItemCount) { for (qsizetype i = dataItemCount; i < dataItemVisualCount; ++i) group->dataItems[i]->deleteLater(); group->dataItems.resize(dataItemCount); } } else if (group->dataItems.size() > 0) { for (auto &&dataItem : group->dataItems) dataItem->deleteLater(); group->dataItems.clear(); } for (auto &&dataItem : group->dataItems) { dataItem->setZ(group->series->zValue()); } if (series->isVisible()) { auto &&dataItems = series->dataItems(); for (int i = 0; i < dataItems.size(); ++i) { if (group->currentDataItem) series->updateDelegate(group->dataItems[i], i); } } else { hideDelegates(series); } Q_TRACE(QGraphs2DCustomRendererHandlePolish_exit); } void CustomRenderer::afterPolish(QList &cleanupSeries) { Q_TRACE(QGraphs2DCustomRendererAfterPolish_entry, static_cast(cleanupSeries.count())); for (auto series : cleanupSeries) { auto customSeries = qobject_cast(series); if (customSeries && m_groups.contains(customSeries)) { auto group = m_groups.value(customSeries); for (auto dataItem : std::as_const(group->dataItems)) dataItem->deleteLater(); delete group; m_groups.remove(customSeries); } } Q_TRACE(QGraphs2DCustomRendererAfterPolish_exit); } void CustomRenderer::updateSeries(QCustomSeries *series) { Q_UNUSED(series); } void CustomRenderer::afterUpdate(QList &cleanupSeries) { Q_UNUSED(cleanupSeries); } QT_END_NAMESPACE