00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "KDChartStockDiagram_p.h"
00024
00025 using namespace KDChart;
00026
00027
00028 class StockDiagram::Private::ThreeDPainter
00029 {
00030 public:
00031 struct ThreeDProperties {
00032 qreal depth;
00033 qreal angle;
00034 bool useShadowColors;
00035 };
00036
00037 ThreeDPainter( QPainter *p )
00038 : painter( p ) {};
00039
00040 QPolygonF drawTwoDLine( const QLineF &line, const QPen &pen,
00041 const ThreeDProperties &props );
00042 QPolygonF drawThreeDLine( const QLineF &line, const QBrush &brush,
00043 const QPen &pen, const ThreeDProperties &props );
00044 QPolygonF drawThreeDRect( const QRectF &rect, const QBrush &brush,
00045 const QPen &pen, const ThreeDProperties &props );
00046
00047 private:
00048 QPointF projectPoint( const QPointF &point, qreal depth, qreal angle ) const;
00049 QColor calcShadowColor( const QColor &color, qreal angle ) const;
00050
00051 QPainter *painter;
00052 };
00053
00060 QPointF StockDiagram::Private::ThreeDPainter::projectPoint( const QPointF &point, qreal depth, qreal angle ) const
00061 {
00062 const qreal angleInRad = DEGTORAD( angle );
00063 const qreal distX = depth * cos( angleInRad );
00064
00065 const qreal distY = depth * -sin( angleInRad );
00066
00067 return QPointF( point.x() + distX, point.y() + distY );
00068 }
00069
00076 QColor StockDiagram::Private::ThreeDPainter::calcShadowColor( const QColor &color, qreal angle ) const
00077 {
00078
00079
00080
00081 const qreal shadowFactor = 0.5;
00082 const qreal sinAngle = 1.0 - qAbs( sin( DEGTORAD( angle ) ) ) * shadowFactor;
00083 return QColor( qRound( color.red() * sinAngle ),
00084 qRound( color.green() * sinAngle ),
00085 qRound( color.blue() * sinAngle ) );
00086 }
00087
00096 QPolygonF StockDiagram::Private::ThreeDPainter::drawTwoDLine( const QLineF &line, const QPen &pen,
00097 const ThreeDProperties &props )
00098 {
00099
00100 PainterSaver painterSaver( painter );
00101
00102
00103 const qreal z = props.depth / 2.0;
00104
00105
00106 const QPointF deepP1 = projectPoint( line.p1(), z, props.angle );
00107 const QPointF deepP2 = projectPoint( line.p2(), z, props.angle );
00108
00109
00110 QPolygonF threeDArea;
00111
00112 const QPointF offset( 0.0, 1.0 );
00113 threeDArea << deepP1 - offset << deepP2 - offset
00114 << deepP1 + offset << deepP2 + offset << deepP1 - offset;
00115
00116 painter->setPen( pen );
00117 painter->drawLine( QLineF( deepP1, deepP2 ) );
00118
00119 return threeDArea;
00120 }
00121
00131 QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDLine( const QLineF &line, const QBrush &brush,
00132 const QPen &pen, const ThreeDProperties &props )
00133 {
00134
00135 PainterSaver painterSaver( painter );
00136
00137 const QPointF p1 = line.p1();
00138 const QPointF p2 = line.p2();
00139
00140
00141 const QPointF deepP1 = projectPoint( p1, props.depth, props.angle );
00142 const QPointF deepP2 = projectPoint( p2, props.depth, props.angle );
00143
00144
00145 QPolygonF threeDArea;
00146 threeDArea << p1 << p2 << deepP2 << deepP1 << p1;
00147
00148
00149
00150 if ( props.useShadowColors ) {
00151 QBrush shadowBrush( brush );
00152 QPen shadowPen( pen );
00153 shadowBrush.setColor( calcShadowColor( brush.color(), props.angle ) );
00154 shadowPen.setColor( calcShadowColor( pen.color(), props.angle ) );
00155 painter->setBrush( shadowBrush );
00156 painter->setPen( shadowPen );
00157 } else {
00158 painter->setBrush( brush );
00159 painter->setPen( pen );
00160 }
00161
00162 painter->drawPolygon( threeDArea );
00163
00164 return threeDArea;
00165 }
00166
00176 QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDRect( const QRectF &rect, const QBrush &brush,
00177 const QPen &pen, const ThreeDProperties &props )
00178 {
00179
00180 PainterSaver painterSaver( painter );
00181
00182
00183 const QRectF normalizedRect = rect.normalized();
00184
00185
00186 const QLineF topSide = QLineF( normalizedRect.topLeft(), normalizedRect.topRight() );
00187 const QLineF bottomSide = QLineF( normalizedRect.bottomLeft(), normalizedRect.bottomRight() );
00188 const QLineF leftSide = QLineF( normalizedRect.topLeft(), normalizedRect.bottomLeft() );
00189 const QLineF rightSide = QLineF( normalizedRect.topRight(), normalizedRect.bottomRight() );
00190
00191 QPolygonF drawnPolygon;
00192
00193
00194 const qreal angle = props.angle;
00195
00196
00197 if ( angle >= 0.0 && angle < 90.0 ) {
00198 drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) );
00199 drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) );
00200
00201 } else if ( angle >= 90.0 && angle < 180.0 ) {
00202 drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) );
00203 drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) );
00204
00205 } else if ( angle >= 180.0 && angle < 270.0 ) {
00206 drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) );
00207 drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) );
00208
00209 } else if ( angle >= 270.0 && angle <= 360.0 ) {
00210 drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) );
00211 drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) );
00212 }
00213
00214
00215 painter->setPen( pen );
00216 painter->setBrush( brush );
00217 painter->drawRect( normalizedRect );
00218
00219 return drawnPolygon;
00220 }
00221
00222
00223 StockDiagram::Private::Private()
00224 : AbstractCartesianDiagram::Private()
00225 {
00226 }
00227
00228 StockDiagram::Private::Private( const Private& r )
00229 : AbstractCartesianDiagram::Private( r )
00230 {
00231 }
00232
00233 StockDiagram::Private::~Private()
00234 {
00235 }
00236
00244 QPointF StockDiagram::Private::projectPoint( PaintContext *context, const QPointF &point ) const
00245 {
00246 return context->coordinatePlane()->translate( QPointF( point.x() + 0.5, point.y() ) );
00247 }
00248
00255 QRectF StockDiagram::Private::projectCandlestick( PaintContext *context, const QPointF &open, const QPointF &close, qreal width ) const
00256 {
00257 const QPointF leftHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 - width / 2.0, close.y() ) );
00258 const QPointF rightLowPoint = context->coordinatePlane()->translate( QPointF( open.x() + 0.5 + width / 2.0, open.y() ) );
00259 const QPointF rightHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 + width / 2.0, close.y() ) );
00260
00261 return QRectF( leftHighPoint, QSizeF( rightHighPoint.x() - leftHighPoint.x(),
00262 rightLowPoint.y() - leftHighPoint.y() ) );
00263 }
00264
00265 void StockDiagram::Private::drawOHLCBar( const CartesianDiagramDataCompressor::DataPoint &open,
00266 const CartesianDiagramDataCompressor::DataPoint &high,
00267 const CartesianDiagramDataCompressor::DataPoint &low,
00268 const CartesianDiagramDataCompressor::DataPoint &close,
00269 PaintContext *context )
00270 {
00271
00272 const int col = low.index.row();
00273
00274 StockBarAttributes attr = diagram->stockBarAttributes( col );
00275 ThreeDBarAttributes threeDAttr = diagram->threeDBarAttributes( col );
00276 const qreal tickLength = attr.tickLength();
00277
00278 const QPointF leftOpenPoint( open.key + 0.5 - tickLength, open.value );
00279 const QPointF rightOpenPoint( open.key + 0.5, open.value );
00280 const QPointF highPoint( high.key + 0.5, high.value );
00281 const QPointF lowPoint( low.key + 0.5, low.value );
00282 const QPointF leftClosePoint( close.key + 0.5, close.value );
00283 const QPointF rightClosePoint( close.key + 0.5 + tickLength, close.value );
00284
00285 bool reversedOrder = false;
00286
00287 if ( threeDAttr.isEnabled() ) {
00288 const int angle = threeDAttr.angle();
00289
00290 if ( ( angle >= 0 && angle < 90 ) || ( angle >= 180 && angle < 270 ) )
00291 reversedOrder = true;
00292
00293 if ( ( angle >= 90 && angle < 180 ) || ( angle >= 270 && angle < 0 ) )
00294 reversedOrder = false;
00295 }
00296
00297 if ( reversedOrder ) {
00298 if ( !open.hidden )
00299 drawLine( col, leftOpenPoint, rightOpenPoint, context );
00300 if ( !low.hidden && !high.hidden )
00301 drawLine( col, lowPoint, highPoint, context );
00302 if ( !close.hidden )
00303 drawLine( col, leftClosePoint, rightClosePoint, context );
00304 } else {
00305 if ( !close.hidden )
00306 drawLine( col, leftClosePoint, rightClosePoint, context );
00307 if ( !low.hidden && !high.hidden )
00308 drawLine( col, lowPoint, highPoint, context );
00309 if ( !open.hidden )
00310 drawLine( col, leftOpenPoint, rightOpenPoint, context );
00311 }
00312
00313 DataValueTextInfoList list;
00314 if ( !open.hidden )
00315 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( open.index ), 0,
00316 PositionPoints( leftOpenPoint ), Position::South, Position::South, open.value );
00317 if ( !high.hidden )
00318 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( high.index ), 0,
00319 PositionPoints( highPoint ), Position::South, Position::South, high.value );
00320 if ( !low.hidden )
00321 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( low.index ), 0,
00322 PositionPoints( lowPoint ), Position::South, Position::South, low.value );
00323 if ( !close.hidden )
00324 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( close.index ), 0,
00325 PositionPoints( rightClosePoint ), Position::South, Position::South, close.value );
00326 paintDataValueTextsAndMarkers( diagram, context, list, false );
00327 }
00328
00336 void StockDiagram::Private::drawCandlestick( const CartesianDiagramDataCompressor::DataPoint &open,
00337 const CartesianDiagramDataCompressor::DataPoint &high,
00338 const CartesianDiagramDataCompressor::DataPoint &low,
00339 const CartesianDiagramDataCompressor::DataPoint &close,
00340 PaintContext *context )
00341 {
00342 PainterSaver painterSaver( context->painter() );
00343
00344
00345 const int row = low.index.row();
00346 const int col = low.index.column();
00347
00348 QPointF bottomCandlestickPoint;
00349 QPointF topCandlestickPoint;
00350 QBrush brush;
00351 QPen pen;
00352 bool drawLowerLine;
00353 bool drawCandlestick = !open.hidden && !close.hidden;
00354 bool drawUpperLine;
00355
00356
00357
00358
00359 if ( open.value <= close.value ) {
00360 pen = diagram->upTrendCandlestickPen( row );
00361 brush = diagram->upTrendCandlestickBrush( row );
00362 bottomCandlestickPoint = QPointF( open.key, open.value );
00363 topCandlestickPoint = QPointF( close.key, close.value );
00364 drawLowerLine = !low.hidden && !open.hidden;
00365 drawUpperLine = !low.hidden && !close.hidden;
00366 } else {
00367 pen = diagram->downTrendCandlestickPen( row );
00368 brush = diagram->downTrendCandlestickBrush( row );
00369 bottomCandlestickPoint = QPointF( close.key, close.value );
00370 topCandlestickPoint = QPointF( open.key, open.value );
00371 drawLowerLine = !low.hidden && !close.hidden;
00372 drawUpperLine = !low.hidden && !open.hidden;
00373 }
00374
00375 StockBarAttributes attr = diagram->stockBarAttributes( col );
00376 ThreeDBarAttributes threeDAttr = diagram->threeDBarAttributes( col );
00377
00378 const QPointF lowPoint = projectPoint( context, QPointF( low.key, low.value ) );
00379 const QPointF highPoint = projectPoint( context, QPointF( high.key, high.value ) );
00380 const QLineF lowerLine = QLineF( lowPoint, projectPoint( context, bottomCandlestickPoint ) );
00381 const QLineF upperLine = QLineF( projectPoint( context, topCandlestickPoint ), highPoint );
00382
00383
00384 QRectF candlestick = projectCandlestick( context, bottomCandlestickPoint,
00385 topCandlestickPoint, attr.candlestickWidth() );
00386
00387
00388 QPolygonF drawnPolygon;
00389
00390
00391 if ( threeDAttr.isEnabled() ) {
00392 ThreeDPainter threeDPainter( context->painter() );
00393
00394 ThreeDPainter::ThreeDProperties threeDProps;
00395 threeDProps.depth = threeDAttr.depth();
00396 threeDProps.angle = threeDAttr.angle();
00397 threeDProps.useShadowColors = threeDAttr.useShadowColors();
00398
00399
00400
00401 if ( threeDProps.angle > 0.0 && threeDProps.angle < 180.0 ) {
00402 if ( drawLowerLine )
00403 drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps );
00404 if ( drawCandlestick )
00405 drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps );
00406 if ( drawUpperLine )
00407 drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps );
00408 } else {
00409 if ( drawUpperLine )
00410 drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps );
00411 if ( drawCandlestick )
00412 drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps );
00413 if ( drawLowerLine )
00414 drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps );
00415 }
00416 } else {
00417 QPainter *const painter = context->painter();
00418 painter->setBrush( brush );
00419 painter->setPen( pen );
00420 if ( drawLowerLine )
00421 painter->drawLine( lowerLine );
00422 if ( drawUpperLine )
00423 painter->drawLine( upperLine );
00424 if ( drawCandlestick )
00425 painter->drawRect( candlestick );
00426
00427
00428 drawnPolygon = candlestick;
00429
00430
00431 }
00432
00433 DataValueTextInfoList list;
00434
00435 if ( !low.hidden )
00436 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( low.index ), 0,
00437 PositionPoints( lowPoint ), Position::South, Position::South, low.value );
00438 if ( drawCandlestick ) {
00439
00440 reverseMapper.addPolygon( row, openValueColumn(), drawnPolygon );
00441 reverseMapper.addPolygon( row, closeValueColumn(), drawnPolygon );
00442
00443 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( open.index ), 0,
00444 PositionPoints( candlestick.bottomRight() ), Position::South, Position::South, open.value );
00445 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( close.index ), 0,
00446 PositionPoints( candlestick.topRight() ), Position::South, Position::South, close.value );
00447 }
00448 if ( !high.hidden )
00449 appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( high.index ), 0,
00450 PositionPoints( highPoint ), Position::South, Position::South, high.value );
00451
00452 paintDataValueTextsAndMarkers( diagram, context, list, false );
00453 }
00454
00463 void StockDiagram::Private::drawLine( int col, const QPointF &point1, const QPointF &point2, PaintContext *context )
00464 {
00465 PainterSaver painterSaver( context->painter() );
00466
00467
00468 const int modelRow = col;
00469 const int modelCol = 0;
00470
00471 const QPen pen = diagram->pen( col );
00472 const QBrush brush = diagram->brush( col );
00473 const ThreeDBarAttributes threeDBarAttr = diagram->threeDBarAttributes( col );
00474
00475 QPointF transP1 = context->coordinatePlane()->translate( point1 );
00476 QPointF transP2 = context->coordinatePlane()->translate( point2 );
00477 QLineF line = QLineF( transP1, transP2 );
00478
00479 if ( threeDBarAttr.isEnabled() ) {
00480 ThreeDPainter::ThreeDProperties threeDProps;
00481 threeDProps.angle = threeDBarAttr.angle();
00482 threeDProps.depth = threeDBarAttr.depth();
00483 threeDProps.useShadowColors = threeDBarAttr.useShadowColors();
00484
00485 ThreeDPainter painter( context->painter() );
00486 reverseMapper.addPolygon( modelCol, modelRow, painter.drawThreeDLine( line, brush, pen, threeDProps ) );
00487 } else {
00488 context->painter()->setPen( pen );
00489
00490 reverseMapper.addLine( modelCol, modelRow, transP1, transP2 );
00491 context->painter()->drawLine( line );
00492 }
00493 }
00494
00500 int StockDiagram::Private::openValueColumn() const
00501 {
00502
00503 return type == HighLowClose ? -1 : 0;
00504 }
00505
00511 int StockDiagram::Private::highValueColumn() const
00512 {
00513 return type == HighLowClose ? 0 : 1;
00514 }
00515
00521 int StockDiagram::Private::lowValueColumn() const
00522 {
00523 return type == HighLowClose ? 1 : 2;
00524 }
00525
00531 int StockDiagram::Private::closeValueColumn() const
00532 {
00533 return type == HighLowClose ? 2 : 3;
00534 }
00535