1/*
2 * This file is part of KQuickCharts
3 * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7
8#include "PieChart.h"
9
10#include <QAbstractItemModel>
11#include <QDebug>
12
13#include "datasource/ChartDataSource.h"
14#include "scenegraph/PieChartNode.h"
15
16PieChart::PieChart(QQuickItem *parent)
17 : Chart(parent)
18{
19 setIndexingMode(Chart::IndexSourceValues);
20 m_range = std::make_unique<RangeGroup>();
21 connect(sender: m_range.get(), signal: &RangeGroup::rangeChanged, context: this, slot: &PieChart::onDataChanged);
22}
23
24RangeGroup *PieChart::range() const
25{
26 return m_range.get();
27}
28
29bool PieChart::filled() const
30{
31 return m_filled;
32}
33
34void PieChart::setFilled(bool newFilled)
35{
36 if (newFilled == m_filled) {
37 return;
38 }
39
40 m_filled = newFilled;
41 update();
42 Q_EMIT filledChanged();
43}
44
45qreal PieChart::thickness() const
46{
47 return m_thickness;
48}
49
50void PieChart::setThickness(qreal newThickness)
51{
52 if (newThickness == m_thickness) {
53 return;
54 }
55
56 m_thickness = newThickness;
57 update();
58 Q_EMIT thicknessChanged();
59}
60
61qreal PieChart::spacing() const
62{
63 return m_spacing;
64}
65
66void PieChart::setSpacing(qreal newSpacing)
67{
68 if (newSpacing == m_spacing) {
69 return;
70 }
71
72 m_spacing = newSpacing;
73 update();
74 Q_EMIT spacingChanged();
75}
76
77QColor PieChart::backgroundColor() const
78{
79 return m_backgroundColor;
80}
81
82void PieChart::setBackgroundColor(const QColor &color)
83{
84 if (color == m_backgroundColor) {
85 return;
86 }
87 m_backgroundColor = color;
88 update();
89 Q_EMIT backgroundColorChanged();
90}
91
92qreal PieChart::fromAngle() const
93{
94 return m_fromAngle;
95}
96
97void PieChart::setFromAngle(qreal newFromAngle)
98{
99 if (qFuzzyCompare(p1: newFromAngle, p2: m_fromAngle)) {
100 return;
101 }
102
103 m_fromAngle = newFromAngle;
104 update();
105 Q_EMIT fromAngleChanged();
106}
107
108qreal PieChart::toAngle() const
109{
110 return m_toAngle;
111}
112
113void PieChart::setToAngle(qreal newToAngle)
114{
115 if (qFuzzyCompare(p1: newToAngle, p2: m_toAngle)) {
116 return;
117 }
118
119 m_toAngle = newToAngle;
120 update();
121 Q_EMIT toAngleChanged();
122}
123
124bool PieChart::smoothEnds() const
125{
126 return m_smoothEnds;
127}
128
129void PieChart::setSmoothEnds(bool newSmoothEnds)
130{
131 if (newSmoothEnds == m_smoothEnds) {
132 return;
133 }
134
135 m_smoothEnds = newSmoothEnds;
136 update();
137 Q_EMIT smoothEndsChanged();
138}
139
140QSGNode *PieChart::updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
141{
142 Q_UNUSED(data);
143 if (!node) {
144 node = new QSGNode{};
145 }
146
147 auto sourceCount = valueSources().size();
148
149 if (m_sections.count() < sourceCount) {
150 return node;
151 }
152
153 auto minDimension = std::min(a: width(), b: height());
154
155 float outerRadius = minDimension;
156 for (int i = 0; i < sourceCount; ++i) {
157 float innerRadius = i == sourceCount - 1 && m_filled ? 0.0 : outerRadius - m_thickness * 2.0;
158
159 if (node->childCount() <= i) {
160 node->appendChildNode(node: new PieChartNode{});
161 }
162
163 auto pieNode = static_cast<PieChartNode *>(node->childAtIndex(i));
164 pieNode->setRect(boundingRect());
165 pieNode->setInnerRadius(innerRadius);
166 pieNode->setOuterRadius(outerRadius);
167 pieNode->setSections(m_sections.at(i));
168 pieNode->setBackgroundColor(m_backgroundColor);
169 pieNode->setColors(m_colors.at(i));
170 pieNode->setFromAngle(m_fromAngle);
171 pieNode->setToAngle(m_toAngle);
172 pieNode->setSmoothEnds(m_smoothEnds);
173
174 outerRadius = innerRadius - m_spacing * 2.0;
175 }
176
177 while (node->childCount() > sourceCount) {
178 auto lastNode = node->childAtIndex(i: node->childCount() - 1);
179 node->removeChildNode(node: lastNode);
180 delete lastNode;
181 }
182
183 return node;
184}
185
186void PieChart::onDataChanged()
187{
188 m_sections.clear();
189 m_colors.clear();
190
191 const auto sources = valueSources();
192 const auto colors = colorSource();
193
194 if (!colors || sources.isEmpty() || !m_range->isValid()) {
195 return;
196 }
197
198 auto maximum = [](ChartDataSource *source) {
199 qreal result = 0.0;
200 for (int i = 0; i < source->itemCount(); ++i) {
201 result += source->item(index: i).toDouble();
202 }
203 return std::max(a: result, b: source->maximum().toDouble());
204 };
205
206 auto indexMode = indexingMode();
207 auto colorIndex = 0;
208 const auto highlightIndex = highlight();
209 auto calculateZeroRange = [](ChartDataSource *) {
210 return 0.0;
211 };
212 auto range = m_range->calculateRange(sources: valueSources(), minimumCallback: calculateZeroRange, maximumCallback: maximum);
213
214 for (auto source : sources) {
215 qreal threshold = range.start;
216 qreal total = 0.0;
217
218 QList<qreal> sections;
219 QList<QColor> sectionColors;
220
221 for (int i = 0; i < source->itemCount(); ++i) {
222 auto value = source->item(index: i).toReal();
223 auto limited = value - threshold;
224 if (limited > 0.0) {
225 if (total + limited >= range.end) {
226 limited = range.end - total;
227 }
228
229 sections << limited;
230 total += limited;
231
232 auto color = colors->item(index: colorIndex).value<QColor>();
233
234 if (highlightIndex >= 0 && highlightIndex != colorIndex) {
235 color = desaturate(input: color);
236 }
237
238 sectionColors << color;
239 }
240 threshold = std::max(a: 0.0, b: threshold - value);
241
242 if (indexMode != IndexEachSource) {
243 colorIndex++;
244 }
245 }
246
247 if (qFuzzyCompare(p1: total, p2: 0.0)) {
248 m_sections << QList<qreal>{0.0};
249 m_colors << QList<QColor>{colors->item(index: colorIndex).value<QColor>()};
250 }
251
252 for (auto &value : sections) {
253 value = value / range.distance;
254 }
255
256 m_sections << sections;
257 m_colors << sectionColors;
258
259 if (indexMode == IndexEachSource) {
260 colorIndex++;
261 } else if (indexMode == IndexSourceValues) {
262 colorIndex = 0;
263 }
264 }
265
266 update();
267}
268
269#include "moc_PieChart.cpp"
270

source code of kquickcharts/src/PieChart.cpp