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