9#include "KChartRingDiagram.h"
10#include "KChartRingDiagram_p.h"
12#include "KChartAttributesModel.h"
13#include "KChartPaintContext.h"
14#include "KChartPainterSaver_p.h"
15#include "KChartPieAttributes.h"
16#include "KChartPolarCoordinatePlane_p.h"
17#include "KChartThreeDPieAttributes.h"
19#include "KChartMath_p.h"
25RingDiagram::Private::Private()
26 : relativeThickness( false )
27 , expandWhenExploded( false )
31RingDiagram::Private::~Private() {}
41RingDiagram::~RingDiagram()
45void RingDiagram::init()
56 if ( other ==
this )
return true;
63 (relativeThickness() == other->relativeThickness()) &&
64 (expandWhenExploded() == other->expandWhenExploded());
67void RingDiagram::setRelativeThickness(
bool relativeThickness )
69 d->relativeThickness = relativeThickness;
72bool RingDiagram::relativeThickness()
const
74 return d->relativeThickness;
77void RingDiagram::setExpandWhenExploded(
bool expand )
79 d->expandWhenExploded = expand;
82bool RingDiagram::expandWhenExploded()
const
84 return d->expandWhenExploded;
89 if ( !checkInvariants(
true ) )
return QPair<QPointF, QPointF>(
QPointF( 0, 0 ),
QPointF( 0, 0 ) );
97 const int rCount = rowCount();
98 const int colCount = columnCount();
99 qreal maxExplode = 0.0;
100 for (
int i = 0; i < rCount; ++i ) {
101 qreal maxExplodeInThisRow = 0.0;
102 for (
int j = 0; j < colCount; ++j ) {
104 maxExplodeInThisRow = qMax( maxExplodeInThisRow, columnAttrs.
explodeFactor() );
106 maxExplode += maxExplodeInThisRow;
109 if ( !d->expandWhenExploded ) {
114 maxExplode /= ( rCount + 1);
115 topRight =
QPointF( 1.0 + maxExplode, 1.0 + maxExplode );
117 topRight =
QPointF( 1.0, 1.0 );
119 return QPair<QPointF, QPointF>( bottomLeft, topRight );
126 ctx.setPainter ( &painter );
139 if ( !checkInvariants(
true) )
142 d->reverseMapper.clear();
146 const int rCount = rowCount();
147 const int colCount = columnCount();
162 qreal totalOffset = 0.0;
163 for (
int i = 0; i < rCount; ++i ) {
164 qreal maxOffsetInThisRow = 0.0;
165 for (
int j = 0; j < colCount; ++j ) {
169 maxOffsetInThisRow = qMax( maxOffsetInThisRow, cellAttrs.gapFactor(
false ) + explode );
171 if ( !d->expandWhenExploded ) {
172 maxOffsetInThisRow -= qreal( i );
174 totalOffset += qMax<qreal>( maxOffsetInThisRow, 0.0 );
182 totalOffset /= ( rCount + 1 );
183 d->size /= ( 1.0 + totalOffset );
188 d->position =
QRectF(
x,
y, d->size, d->size );
193 d->forgetAlreadyPaintedDataValues();
194 for (
int iRow = 0; iRow < rCount; ++iRow ) {
199 const qreal sectorsPerValue = 360.0 / sum;
201 for (
int iColumn = 0; iColumn < colCount; ++iColumn ) {
204 const qreal cellValue = qAbs(
model()->data(
model()->index( iRow, iColumn,
rootIndex() ) )
208 d->startAngles[ iRow ][ iColumn ] = currentValue;
209 d->angleLens[ iRow ][ iColumn ] = cellValue * sectorsPerValue;
211 d->angleLens[ iRow ][ iColumn ] = 0.0;
212 if ( iColumn > 0.0 ) {
213 d->startAngles[ iRow ][ iColumn ] = d->startAngles[ iRow ][ iColumn - 1 ];
215 d->startAngles[ iRow ][ iColumn ] = currentValue;
219 currentValue = d->startAngles[ iRow ][ iColumn ] + d->angleLens[ iRow ][ iColumn ];
221 drawOneSlice( ctx->painter(), iRow, iColumn,
granularity() );
226#if defined ( Q_WS_WIN)
227#define trunc(x) ((int)(x))
230void RingDiagram::drawOneSlice(
QPainter* painter, uint dataset, uint slice, qreal granularity )
233 const qreal angleLen = d->angleLens[ dataset ][ slice ];
235 drawPieSurface( painter, dataset, slice,
granularity );
241 AbstractPieDiagram ::resize(
size );
244void RingDiagram::drawPieSurface(
QPainter* painter, uint dataset, uint slice, qreal granularity )
247 qreal angleLen = d->angleLens[ dataset ][ slice ];
249 qreal startAngle = d->startAngles[ dataset ][ slice ];
255 const int rCount = rowCount();
256 const int colCount = columnCount();
260 QRectF drawPosition = d->position;
265 if ( threeDAttrs.isEnabled() ) {
266 br = threeDAttrs.threeDBrush( br, drawPosition );
272 if ( angleLen == 360 ) {
277 bool perfectMatch =
false;
279 qreal circularGap = 0.0;
281 if ( attrs.gapFactor(
true ) > 0.0 ) {
283 circularGap = attrs.gapFactor(
true );
290 qreal actualStartAngle = startAngle + circularGap;
291 qreal actualAngleLen = angleLen - 2 * circularGap;
293 qreal totalRadialExplode = 0.0;
294 qreal maxRadialExplode = 0.0;
296 qreal totalRadialGap = 0.0;
297 qreal maxRadialGap = 0.0;
298 for ( uint i = rCount - 1; i > dataset; --i ) {
299 qreal maxRadialExplodeInThisRow = 0.0;
300 qreal maxRadialGapInThisRow = 0.0;
301 for (
int j = 0; j < colCount; ++j ) {
303 if ( d->expandWhenExploded ) {
304 maxRadialGapInThisRow = qMax( maxRadialGapInThisRow, cellAttrs.gapFactor(
false ) );
308 if ( cellAttrs.explode() && d->expandWhenExploded ) {
309 maxRadialExplodeInThisRow = qMax( maxRadialExplodeInThisRow, cellAttrs.explodeFactor() );
312 maxRadialExplode += maxRadialExplodeInThisRow;
313 maxRadialGap += maxRadialGapInThisRow;
319 totalRadialGap = maxRadialGap + attrs.gapFactor(
false );
320 totalRadialExplode = attrs.explode() ? maxRadialExplode + attrs.explodeFactor() : maxRadialExplode;
322 while ( degree <= actualAngleLen ) {
323 const QPointF p = pointOnEllipse( drawPosition, dataset, slice,
false, actualStartAngle + degree,
324 totalRadialGap, totalRadialExplode );
329 if ( ! perfectMatch ) {
330 poly.
append( pointOnEllipse( drawPosition, dataset, slice,
false, actualStartAngle + actualAngleLen,
331 totalRadialGap, totalRadialExplode ) );
336 const QPointF innerCenterPoint( poly[
int(iPoint / 2) ] );
338 actualStartAngle = startAngle + circularGap;
339 actualAngleLen = angleLen - 2 * circularGap;
341 degree = actualAngleLen;
343 const int lastInnerBrinkPoint = iPoint;
344 while ( degree >= 0 ) {
345 poly.
append( pointOnEllipse( drawPosition, dataset, slice,
true, actualStartAngle + degree,
346 totalRadialGap, totalRadialExplode ) );
347 perfectMatch = (degree == 0);
352 if ( ! perfectMatch ) {
353 poly.
append( pointOnEllipse( drawPosition, dataset, slice,
true, actualStartAngle,
354 totalRadialGap, totalRadialExplode ) );
359 const QPointF outerCenterPoint( poly[ lastInnerBrinkPoint +
int((iPoint - lastInnerBrinkPoint) / 2) ] );
366 d->reverseMapper.addPolygon( index.row(), index.column(), poly );
368 const QPointF centerPoint = (innerCenterPoint + outerCenterPoint) / 2.0;
370 const PainterSaver ps( painter );
375 const QPointF& p2 = poly[ lastInnerBrinkPoint ];
376 const QLineF line( p1, p2 );
378 const qreal angle = line.dx() == 0 ? 0.0 : atan( line.dy() / line.dx() );
380 painter->
rotate( angle / 2.0 / 3.141592653589793 * 360.0 );
384 paintDataValueText( painter, index, centerPoint, angleLen*sum / 360 );
390QPointF RingDiagram::pointOnEllipse(
const QRectF& rect,
int dataset,
int slice,
bool outer, qreal angle,
391 qreal totalGapFactor, qreal totalExplodeFactor )
393 qreal angleLen = d->angleLens[ dataset ][ slice ];
394 qreal startAngle = d->startAngles[ dataset ][ slice ];
396 const int rCount = rowCount() * 2;
398 qreal
level = outer ? ( rCount - dataset - 1 ) + 2 : ( rCount - dataset - 1 ) + 1;
400 const qreal offsetX = rCount > 0 ?
level *
rect.width() / ( ( rCount + 1 ) * 2 ) : 0.0;
401 const qreal offsetY = rCount > 0 ?
level *
rect.height() / ( ( rCount + 1 ) * 2 ) : 0.0;
402 const qreal centerOffsetX = rCount > 0 ? totalExplodeFactor *
rect.width() / ( ( rCount + 1 ) * 2 ) : 0.0;
403 const qreal centerOffsetY = rCount > 0 ? totalExplodeFactor *
rect.height() / ( ( rCount + 1 ) * 2 ) : 0.0;
404 const qreal gapOffsetX = rCount > 0 ? totalGapFactor *
rect.width() / ( ( rCount + 1 ) * 2 ) : 0.0;
405 const qreal gapOffsetY = rCount > 0 ? totalGapFactor *
rect.height() / ( ( rCount + 1 ) * 2 ) : 0.0;
407 qreal explodeAngleRad = DEGTORAD( angle );
408 qreal cosAngle = cos( explodeAngleRad );
409 qreal sinAngle = -sin( explodeAngleRad );
410 qreal explodeAngleCenterRad = DEGTORAD( startAngle + angleLen / 2.0 );
411 qreal cosAngleCenter = cos( explodeAngleCenterRad );
412 qreal sinAngleCenter = -sin( explodeAngleCenterRad );
413 return QPointF( ( offsetX + gapOffsetX ) * cosAngle + centerOffsetX * cosAngleCenter +
rect.center().x(),
414 ( offsetY + gapOffsetY ) * sinAngle + centerOffsetY * sinAngleCenter +
rect.center().y() );
420 const int rCount = rowCount();
421 const int colCount = columnCount();
423 for (
int i = 0; i < rCount; ++i ) {
424 for (
int j = 0; j < colCount; ++j ) {
433 Q_ASSERT( dataset <
model()->rowCount() );
434 const int colCount = columnCount();
436 for (
int j = 0; j < colCount; ++j ) {
448qreal RingDiagram::numberOfDatasets()
const
Declaring the class KChart::DataValueAttributes.
DataValueAttributes dataValueAttributes() const
Retrieve the DataValueAttributes specified globally.
QPen pen() const
Retrieve the pen to be used for painting datapoints globally.
QBrush brush() const
Retrieve the brush to be used for painting datapoints globally.
Base class for any diagram type.
bool compare(const AbstractPieDiagram *other) const
Returns true if both diagrams have the same settings.
qreal granularity() const
bool autoRotateLabels() const
TextAttributes textAttributes() const
Stores information about painting diagrams.
A set of attributes controlling the appearance of pie charts.
qreal explodeFactor() const
qreal startPosition() const
Retrieve the rotation of the coordinate plane.
RingDiagram defines a common ring diagram.
void paint(PaintContext *paintContext) override
\reimpl
void resize(const QSizeF &area) override
\reimpl
qreal numberOfGridRings() const override
\reimpl
qreal valueTotals() const override
\reimpl
bool compare(const RingDiagram *other) const
Returns true if both diagrams have the same settings.
qreal numberOfValuesPerDataset() const override
\reimpl
const QPair< QPointF, QPointF > calculateDataBoundaries() const override
\reimpl
virtual RingDiagram * clone() const
Creates an exact copy of this diagram.
A set of text attributes.
A set of 3D pie attributes.
QStringView level(QStringView ifopt)
virtual int columnCount(const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
QAbstractItemModel * model() const const
QModelIndex rootIndex() const const
void append(QList< T > &&value)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void setBrush(Qt::BrushStyle style)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void translate(const QPoint &offset)
bool isEmpty() const const
QRect contentsRect() const const