# KDChartLineDiagram.cpp

Go to the documentation of this file.

    00001 /****************************************************************************
    00002  ** Copyright (C) 2006 Klarävdalens Datakonsult AB.  All rights reserved.
    00003  **
    00004  ** This file is part of the KD Chart library.
    00005  **
    00006  ** This file may be distributed and/or modified under the terms of the
    00007  ** GNU General Public License version 2 as published by the Free Software
    00008  ** Foundtion and appearing in the file LICENSE.GPL included in the
    00009  ** packaging of this file.
    00010  **
    00011  ** Licensees holding valid commercial KD Chart licenses may use this file in
    00012  ** accordance with the KD Chart Commercial License Agreement provided with
    00013  ** the Software.
    00014  **
    00015  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    00016  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    00017  **
    00018  ** See http://www.kdab.net/kdchart for
    00019  **   information about KDChart Commercial License Agreements.
    00020  **
    00021  ** Contact info@kdab.net if any conditions of this
    00022  ** licensing are not clear to you.
    00023  **
    00024  **********************************************************************/
    00025
    00026 #include <QDebug>
    00027 #include <QPainter>
    00028 #include <QString>
    00029 #include <QPainterPath>
    00030 #include <QPen>
    00031 #include <QVector>
    00032
    00033 #include "KDChartLineDiagram.h"
    00034 #include "KDChartLineDiagram_p.h"
    00035 #include "KDChartBarDiagram.h"
    00036 #include "KDChartPalette.h"
    00037 #include "KDChartPosition.h"
    00038 #include "KDChartTextAttributes.h"
    00039 #include "KDChartThreeDLineAttributes.h"
    00040 #include "KDChartAttributesModel.h"
    00041 #include "KDChartAbstractGrid.h"
    00042 #include "KDChartDataValueAttributes.h"
    00043
    00044 #include <KDABLibFakes>
    00045
    00046
    00047 using namespace KDChart;
    00048
    00049 LineDiagram::Private::Private()
    00050   :lineType ( Normal )
    00051 {
    00052 }
    00053
    00054 LineDiagram::Private::~Private() {}
    00055
    00056
    00057 #define d d_func()
    00058
    00059
    00060 LineDiagram::LineDiagram( QWidget* parent, CartesianCoordinatePlane* plane ) :
    00061     AbstractCartesianDiagram( new Private(), parent, plane )
    00062 {
    00063     init();
    00064 }
    00065
    00066 void LineDiagram::init()
    00067 {
    00068 }
    00069
    00070 LineDiagram::~LineDiagram()
    00071 {
    00072 }
    00073
    00074 LineDiagram * LineDiagram::clone() const
    00075 {
    00076     return new LineDiagram( new Private( *d ) );
    00077 }
    00078
    00079
    00080 bool LineDiagram::compare( const LineDiagram* other )const
    00081 {
    00082     if( other == this ) return true;
    00083     if( ! other ){
    00084         //qDebug() << "LineDiagram::compare() cannot compare to Null pointer";
    00085         return false;
    00086     }
    00087     /*
    00088     qDebug() <<"\n             LineDiagram::compare():";
    00089             // compare own properties
    00090     qDebug() << (type() == other->type());
    00091     */
    00092     return  // compare the base class
    00093             ( static_cast<const AbstractCartesianDiagram*>(this)->compare( other ) ) &&
    00094             // compare own properties
    00095             (type() == other->type());
    00096 }
    00097
    00098
    00099 void LineDiagram::setType( const LineType type )
    00100 {
    00101    if ( d->lineType == type ) return;
    00102    if ( type != LineDiagram::Normal && datasetDimension() > 1 ) {
    00103        Q_ASSERT_X ( false, "setType()",
    00104                     "This line chart type can't be used with multi-dimensional data." );
    00105        return;
    00106    }
    00107    d->lineType = type;
    00108    // AbstractAxis settings - see AbstractDiagram and CartesianAxis
    00109    setPercentMode( type == LineDiagram::Percent );
    00110    setDataBoundariesDirty();
    00111    emit layoutChanged( this );
    00112    emit propertiesChanged();
    00113 }
    00114
    00115 LineDiagram::LineType LineDiagram::type() const
    00116 {
    00117    return d->lineType;
    00118 }
    00119
    00120 void LineDiagram::setLineAttributes( const LineAttributes & ta )
    00121 {
    00122      d->attributesModel->setModelData(
    00123         qVariantFromValue( ta ),
    00124         LineAttributesRole );
    00125    emit propertiesChanged();
    00126 }
    00127
    00128 void LineDiagram::setLineAttributes(
    00129         int column,
    00130     const LineAttributes & ta )
    00131 {
    00132     d->attributesModel->setHeaderData(
    00133             column,
    00134             Qt::Vertical,
    00135             qVariantFromValue( ta ),
    00136             LineAttributesRole );
    00137     emit propertiesChanged();
    00138 }
    00139
    00140 void LineDiagram::resetLineAttributes( int column )
    00141 {
    00142     d->attributesModel->resetHeaderData(
    00143             column, Qt::Vertical, LineAttributesRole );
    00144     emit propertiesChanged();
    00145 }
    00146
    00147 void LineDiagram::setLineAttributes(
    00148         const QModelIndex & index,
    00149     const LineAttributes & ta )
    00150 {
    00151     d->attributesModel->setData(
    00152             d->attributesModel->mapFromSource(index),
    00153     qVariantFromValue( ta ),
    00154     LineAttributesRole );
    00155     emit propertiesChanged();
    00156 }
    00157
    00161 void LineDiagram::resetLineAttributes( const QModelIndex & index )
    00162 {
    00163     d->attributesModel->resetData(
    00164             d->attributesModel->mapFromSource(index), LineAttributesRole );
    00165     emit propertiesChanged();
    00166 }
    00167
    00168 LineAttributes LineDiagram::lineAttributes() const
    00169 {
    00170     return qVariantValue<LineAttributes>(
    00171         d->attributesModel->data( KDChart::LineAttributesRole ) );
    00172 }
    00173
    00174 LineAttributes LineDiagram::lineAttributes( int column ) const
    00175 {
    00176     return qVariantValue<LineAttributes>(
    00177         d->attributesModel->data(
    00178             d->attributesModel->mapFromSource( columnToIndex( column ) ),
    00179             KDChart::LineAttributesRole ) );
    00180 }
    00181
    00182 LineAttributes LineDiagram::lineAttributes(
    00183     const QModelIndex & index ) const
    00184 {
    00185     return qVariantValue<LineAttributes>(
    00186         d->attributesModel->data(
    00187             d->attributesModel->mapFromSource(index),
    00188             KDChart::LineAttributesRole ) );
    00189 }
    00190
    00191 void LineDiagram::setThreeDLineAttributes(
    00192     const ThreeDLineAttributes & ta )
    00193 {
    00194     setDataBoundariesDirty();
    00195     d->attributesModel->setModelData(
    00196         qVariantFromValue( ta ),
    00197         ThreeDLineAttributesRole );
    00198    emit propertiesChanged();
    00199 }
    00200
    00201 void LineDiagram::setThreeDLineAttributes(
    00202     int column,
    00203     const ThreeDLineAttributes & ta )
    00204 {
    00205     setDataBoundariesDirty();
    00206     d->attributesModel->setHeaderData(
    00207         column,
    00208         Qt::Vertical,
    00209         qVariantFromValue( ta ),
    00210         ThreeDLineAttributesRole );
    00211    emit propertiesChanged();
    00212 }
    00213
    00214 void LineDiagram::setThreeDLineAttributes(
    00215     const QModelIndex & index,
    00216     const ThreeDLineAttributes & ta )
    00217 {
    00218     setDataBoundariesDirty();
    00219     d->attributesModel->setData(
    00220         d->attributesModel->mapFromSource(index),
    00221         qVariantFromValue( ta ),
    00222         ThreeDLineAttributesRole );
    00223    emit propertiesChanged();
    00224 }
    00225
    00226 ThreeDLineAttributes LineDiagram::threeDLineAttributes() const
    00227 {
    00228     return qVariantValue<ThreeDLineAttributes>(
    00229         d->attributesModel->data( KDChart::ThreeDLineAttributesRole ) );
    00230 }
    00231
    00232 ThreeDLineAttributes LineDiagram::threeDLineAttributes( int column ) const
    00233 {
    00234     return qVariantValue<ThreeDLineAttributes>(
    00235         d->attributesModel->data(
    00236             d->attributesModel->mapFromSource( columnToIndex( column ) ),
    00237             KDChart::ThreeDLineAttributesRole ) );
    00238 }
    00239
    00240 ThreeDLineAttributes LineDiagram::threeDLineAttributes(
    00241     const QModelIndex & index ) const
    00242 {
    00243     return qVariantValue<ThreeDLineAttributes>(
    00244         d->attributesModel->data(
    00245             d->attributesModel->mapFromSource( index ),
    00246             KDChart::ThreeDLineAttributesRole ) );
    00247 }
    00248
    00249 double LineDiagram::threeDItemDepth( const QModelIndex & index ) const
    00250 {
    00251     return threeDLineAttributes( index ).validDepth();
    00252 }
    00253
    00254 double LineDiagram::threeDItemDepth( int column ) const
    00255 {
    00256     return qVariantValue<ThreeDLineAttributes>(
    00257         d->attributesModel->headerData (
    00258             column,
    00259             Qt::Vertical,
    00260             KDChart::ThreeDLineAttributesRole ) ).validDepth();
    00261 }
    00262
    00263 void LineDiagram::resizeEvent ( QResizeEvent* )
    00264 {
    00265 }
    00266
    00267 const QPair<QPointF, QPointF> LineDiagram::calculateDataBoundaries() const
    00268 {
    00269     if ( !checkInvariants( true ) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
    00270
    00271     // note: calculateDataBoundaries() is ignoring the hidden flags.
    00272     //       That's not a bug but a feature: Hiding data does not mean removing them.
    00273     // For totally removing data from KD Chart's view people can use e.g. a proxy model ...
    00274
    00275     const int rowCount = d->attributesModel->rowCount(attributesModelRootIndex());
    00276     const int colCount = d->attributesModel->columnCount(attributesModelRootIndex());
    00277     double xMin = 0;
    00278     double xMax = rowCount -1;
    00279     double yMin = 0, yMax = 0;
    00280     bool bOK;
    00281
    00282     // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal
    00283     switch ( type() ){
    00284     case LineDiagram::Normal:
    00285     {
    00286         bool bStarting = true;
    00287         for( int i = datasetDimension()-1; i < colCount; i += datasetDimension() ) {
    00288             for ( int j=0; j< rowCount; ++j ) {
    00289                 const double value = valueForCellTesting( j, i, bOK );
    00290                 double xvalue;
    00291                 if( datasetDimension() > 1 && bOK )
    00292                     xvalue = valueForCellTesting( j, i-1, bOK );
    00293                 if( bOK ){
    00294                     if( bStarting ){
    00295                         yMin = value;
    00296                         yMax = value;
    00297                     }else{
    00298                         yMin = qMin( yMin, value );
    00299                         yMax = qMax( yMax, value );
    00300                     }
    00301                     if ( datasetDimension() > 1 ) {
    00302                         if( bStarting ){
    00303                             xMin = xvalue;
    00304                             xMax = xvalue;
    00305                         }else{
    00306                             xMin = qMin( xMin, xvalue );
    00307                             xMax = qMax( xMax, xvalue );
    00308                         }
    00309                     }
    00310                     bStarting = false;
    00311                 }
    00312             }
    00313         }
    00314
    00315         // the following code is replaced by CartesianCoordinatePlane's automatic range / zoom adjusting
    00316         //if( yMin > 0 && yMax / yMin >= 2.0 )
    00317         //    yMin = 0;
    00318         //else if( yMax < 0 && yMax / yMin <= 0.5 )
    00319         //    yMax = 0;
    00320     }
    00321     break;
    00322     case LineDiagram::Stacked:
    00323     {
    00324         bool bStarting = true;
    00325         for ( int j=0; j< rowCount; ++j ) {
    00326             // calculate sum of values per column - Find out stacked Min/Max
    00327             double stackedValues = 0;
    00328             for( int i = datasetDimension()-1; i < colCount; i += datasetDimension() ) {
    00329                 const double value = valueForCellTesting( j, i, bOK );
    00330                 if( bOK )
    00331                     stackedValues += value;
    00332             }
    00333             if( bStarting ){
    00334                 yMin = stackedValues;
    00335                 yMax = stackedValues;
    00336                 bStarting = false;
    00337             }else{
    00338                 // Pending Michel:
    00339                 // I am taking in account all values negatives or positives
    00340                 // take in account all stacked values
    00341                 yMin = qMin( qMin( yMin,  0.0 ), stackedValues );
    00342                 yMax = qMax( yMax, stackedValues );
    00343             }
    00344         }
    00345     }
    00346     break;
    00347     case LineDiagram::Percent:
    00348     {
    00349         for( int i = datasetDimension()-1; i < colCount; i += datasetDimension() ) {
    00350             for ( int j=0; j< rowCount; ++j ) {
    00351                 // Ordinate should begin at 0 the max value being the 100% pos
    00352                 const double value = valueForCellTesting( j, i, bOK );
    00353                 if( bOK )
    00354                     yMax = qMax( yMax, value );
    00355             }
    00356         }
    00357     }
    00358     break;
    00359     default:
    00360         Q_ASSERT_X ( false, "calculateDataBoundaries()",
    00361                      "Type item does not match a defined line chart Type." );
    00362     }
    00363
    00364     QPointF bottomLeft( QPointF( xMin, yMin ) );
    00365     QPointF topRight(   QPointF( xMax, yMax ) );
    00366     //qDebug() << "LineDiagram::calculateDataBoundaries () returns ( " << bottomLeft << topRight <<")";
    00367     return QPair<QPointF, QPointF> ( bottomLeft, topRight );
    00368 }
    00369
    00370
    00371 void LineDiagram::paintEvent ( QPaintEvent*)
    00372 {
    00373 //qDebug() << "starting LineDiagram::paintEvent ( QPaintEvent*)";
    00374     QPainter painter ( viewport() );
    00375     PaintContext ctx;
    00376     ctx.setPainter ( &painter );
    00377     ctx.setRectangle ( QRectF ( 0, 0, width(), height() ) );
    00378     paint ( &ctx );
    00379 //qDebug() << "         LineDiagram::paintEvent ( QPaintEvent*) ended.";
    00380 }
    00381
    00382
    00383 double LineDiagram::valueForCellTesting( int row, int column,
    00384                                          bool& bOK,
    00385                                          bool showHiddenCellsAsInvalid ) const
    00386 {
    00387     double value;
    00388     if( showHiddenCellsAsInvalid && isHidden( model()->index( row, column, rootIndex() ) ) )
    00389         bOK = false;
    00390     else
    00391         value = d->attributesModel->data(
    00392                     d->attributesModel->index( row, column, attributesModelRootIndex() )
    00393                 ).toDouble( &bOK );
    00394     return bOK ? value : 0.0;
    00395 }
    00396
    00397
    00398 LineAttributes::MissingValuesPolicy LineDiagram::getCellValues(
    00399       int row, int column,
    00400       bool shiftCountedXValuesByHalfSection,
    00401       double& valueX, double& valueY ) const
    00402 {
    00403     LineAttributes::MissingValuesPolicy policy;
    00404
    00405     bool bOK = true;
    00406     valueX = ( datasetDimension() > 1 && column > 0 )
    00407            ? valueForCellTesting( row, column-1, bOK, true )
    00408            : ((shiftCountedXValuesByHalfSection ? 0.5 : 0.0) + row);
    00409     if( bOK )
    00410         valueY = valueForCellTesting( row, column, bOK, true );
    00411     if( bOK ){
    00412         policy = LineAttributes::MissingValuesPolicyIgnored;
    00413     }else{
    00414         // missing value: find out the policy
    00415         QModelIndex index = model()->index( row, column, rootIndex() );
    00416         LineAttributes la = lineAttributes( index );
    00417         policy = la.missingValuesPolicy();
    00418     }
    00419     return policy;
    00420 }
    00421
    00422 /*commenting this include: used for testing
    00423 notice: Windows compilers need this include to
    00424 be written before the #define d(d_func()) above*/
    00425 //#include <QTime>
    00426
    00427 void LineDiagram::paint( PaintContext* ctx )
    00428 {
    00429 //qDebug() << "    start diag::paint()";
    00430     // note: Not having any data model assigned is no bug
    00431     //       but we can not draw a diagram then either.
    00432     if ( !checkInvariants( true ) ) return;
    00433     if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return;
    00434
    00435     // Make sure counted x values (== in diagrams with 1-dimensional data cells)
    00436     // get shifted by 0.5, if the diagram's reference diagram is a BarDiagram.
    00437     // So we get the lines to start/end at the middle of the respective bar groups.
    00438     const bool shiftCountedXValuesByHalfSection =
    00439             (dynamic_cast< BarDiagram* >( referenceDiagram() ) != 0);
    00440
    00441     //QTime t = QTime::currentTime();
    00442
    00443     const QPair<QPointF, QPointF> boundaries = dataBoundaries();
    00444     const QPointF bottomLeft = boundaries.first;
    00445     const QPointF topRight = boundaries.second;
    00446
    00447     int maxFound = 0;
    00448     {   // find the last column number that is not hidden
    00449         const int columnCount = d->attributesModel->columnCount(attributesModelRootIndex());
    00450         for( int iColumn =  datasetDimension()-1;
    00451             iColumn <  columnCount;
    00452             iColumn += datasetDimension() )
    00453             if( ! isHidden( iColumn ) )
    00454                 maxFound = iColumn;
    00455     }
    00456     const int lastVisibleColumn = maxFound;
    00457     const int rowCount = d->attributesModel->rowCount(attributesModelRootIndex());
    00458
    00459     DataValueTextInfoList list;
    00460     LineAttributesInfoList lineList;
    00461     LineAttributes::MissingValuesPolicy policy;
    00462
    00463     // paint different line types Normal - Stacked - Percent - Default Normal
    00464     switch ( type() )
    00465     {
    00466         case LineDiagram::Normal:
    00467         {
    00468             for( int iColumn  = datasetDimension()-1;
    00469                      iColumn <= lastVisibleColumn;
    00470                      iColumn += datasetDimension() ) {
    00471
    00472                 //display area can be set by dataset ( == column) and/or by cell
    00473                 LineAttributes laPreviousCell; // by default no area is drawn
    00474                 QModelIndex indexPreviousCell;
    00475                 QList<QPolygonF> areas;
    00476
    00477                 bool bValuesFound = false;
    00478                 double lastValueX, lastValueY;
    00479                 double valueX, valueY;
    00480                 for ( int iRow = 0; iRow < rowCount; ++iRow ) {
    00481                     bool skipThisCell = false;
    00482                     // trying to find a fromPoint
    00483                     policy = getCellValues( iRow, iColumn,
    00484                                             shiftCountedXValuesByHalfSection,
    00485                                             valueX, valueY );
    00486                     switch( policy ){
    00487                         case LineAttributes::MissingValuesAreBridged:
    00488                             if( bValuesFound ){
    00489                                 valueX = lastValueX;
    00490                                 valueY = lastValueY;
    00491                             }else{
    00492                                 skipThisCell = true;
    00493                             }
    00494                             break;
    00495                         case LineAttributes::MissingValuesHideSegments:
    00496                             skipThisCell = true;
    00497                             break;
    00498                         case LineAttributes::MissingValuesShownAsZero:
    00499                             // fall through intended
    00500                         case LineAttributes::MissingValuesPolicyIgnored:
    00501                             lastValueX = valueX;
    00502                             lastValueY = valueY;
    00503                             bValuesFound = true;
    00504                             break;
    00505                     }
    00506                     if( ! skipThisCell ){
    00507                         // trying to find a toPoint
    00508                         double nextValueX, nextValueY;
    00509                         bool foundToPoint = false;
    00510                         int iNextRow = iRow+1;
    00511                         while ( ! (foundToPoint || skipThisCell || iNextRow >= rowCount) ) {
    00512                             policy = getCellValues(
    00513                                     iNextRow, iColumn,
    00514                                     shiftCountedXValuesByHalfSection,
    00515                                     nextValueX, nextValueY );
    00516                             switch( policy ){
    00517                                 case LineAttributes::MissingValuesAreBridged:
    00518                                     // The cell has no valid value, so we  make sure that
    00519                                     // this cell will not be processed by the next iteration
    00520                                     // of the iRow loop:
    00521                                     ++iRow;
    00522                                     break;
    00523                                 case LineAttributes::MissingValuesHideSegments:
    00524                                     // The cell has no valid value, so we  make sure that
    00525                                     // this cell will not be processed by the next iteration
    00526                                     // of the iRow loop:
    00527                                     skipThisCell = true;
    00528                                     ++iRow;
    00529                                     break;
    00530                                 case LineAttributes::MissingValuesShownAsZero:
    00531                                     // fall through intended
    00532                                 case LineAttributes::MissingValuesPolicyIgnored:
    00533                                     foundToPoint = true;
    00534                                     break;
    00535                             }
    00536                             ++iNextRow;
    00537                         }
    00538                         if( ! skipThisCell ){
    00539                             const bool isPositive = (valueY >= 0.0);
    00540                             const QModelIndex index = model()->index( iRow, iColumn, rootIndex() );
    00541                             const LineAttributes laCell = lineAttributes( index );
    00542                             const bool bDisplayCellArea = laCell.displayArea();
    00543
    00544                             QPointF fromPoint = coordinatePlane()->translate( QPointF( valueX, valueY ) );
    00545
    00546                             const QPointF ptNorthWest(
    00547                                 (bDisplayCellArea && ! isPositive)
    00548                                 ? coordinatePlane()->translate( QPointF( valueX, 0.0 ) )
    00549                                 : fromPoint );
    00550                             const QPointF ptSouthWest(
    00551                                 (bDisplayCellArea && isPositive)
    00552                                 ? coordinatePlane()->translate( QPointF( valueX, 0.0 ) )
    00553                                 : fromPoint );
    00554                             //qDebug() << "--> ptNorthWest:" << ptNorthWest;
    00555                             //qDebug() << "--> ptSouthWest:" << ptSouthWest;
    00556                             QPointF ptNorthEast;
    00557                             QPointF ptSouthEast;
    00558
    00559                             if( foundToPoint ){
    00560                                 QPointF toPoint = coordinatePlane()->translate( QPointF( nextValueX, nextValueY ) );
    00561                                 lineList.append( LineAttributesInfo( index, fromPoint, toPoint ) );
    00562                                 ptNorthEast =
    00563                                     (bDisplayCellArea && ! isPositive)
    00564                                     ? coordinatePlane()->translate( QPointF( nextValueX, 0.0 ) )
    00565                                     : toPoint;
    00566                                 ptSouthEast =
    00567                                     (bDisplayCellArea && isPositive)
    00568                                     ? coordinatePlane()->translate( QPointF( nextValueX, 0.0 ) )
    00569                                     : toPoint;
    00570                                 // we can't take as a condition the line attributes
    00571                                 // to be different from a cell to another.
    00572                                 // e.g the user should be able to have different brush
    00573                                 // which is not reflected in the line attributes
    00574                                 // see examples/Area which show such an option
    00575                                 if( areas.count() /*&& laCell != laPreviousCell*/ ){
    00576                                     paintAreas( ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
    00577                                     areas.clear();
    00578                                 }
    00579                                 if( bDisplayCellArea ){
    00580                                     QPolygonF poly;
    00581                                     poly << ptNorthWest << ptNorthEast << ptSouthEast << ptSouthWest;
    00582                                     //qDebug() << "ptNorthWest:" << ptNorthWest;
    00583                                     //qDebug() << "ptNorthEast:" << ptNorthEast;
    00584                                     //qDebug() << "ptSouthEast:" << ptSouthEast;
    00585                                     //qDebug() << "ptSouthWest:" << ptSouthWest;
    00586                                     //qDebug() << "polygon:" << poly;
    00587                                     areas << poly;
    00588                                     laPreviousCell = laCell;
    00589                                     indexPreviousCell = index;
    00590                                 }
    00591                             }else{
    00592                                 ptNorthEast = ptNorthWest;
    00593                                 ptSouthEast = ptSouthWest;
    00594                             }
    00595
    00596                             const PositionPoints pts( ptNorthWest, ptNorthEast, ptSouthEast, ptSouthWest );
    00597                             d->appendDataValueTextInfoToList( this, list, index, pts,
    00598                                     Position::NorthWest, Position::SouthWest,
    00599                                     valueY );
    00600                         }
    00601                     }
    00602                 }
    00603                 if( areas.count() ){
    00604                     paintAreas( ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
    00605                     areas.clear();
    00606                 }
    00607             }
    00608         }
    00609             break;
    00610         case LineDiagram::Stacked:
    00611             // fall-through intended
    00612         case LineDiagram::Percent:
    00613         {
    00614             //FIXME(khz): add LineAttributes::MissingValuesPolicy support for LineDiagram::Stacked and ::Percent
    00615
    00616             const bool isPercentMode = type() == LineDiagram::Percent;
    00617             double maxValue = 100; // always 100%
    00618             double sumValues = 0;
    00619             QVector <double > percentSumValues;
    00620
    00621             //calculate sum of values for each column and store
    00622             if( isPercentMode ){
    00623                 for ( int j=0; j<rowCount ; ++j ) {
    00624                     for( int i =  datasetDimension()-1;
    00625                         i <= lastVisibleColumn;
    00626                         i += datasetDimension() ) {
    00627                             double tmpValue = valueForCell( j, i );
    00628                             if ( tmpValue > 0 )
    00629                                 sumValues += tmpValue;
    00630                             if ( i == lastVisibleColumn ) {
    00631                                 percentSumValues <<  sumValues ;
    00632                                 sumValues = 0;
    00633                             }
    00634                         }
    00635                 }
    00636             }
    00637
    00638             QList<QPointF> bottomPoints;
    00639             bool bFirstDataset = true;
    00640
    00641             for( int iColumn =  datasetDimension()-1;
    00642                  iColumn <= lastVisibleColumn;
    00643                  iColumn += datasetDimension() ) {
    00644
    00645                 //display area can be set by dataset ( == column) and/or by cell
    00646                 LineAttributes laPreviousCell; // by default no area is drawn
    00647                 QModelIndex indexPreviousCell;
    00648                 QList<QPolygonF> areas;
    00649                 QList<QPointF> points;
    00650
    00651                 for ( int iRow = 0; iRow< rowCount; ++iRow ) {
    00652                     const QModelIndex index = model()->index( iRow, iColumn, rootIndex() );
    00653                     const LineAttributes laCell = lineAttributes( index );
    00654                     const bool bDisplayCellArea = laCell.displayArea();
    00655
    00656                     double stackedValues = 0, nextValues = 0;
    00657                     for ( int iColumn2 = iColumn;
    00658                           iColumn2 >= datasetDimension()-1;
    00659                           iColumn2 -= datasetDimension() )
    00660                     {
    00661                         const double val = valueForCell( iRow, iColumn2 );
    00662                         if( val > 0 || ! isPercentMode )
    00663                             stackedValues += val;
    00664                         //qDebug() << valueForCell( iRow, iColumn2 );
    00665                         if ( iRow+1 < rowCount ){
    00666                             const double val = valueForCell( iRow+1, iColumn2 );
    00667                             if( val > 0 || ! isPercentMode )
    00668                                 nextValues += val;
    00669                         }
    00670                     }
    00671                     if( isPercentMode ){
    00672                         if ( percentSumValues.at( iRow ) != 0  )
    00673                             stackedValues = stackedValues / percentSumValues.at( iRow ) * maxValue;
    00674                         else
    00675                             stackedValues = 0.0;
    00676                     }
    00677                     //qDebug() << stackedValues << endl;
    00678                     QPointF nextPoint = coordinatePlane()->translate( QPointF( iRow, stackedValues ) );
    00679                     points << nextPoint;
    00680
    00681                     const QPointF ptNorthWest( nextPoint );
    00682                     const QPointF ptSouthWest(
    00683                             bDisplayCellArea
    00684                             ? ( bFirstDataset
    00685                                 ? coordinatePlane()->translate( QPointF( iRow, 0.0 ) )
    00686                                 : bottomPoints.at( iRow )
    00687                               )
    00688                             : nextPoint );
    00689                     QPointF ptNorthEast;
    00690                     QPointF ptSouthEast;
    00691
    00692                     if ( iRow+1 < rowCount ){
    00693                         if( isPercentMode ){
    00694                             if ( percentSumValues.at( iRow+1 ) != 0  )
    00695                                 nextValues = nextValues / percentSumValues.at( iRow+1 ) * maxValue;
    00696                             else
    00697                                 nextValues = 0.0;
    00698                         }
    00699                         QPointF toPoint = coordinatePlane()->translate( QPointF( iRow+1, nextValues ) );
    00700                         lineList.append( LineAttributesInfo( index, nextPoint, toPoint ) );
    00701                         ptNorthEast = toPoint;
    00702                         ptSouthEast =
    00703                             bDisplayCellArea
    00704                             ? ( bFirstDataset
    00705                                 ? coordinatePlane()->translate( QPointF( iRow+1, 0.0 ) )
    00706                                 : bottomPoints.at( iRow+1 )
    00707                               )
    00708                             : toPoint;
    00709                         if( areas.count() && laCell != laPreviousCell ){
    00710                             paintAreas( ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
    00711                             areas.clear();
    00712                         }
    00713                         if( bDisplayCellArea ){
    00714                             QPolygonF poly;
    00715                             poly << ptNorthWest << ptNorthEast << ptSouthEast << ptSouthWest;
    00716                             areas << poly;
    00717                             laPreviousCell = laCell;
    00718                             indexPreviousCell = index;
    00719                         }else{
    00720                             //qDebug() << "no area shown for row"<<iRow<<"  column"<<iColumn;
    00721                         }
    00722                     }else{
    00723                         ptNorthEast = ptNorthWest;
    00724                         ptSouthEast = ptSouthWest;
    00725                     }
    00726
    00727                     const PositionPoints pts( ptNorthWest, ptNorthEast, ptSouthEast, ptSouthWest );
    00728                     d->appendDataValueTextInfoToList( this, list, index, pts,
    00729                             Position::NorthWest, Position::SouthWest,
    00730                             valueForCell( iRow, iColumn ) );
    00731                 }
    00732                 if( areas.count() ){
    00733                     paintAreas( ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
    00734                     areas.clear();
    00735                 }
    00736                 bottomPoints = points;
    00737                 bFirstDataset = false;
    00738             }
    00739         }
    00740             break;
    00741         default:
    00742             Q_ASSERT_X ( false, "paint()",
    00743                          "Type item does not match a defined line chart Type." );
    00744     }
    00745     // paint all lines and their attributes
    00746     {
    00747         PainterSaver painterSaver( ctx->painter() );
    00748         if ( antiAliasing() )
    00749             ctx->painter()->setRenderHint ( QPainter::Antialiasing );
    00750         LineAttributesInfoListIterator itline ( lineList );
    00751
    00752         //qDebug() << "Rendering 1 in: " << t.msecsTo( QTime::currentTime() ) << endl;
    00753
    00754         QBrush curBrush;
    00755         QPen curPen;
    00756         QPolygonF points;
    00757         while ( itline.hasNext() ) {
    00758             const LineAttributesInfo& lineInfo = itline.next();
    00759             const QModelIndex& index = lineInfo.index;
    00760             const ThreeDLineAttributes td = threeDLineAttributes( index );
    00761             if( td.isEnabled() ){
    00762                 paintThreeDLines( ctx, index, lineInfo.value, lineInfo.nextValue, td.depth() );
    00763             }else{
    00764                 const QBrush br( brush( index ) );
    00765                 const QPen   pn( pen(   index ) );
    00766                 if( points.count() && points.last() == lineInfo.value && curBrush == br && curPen == pn ){
    00767                     points << lineInfo.nextValue;
    00768                 }else{
    00769                     if( points.count() )
    00770                         paintPolyline( ctx, curBrush, curPen, points );
    00771                     curBrush = br;
    00772                     curPen   = pn;
    00773                     points.clear();
    00774                     points << lineInfo.value << lineInfo.nextValue;
    00775                 }
    00776             }
    00777         }
    00778         if( points.count() )
    00779             paintPolyline( ctx, curBrush, curPen, points );
    00780     }
    00781     // paint all data value texts and the point markers
    00782     d->paintDataValueTextsAndMarkers( this, ctx, list, true );
    00783     //qDebug() << "Rendering 2 in: " << t.msecsTo( QTime::currentTime() ) << endl;
    00784 }
    00785
    00786
    00787 void LineDiagram::paintPolyline( PaintContext* ctx,
    00788                                  const QBrush& brush, const QPen& pen,
    00789                                  const QPolygonF& points ) const
    00790 {
    00791     ctx->painter()->setBrush( brush );
    00792     ctx->painter()->setPen(
    00793         QPen( pen.color(),
    00794               pen.width(),
    00795               pen.style(),
    00796               Qt::FlatCap,
    00797               Qt::MiterJoin ) );
    00798     ctx->painter()->drawPolyline( points );
    00799 }
    00800
    00801
    00802 /* old:
    00803 void LineDiagram::paintLines( PaintContext* ctx, const QModelIndex& index,
    00804                               const QPointF& from, const QPointF& to )
    00805 {
    00806     ThreeDLineAttributes td = threeDLineAttributes(index);
    00807     if ( td.isEnabled() ) {
    00808         const double lineDepth = td.depth();
    00809         paintThreeDLines( ctx, index, from, to, lineDepth );
    00810     } else {
    00811         ctx->painter()->setBrush( brush( index ) );
    00812         ctx->painter()->setPen( pen( index ) );
    00813         if ( index.row() + 1 < d->attributesModel->rowCount(attributesModelRootIndex()) ) {
    00814             if ( ctx->rectangle().contains( from ) || ctx->rectangle().contains( to ) ) {
    00815                 ctx->painter()->drawLine( from, to );
    00816             }
    00817         }
    00818     }
    00819 }
    00820 */
    00821
    00822
    00823 void LineDiagram::paintAreas( PaintContext* ctx, const QModelIndex& index, const QPolygonF& area, const uint transparency )
    00824 {
    00825     QColor trans( brush(index).color() );
    00826     QPen indexPen( pen(index) );
    00827     trans.setAlpha( transparency );
    00828     indexPen.setColor( trans );
    00829     PainterSaver painterSaver( ctx->painter() );
    00830     if ( antiAliasing() )
    00831         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
    00832     ctx->painter()->setPen( indexPen );
    00833     ctx->painter()->setBrush( trans ) ;
    00834     ctx->painter()->drawPolygon( area );//pol );
    00835 }
    00836
    00837 void LineDiagram::paintAreas( PaintContext* ctx, const QModelIndex& index, const QList<QPolygonF>& areas, const uint transparency )
    00838 {
    00839     QColor trans( brush(index).color() );
    00840     trans.setAlpha( transparency );
    00841     QPen indexPen( pen(index) );
    00842     indexPen.setColor( trans );
    00843     PainterSaver painterSaver( ctx->painter() );
    00844     if ( antiAliasing() )
    00845         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
    00846     ctx->painter()->setPen( indexPen );
    00847     ctx->painter()->setBrush( trans );
    00848     QPainterPath path;
    00849     for( int i=0; i<areas.count(); ++i ){
    00850         path.addPolygon( areas[i] );
    00851         path.closeSubpath();
    00852         //qDebug() << "LineDiagram::paintAreas() adding path:"<<areas[i];
    00853     }
    00854     //qDebug() << endl;
    00855     ctx->painter()->drawPath( path );
    00856 }
    00857
    00864 const QPointF LineDiagram::project( QPointF point, QPointF maxLimits, double z, const QModelIndex& index ) const
    00865 {
    00866   ThreeDLineAttributes td = threeDLineAttributes( index );
    00867
    00868     //Pending Michel FIXME - the rotation does not work as expected atm
    00869     double xrad = DEGTORAD( td.lineXRotation() );
    00870     double yrad = DEGTORAD( td.lineYRotation() );
    00871     QPointF ret = QPointF(point.x()*cos( yrad ) + z * sin( yrad ) ,  point.y()*cos( xrad ) - z * sin( xrad ) );
    00872     return ret;
    00873 }
    00874
    00875 void LineDiagram::paintThreeDLines(PaintContext* ctx, const QModelIndex& index, const QPointF& from, const QPointF& to, const double depth  )
    00876 {
    00877     // retrieve the boundaries
    00878     const QPair<QPointF, QPointF> boundaries = dataBoundaries ();
    00879     QPointF maxLimits = boundaries.second;
    00880     QVector <QPointF > segmentPoints;
    00881     QPointF topLeft = project( from, maxLimits, depth, index  );
    00882     QPointF topRight = project ( to, maxLimits, depth, index  );
    00883
    00884     segmentPoints << from << topLeft << topRight << to;
    00885     QPolygonF segment ( segmentPoints );
    00886     QBrush indexBrush ( brush( index ) );
    00887     PainterSaver painterSaver( ctx->painter() );
    00888     if ( antiAliasing() )
    00889         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
    00890     ctx->painter()->setBrush( indexBrush );
    00891     ctx->painter()->setPen( pen( index ) ) ;
    00892     ctx->painter()->drawPolygon( segment );
    00893 }
    00894
    00895 void LineDiagram::resize ( const QSizeF& )
    00896 {
    00897 }
    00898
    00899 const int LineDiagram::numberOfAbscissaSegments () const
    00900 {
    00901     return d->attributesModel->rowCount(attributesModelRootIndex());
    00902 }
    00903
    00904 const int LineDiagram::numberOfOrdinateSegments () const
    00905 {
    00906     return d->attributesModel->columnCount(attributesModelRootIndex());
    00907 }
    00908
    00909 //#endif
    00910
    00911