KDChartLineDiagram_p.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL included.
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 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "KDChartLineDiagram.h"
00024 #include "KDChartDataValueAttributes.h"
00025 
00026 #include "KDChartLineDiagram_p.h"
00027 
00028 using namespace KDChart;
00029 using namespace std;
00030 
00031 LineDiagram::Private::Private( const Private& rhs )
00032     : AbstractCartesianDiagram::Private( rhs )
00033 {
00034 }
00035 
00036 void LineDiagram::Private::paintPolyline(
00037     PaintContext* ctx,
00038     const QBrush& brush, const QPen& pen,
00039     const QPolygonF& points ) const
00040 {
00041     ctx->painter()->setBrush( brush );
00042     ctx->painter()->setPen( PrintingParameters::scalePen(
00043         QPen( pen.color(),
00044               pen.width(),
00045               pen.style(),
00046               Qt::FlatCap,
00047               Qt::MiterJoin ) ) );
00048 #if QT_VERSION > 0x040299
00049     ctx->painter()->drawPolyline( points );
00050 #else
00051     // FIXME (Mirko) verify, this sounds reverse-logical
00052     // For Qt versions older than 4.3 drawPolyline is VERY slow
00053     // so we use traditional line segments drawing instead then.
00054     for (int i = 0; i < points.size()-1; ++i)
00055         ctx->painter()->drawLine( points.at(i), points.at(i+1) );
00056 #endif
00057 }
00058 
00064 const QPointF LineDiagram::LineDiagramType::project(
00065     QPointF point, QPointF maxLimits,
00066     double z, const QModelIndex& index ) const
00067 {
00068     Q_UNUSED( maxLimits );
00069     ThreeDLineAttributes td = diagram()->threeDLineAttributes( index );
00070 
00071     //Pending Michel FIXME - the rotation does not work as expected atm
00072     double xrad = DEGTORAD( td.lineXRotation() );
00073     double yrad = DEGTORAD( td.lineYRotation() );
00074     QPointF ret = QPointF(point.x()*cos( yrad ) + z * sin( yrad ) ,  point.y()*cos( xrad ) - z * sin( xrad ) );
00075     return ret;
00076 }
00077 
00078 void LineDiagram::LineDiagramType::paintThreeDLines(
00079     PaintContext* ctx, const QModelIndex& index,
00080     const QPointF& from, const QPointF& to, const double depth  )
00081 {
00082     // retrieve the boundaries
00083     const QPair< QPointF, QPointF > boundaries = diagram()->dataBoundaries();
00084     const QPointF& maxLimits = boundaries.second;
00085     const QPointF topLeft = project( from, maxLimits, depth, index  );
00086     const QPointF topRight = project ( to, maxLimits, depth, index  );
00087 
00088     const QPolygonF segment = QPolygonF() << from << topLeft << topRight << to;
00089     const QBrush indexBrush ( diagram()->brush( index ) );
00090     const PainterSaver painterSaver( ctx->painter() );
00091 
00092     if( diagram()->antiAliasing() )
00093         ctx->painter()->setRenderHint( QPainter::Antialiasing );
00094 
00095     ctx->painter()->setBrush( indexBrush );
00096     ctx->painter()->setPen( PrintingParameters::scalePen( diagram()->pen( index ) ) );
00097 
00098     reverseMapper().addPolygon( index.row(), index.column(), segment );
00099     ctx->painter()->drawPolygon( segment );
00100 }
00101 
00102 // this method is factored out from LineDiagram::paint, and contains
00103 // the common parts of the method that  previously implemented all
00104 // chart types in one
00105 void LineDiagram::LineDiagramType::paintElements(
00106     PaintContext* ctx,
00107     DataValueTextInfoList& list,
00108     LineAttributesInfoList& lineList,
00109     LineAttributes::MissingValuesPolicy policy )
00110 {
00111     Q_UNUSED( policy );
00112     // paint all lines and their attributes
00113     const PainterSaver painterSaver( ctx->painter() );
00114     if ( diagram()->antiAliasing() )
00115         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
00116     LineAttributesInfoListIterator itline ( lineList );
00117 
00118     QBrush curBrush;
00119     QPen curPen;
00120     QPolygonF points;
00121     while ( itline.hasNext() ) {
00122         const LineAttributesInfo& lineInfo = itline.next();
00123         const QModelIndex& index = lineInfo.index;
00124         const ThreeDLineAttributes td = diagram()->threeDLineAttributes( index );
00125         const ValueTrackerAttributes vt = diagram()->valueTrackerAttributes( index );
00126 
00127         if( td.isEnabled() ){
00128             paintThreeDLines( ctx, index, lineInfo.value, lineInfo.nextValue, td.depth() );
00129         } else {
00130             const QBrush br( diagram()->brush( index ) );
00131             const QPen pn( diagram()->pen( index ) );
00132             if( points.count() && points.last() == lineInfo.value && curBrush == br && curPen == pn ) {
00133                 // line goes from last value in points to lineInfo.nextValue
00134                 reverseMapper().addLine( lineInfo.index.row(), lineInfo.index.column(), points.last(), lineInfo.nextValue );
00135                 points << lineInfo.nextValue;
00136             } else {
00137                 if( points.count() )
00138                     paintPolyline( ctx, curBrush, curPen, points );
00139                 curBrush = br;
00140                 curPen   = pn;
00141                 points.clear();
00142                 // line goes from lineInfo.value to lineInfo,nextValue
00143                 reverseMapper().addLine( lineInfo.index.row(), lineInfo.index.column(), lineInfo.value, lineInfo.nextValue );
00144                 points << lineInfo.value << lineInfo.nextValue;
00145             }
00146         }
00147 
00148         if( vt.isEnabled() )
00149             paintValueTracker( ctx, vt, lineInfo.value );
00150     }
00151     if( points.count() )
00152         paintPolyline( ctx, curBrush, curPen, points );
00153     // paint all data value texts and the point markers
00154     paintDataValueTextsAndMarkers( diagram(), ctx, list, true );
00155 }
00156 
00157 AttributesModel* LineDiagram::LineDiagramType::attributesModel() const
00158 {
00159     return m_private->attributesModel;
00160 }
00161 
00162 QModelIndex LineDiagram::LineDiagramType::attributesModelRootIndex() const
00163 {
00164     return m_private->diagram->attributesModelRootIndex();
00165 }
00166 
00167 int LineDiagram::LineDiagramType::datasetDimension() const
00168 {
00169     return m_private->datasetDimension;
00170 }
00171 
00172 ReverseMapper& LineDiagram::LineDiagramType::reverseMapper()
00173 {
00174     return m_private->reverseMapper;
00175 }
00176 
00177 LineAttributes::MissingValuesPolicy LineDiagram::LineDiagramType::getCellValues(
00178     int row, int column,
00179     bool shiftCountedXValuesByHalfSection,
00180     double& valueX, double& valueY ) const
00181 {
00182     return m_private->diagram->getCellValues( row, column, shiftCountedXValuesByHalfSection,
00183                                               valueX, valueY );
00184 }
00185 
00186 double LineDiagram::LineDiagramType::valueForCellTesting(
00187     int row, int column,
00188     bool& bOK,
00189     bool showHiddenCellsAsInvalid) const
00190 {
00191     return m_private->diagram->valueForCellTesting( row, column, bOK, showHiddenCellsAsInvalid );
00192 }
00193 
00194 LineDiagram* LineDiagram::LineDiagramType::diagram() const
00195 {
00196     return m_private->diagram;
00197 }
00198 
00199 void LineDiagram::LineDiagramType::paintAreas(
00200     PaintContext* ctx,
00201     const QModelIndex& index, const QList< QPolygonF >& areas,
00202     const uint transparency )
00203 {
00204     QColor trans = diagram()->brush( index ).color();
00205     trans.setAlpha( transparency );
00206     QPen indexPen = diagram()->pen(index);
00207     indexPen.setColor( trans );
00208     const PainterSaver painterSaver( ctx->painter() );
00209 
00210     if( diagram()->antiAliasing() )
00211         ctx->painter()->setRenderHint( QPainter::Antialiasing );
00212 
00213     ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) );
00214     ctx->painter()->setBrush( trans );
00215 
00216     QPainterPath path;
00217     for( int i = 0; i < areas.count(); ++i )
00218     {
00219         const QPolygonF& p = areas[ i ];
00220         path.addPolygon( p );
00221         reverseMapper().addPolygon( index.row(), index.column(), p );
00222         path.closeSubpath();
00223     }
00224     ctx->painter()->drawPath( path );
00225 }
00226 
00227 double LineDiagram::LineDiagramType::valueForCell( int row, int column )
00228 {
00229     return diagram()->valueForCell( row, column );
00230 }
00231 
00232 void LineDiagram::LineDiagramType::appendDataValueTextInfoToList(
00233             AbstractDiagram * diagram,
00234             DataValueTextInfoList & list,
00235             const QModelIndex & index,
00236             const CartesianDiagramDataCompressor::CachePosition * position,
00237             const PositionPoints& points,
00238             const Position& autoPositionPositive,
00239             const Position& autoPositionNegative,
00240             const qreal value )
00241 {
00242     Q_UNUSED( autoPositionNegative );
00243     m_private->appendDataValueTextInfoToList( diagram, list, index, position, points,
00244                                               autoPositionPositive, autoPositionPositive, value );
00245 }
00246 
00247 void LineDiagram::LineDiagramType::paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, const QPointF& at )
00248 {
00249     CartesianCoordinatePlane* plane = qobject_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() );
00250     if( !plane )
00251         return;
00252 
00253     DataDimensionsList gridDimensions = ctx->coordinatePlane()->gridDimensionsList();
00254     const QPointF bottomLeft( ctx->coordinatePlane()->translate(
00255                               QPointF( plane->isHorizontalRangeReversed() ?
00256                                            gridDimensions.at( 0 ).end :
00257                                            gridDimensions.at( 0 ).start,
00258                                        plane->isVerticalRangeReversed() ?
00259                                            gridDimensions.at( 1 ).end :
00260                                            gridDimensions.at( 1 ).start ) ) );
00261     const QPointF markerPoint = at;
00262     const QPointF ordinatePoint( bottomLeft.x(), at.y() );
00263     const QPointF abscissaPoint( at.x(), bottomLeft.y() );
00264 
00265     const QSizeF markerSize = vt.markerSize();
00266     const QRectF ellipseMarker = QRectF( at.x() - markerSize.width() / 2,
00267                                          at.y() - markerSize.height() / 2,
00268                                          markerSize.width(), markerSize.height() );
00269 
00270     const QPointF ordinateMarker[3] = {
00271         QPointF( ordinatePoint.x(), at.y() + markerSize.height() / 2 ),
00272         QPointF( ordinatePoint.x() + markerSize.width() / 2, at.y() ),
00273         QPointF( ordinatePoint.x(), at.y() - markerSize.height() / 2 )
00274     };
00275 
00276     const QPointF abscissaMarker[3] = {
00277         QPointF( at.x() + markerSize.width() / 2, abscissaPoint.y() ),
00278         QPointF( at.x(), abscissaPoint.y() - markerSize.height() / 2 ),
00279         QPointF( at.x() - markerSize.width() / 2, abscissaPoint.y() )
00280     };
00281 
00282     QPointF topLeft = ordinatePoint;
00283     QPointF bottomRightOffset = abscissaPoint - topLeft;
00284     QSizeF size( bottomRightOffset.x(), bottomRightOffset.y() );
00285     QRectF area( topLeft, size );
00286 
00287     PainterSaver painterSaver( ctx->painter() );
00288     ctx->painter()->setPen( PrintingParameters::scalePen( vt.pen() ) );
00289     ctx->painter()->setBrush( QBrush() );
00290 
00291     ctx->painter()->drawLine( markerPoint, ordinatePoint );
00292     ctx->painter()->drawLine( markerPoint, abscissaPoint );
00293 
00294     ctx->painter()->fillRect( area, vt.areaBrush() );
00295 
00296     ctx->painter()->drawEllipse( ellipseMarker );
00297 
00298     ctx->painter()->setBrush( vt.pen().color() );
00299     ctx->painter()->drawPolygon( ordinateMarker, 3 );
00300     ctx->painter()->drawPolygon( abscissaMarker, 3 );
00301 }
00302 
00303 CartesianDiagramDataCompressor& LineDiagram::LineDiagramType::compressor() const
00304 {
00305     return m_private->compressor;
00306 }
00307 
00308 double LineDiagram::LineDiagramType::interpolateMissingValue( const CartesianDiagramDataCompressor::CachePosition& pos ) const
00309 {
00310     double leftValue = std::numeric_limits< double >::quiet_NaN();
00311     double rightValue = std::numeric_limits< double >::quiet_NaN();
00312     int missingCount = 1;
00313 
00314     const int column = pos.second;
00315     const int row = pos.first;
00316     const int rowCount = compressor().modelDataRows();
00317 
00318     // iterate back and forth to find valid values
00319     for( int r1 = row - 1; r1 > 0; --r1 )
00320     {
00321         const CartesianDiagramDataCompressor::CachePosition position( r1, column );
00322         const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00323         leftValue = point.value;
00324         if( !ISNAN( point.value ) )
00325             break;
00326         ++missingCount;
00327     }
00328     for( int r2 = row + 1; r2 < rowCount; ++r2 )
00329     {
00330         const CartesianDiagramDataCompressor::CachePosition position( r2, column );
00331         const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00332         rightValue = point.value;
00333         if( !ISNAN( point.value ) )
00334             break;
00335         ++missingCount;
00336     }
00337     if( !ISNAN( leftValue ) && !ISNAN( rightValue ) )
00338         return leftValue + ( rightValue - leftValue ) / ( missingCount + 1 );
00339     else
00340         return std::numeric_limits< double >::quiet_NaN();
00341 }