/** * Draws a line connecting the low and the high value of an OHLC chart * * @param low The low data point * @param high The high data point * @param context The context to draw the candlestick in */ void StockDiagram::Private::drawCandlestick( int /*dataset*/, const CartesianDiagramDataCompressor::DataPoint &open, const CartesianDiagramDataCompressor::DataPoint &high, const CartesianDiagramDataCompressor::DataPoint &low, const CartesianDiagramDataCompressor::DataPoint &close, PaintContext *context ) { PainterSaver painterSaver( context->painter() ); // Note: A row in the model is a column in a StockDiagram, and the other way around const int row = low.index.row(); const int col = low.index.column(); QPointF bottomCandlestickPoint; QPointF topCandlestickPoint; QBrush brush; QPen pen; bool drawLowerLine; bool drawCandlestick = !open.hidden && !close.hidden; bool drawUpperLine; // Find out if we need to paint a down-trend or up-trend candlestick // and set brush and pen accordingly // Also, determine what the top and bottom points of the candlestick are if ( open.value <= close.value ) { pen = stockDiagram()->upTrendCandlestickPen( row ); brush = stockDiagram()->upTrendCandlestickBrush( row ); bottomCandlestickPoint = QPointF( open.key, open.value ); topCandlestickPoint = QPointF( close.key, close.value ); drawLowerLine = !low.hidden && !open.hidden; drawUpperLine = !low.hidden && !close.hidden; } else { pen = stockDiagram()->downTrendCandlestickPen( row ); brush = stockDiagram()->downTrendCandlestickBrush( row ); bottomCandlestickPoint = QPointF( close.key, close.value ); topCandlestickPoint = QPointF( open.key, open.value ); drawLowerLine = !low.hidden && !close.hidden; drawUpperLine = !low.hidden && !open.hidden; } StockBarAttributes attr = stockDiagram()->stockBarAttributes( col ); ThreeDBarAttributes threeDAttr = stockDiagram()->threeDBarAttributes( col ); const QPointF lowPoint = projectPoint( context, QPointF( low.key, low.value ) ); const QPointF highPoint = projectPoint( context, QPointF( high.key, high.value ) ); const QLineF lowerLine = QLineF( lowPoint, projectPoint( context, bottomCandlestickPoint ) ); const QLineF upperLine = QLineF( projectPoint( context, topCandlestickPoint ), highPoint ); // Convert the data point into coordinates on the coordinate plane QRectF candlestick = projectCandlestick( context, bottomCandlestickPoint, topCandlestickPoint, attr.candlestickWidth() ); // Remember the drawn polygon to add it to the ReverseMapper later QPolygonF drawnPolygon; // Use the ThreeDPainter class to draw a 3D candlestick if ( threeDAttr.isEnabled() ) { ThreeDPainter threeDPainter( context->painter() ); ThreeDPainter::ThreeDProperties threeDProps; threeDProps.depth = threeDAttr.depth(); threeDProps.angle = threeDAttr.angle(); threeDProps.useShadowColors = threeDAttr.useShadowColors(); // If the perspective angle is within [0,180], we paint from bottom to top, // otherwise from top to bottom to ensure the correct z order if ( threeDProps.angle > 0.0 && threeDProps.angle < 180.0 ) { if ( drawLowerLine ) drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps ); if ( drawCandlestick ) drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps ); if ( drawUpperLine ) drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps ); } else { if ( drawUpperLine ) drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps ); if ( drawCandlestick ) drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps ); if ( drawLowerLine ) drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps ); } } else { QPainter *const painter = context->painter(); painter->setBrush( brush ); painter->setPen( pen ); if ( drawLowerLine ) painter->drawLine( lowerLine ); if ( drawUpperLine ) painter->drawLine( upperLine ); if ( drawCandlestick ) painter->drawRect( candlestick ); // The 2D representation is the projected candlestick itself drawnPolygon = candlestick; // FIXME: Add lower and upper line to reverse mapper } LabelPaintCache lpc; if ( !low.hidden ) addLabel( &lpc, diagram->attributesModel()->mapToSource( low.index ), 0, PositionPoints( lowPoint ), Position::South, Position::South, low.value ); if ( drawCandlestick ) { // Both, the open as well as the close value are represented by this candlestick reverseMapper.addPolygon( row, openValueColumn(), drawnPolygon ); reverseMapper.addPolygon( row, closeValueColumn(), drawnPolygon ); addLabel( &lpc, diagram->attributesModel()->mapToSource( open.index ), 0, PositionPoints( candlestick.bottomRight() ), Position::South, Position::South, open.value ); addLabel( &lpc, diagram->attributesModel()->mapToSource( close.index ), 0, PositionPoints( candlestick.topRight() ), Position::South, Position::South, close.value ); } if ( !high.hidden ) addLabel( &lpc, diagram->attributesModel()->mapToSource( high.index ), 0, PositionPoints( highPoint ), Position::South, Position::South, high.value ); paintDataValueTextsAndMarkers( context, lpc, false ); }
bool StockBarAttributes::operator==( const StockBarAttributes& r ) const { return candlestickWidth() == r.candlestickWidth() && tickLength() == r.tickLength(); }