File indexing completed on 2024-05-05 16:16:43

0001 /*
0002  * This file is part of KQuickCharts
0003  * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006  */
0007 
0008 #include "LineChartNode.h"
0009 
0010 #include <QtMath>
0011 
0012 #include "LineChartMaterial.h"
0013 #include "LineSegmentNode.h"
0014 
0015 static const int MaxPointsInSegment = 10;
0016 
0017 qreal calculateNormalizedLineWidth(qreal pixelWidth, const QRectF &rect)
0018 {
0019     if (qFuzzyIsNull(pixelWidth)) {
0020         return 0.0;
0021     }
0022 
0023     qreal min = 0.6 / std::max(rect.width(), rect.height());
0024     return std::max(min, (pixelWidth - 1.0) / (std::min(rect.width(), rect.height()) * 4.0));
0025 }
0026 
0027 LineChartNode::LineChartNode()
0028 {
0029 }
0030 
0031 LineChartNode::~LineChartNode()
0032 {
0033 }
0034 
0035 void LineChartNode::setRect(const QRectF &rect, qreal devicePixelRatio)
0036 {
0037     if (rect == m_rect) {
0038         return;
0039     }
0040 
0041     m_rect = rect;
0042     m_aspect = m_rect.height() / m_rect.width();
0043 
0044     auto nativeSize = QSizeF(m_rect.width() * devicePixelRatio, m_rect.height() * devicePixelRatio);
0045     auto diagonal = std::sqrt(nativeSize.width() * nativeSize.width() + nativeSize.height() * nativeSize.height());
0046     m_smoothing = 1.0 / diagonal;
0047 }
0048 
0049 void LineChartNode::setLineWidth(float width)
0050 {
0051     if (qFuzzyCompare(width, m_lineWidth)) {
0052         return;
0053     }
0054 
0055     m_lineWidth = width;
0056 }
0057 
0058 void LineChartNode::setLineColor(const QColor &color)
0059 {
0060     if (m_lineColor == color) {
0061         return;
0062     }
0063 
0064     m_lineColor = color;
0065 }
0066 
0067 void LineChartNode::setFillColor(const QColor &color)
0068 {
0069     if (m_fillColor == color) {
0070         return;
0071     }
0072 
0073     m_fillColor = color;
0074 }
0075 
0076 void LineChartNode::setValues(const QVector<QVector2D> &values)
0077 {
0078     m_values = values;
0079 }
0080 
0081 void LineChartNode::updatePoints()
0082 {
0083     if (m_values.isEmpty()) {
0084         return;
0085     }
0086 
0087     auto segmentCount = qCeil(qreal(m_values.count()) / MaxPointsInSegment);
0088 
0089     auto currentX = m_rect.left();
0090     auto pointStart = 0;
0091     auto pointsPerSegment = MaxPointsInSegment;
0092 
0093     for (int i = 0; i < segmentCount; ++i) {
0094         if (i >= childCount()) {
0095             appendChildNode(new LineSegmentNode{});
0096         }
0097 
0098         auto segment = static_cast<LineSegmentNode *>(childAtIndex(i));
0099 
0100         auto segmentPoints = m_values.mid(pointStart, pointsPerSegment);
0101         pointStart += pointsPerSegment;
0102 
0103         auto segmentWidth = segmentPoints.last().x() - currentX;
0104         auto rect = QRectF(currentX, m_rect.top(), segmentWidth, m_rect.height());
0105 
0106         segment->setRect(rect);
0107         segment->setAspect(segmentWidth / m_rect.width(), m_aspect);
0108         segment->setSmoothing(m_smoothing);
0109         segment->setLineWidth(calculateNormalizedLineWidth(m_lineWidth, m_rect));
0110         segment->setLineColor(m_lineColor);
0111         segment->setFillColor(m_fillColor);
0112         segment->setValues(segmentPoints);
0113         segment->setFarLeft(m_values.at(std::max(0, pointStart - pointsPerSegment - 1)));
0114         segment->setFarRight(m_values.at(std::min<int>(m_values.count() - 1, pointStart + 1)));
0115         segment->update();
0116 
0117         currentX += segmentWidth;
0118     }
0119 
0120     while (childCount() > segmentCount) {
0121         auto child = childAtIndex(childCount() - 1);
0122         removeChildNode(child);
0123         delete child;
0124     }
0125 }