1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtGraphs/qpieseries.h>
5#include <QtGraphs/qpieslice.h>
6#include <QtQuick/private/qquicktext_p.h>
7#include <private/pierenderer_p.h>
8#include <private/qabstractseries_p.h>
9#include <private/qgraphsview_p.h>
10#include <private/qpieseries_p.h>
11#include <private/qpieslice_p.h>
12#include <private/qquickshape_p.h>
13#include <private/qquicksvgparser_p.h>
14
15PieRenderer::PieRenderer(QGraphsView *graph)
16 : QQuickItem(graph)
17 , m_graph(graph)
18 , m_painterPath()
19{
20 setFlag(flag: QQuickItem::ItemHasContents);
21 setClip(true);
22
23 m_shape = new QQuickShape(this);
24 m_shape->setParentItem(this);
25 m_shape->setPreferredRendererType(QQuickShape::CurveRenderer);
26}
27
28PieRenderer::~PieRenderer() {}
29
30void PieRenderer::setSize(QSizeF size)
31{
32 QQuickItem::setSize(size);
33}
34
35void PieRenderer::handlePolish(QPieSeries *series)
36{
37 for (QPieSlice *slice : series->slices()) {
38 QPieSlicePrivate *d = slice->d_func();
39 QQuickShapePath *shapePath = d->m_shapePath;
40 QQuickShapePath *labelPath = d->m_labelPath;
41 auto labelElements = labelPath->pathElements();
42 auto pathElements = shapePath->pathElements();
43 auto labelItem = d->m_labelItem;
44
45 if (!m_activeSlices.contains(key: slice)) {
46 auto data = m_shape->data();
47 data.append(&data, shapePath);
48 SliceData sliceData{};
49 sliceData.initialized = false;
50 m_activeSlices.insert(key: slice, value: sliceData);
51 }
52
53 QQuickShape *labelShape = d->m_labelShape;
54 labelShape->setVisible(series->isVisible() && d->m_isLabelVisible);
55 labelItem->setVisible(series->isVisible() && d->m_isLabelVisible);
56
57 if (!series->isVisible()) {
58 pathElements.clear(&pathElements);
59 labelElements.clear(&labelElements);
60 continue;
61 }
62
63 if (!shapePath->parent())
64 shapePath->setParent(m_shape);
65
66 if (!d->m_labelItem->parent()) {
67 d->m_labelItem->setParent(this);
68 d->m_labelItem->setParentItem(this);
69 }
70
71 if (!labelShape->parent()) {
72 labelShape->setParent(this);
73 labelShape->setParentItem(this);
74 }
75 }
76
77 if (!series->isVisible())
78 return;
79
80 QPointF center = QPointF(size().width() * series->horizontalPosition(),
81 size().height() * series->verticalPosition());
82 qreal radius = size().width() > size().height() ? size().height() : size().width();
83 radius *= (.5 * series->pieSize());
84
85 QGraphsTheme *theme = m_graph->theme();
86
87 if (!theme)
88 return;
89
90 if (m_colorIndex < 0)
91 m_colorIndex = m_graph->graphSeriesCount();
92 m_graph->setGraphSeriesCount(m_colorIndex + series->slices().size());
93
94 int sliceIndex = 0;
95 QList<QLegendData> legendDataList;
96 for (QPieSlice *slice : series->slices()) {
97 m_painterPath.clear();
98
99 QPieSlicePrivate *d = slice->d_func();
100
101 // update slice
102 QQuickShapePath *shapePath = d->m_shapePath;
103
104 const auto &borderColors = theme->borderColors();
105 int index = sliceIndex % borderColors.size();
106 QColor borderColor = borderColors.at(i: index);
107 if (d->m_borderColor.isValid())
108 borderColor = d->m_borderColor;
109 qreal borderWidth = theme->borderWidth();
110 if (d->m_borderWidth > 0.0)
111 borderWidth = d->m_borderWidth;
112 const auto &seriesColors = theme->seriesColors();
113 index = sliceIndex % seriesColors.size();
114 QColor color = seriesColors.at(i: index);
115 if (d->m_color.isValid())
116 color = d->m_color;
117 shapePath->setStrokeWidth(borderWidth);
118 shapePath->setStrokeColor(borderColor);
119 shapePath->setFillColor(color);
120
121 if (!m_activeSlices.contains(key: slice))
122 return;
123
124 qreal radian = qDegreesToRadians(degrees: slice->startAngle());
125 qreal startBigX = radius * qSin(v: radian);
126 qreal startBigY = radius * qCos(v: radian);
127
128 qreal startSmallX = startBigX * series->holeSize();
129 qreal startSmallY = startBigY * series->holeSize();
130
131 qreal explodeDistance = .0;
132 if (slice->isExploded())
133 explodeDistance = slice->explodeDistanceFactor() * radius;
134 radian = qDegreesToRadians(degrees: slice->startAngle() + (slice->angleSpan() * .5));
135 qreal xShift = center.x() + (explodeDistance * qSin(v: radian));
136 qreal yShift = center.y() - (explodeDistance * qCos(v: radian));
137
138 qreal pointX = startBigY * qSin(v: radian) + startBigX * qCos(v: radian);
139 qreal pointY = startBigY * qCos(v: radian) - startBigX * qSin(v: radian);
140
141 QRectF rect(center.x() - radius + (explodeDistance * qSin(v: radian)),
142 center.y() - radius - (explodeDistance * qCos(v: radian)),
143 radius * 2,
144 radius * 2);
145
146 shapePath->setStartX(center.x());
147 shapePath->setStartY(center.y());
148
149 if (series->holeSize() > 0) {
150 QRectF insideRect(center.x() - series->holeSize() * radius
151 + (explodeDistance * qSin(v: radian)),
152 center.y() - series->holeSize() * radius
153 - (explodeDistance * qCos(v: radian)),
154 series->holeSize() * radius * 2,
155 series->holeSize() * radius * 2);
156
157 m_painterPath.arcMoveTo(rect, angle: -slice->startAngle() + 90);
158 m_painterPath.arcTo(rect, startAngle: -slice->startAngle() + 90, arcLength: -slice->angleSpan());
159 m_painterPath.arcTo(rect: insideRect,
160 startAngle: -slice->startAngle() + 90 - slice->angleSpan(),
161 arcLength: slice->angleSpan());
162 m_painterPath.closeSubpath();
163 } else {
164 m_painterPath.moveTo(p: rect.center());
165 m_painterPath.arcTo(rect, startAngle: -slice->startAngle() + 90, arcLength: -slice->angleSpan());
166 m_painterPath.closeSubpath();
167 }
168
169 radian = qDegreesToRadians(degrees: slice->angleSpan());
170
171 pointX = startSmallY * qSin(v: radian) + startSmallX * qCos(v: radian);
172 pointY = startSmallY * qCos(v: radian) - startSmallX * qSin(v: radian);
173
174 d->m_largeArc = {xShift + pointX, yShift - pointY};
175
176 shapePath->setPath(m_painterPath);
177 m_painterPath.clear();
178
179 radian = qDegreesToRadians(degrees: slice->startAngle() + (slice->angleSpan() * .5));
180 startBigX = radius * qSin(v: radian);
181 startBigY = radius * qCos(v: radian);
182
183 pointX = radius * (1.0 + d->m_labelArmLengthFactor) * qSin(v: radian);
184 pointY = radius * (1.0 + d->m_labelArmLengthFactor) * qCos(v: radian);
185
186 m_painterPath.moveTo(x: xShift + startBigX, y: yShift - startBigY);
187 m_painterPath.lineTo(x: xShift + pointX, y: yShift - pointY);
188
189 d->m_centerLine = {xShift + pointX, yShift - pointY};
190
191 d->m_labelArm = {xShift + pointX, yShift - pointY};
192
193 auto labelWidth = radian > M_PI ? -d->m_labelItem->width() : d->m_labelItem->width();
194 m_painterPath.lineTo(x: d->m_labelArm.x() + labelWidth, y: d->m_labelArm.y());
195
196 d->setLabelPosition(d->m_labelPosition);
197 d->m_labelPath->setPath(m_painterPath);
198
199 sliceIndex++;
200 legendDataList.push_back(t: {.color: color, .borderColor: borderColor, .label: d->m_labelText});
201 }
202 series->d_func()->setLegendData(legendDataList);
203}
204
205void PieRenderer::afterPolish(QList<QAbstractSeries *> &cleanupSeries)
206{
207 for (auto series : cleanupSeries) {
208 auto pieSeries = qobject_cast<QPieSeries *>(object: series);
209 if (pieSeries) {
210 for (QPieSlice *slice : pieSeries->slices()) {
211 QPieSlicePrivate *d = slice->d_func();
212 auto labelElements = d->m_labelPath->pathElements();
213 auto shapeElements = d->m_shapePath->pathElements();
214
215 labelElements.clear(&labelElements);
216 shapeElements.clear(&shapeElements);
217
218 slice->deleteLater();
219 d->m_labelItem->deleteLater();
220
221 m_activeSlices.remove(key: slice);
222 }
223 }
224 }
225}
226
227void PieRenderer::updateSeries(QPieSeries *series)
228{
229 auto needPolish = false;
230
231 for (auto &sliceData : m_activeSlices) {
232 if (!sliceData.initialized) {
233 sliceData.initialized = true;
234 needPolish = true;
235 }
236 }
237
238 if (needPolish)
239 handlePolish(series);
240}
241
242void PieRenderer::afterUpdate(QList<QAbstractSeries *> &cleanupSeries)
243{
244 Q_UNUSED(cleanupSeries);
245}
246
247void PieRenderer::markedDeleted(QList<QPieSlice *> deleted)
248{
249 auto emptyPath = QPainterPath{};
250
251 for (auto slice : deleted) {
252 auto d = slice->d_func();
253 d->m_shapePath->setPath(emptyPath);
254 d->m_labelPath->setPath(emptyPath);
255 d->m_labelItem->deleteLater();
256 m_activeSlices.remove(key: slice);
257 }
258}
259

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtgraphs/src/graphs2d/qsgrenderer/pierenderer.cpp