12 #include "KDChartRingDiagram_p.h"
17 #include "KDChartPainterSaver_p.h"
19 #include "KDChartPolarCoordinatePlane_p.h"
24 #include <KDABLibFakes>
28 RingDiagram::Private::Private()
32 RingDiagram::Private::~Private()
48 void RingDiagram::init()
80 return d->relativeThickness;
85 d->expandWhenExploded = expand;
90 return d->expandWhenExploded;
96 return QPair<QPointF, QPointF>(QPointF(0, 0), QPointF(0, 0));
100 QPointF bottomLeft(0, 0);
106 qreal maxExplode = 0.0;
107 for (
int i = 0; i < rCount; ++i) {
108 qreal maxExplodeInThisRow = 0.0;
109 for (
int j = 0; j < colCount; ++j) {
111 maxExplodeInThisRow = qMax(maxExplodeInThisRow, columnAttrs.
explodeFactor());
113 maxExplode += maxExplodeInThisRow;
116 if (!
d->expandWhenExploded) {
121 maxExplode /= (rCount + 1);
122 topRight = QPointF(1.0 + maxExplode, 1.0 + maxExplode);
124 topRight = QPointF(1.0, 1.0);
126 return QPair<QPointF, QPointF>(bottomLeft, topRight);
131 QPainter painter(viewport());
149 d->reverseMapper.clear();
158 if (contentsRect.isEmpty())
161 d->startAngles = QVector<QVector<qreal>>(rCount, QVector<qreal>(colCount));
162 d->angleLens = QVector<QVector<qreal>>(rCount, QVector<qreal>(colCount));
165 d->size = qMin(contentsRect.width(), contentsRect.height());
169 qreal totalOffset = 0.0;
170 for (
int i = 0; i < rCount; ++i) {
171 qreal maxOffsetInThisRow = 0.0;
172 for (
int j = 0; j < colCount; ++j) {
176 maxOffsetInThisRow = qMax(maxOffsetInThisRow, cellAttrs.
gapFactor(
false) + explode);
178 if (!
d->expandWhenExploded) {
179 maxOffsetInThisRow -= qreal(i);
181 totalOffset += qMax(maxOffsetInThisRow, ( qreal )0.0);
189 totalOffset /= (rCount + 1);
190 d->size /= (1.0 + totalOffset);
192 qreal x = (contentsRect.width() ==
d->size) ? 0.0 : ((contentsRect.width() -
d->size) / 2.0);
193 qreal y = (contentsRect.height() ==
d->size) ? 0.0 : ((contentsRect.height() -
d->size) / 2.0);
194 d->position = QRectF(x, y,
d->size,
d->size);
195 d->position.translate(contentsRect.left(), contentsRect.top());
201 d->forgetAlreadyPaintedDataValues();
202 for (
int iRow = 0; iRow < rCount; ++iRow) {
207 const qreal sectorsPerValue = 360.0 / sum;
209 for (
int iColumn = 0; iColumn < colCount; ++iColumn) {
212 const qreal cellValue = qAbs(model()->data(model()->index(iRow, iColumn, rootIndex()))
216 d->startAngles[iRow][iColumn] = currentValue;
217 d->angleLens[iRow][iColumn] = cellValue * sectorsPerValue;
219 d->angleLens[iRow][iColumn] = 0.0;
221 d->startAngles[iRow][iColumn] =
d->startAngles[iRow][iColumn - 1];
223 d->startAngles[iRow][iColumn] = currentValue;
227 currentValue =
d->startAngles[iRow][iColumn] +
d->angleLens[iRow][iColumn];
234 #if defined(Q_WS_WIN)
235 #define trunc(x) (( int )(x))
243 void RingDiagram::drawOneSlice(QPainter *painter, uint dataset, uint slice, qreal granularity)
246 const qreal angleLen =
d->angleLens[dataset][slice];
248 drawPieSurface(painter, dataset, slice,
granularity);
263 void RingDiagram::drawPieSurface(QPainter *painter, uint dataset, uint slice, qreal granularity)
266 qreal angleLen =
d->angleLens[dataset][slice];
268 qreal startAngle =
d->startAngles[dataset][slice];
270 QModelIndex index(model()->index(dataset, slice, rootIndex()));
279 QRectF drawPosition =
d->position;
281 painter->setRenderHint(QPainter::Antialiasing);
283 QBrush br =
brush(index);
284 if (threeDAttrs.isEnabled()) {
285 br = threeDAttrs.threeDBrush(br, drawPosition);
287 painter->setBrush(br);
289 painter->setPen(
pen(index));
291 if (angleLen == 360) {
296 bool perfectMatch =
false;
298 qreal circularGap = 0.0;
300 if (attrs.gapFactor(
true) > 0.0) {
302 circularGap = attrs.gapFactor(
true);
309 qreal actualStartAngle = startAngle + circularGap;
310 qreal actualAngleLen = angleLen - 2 * circularGap;
312 qreal totalRadialExplode = 0.0;
313 qreal maxRadialExplode = 0.0;
315 qreal totalRadialGap = 0.0;
316 qreal maxRadialGap = 0.0;
317 for (uint i = rCount - 1; i > dataset; --i) {
318 qreal maxRadialExplodeInThisRow = 0.0;
319 qreal maxRadialGapInThisRow = 0.0;
320 for (
int j = 0; j < colCount; ++j) {
322 if (
d->expandWhenExploded) {
323 maxRadialGapInThisRow = qMax(maxRadialGapInThisRow, cellAttrs.gapFactor(
false));
327 if (cellAttrs.explode() &&
d->expandWhenExploded) {
328 maxRadialExplodeInThisRow = qMax(maxRadialExplodeInThisRow, cellAttrs.explodeFactor());
331 maxRadialExplode += maxRadialExplodeInThisRow;
332 maxRadialGap += maxRadialGapInThisRow;
338 totalRadialGap = maxRadialGap + attrs.gapFactor(
false);
339 totalRadialExplode = attrs.explode() ? maxRadialExplode + attrs.explodeFactor() : maxRadialExplode;
341 while (degree <= actualAngleLen) {
342 const QPointF p = pointOnEllipse(drawPosition, dataset, slice,
false, actualStartAngle + degree,
343 totalRadialGap, totalRadialExplode);
349 poly.append(pointOnEllipse(drawPosition, dataset, slice,
false, actualStartAngle + actualAngleLen,
350 totalRadialGap, totalRadialExplode));
355 const QPointF innerCenterPoint(poly[
int(iPoint / 2)]);
357 actualStartAngle = startAngle + circularGap;
358 actualAngleLen = angleLen - 2 * circularGap;
360 degree = actualAngleLen;
362 const int lastInnerBrinkPoint = iPoint;
363 while (degree >= 0) {
364 poly.append(pointOnEllipse(drawPosition, dataset, slice,
true, actualStartAngle + degree,
365 totalRadialGap, totalRadialExplode));
366 perfectMatch = (degree == 0);
372 poly.append(pointOnEllipse(drawPosition, dataset, slice,
true, actualStartAngle,
373 totalRadialGap, totalRadialExplode));
378 const QPointF outerCenterPoint(poly[lastInnerBrinkPoint +
int((iPoint - lastInnerBrinkPoint) / 2)]);
383 painter->drawPolygon(poly);
385 d->reverseMapper.addPolygon(index.row(), index.column(), poly);
387 const QPointF centerPoint = (innerCenterPoint + outerCenterPoint) / 2.0;
389 const PainterSaver ps(painter);
392 const QPointF &p1 = poly.last();
393 const QPointF &p2 = poly[lastInnerBrinkPoint];
394 const QLineF line(p1, p2);
396 const qreal angle = line.dx() == 0 ? 0.0 : atan(line.dy() / line.dx());
397 painter->translate(centerPoint);
398 painter->rotate(angle / 2.0 / 3.141592653589793 * 360.0);
399 painter->translate(-centerPoint);
411 QPointF RingDiagram::pointOnEllipse(
const QRectF &rect,
int dataset,
int slice,
bool outer, qreal angle,
412 qreal totalGapFactor, qreal totalExplodeFactor)
414 qreal angleLen =
d->angleLens[dataset][slice];
415 qreal startAngle =
d->startAngles[dataset][slice];
419 qreal level = outer ? (rCount - dataset - 1) + 2 : (rCount - dataset - 1) + 1;
421 const qreal offsetX = rCount > 0 ? level * rect.width() / ((rCount + 1) * 2) : 0.0;
422 const qreal offsetY = rCount > 0 ? level * rect.height() / ((rCount + 1) * 2) : 0.0;
423 const qreal centerOffsetX = rCount > 0 ? totalExplodeFactor * rect.width() / ((rCount + 1) * 2) : 0.0;
424 const qreal centerOffsetY = rCount > 0 ? totalExplodeFactor * rect.height() / ((rCount + 1) * 2) : 0.0;
425 const qreal gapOffsetX = rCount > 0 ? totalGapFactor * rect.width() / ((rCount + 1) * 2) : 0.0;
426 const qreal gapOffsetY = rCount > 0 ? totalGapFactor * rect.height() / ((rCount + 1) * 2) : 0.0;
428 qreal explodeAngleRad = DEGTORAD(angle);
429 qreal cosAngle = cos(explodeAngleRad);
430 qreal sinAngle = -sin(explodeAngleRad);
431 qreal explodeAngleCenterRad = DEGTORAD(startAngle + angleLen / 2.0);
432 qreal cosAngleCenter = cos(explodeAngleCenterRad);
433 qreal sinAngleCenter = -sin(explodeAngleCenterRad);
434 return QPointF((offsetX + gapOffsetX) * cosAngle + centerOffsetX * cosAngleCenter + rect.center().x(),
435 (offsetY + gapOffsetY) * sinAngle + centerOffsetY * sinAngleCenter + rect.center().y());
444 for (
int i = 0; i < rCount; ++i) {
445 for (
int j = 0; j < colCount; ++j) {
446 total += qAbs(model()->data(model()->index(i, j, rootIndex())).toReal());
454 Q_ASSERT(dataset < model()->
rowCount());
457 for (
int j = 0; j < colCount; ++j) {
458 total += qAbs(model()->data(model()->index(dataset, j, rootIndex())).toReal());
466 return model() ? model()->columnCount(rootIndex()) : 0.0;
471 return model() ? model()->rowCount(rootIndex()) : 0.0;
Declaring the class KDChart::DataValueAttributes.
virtual bool checkInvariants(bool justReturnTheStatus=false) const
DataValueAttributes dataValueAttributes() const
void paintDataValueText(QPainter *painter, const QModelIndex &index, const QPointF &pos, qreal value)
Base class for any diagram type.
PieAttributes pieAttributes() const
qreal granularity() const
bool autoRotateLabels() const
ThreeDPieAttributes threeDPieAttributes() const
const PolarCoordinatePlane * polarCoordinatePlane() const
TextAttributes textAttributes() const
Stores information about painting diagrams.
void setPainter(QPainter *painter)
void setRectangle(const QRectF &rect)
const QRectF rectangle() const
QPainter * painter() const
A set of attributes controlling the appearance of pie charts.
qreal gapFactor(bool circular) const
qreal explodeFactor() const
qreal startPosition() const
RingDiagram defines a common ring diagram.
bool relativeThickness() const
void setRelativeThickness(bool relativeThickness)
void paint(PaintContext *paintContext) override
void resize(const QSizeF &area) override
qreal numberOfGridRings() const override
qreal valueTotals() const override
void paintEvent(QPaintEvent *) override
qreal numberOfDatasets() const override
bool compare(const RingDiagram *other) const
qreal numberOfValuesPerDataset() const override
const QPair< QPointF, QPointF > calculateDataBoundaries() const override
virtual void setExpandWhenExploded(bool expand)
virtual bool expandWhenExploded() const
virtual RingDiagram * clone() const
RingDiagram(QWidget *parent=nullptr, PolarCoordinatePlane *plane=nullptr)
void resizeEvent(QResizeEvent *) override
A set of text attributes.
A set of 3D pie attributes.