00001 #include "KDChartStockDiagram_p.h"
00002
00003 using namespace KDChart;
00004
00005
00006 class StockDiagram::Private::ThreeDPainter
00007 {
00008 public:
00009 struct ThreeDProperties {
00010 qreal depth;
00011 qreal angle;
00012 bool useShadowColors;
00013 };
00014
00015 ThreeDPainter( QPainter *p )
00016 : painter( p ) {};
00017
00018 QPolygonF drawTwoDLine( const QLineF &line, const QPen &pen,
00019 const ThreeDProperties &props );
00020 QPolygonF drawThreeDLine( const QLineF &line, const QBrush &brush,
00021 const QPen &pen, const ThreeDProperties &props );
00022 QPolygonF drawThreeDRect( const QRectF &rect, const QBrush &brush,
00023 const QPen &pen, const ThreeDProperties &props );
00024
00025 private:
00026 QPointF projectPoint( const QPointF &point, qreal depth, qreal angle ) const;
00027 QColor calcShadowColor( const QColor &color, qreal angle ) const;
00028
00029 QPainter *painter;
00030 };
00031
00038 QPointF StockDiagram::Private::ThreeDPainter::projectPoint( const QPointF &point, qreal depth, qreal angle ) const
00039 {
00040 const qreal angleInRad = DEGTORAD( angle );
00041 const qreal distX = depth * cos( angleInRad );
00042
00043 const qreal distY = depth * -sin( angleInRad );
00044
00045 return QPointF( point.x() + distX, point.y() + distY );
00046 }
00047
00054 QColor StockDiagram::Private::ThreeDPainter::calcShadowColor( const QColor &color, qreal angle ) const
00055 {
00056
00057
00058
00059 const qreal shadowFactor = 0.5;
00060 const qreal sinAngle = 1.0 - qAbs( sin( DEGTORAD( angle ) ) ) * shadowFactor;
00061 return QColor( qRound( color.red() * sinAngle ),
00062 qRound( color.green() * sinAngle ),
00063 qRound( color.blue() * sinAngle ) );
00064 }
00065
00074 QPolygonF StockDiagram::Private::ThreeDPainter::drawTwoDLine( const QLineF &line, const QPen &pen,
00075 const ThreeDProperties &props )
00076 {
00077
00078 PainterSaver painterSaver( painter );
00079
00080
00081 const qreal z = props.depth / 2.0;
00082
00083
00084 const QPointF deepP1 = projectPoint( line.p1(), z, props.angle );
00085 const QPointF deepP2 = projectPoint( line.p2(), z, props.angle );
00086
00087
00088 QPolygonF threeDArea;
00089
00090 const QPointF offset( 0.0, 1.0 );
00091 threeDArea << deepP1 - offset << deepP2 - offset
00092 << deepP1 + offset << deepP2 + offset << deepP1 - offset;
00093
00094 painter->setPen( pen );
00095 painter->drawLine( QLineF( deepP1, deepP2 ) );
00096
00097 return threeDArea;
00098 }
00099
00109 QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDLine( const QLineF &line, const QBrush &brush,
00110 const QPen &pen, const ThreeDProperties &props )
00111 {
00112
00113 PainterSaver painterSaver( painter );
00114
00115 const QPointF p1 = line.p1();
00116 const QPointF p2 = line.p2();
00117
00118
00119 const QPointF deepP1 = projectPoint( p1, props.depth, props.angle );
00120 const QPointF deepP2 = projectPoint( p2, props.depth, props.angle );
00121
00122
00123 QPolygonF threeDArea;
00124 threeDArea << p1 << p2 << deepP2 << deepP1 << p1;
00125
00126
00127
00128 if ( props.useShadowColors ) {
00129 QBrush shadowBrush( brush );
00130 QPen shadowPen( pen );
00131 shadowBrush.setColor( calcShadowColor( brush.color(), props.angle ) );
00132 shadowPen.setColor( calcShadowColor( pen.color(), props.angle ) );
00133 painter->setBrush( shadowBrush );
00134 painter->setPen( shadowPen );
00135 } else {
00136 painter->setBrush( brush );
00137 painter->setPen( pen );
00138 }
00139
00140 painter->drawPolygon( threeDArea );
00141
00142 return threeDArea;
00143 }
00144
00154 QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDRect( const QRectF &rect, const QBrush &brush,
00155 const QPen &pen, const ThreeDProperties &props )
00156 {
00157
00158 PainterSaver painterSaver( painter );
00159
00160
00161 const QRectF normalizedRect = rect.normalized();
00162
00163
00164 const QLineF topSide = QLineF( normalizedRect.topLeft(), normalizedRect.topRight() );
00165 const QLineF bottomSide = QLineF( normalizedRect.bottomLeft(), normalizedRect.bottomRight() );
00166 const QLineF leftSide = QLineF( normalizedRect.topLeft(), normalizedRect.bottomLeft() );
00167 const QLineF rightSide = QLineF( normalizedRect.topRight(), normalizedRect.bottomRight() );
00168
00169 QPolygonF drawnPolygon;
00170
00171
00172 const qreal angle = props.angle;
00173
00174
00175 if ( angle >= 0.0 && angle < 90.0 ) {
00176 drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) );
00177 drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) );
00178
00179 } else if ( angle >= 90.0 && angle < 180.0 ) {
00180 drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) );
00181 drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) );
00182
00183 } else if ( angle >= 180.0 && angle < 270.0 ) {
00184 drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) );
00185 drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) );
00186
00187 } else if ( angle >= 270.0 && angle <= 360.0 ) {
00188 drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) );
00189 drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) );
00190 }
00191
00192
00193 painter->setPen( pen );
00194 painter->setBrush( brush );
00195 painter->drawRect( normalizedRect );
00196
00197 return drawnPolygon;
00198 }
00199
00200
00201 StockDiagram::Private::Private()
00202 : AbstractCartesianDiagram::Private()
00203 {
00204 }
00205
00206 StockDiagram::Private::Private( const Private& r )
00207 : AbstractCartesianDiagram::Private( r )
00208 {
00209 }
00210
00211 StockDiagram::Private::~Private()
00212 {
00213 }
00214
00222 QPointF StockDiagram::Private::projectPoint( PaintContext *context, const QPointF &point ) const
00223 {
00224 return context->coordinatePlane()->translate( QPointF( point.x() + 0.5, point.y() ) );
00225 }
00226
00233 QRectF StockDiagram::Private::projectCandlestick( PaintContext *context, const QPointF &open, const QPointF &close, qreal width ) const
00234 {
00235 const QPointF leftHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 - width / 2.0, close.y() ) );
00236 const QPointF rightLowPoint = context->coordinatePlane()->translate( QPointF( open.x() + 0.5 + width / 2.0, open.y() ) );
00237 const QPointF rightHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 + width / 2.0, close.y() ) );
00238
00239 return QRectF( leftHighPoint, QSizeF( rightHighPoint.x() - leftHighPoint.x(),
00240 rightLowPoint.y() - leftHighPoint.y() ) );
00241 }
00242
00243 void StockDiagram::Private::drawOHLCBar( const CartesianDiagramDataCompressor::DataPoint &open,
00244 const CartesianDiagramDataCompressor::DataPoint &high,
00245 const CartesianDiagramDataCompressor::DataPoint &low,
00246 const CartesianDiagramDataCompressor::DataPoint &close,
00247 PaintContext *context )
00248 {
00249
00250 const int col = low.index.row();
00251
00252 StockBarAttributes attr = diagram->stockBarAttributes( col );
00253 ThreeDBarAttributes threeDAttr = diagram->threeDBarAttributes( col );
00254 const qreal tickLength = attr.tickLength();
00255
00256 const QPointF leftOpenPoint( open.key + 0.5 - tickLength, open.value );
00257 const QPointF rightOpenPoint( open.key + 0.5, open.value );
00258 const QPointF highPoint( high.key + 0.5, high.value );
00259 const QPointF lowPoint( low.key + 0.5, low.value );
00260 const QPointF leftClosePoint( close.key + 0.5, close.value );
00261 const QPointF rightClosePoint( close.key + 0.5 + tickLength, close.value );
00262
00263 bool reversedOrder = false;
00264
00265 if ( threeDAttr.isEnabled() ) {
00266 const int angle = threeDAttr.angle();
00267
00268 if ( angle >= 0 && angle < 90 || angle >= 180 && angle < 270 )
00269 reversedOrder = true;
00270
00271 if ( angle >= 90 && angle < 180 || angle >= 270 && angle < 0 )
00272 reversedOrder = false;
00273 }
00274
00275 if ( reversedOrder ) {
00276 if ( !open.hidden )
00277 drawLine( col, leftOpenPoint, rightOpenPoint, context );
00278 if ( !low.hidden && !high.hidden )
00279 drawLine( col, lowPoint, highPoint, context );
00280 if ( !close.hidden )
00281 drawLine( col, leftClosePoint, rightClosePoint, context );
00282 } else {
00283 if ( !close.hidden )
00284 drawLine( col, leftClosePoint, rightClosePoint, context );
00285 if ( !low.hidden && !high.hidden )
00286 drawLine( col, lowPoint, highPoint, context );
00287 if ( !open.hidden )
00288 drawLine( col, leftOpenPoint, rightOpenPoint, context );
00289 }
00290
00291 DataValueTextInfoList list;
00292 if ( !open.hidden )
00293 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( open.index ), 0,
00294 PositionPoints( leftOpenPoint ), Position::South, Position::South, open.value );
00295 if ( !high.hidden )
00296 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( high.index ), 0,
00297 PositionPoints( highPoint ), Position::South, Position::South, high.value );
00298 if ( !low.hidden )
00299 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( low.index ), 0,
00300 PositionPoints( lowPoint ), Position::South, Position::South, low.value );
00301 if ( !close.hidden )
00302 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( close.index ), 0,
00303 PositionPoints( rightClosePoint ), Position::South, Position::South, close.value );
00304 paintDataValueTextsAndMarkers( diagram, context, list, false );
00305 }
00306
00314 void StockDiagram::Private::drawCandlestick( const CartesianDiagramDataCompressor::DataPoint &open,
00315 const CartesianDiagramDataCompressor::DataPoint &high,
00316 const CartesianDiagramDataCompressor::DataPoint &low,
00317 const CartesianDiagramDataCompressor::DataPoint &close,
00318 PaintContext *context )
00319 {
00320 PainterSaver painterSaver( context->painter() );
00321
00322
00323 const int row = low.index.row();
00324 const int col = low.index.column();
00325
00326 QPointF bottomCandlestickPoint;
00327 QPointF topCandlestickPoint;
00328 QBrush brush;
00329 QPen pen;
00330 bool drawLowerLine;
00331 bool drawCandlestick = !open.hidden && !close.hidden;
00332 bool drawUpperLine;
00333
00334
00335
00336
00337 if ( open.value <= close.value ) {
00338 pen = diagram->upTrendCandlestickPen( row );
00339 brush = diagram->upTrendCandlestickBrush( row );
00340 bottomCandlestickPoint = QPointF( open.key, open.value );
00341 topCandlestickPoint = QPointF( close.key, close.value );
00342 drawLowerLine = !low.hidden && !open.hidden;
00343 drawUpperLine = !low.hidden && !close.hidden;
00344 } else {
00345 pen = diagram->downTrendCandlestickPen( row );
00346 brush = diagram->downTrendCandlestickBrush( row );
00347 bottomCandlestickPoint = QPointF( close.key, close.value );
00348 topCandlestickPoint = QPointF( open.key, open.value );
00349 drawLowerLine = !low.hidden && !close.hidden;
00350 drawUpperLine = !low.hidden && !open.hidden;
00351 }
00352
00353 StockBarAttributes attr = diagram->stockBarAttributes( col );
00354 ThreeDBarAttributes threeDAttr = diagram->threeDBarAttributes( col );
00355
00356 const QPointF lowPoint = projectPoint( context, QPointF( low.key, low.value ) );
00357 const QPointF highPoint = projectPoint( context, QPointF( high.key, high.value ) );
00358 const QLineF lowerLine = QLineF( lowPoint, projectPoint( context, bottomCandlestickPoint ) );
00359 const QLineF upperLine = QLineF( projectPoint( context, topCandlestickPoint ), highPoint );
00360
00361
00362 QRectF candlestick = projectCandlestick( context, bottomCandlestickPoint,
00363 topCandlestickPoint, attr.candlestickWidth() );
00364
00365
00366 QPolygonF drawnPolygon;
00367
00368
00369 if ( threeDAttr.isEnabled() ) {
00370 ThreeDPainter threeDPainter( context->painter() );
00371
00372 ThreeDPainter::ThreeDProperties threeDProps;
00373 threeDProps.depth = threeDAttr.depth();
00374 threeDProps.angle = threeDAttr.angle();
00375 threeDProps.useShadowColors = threeDAttr.useShadowColors();
00376
00377
00378
00379 if ( threeDProps.angle > 0.0 && threeDProps.angle < 180.0 ) {
00380 if ( drawLowerLine )
00381 drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps );
00382 if ( drawCandlestick )
00383 drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps );
00384 if ( drawUpperLine )
00385 drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps );
00386 } else {
00387 if ( drawUpperLine )
00388 drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps );
00389 if ( drawCandlestick )
00390 drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps );
00391 if ( drawLowerLine )
00392 drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps );
00393 }
00394 } else {
00395 QPainter *const painter = context->painter();
00396 painter->setBrush( brush );
00397 painter->setPen( pen );
00398 if ( drawLowerLine )
00399 painter->drawLine( lowerLine );
00400 if ( drawUpperLine )
00401 painter->drawLine( upperLine );
00402 if ( drawCandlestick )
00403 painter->drawRect( candlestick );
00404
00405
00406 drawnPolygon = candlestick;
00407
00408
00409 }
00410
00411 DataValueTextInfoList list;
00412
00413 if ( !low.hidden )
00414 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( low.index ), 0,
00415 PositionPoints( lowPoint ), Position::South, Position::South, low.value );
00416 if ( drawCandlestick ) {
00417
00418 reverseMapper.addPolygon( row, openValueColumn(), drawnPolygon );
00419 reverseMapper.addPolygon( row, closeValueColumn(), drawnPolygon );
00420
00421 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( open.index ), 0,
00422 PositionPoints( candlestick.bottomRight() ), Position::South, Position::South, open.value );
00423 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( close.index ), 0,
00424 PositionPoints( candlestick.topRight() ), Position::South, Position::South, close.value );
00425 }
00426 if ( !high.hidden )
00427 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( high.index ), 0,
00428 PositionPoints( highPoint ), Position::South, Position::South, high.value );
00429
00430 paintDataValueTextsAndMarkers( diagram, context, list, false );
00431 }
00432
00441 void StockDiagram::Private::drawLine( int col, const QPointF &point1, const QPointF &point2, PaintContext *context )
00442 {
00443 PainterSaver painterSaver( context->painter() );
00444
00445
00446 const int modelRow = col;
00447 const int modelCol = 0;
00448
00449 const QPen pen = diagram->pen( col );
00450 const QBrush brush = diagram->brush( col );
00451 const ThreeDBarAttributes threeDBarAttr = diagram->threeDBarAttributes( col );
00452
00453 QPointF transP1 = context->coordinatePlane()->translate( point1 );
00454 QPointF transP2 = context->coordinatePlane()->translate( point2 );
00455 QLineF line = QLineF( transP1, transP2 );
00456
00457 if ( threeDBarAttr.isEnabled() ) {
00458 ThreeDPainter::ThreeDProperties threeDProps;
00459 threeDProps.angle = threeDBarAttr.angle();
00460 threeDProps.depth = threeDBarAttr.depth();
00461 threeDProps.useShadowColors = threeDBarAttr.useShadowColors();
00462
00463 ThreeDPainter painter( context->painter() );
00464 reverseMapper.addPolygon( modelCol, modelRow, painter.drawThreeDLine( line, brush, pen, threeDProps ) );
00465 } else {
00466 context->painter()->setPen( pen );
00467
00468 reverseMapper.addLine( modelCol, modelRow, transP1, transP2 );
00469 context->painter()->drawLine( line );
00470 }
00471 }
00472
00478 int StockDiagram::Private::openValueColumn() const
00479 {
00480
00481 return type == HighLowClose ? -1 : 0;
00482 }
00483
00489 int StockDiagram::Private::highValueColumn() const
00490 {
00491 return type == HighLowClose ? 0 : 1;
00492 }
00493
00499 int StockDiagram::Private::lowValueColumn() const
00500 {
00501 return type == HighLowClose ? 1 : 2;
00502 }
00503
00509 int StockDiagram::Private::closeValueColumn() const
00510 {
00511 return type == HighLowClose ? 2 : 3;
00512 }
00513