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 "KChartRadarGrid.h" 0010 0011 #include "KChartPaintContext.h" 0012 #include "KChartRadarDiagram.h" 0013 #include "KChartPieDiagram.h" 0014 #include "KChartPrintingParameters.h" 0015 #include "KChartMath_p.h" 0016 0017 #include <QPainter> 0018 0019 0020 using namespace KChart; 0021 0022 0023 DataDimensionsList RadarGrid::calculateGrid( 0024 const DataDimensionsList& rawDataDimensions ) const 0025 { 0026 qDebug("Calling PolarGrid::calculateGrid()"); 0027 DataDimensionsList l; 0028 0029 //FIXME(khz): do the real calculation 0030 0031 l = rawDataDimensions; 0032 0033 return l; 0034 } 0035 0036 static qreal fitFontSizeToGeometry( const QString& text, const QFont& font, const QRectF& geometry, const TextAttributes& ta ) 0037 { 0038 QFont f = font; 0039 const qreal origResult = f.pointSizeF(); 0040 qreal result = origResult; 0041 const QSizeF mySize = geometry.size(); 0042 if ( mySize.isNull() ) 0043 return result; 0044 0045 const QString t = text; 0046 QFontMetrics fm( f ); 0047 while ( true ) 0048 { 0049 const QSizeF textSize = rotatedRect( fm.boundingRect( t ), ta.rotation() ).normalized().size(); 0050 0051 if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) 0052 return result; 0053 0054 result -= 0.5; 0055 if ( result <= 0.0 ) 0056 return origResult; 0057 f.setPointSizeF( result ); 0058 fm = QFontMetrics( f ); 0059 } 0060 } 0061 0062 QPointF scaleToRealPosition( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect, const AbstractCoordinatePlane& plane ) 0063 { 0064 QPointF result = plane.translate( origin ); 0065 result -= sourceRect.topLeft(); 0066 result.setX( result.x() / sourceRect.width() * destRect.width() ); 0067 result.setY( result.y() / sourceRect.height() * destRect.height() ); 0068 result += destRect.topLeft(); 0069 return result; 0070 } 0071 0072 QPointF scaleToRect( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect ) 0073 { 0074 QPointF result( origin ); 0075 result -= sourceRect.topLeft(); 0076 result.setX( result.x() / sourceRect.width() * destRect.width() ); 0077 result.setY( result.y() / sourceRect.height() * destRect.height() ); 0078 result += destRect.topLeft(); 0079 return result; 0080 } 0081 0082 void RadarGrid::drawGrid( PaintContext* context ) 0083 { 0084 const QBrush backupBrush( context->painter()->brush() ); 0085 context->painter()->setBrush( QBrush() ); 0086 RadarCoordinatePlane* plane = dynamic_cast< RadarCoordinatePlane* >( context->coordinatePlane() ); 0087 Q_ASSERT( plane ); 0088 Q_ASSERT( plane->diagram() ); 0089 QPair< QPointF, QPointF > boundaries = plane->diagram()->dataBoundaries(); 0090 Q_ASSERT_X ( plane, "PolarGrid::drawGrid", 0091 "Bad function call: PaintContext::coodinatePlane() NOT a polar plane." ); 0092 0093 const GridAttributes gridAttrsCircular( plane->gridAttributes( true ) ); 0094 const GridAttributes gridAttrsSagittal( plane->gridAttributes( false ) ); 0095 0096 //qDebug() << "OK:"; 0097 if ( !gridAttrsCircular.isGridVisible() && !gridAttrsSagittal.isGridVisible() ) return; 0098 //qDebug() << "A"; 0099 0100 // FIXME: we paint the rulers to the settings of the first diagram for now: 0101 AbstractPolarDiagram* dgr = dynamic_cast<AbstractPolarDiagram*> (plane->diagrams().first() ); 0102 Q_ASSERT ( dgr ); // only polar diagrams are allowed here 0103 0104 0105 // Do not draw a grid for pie diagrams 0106 if ( dynamic_cast<PieDiagram*> (plane->diagrams().first() ) ) return; 0107 0108 0109 context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) ); 0110 const qreal min = dgr->dataBoundaries().first.y(); 0111 QPointF origin = plane->translate( QPointF( min, 0 ) ) + context->rectangle().topLeft(); 0112 //qDebug() << "origin" << origin; 0113 0114 const qreal r = qAbs( min ) + dgr->dataBoundaries().second.y(); // use the full extents 0115 0116 // distance between two axis lines 0117 const qreal step = ( r - qAbs( min ) ) / ( dgr->numberOfGridRings() ); 0118 0119 // calculate the height needed for text to be displayed at the bottom and top of the chart 0120 QPointF topLeft = context->rectangle().topLeft(); 0121 Q_ASSERT( plane->diagram()->model() ); 0122 TextAttributes ta = plane->textAttributes(); 0123 const int numberOfSpokes = ( int ) ( 360 / plane->angleUnit() ); 0124 const qreal stepWidth = boundaries.second.y() / ( dgr->numberOfGridRings() ); 0125 QRectF destRect = context->rectangle(); 0126 if (ta.isVisible() ) 0127 { 0128 QAbstractItemModel* model = plane->diagram()->model(); 0129 QRectF fontRect = context->rectangle(); 0130 fontRect.setSize( QSizeF( fontRect.width(), step / 2.0 ) ); 0131 const qreal labelFontSize = fitFontSizeToGeometry( QString::fromLatin1( "TestXYWQgqy" ), ta.font(), fontRect, ta ); 0132 QFont labelFont = ta.font(); 0133 context->painter()->setPen( ta.pen() ); 0134 labelFont.setPointSizeF( labelFontSize ); 0135 const QFontMetricsF metric( labelFont ); 0136 const qreal labelHeight = metric.height(); 0137 QPointF offset; 0138 destRect.setY( destRect.y() + 2 * labelHeight ); 0139 destRect.setHeight( destRect.height() - 4 * labelHeight ); 0140 offset.setY( labelHeight ); 0141 offset.setX( 0 ); 0142 topLeft += offset; 0143 origin += offset; 0144 origin = scaleToRealPosition( QPointF( min, 0 ), context->rectangle(), destRect, *plane ); 0145 0146 const qreal aWidth = metric.boundingRect( QString::fromLatin1( "A" ) ).width(); 0147 const QLineF startLine( origin, scaleToRealPosition( QPointF( r - qAbs( min ), 0 ), context->rectangle(), destRect, *plane ) ); 0148 for ( int i = 0; i < model->rowCount(); ++i ) 0149 { 0150 const QLineF currentLine( origin, scaleToRealPosition( QPointF( r - qAbs( min ), i ), context->rectangle(), destRect, *plane ) ); 0151 const int angle = ( int ) startLine.angleTo( currentLine ) % 360; 0152 const qreal angleTest = qAbs( angle - 180 ); 0153 const QString data = model->headerData( i, Qt::Vertical ).toString(); 0154 const qreal xOffset = metric.boundingRect( data ).width() / 2.0; 0155 if ( angleTest < 5.0 ) 0156 context->painter()->drawText( currentLine.pointAt( 1 ) + QPointF( -xOffset, labelHeight + qAbs( min ) ) , data ); 0157 else if ( qAbs( angleTest - 180 ) < 5.0 ) 0158 context->painter()->drawText( currentLine.pointAt( 1 ) - QPointF( xOffset, labelHeight + qAbs( min ) ) , data ); 0159 else if ( angle < 175 && angle > 5 ) 0160 context->painter()->drawText( currentLine.pointAt( 1 ) - QPointF( xOffset * 2 + qAbs( min ) + aWidth, -labelHeight/ 2.0 + qAbs( min ) ) , data ); 0161 else if ( angle < 355 && angle > 185 ) 0162 context->painter()->drawText( currentLine.pointAt( 1 ) + QPointF( qAbs( min ) + aWidth, labelHeight/ 2.0 + qAbs( min ) ) , data ); 0163 0164 } 0165 } 0166 context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) ); 0167 if ( plane->globalGridAttributes().isGridVisible() ) 0168 { 0169 for ( int j = 1; j < dgr->numberOfGridRings() + 1; ++j ) 0170 { 0171 QPointF oldPoint( scaleToRealPosition( QPointF( j * step - qAbs( min ), numberOfSpokes - 1 ), context->rectangle(), destRect, *plane ) ); 0172 for ( int i = 0; i < numberOfSpokes ; ++i ) { 0173 const QPointF newPoint = scaleToRealPosition( QPointF( j * step - qAbs( min ), i ), context->rectangle(), destRect, *plane ); 0174 context->painter()->drawLine( oldPoint, newPoint ); 0175 oldPoint = newPoint; 0176 0177 context->painter()->drawLine( origin, newPoint ); 0178 } 0179 } 0180 context->painter()->setPen( ta.pen() ); 0181 qreal fontSize = 0; 0182 for ( int i = 0; i < dgr->numberOfGridRings() + 1; ++i ) 0183 { 0184 const QString text = QString::number( i * stepWidth ); 0185 const QPointF translatedPoint = scaleToRealPosition( QPointF( i * step - qAbs( min ), 0 ), context->rectangle(), destRect, *plane ); 0186 const QFontMetrics metric( ta.font()/*QFont( "Arial", 10 )*/ ); 0187 const qreal textLength = metric.boundingRect( text ).width(); 0188 const qreal textHeight = metric.height() / 2.0; 0189 QPointF textOffset( textLength, -textHeight ); 0190 textOffset = scaleToRect( textOffset, context->rectangle(), destRect ); 0191 QPointF _topLeft = topLeft; 0192 _topLeft.setY( translatedPoint.y() ); 0193 QRectF boundary( _topLeft, ( translatedPoint + QPointF( 0, step / 2.0 ) ) ); 0194 const qreal calcFontSize = fitFontSizeToGeometry( text, ta.font(), boundary, ta ); 0195 if ( fontSize != calcFontSize ) 0196 { 0197 QFont paintFont( ta.font() ); 0198 paintFont.setPointSizeF( calcFontSize ); 0199 ta.setFont( paintFont ); 0200 ta.setFontSize( calcFontSize ); 0201 const qreal textHeight2 = QFontMetricsF( paintFont ).height() / 2.0; 0202 textOffset.setY( - textHeight2 ); 0203 textOffset = scaleToRect( textOffset, context->rectangle(), destRect ); 0204 context->painter()->setFont( paintFont ); 0205 fontSize = calcFontSize; 0206 } 0207 context->painter()->drawText( translatedPoint + destRect.topLeft() - textOffset, text ); 0208 0209 } 0210 } 0211 plane->setTextAttributes( ta ); 0212 context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) ); 0213 context->painter()->setBrush( backupBrush ); 0214 }