KD Chart 2 [rev.2.4]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 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.txt 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( points.count() ) 00149 paintPolyline( ctx, curBrush, curPen, points ); 00150 00151 itline.toFront(); 00152 while ( itline.hasNext() ) { 00153 const LineAttributesInfo& lineInfo = itline.next(); 00154 const QModelIndex& index = lineInfo.index; 00155 const ValueTrackerAttributes vt = diagram()->valueTrackerAttributes( index ); 00156 if( vt.isEnabled() ) 00157 paintValueTracker( ctx, vt, lineInfo.value ); 00158 } 00159 00160 // paint all data value texts and the point markers 00161 paintDataValueTextsAndMarkers( diagram(), ctx, list, true ); 00162 } 00163 00164 AttributesModel* LineDiagram::LineDiagramType::attributesModel() const 00165 { 00166 return m_private->attributesModel; 00167 } 00168 00169 QModelIndex LineDiagram::LineDiagramType::attributesModelRootIndex() const 00170 { 00171 return m_private->diagram->attributesModelRootIndex(); 00172 } 00173 00174 int LineDiagram::LineDiagramType::datasetDimension() const 00175 { 00176 return m_private->datasetDimension; 00177 } 00178 00179 ReverseMapper& LineDiagram::LineDiagramType::reverseMapper() 00180 { 00181 return m_private->reverseMapper; 00182 } 00183 00184 LineAttributes::MissingValuesPolicy LineDiagram::LineDiagramType::getCellValues( 00185 int row, int column, 00186 bool shiftCountedXValuesByHalfSection, 00187 double& valueX, double& valueY ) const 00188 { 00189 return m_private->diagram->getCellValues( row, column, shiftCountedXValuesByHalfSection, 00190 valueX, valueY ); 00191 } 00192 00193 double LineDiagram::LineDiagramType::valueForCellTesting( 00194 int row, int column, 00195 bool& bOK, 00196 bool showHiddenCellsAsInvalid) const 00197 { 00198 return m_private->diagram->valueForCellTesting( row, column, bOK, showHiddenCellsAsInvalid ); 00199 } 00200 00201 LineDiagram* LineDiagram::LineDiagramType::diagram() const 00202 { 00203 return m_private->diagram; 00204 } 00205 00206 void LineDiagram::LineDiagramType::paintAreas( 00207 PaintContext* ctx, 00208 const QModelIndex& index, const QList< QPolygonF >& areas, 00209 const uint transparency ) 00210 { 00211 QColor trans = diagram()->brush( index ).color(); 00212 trans.setAlpha( transparency ); 00213 QPen indexPen = diagram()->pen(index); 00214 indexPen.setColor( trans ); 00215 const PainterSaver painterSaver( ctx->painter() ); 00216 00217 if( diagram()->antiAliasing() ) 00218 ctx->painter()->setRenderHint( QPainter::Antialiasing ); 00219 00220 ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) ); 00221 ctx->painter()->setBrush( trans ); 00222 00223 QPainterPath path; 00224 for( int i = 0; i < areas.count(); ++i ) 00225 { 00226 const QPolygonF& p = areas[ i ]; 00227 path.addPolygon( p ); 00228 reverseMapper().addPolygon( index.row(), index.column(), p ); 00229 path.closeSubpath(); 00230 } 00231 ctx->painter()->drawPath( path ); 00232 } 00233 00234 double LineDiagram::LineDiagramType::valueForCell( int row, int column ) 00235 { 00236 return diagram()->valueForCell( row, column ); 00237 } 00238 00239 void LineDiagram::LineDiagramType::appendDataValueTextInfoToList( 00240 AbstractDiagram * diagram, 00241 DataValueTextInfoList & list, 00242 const QModelIndex & index, 00243 const CartesianDiagramDataCompressor::CachePosition * position, 00244 const PositionPoints& points, 00245 const Position& autoPositionPositive, 00246 const Position& autoPositionNegative, 00247 const qreal value ) 00248 { 00249 Q_UNUSED( autoPositionNegative ); 00250 m_private->appendDataValueTextInfoToList( diagram, list, index, position, points, 00251 autoPositionPositive, autoPositionPositive, value ); 00252 } 00253 00254 void LineDiagram::LineDiagramType::paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, const QPointF& at ) 00255 { 00256 CartesianCoordinatePlane* plane = qobject_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() ); 00257 if( !plane ) 00258 return; 00259 00260 DataDimensionsList gridDimensions = ctx->coordinatePlane()->gridDimensionsList(); 00261 const QPointF bottomLeft( ctx->coordinatePlane()->translate( 00262 QPointF( plane->isHorizontalRangeReversed() ? 00263 gridDimensions.at( 0 ).end : 00264 gridDimensions.at( 0 ).start, 00265 plane->isVerticalRangeReversed() ? 00266 gridDimensions.at( 1 ).end : 00267 gridDimensions.at( 1 ).start ) ) ); 00268 const QPointF markerPoint = at; 00269 const QPointF ordinatePoint( bottomLeft.x(), at.y() ); 00270 const QPointF abscissaPoint( at.x(), bottomLeft.y() ); 00271 00272 const QSizeF markerSize = vt.markerSize(); 00273 const QRectF ellipseMarker = QRectF( at.x() - markerSize.width() / 2, 00274 at.y() - markerSize.height() / 2, 00275 markerSize.width(), markerSize.height() ); 00276 00277 const QPointF ordinateMarker[3] = { 00278 QPointF( ordinatePoint.x(), at.y() + markerSize.height() / 2 ), 00279 QPointF( ordinatePoint.x() + markerSize.width() / 2, at.y() ), 00280 QPointF( ordinatePoint.x(), at.y() - markerSize.height() / 2 ) 00281 }; 00282 00283 const QPointF abscissaMarker[3] = { 00284 QPointF( at.x() + markerSize.width() / 2, abscissaPoint.y() ), 00285 QPointF( at.x(), abscissaPoint.y() - markerSize.height() / 2 ), 00286 QPointF( at.x() - markerSize.width() / 2, abscissaPoint.y() ) 00287 }; 00288 00289 QPointF topLeft = ordinatePoint; 00290 QPointF bottomRightOffset = abscissaPoint - topLeft; 00291 QSizeF size( bottomRightOffset.x(), bottomRightOffset.y() ); 00292 QRectF area( topLeft, size ); 00293 00294 PainterSaver painterSaver( ctx->painter() ); 00295 ctx->painter()->setPen( PrintingParameters::scalePen( vt.pen() ) ); 00296 ctx->painter()->setBrush( QBrush() ); 00297 00298 ctx->painter()->drawLine( markerPoint, ordinatePoint ); 00299 ctx->painter()->drawLine( markerPoint, abscissaPoint ); 00300 00301 ctx->painter()->fillRect( area, vt.areaBrush() ); 00302 00303 ctx->painter()->drawEllipse( ellipseMarker ); 00304 00305 ctx->painter()->setBrush( vt.pen().color() ); 00306 ctx->painter()->drawPolygon( ordinateMarker, 3 ); 00307 ctx->painter()->drawPolygon( abscissaMarker, 3 ); 00308 } 00309 00310 CartesianDiagramDataCompressor& LineDiagram::LineDiagramType::compressor() const 00311 { 00312 return m_private->compressor; 00313 } 00314 00315 double LineDiagram::LineDiagramType::interpolateMissingValue( const CartesianDiagramDataCompressor::CachePosition& pos ) const 00316 { 00317 double leftValue = std::numeric_limits< double >::quiet_NaN(); 00318 double rightValue = std::numeric_limits< double >::quiet_NaN(); 00319 int missingCount = 1; 00320 00321 const int column = pos.second; 00322 const int row = pos.first; 00323 const int rowCount = compressor().modelDataRows(); 00324 00325 // iterate back and forth to find valid values 00326 for( int r1 = row - 1; r1 > 0; --r1 ) 00327 { 00328 const CartesianDiagramDataCompressor::CachePosition position( r1, column ); 00329 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 00330 leftValue = point.value; 00331 if( !ISNAN( point.value ) ) 00332 break; 00333 ++missingCount; 00334 } 00335 for( int r2 = row + 1; r2 < rowCount; ++r2 ) 00336 { 00337 const CartesianDiagramDataCompressor::CachePosition position( r2, column ); 00338 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 00339 rightValue = point.value; 00340 if( !ISNAN( point.value ) ) 00341 break; 00342 ++missingCount; 00343 } 00344 if( !ISNAN( leftValue ) && !ISNAN( rightValue ) ) 00345 return leftValue + ( rightValue - leftValue ) / ( missingCount + 1 ); 00346 else 00347 return std::numeric_limits< double >::quiet_NaN(); 00348 }