File indexing completed on 2024-10-06 03:40:53
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 "PieChart.h" 0009 0010 #include <QAbstractItemModel> 0011 #include <QDebug> 0012 0013 #include "RangeGroup.h" 0014 #include "datasource/ChartDataSource.h" 0015 #include "scenegraph/PieChartNode.h" 0016 0017 PieChart::PieChart(QQuickItem *parent) 0018 : Chart(parent) 0019 { 0020 setIndexingMode(Chart::IndexSourceValues); 0021 m_range = std::make_unique<RangeGroup>(); 0022 connect(m_range.get(), &RangeGroup::rangeChanged, this, &PieChart::onDataChanged); 0023 } 0024 0025 RangeGroup *PieChart::range() const 0026 { 0027 return m_range.get(); 0028 } 0029 0030 bool PieChart::filled() const 0031 { 0032 return m_filled; 0033 } 0034 0035 void PieChart::setFilled(bool newFilled) 0036 { 0037 if (newFilled == m_filled) { 0038 return; 0039 } 0040 0041 m_filled = newFilled; 0042 update(); 0043 Q_EMIT filledChanged(); 0044 } 0045 0046 qreal PieChart::thickness() const 0047 { 0048 return m_thickness; 0049 } 0050 0051 void PieChart::setThickness(qreal newThickness) 0052 { 0053 if (newThickness == m_thickness) { 0054 return; 0055 } 0056 0057 m_thickness = newThickness; 0058 update(); 0059 Q_EMIT thicknessChanged(); 0060 } 0061 0062 qreal PieChart::spacing() const 0063 { 0064 return m_spacing; 0065 } 0066 0067 void PieChart::setSpacing(qreal newSpacing) 0068 { 0069 if (newSpacing == m_spacing) { 0070 return; 0071 } 0072 0073 m_spacing = newSpacing; 0074 update(); 0075 Q_EMIT spacingChanged(); 0076 } 0077 0078 QColor PieChart::backgroundColor() const 0079 { 0080 return m_backgroundColor; 0081 } 0082 0083 void PieChart::setBackgroundColor(const QColor &color) 0084 { 0085 if (color == m_backgroundColor) { 0086 return; 0087 } 0088 m_backgroundColor = color; 0089 update(); 0090 Q_EMIT backgroundColorChanged(); 0091 } 0092 0093 qreal PieChart::fromAngle() const 0094 { 0095 return m_fromAngle; 0096 } 0097 0098 void PieChart::setFromAngle(qreal newFromAngle) 0099 { 0100 if (qFuzzyCompare(newFromAngle, m_fromAngle)) { 0101 return; 0102 } 0103 0104 m_fromAngle = newFromAngle; 0105 update(); 0106 Q_EMIT fromAngleChanged(); 0107 } 0108 0109 qreal PieChart::toAngle() const 0110 { 0111 return m_toAngle; 0112 } 0113 0114 void PieChart::setToAngle(qreal newToAngle) 0115 { 0116 if (qFuzzyCompare(newToAngle, m_toAngle)) { 0117 return; 0118 } 0119 0120 m_toAngle = newToAngle; 0121 update(); 0122 Q_EMIT toAngleChanged(); 0123 } 0124 0125 bool PieChart::smoothEnds() const 0126 { 0127 return m_smoothEnds; 0128 } 0129 0130 void PieChart::setSmoothEnds(bool newSmoothEnds) 0131 { 0132 if (newSmoothEnds == m_smoothEnds) { 0133 return; 0134 } 0135 0136 m_smoothEnds = newSmoothEnds; 0137 update(); 0138 Q_EMIT smoothEndsChanged(); 0139 } 0140 0141 QSGNode *PieChart::updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) 0142 { 0143 Q_UNUSED(data); 0144 if (!node) { 0145 node = new QSGNode{}; 0146 } 0147 0148 auto sourceCount = valueSources().size(); 0149 0150 if (m_sections.count() < sourceCount) { 0151 return node; 0152 } 0153 0154 auto minDimension = std::min(width(), height()); 0155 0156 float outerRadius = minDimension; 0157 for (int i = 0; i < sourceCount; ++i) { 0158 float innerRadius = i == sourceCount - 1 && m_filled ? 0.0 : outerRadius - m_thickness; 0159 0160 if (node->childCount() <= i) { 0161 node->appendChildNode(new PieChartNode{}); 0162 } 0163 0164 auto pieNode = static_cast<PieChartNode *>(node->childAtIndex(i)); 0165 pieNode->setRect(boundingRect()); 0166 pieNode->setInnerRadius(innerRadius); 0167 pieNode->setOuterRadius(outerRadius); 0168 pieNode->setSections(m_sections.at(i)); 0169 pieNode->setBackgroundColor(m_backgroundColor); 0170 pieNode->setColors(m_colors.at(i)); 0171 pieNode->setFromAngle(m_fromAngle); 0172 pieNode->setToAngle(m_toAngle); 0173 pieNode->setSmoothEnds(m_smoothEnds); 0174 0175 outerRadius = outerRadius - m_thickness - m_spacing; 0176 } 0177 0178 while (node->childCount() > sourceCount) { 0179 auto lastNode = node->childAtIndex(node->childCount() - 1); 0180 node->removeChildNode(lastNode); 0181 delete lastNode; 0182 } 0183 0184 return node; 0185 } 0186 0187 void PieChart::onDataChanged() 0188 { 0189 m_sections.clear(); 0190 m_colors.clear(); 0191 0192 const auto sources = valueSources(); 0193 const auto colors = colorSource(); 0194 0195 if (!colors || sources.isEmpty() || !m_range->isValid()) { 0196 return; 0197 } 0198 0199 auto maximum = [](ChartDataSource *source) { 0200 qreal result = 0.0; 0201 for (int i = 0; i < source->itemCount(); ++i) { 0202 result += source->item(i).toDouble(); 0203 } 0204 return std::max(result, source->maximum().toDouble()); 0205 }; 0206 0207 auto indexMode = indexingMode(); 0208 auto colorIndex = 0; 0209 auto calculateZeroRange = [](ChartDataSource *) { 0210 return 0.0; 0211 }; 0212 auto range = m_range->calculateRange(valueSources(), calculateZeroRange, maximum); 0213 0214 for (auto source : sources) { 0215 qreal threshold = range.start; 0216 qreal total = 0.0; 0217 0218 QList<qreal> sections; 0219 QList<QColor> sectionColors; 0220 0221 for (int i = 0; i < source->itemCount(); ++i) { 0222 auto value = source->item(i).toReal(); 0223 auto limited = value - threshold; 0224 if (limited > 0.0) { 0225 if (total + limited >= range.end) { 0226 limited = range.end - total; 0227 } 0228 0229 sections << limited; 0230 total += limited; 0231 0232 auto color = colors->item(colorIndex).value<QColor>(); 0233 sectionColors << color; 0234 } 0235 threshold = std::max(0.0, threshold - value); 0236 0237 if (indexMode != IndexEachSource) { 0238 colorIndex++; 0239 } 0240 } 0241 0242 if (qFuzzyCompare(total, 0.0)) { 0243 m_sections << QList<qreal>{0.0}; 0244 m_colors << QList<QColor>{colors->item(colorIndex).value<QColor>()}; 0245 } 0246 0247 for (auto &value : sections) { 0248 value = value / range.distance; 0249 } 0250 0251 m_sections << sections; 0252 m_colors << sectionColors; 0253 0254 if (indexMode == IndexEachSource) { 0255 colorIndex++; 0256 } else if (indexMode == IndexSourceValues) { 0257 colorIndex = 0; 0258 } 0259 } 0260 0261 update(); 0262 } 0263 0264 #include "moc_PieChart.cpp"