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 "PieChartNode.h"
9
10#include <algorithm>
11
12#include <QColor>
13#include <QSGGeometry>
14#include <cmath>
15
16#include "PieChartMaterial.h"
17
18static const qreal pi = std::acos(x: -1.0);
19
20inline QVector4D colorToVec4(const QColor &color)
21{
22 return QVector4D{float(color.redF()), float(color.greenF()), float(color.blueF()), float(color.alphaF())};
23}
24
25inline qreal degToRad(qreal deg)
26{
27 return (deg / 180.0) * pi;
28}
29
30PieChartNode::PieChartNode()
31 : PieChartNode(QRectF{})
32{
33}
34
35PieChartNode::PieChartNode(const QRectF &rect)
36{
37 m_geometry = new QSGGeometry{QSGGeometry::defaultAttributes_TexturedPoint2D(), 4};
38 QSGGeometry::updateTexturedRectGeometry(g: m_geometry, rect, sourceRect: QRectF{0, 0, 1, 1});
39 setGeometry(m_geometry);
40
41 m_material = new PieChartMaterial{};
42 setMaterial(m_material);
43
44 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
45}
46
47PieChartNode::~PieChartNode()
48{
49}
50
51void PieChartNode::setRect(const QRectF &rect)
52{
53 if (rect == m_rect) {
54 return;
55 }
56
57 m_rect = rect;
58 QSGGeometry::updateTexturedRectGeometry(g: m_geometry, rect: m_rect, sourceRect: QRectF{0, 0, 1, 1});
59 markDirty(bits: QSGNode::DirtyGeometry);
60
61 auto minDimension = qMin(a: m_rect.width(), b: m_rect.height());
62
63 QVector2D aspect{1.0, 1.0};
64 aspect.setX(rect.width() / minDimension);
65 aspect.setY(rect.height() / minDimension);
66 m_material->setAspectRatio(aspect);
67
68 m_material->setInnerRadius(m_innerRadius / minDimension);
69 m_material->setOuterRadius(m_outerRadius / minDimension);
70
71 markDirty(bits: QSGNode::DirtyMaterial);
72}
73
74void PieChartNode::setInnerRadius(qreal radius)
75{
76 if (qFuzzyCompare(p1: radius, p2: m_innerRadius)) {
77 return;
78 }
79
80 m_innerRadius = radius;
81
82 auto minDimension = qMin(a: m_rect.width(), b: m_rect.height());
83 m_material->setInnerRadius(m_innerRadius / minDimension);
84
85 markDirty(bits: QSGNode::DirtyMaterial);
86}
87
88void PieChartNode::setOuterRadius(qreal radius)
89{
90 if (qFuzzyCompare(p1: radius, p2: m_outerRadius)) {
91 return;
92 }
93
94 m_outerRadius = radius;
95
96 auto minDimension = qMin(a: m_rect.width(), b: m_rect.height());
97 m_material->setOuterRadius(m_outerRadius / minDimension);
98
99 markDirty(bits: QSGNode::DirtyMaterial);
100}
101
102void PieChartNode::setColors(const QList<QColor> &colors)
103{
104 m_colors = colors;
105 updateSegments();
106}
107
108void PieChartNode::setSections(const QList<qreal> &sections)
109{
110 m_sections = sections;
111 updateSegments();
112}
113
114void PieChartNode::setBackgroundColor(const QColor &color)
115{
116 if (color == m_backgroundColor) {
117 return;
118 }
119
120 m_backgroundColor = color;
121 m_material->setBackgroundColor(color);
122 markDirty(bits: QSGNode::DirtyMaterial);
123}
124
125void PieChartNode::setFromAngle(qreal angle)
126{
127 if (qFuzzyCompare(p1: angle, p2: m_fromAngle)) {
128 return;
129 }
130
131 m_fromAngle = angle;
132 m_material->setFromAngle(degToRad(deg: angle));
133 updateSegments();
134}
135
136void PieChartNode::setToAngle(qreal angle)
137{
138 if (qFuzzyCompare(p1: angle, p2: m_fromAngle)) {
139 return;
140 }
141
142 m_toAngle = angle;
143 m_material->setToAngle(degToRad(deg: angle));
144 updateSegments();
145}
146
147void PieChartNode::setSmoothEnds(bool smooth)
148{
149 if (smooth == m_smoothEnds) {
150 return;
151 }
152
153 m_smoothEnds = smooth;
154 m_material->setSmoothEnds(smooth);
155 markDirty(bits: QSGNode::DirtyMaterial);
156}
157
158void PieChartNode::updateSegments()
159{
160 if (m_sections.isEmpty() || m_sections.size() != m_colors.size()) {
161 return;
162 }
163
164 qreal startAngle = degToRad(deg: m_fromAngle);
165 qreal totalAngle = degToRad(deg: m_toAngle - m_fromAngle);
166
167 QList<QVector2D> segments;
168 QList<QVector4D> colors;
169
170 for (int i = 0; i < m_sections.size(); ++i) {
171 QVector2D segment{float(startAngle), float(startAngle + m_sections.at(i) * totalAngle)};
172 segments << segment;
173 startAngle = segment.y();
174 colors << colorToVec4(color: m_colors.at(i));
175 }
176
177 if (m_sections.size() == 1 && qFuzzyCompare(p1: m_sections.at(i: 0), p2: 0.0)) {
178 segments.clear();
179 }
180
181 m_material->setSegments(segments);
182 m_material->setColors(colors);
183
184 markDirty(bits: QSGNode::DirtyMaterial);
185}
186

source code of kquickcharts/src/scenegraph/PieChartNode.cpp