File indexing completed on 2024-12-15 04:02:35

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KChartPolarDiagram.h"
0010 #include "KChartPolarDiagram_p.h"
0011 
0012 #include "KChartPaintContext.h"
0013 #include "KChartPainterSaver_p.h"
0014 #include "KChartMath_p.h"
0015 
0016 #include <QPainter>
0017 
0018 using namespace KChart;
0019 
0020 PolarDiagram::Private::Private() :
0021     rotateCircularLabels( false ),
0022     closeDatasets( false )
0023 {
0024 }
0025 
0026 PolarDiagram::Private::~Private() {}
0027 
0028 #define d d_func()
0029 
0030 PolarDiagram::PolarDiagram( QWidget* parent, PolarCoordinatePlane* plane ) :
0031     AbstractPolarDiagram( new Private( ), parent, plane )
0032 {
0033     //init();
0034 }
0035 
0036 PolarDiagram::~PolarDiagram()
0037 {
0038 }
0039 
0040 
0041 void PolarDiagram::init()
0042 {
0043     setShowDelimitersAtPosition( Position::Unknown, false );
0044     setShowDelimitersAtPosition( Position::Center, false );
0045     setShowDelimitersAtPosition( Position::NorthWest, false );
0046     setShowDelimitersAtPosition( Position::North, true );
0047     setShowDelimitersAtPosition( Position::NorthEast, false );
0048     setShowDelimitersAtPosition( Position::West, false );
0049     setShowDelimitersAtPosition( Position::East, false );
0050     setShowDelimitersAtPosition( Position::SouthWest, false );
0051     setShowDelimitersAtPosition( Position::South, true );
0052     setShowDelimitersAtPosition( Position::SouthEast, false );
0053     setShowDelimitersAtPosition( Position::Floating, false );
0054 
0055     setShowLabelsAtPosition( Position::Unknown, false );
0056     setShowLabelsAtPosition( Position::Center, false );
0057     setShowLabelsAtPosition( Position::NorthWest, false );
0058     setShowLabelsAtPosition( Position::North, true );
0059     setShowLabelsAtPosition( Position::NorthEast, false );
0060     setShowLabelsAtPosition( Position::West, false );
0061     setShowLabelsAtPosition( Position::East, false );
0062     setShowLabelsAtPosition( Position::SouthWest, false );
0063     setShowLabelsAtPosition( Position::South, true );
0064     setShowLabelsAtPosition( Position::SouthEast, false );
0065     setShowLabelsAtPosition( Position::Floating, false );
0066 }
0067 
0068 PolarDiagram * PolarDiagram::clone() const
0069 {
0070     PolarDiagram* newDiagram = new PolarDiagram( new Private( *d ) );
0071     // This needs to be copied after the fact
0072     newDiagram->d->showDelimitersAtPosition = d->showDelimitersAtPosition;
0073     newDiagram->d->showLabelsAtPosition = d->showLabelsAtPosition;
0074     newDiagram->d->rotateCircularLabels = d->rotateCircularLabels;
0075     newDiagram->d->closeDatasets = d->closeDatasets;
0076     return newDiagram;
0077 }
0078 
0079 const QPair<QPointF, QPointF> PolarDiagram::calculateDataBoundaries () const
0080 {
0081     if ( !checkInvariants(true) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
0082     const int rowCount = model()->rowCount(rootIndex());
0083     const int colCount = model()->columnCount(rootIndex());
0084     qreal xMin = 0.0;
0085     qreal xMax = colCount;
0086     qreal yMin = 0, yMax = 0;
0087     for ( int iCol=0; iCol<colCount; ++iCol ) {
0088         for ( int iRow=0; iRow< rowCount; ++iRow ) {
0089             qreal value = model()->data( model()->index( iRow, iCol, rootIndex() ) ).toReal(); // checked
0090             yMax = qMax( yMax, value );
0091             yMin = qMin( yMin, value );
0092         }
0093     }
0094     QPointF bottomLeft ( QPointF( xMin, yMin ) );
0095     QPointF topRight ( QPointF( xMax, yMax ) );
0096     return QPair<QPointF, QPointF> ( bottomLeft,  topRight );
0097 }
0098 
0099 
0100 
0101 void PolarDiagram::paintEvent ( QPaintEvent*)
0102 {
0103     QPainter painter ( viewport() );
0104     PaintContext ctx;
0105     ctx.setPainter ( &painter );
0106     ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
0107     paint ( &ctx );
0108 }
0109 
0110 void PolarDiagram::resizeEvent ( QResizeEvent*)
0111 {
0112 }
0113 
0114 void PolarDiagram::paintPolarMarkers( PaintContext* ctx, const QPolygonF& polygon )
0115 {
0116     Q_UNUSED(ctx);
0117     Q_UNUSED(polygon);
0118     // obsolete, since we are using real markers now!
0119 }
0120 
0121 void PolarDiagram::paint( PaintContext* ctx )
0122 {
0123     qreal dummy1, dummy2;
0124     paint( ctx, true,  dummy1, dummy2 );
0125     paint( ctx, false, dummy1, dummy2 );
0126 }
0127 
0128 void PolarDiagram::paint( PaintContext* ctx,
0129                           bool calculateListAndReturnScale,
0130                           qreal& newZoomX, qreal& newZoomY )
0131 {
0132     // note: Not having any data model assigned is no bug
0133     //       but we can not draw a diagram then either.
0134     if ( !checkInvariants(true) )
0135         return;
0136     d->reverseMapper.clear();
0137 
0138     const int rowCount = model()->rowCount( rootIndex() );
0139     const int colCount = model()->columnCount( rootIndex() );
0140 
0141     if ( calculateListAndReturnScale ) {
0142         // Check if all of the data value texts / data comments fit into the available space...
0143         d->labelPaintCache.clear();
0144 
0145         for ( int iCol = 0; iCol < colCount; ++iCol ) {
0146             for ( int iRow=0; iRow < rowCount; ++iRow ) {
0147                 QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
0148                 const qreal value = model()->data( index ).toReal();
0149                 QPointF point = coordinatePlane()->translate(
0150                         QPointF( value, iRow ) ) + ctx->rectangle().topLeft();
0151                 //qDebug() << point;
0152                 d->addLabel( &d->labelPaintCache, index, nullptr, PositionPoints( point ),
0153                              Position::Center, Position::Center, value );
0154             }
0155         }
0156 
0157         newZoomX = coordinatePlane()->zoomFactorX();
0158         newZoomY = coordinatePlane()->zoomFactorY();
0159 
0160         if ( d->labelPaintCache.paintReplay.count() ) {
0161             // ...and zoom out if necessary
0162             const qreal oldZoomX = newZoomX;
0163             const qreal oldZoomY = newZoomY;
0164 
0165             QRectF txtRectF;
0166             d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF );
0167             const QRect txtRect = txtRectF.toRect();
0168             const QRect curRect = coordinatePlane()->geometry();
0169             const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right()  - txtRect.right() );
0170             const qreal gapY = qMin( txtRect.top()  - curRect.top(),  curRect.bottom() - txtRect.bottom() );
0171             if ( gapX < 0.0 ) {
0172                 newZoomX = oldZoomX * ( 1.0 + ( gapX - 1.0 ) / curRect.width() );
0173             }
0174             if ( gapY < 0.0 ) {
0175                 newZoomY = oldZoomY * ( 1.0 + ( gapY - 1.0 ) / curRect.height() );
0176             }
0177         }
0178     } else {
0179         // Paint the data sets
0180         for ( int iCol = 0; iCol < colCount; ++iCol ) {
0181             //TODO(khz): As of yet PolarDiagram can not show per-segment line attributes
0182             //           but it draws every polyline in one go - using one color.
0183             //           This needs to be enhanced to allow for cell-specific settings
0184             //           in the same way as LineDiagram does it.
0185             QBrush brush = d->datasetAttrs( iCol, KChart::DatasetBrushRole ).value<QBrush>();
0186             QPolygonF polygon;
0187             for ( int iRow = 0; iRow < rowCount; ++iRow ) {
0188                 QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
0189                 const qreal value = model()->data( index ).toReal();
0190                 QPointF point = coordinatePlane()->translate( QPointF( value, iRow ) )
0191                                 + ctx->rectangle().topLeft();
0192                 polygon.append( point );
0193                 //qDebug() << point;
0194             }
0195             if ( closeDatasets() && !polygon.isEmpty() ) {
0196                 // close the circle by connecting the last data point to the first
0197                 polygon.append( polygon.first() );
0198             }
0199 
0200             PainterSaver painterSaver( ctx->painter() );
0201             ctx->painter()->setRenderHint ( QPainter::Antialiasing );
0202             ctx->painter()->setBrush( brush );
0203             QPen p = d->datasetAttrs( iCol, KChart::DatasetPenRole ).value< QPen >();
0204             if ( p.style() != Qt::NoPen )
0205             {
0206                 ctx->painter()->setPen( PrintingParameters::scalePen( p ) );
0207                 ctx->painter()->drawPolyline( polygon );
0208             }
0209         }
0210         d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true );
0211     }
0212 }
0213 
0214 void PolarDiagram::resize ( const QSizeF& size )
0215 {
0216     AbstractPolarDiagram::resize(size);
0217 }
0218 
0219 /*virtual*/
0220 qreal PolarDiagram::valueTotals () const
0221 {
0222     return model()->rowCount(rootIndex());
0223 }
0224 
0225 /*virtual*/
0226 qreal PolarDiagram::numberOfValuesPerDataset() const
0227 {
0228     return model() ? model()->rowCount(rootIndex()) : 0.0;
0229 }
0230 
0231 /*virtual*/
0232 qreal PolarDiagram::numberOfGridRings() const
0233 {
0234     return 5; // FIXME
0235 }
0236 
0237 void PolarDiagram::setZeroDegreePosition( int degrees )
0238 {
0239     Q_UNUSED( degrees );
0240     qWarning() << "Deprecated PolarDiagram::setZeroDegreePosition() called, setting ignored.";
0241 }
0242 
0243 int PolarDiagram::zeroDegreePosition() const
0244 {
0245     qWarning() << "Deprecated PolarDiagram::zeroDegreePosition() called.";
0246     return 0;
0247 }
0248 
0249 void PolarDiagram::setRotateCircularLabels( bool rotateCircularLabels )
0250 {
0251     d->rotateCircularLabels = rotateCircularLabels;
0252 }
0253 
0254 bool PolarDiagram::rotateCircularLabels() const
0255 {
0256     return d->rotateCircularLabels;
0257 }
0258 
0259 void PolarDiagram::setCloseDatasets( bool closeDatasets )
0260 {
0261     d->closeDatasets = closeDatasets;
0262 }
0263 
0264 bool PolarDiagram::closeDatasets() const
0265 {
0266     return d->closeDatasets;
0267 }
0268 
0269 void PolarDiagram::setShowDelimitersAtPosition( Position position,
0270                                                        bool showDelimiters )
0271 {
0272     d->showDelimitersAtPosition[position.value()] = showDelimiters;
0273 }
0274 
0275 void PolarDiagram::setShowLabelsAtPosition( Position position,
0276                                                    bool showLabels )
0277 {
0278     d->showLabelsAtPosition[position.value()] = showLabels;
0279 }
0280 
0281 bool PolarDiagram::showDelimitersAtPosition( Position position ) const
0282 {
0283     return d->showDelimitersAtPosition[position.value()];
0284 }
0285 
0286 bool PolarDiagram::showLabelsAtPosition( Position position ) const
0287 {
0288     return d->showLabelsAtPosition[position.value()];
0289 }
0290 
0291 
0292