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 }