void StockDiagram::Private::drawOHLCBar( int dataset, const CartesianDiagramDataCompressor::DataPoint &open,
        const CartesianDiagramDataCompressor::DataPoint &high,
        const CartesianDiagramDataCompressor::DataPoint &low,
        const CartesianDiagramDataCompressor::DataPoint &close,
        PaintContext *context )
{
    // Note: A row in the model is a column in a StockDiagram
    const int col = low.index.row();

    StockBarAttributes attr = stockDiagram()->stockBarAttributes( col );
    ThreeDBarAttributes threeDAttr = stockDiagram()->threeDBarAttributes( col );
    const qreal tickLength = attr.tickLength();

    const QPointF leftOpenPoint( open.key + 0.5 - tickLength, open.value );
    const QPointF rightOpenPoint( open.key + 0.5, open.value );
    const QPointF highPoint( high.key + 0.5, high.value );
    const QPointF lowPoint( low.key + 0.5, low.value );
    const QPointF leftClosePoint( close.key + 0.5, close.value );
    const QPointF rightClosePoint( close.key + 0.5 + tickLength, close.value );

    bool reversedOrder = false;
    // If 3D mode is enabled, we have to make sure the z-order is right
    if ( threeDAttr.isEnabled() ) {
        const int angle = threeDAttr.angle();
        // Z-order is from right to left
        if ( ( angle >= 0 && angle < 90 ) || ( angle >= 180 && angle < 270 ) )
            reversedOrder = true;
        // Z-order is from left to right
        if ( ( angle >= 90 && angle < 180 ) || ( angle >= 270 && angle < 0 ) )
            reversedOrder = false;
    }

    if ( reversedOrder ) {
        if ( !open.hidden )
            drawLine( dataset, col, leftOpenPoint, rightOpenPoint, context ); // Open marker
        if ( !low.hidden && !high.hidden )
            drawLine( dataset, col, lowPoint, highPoint, context ); // Low-High line
        if ( !close.hidden )
            drawLine( dataset, col, leftClosePoint, rightClosePoint, context ); // Close marker
    } else {
        if ( !close.hidden )
            drawLine( dataset, col, leftClosePoint, rightClosePoint, context ); // Close marker
        if ( !low.hidden && !high.hidden )
            drawLine( dataset, col, lowPoint, highPoint, context ); // Low-High line
        if ( !open.hidden )
            drawLine( dataset, col, leftOpenPoint, rightOpenPoint, context ); // Open marker
    }

    LabelPaintCache lpc;
    if ( !open.hidden ) {
        addLabel( &lpc, diagram->attributesModel()->mapToSource( open.index ), 0,
                            PositionPoints( leftOpenPoint ), Position::South, Position::South, open.value );
    }
    if ( !high.hidden ) {
        addLabel( &lpc, diagram->attributesModel()->mapToSource( high.index ), 0,
                            PositionPoints( highPoint ), Position::South, Position::South, high.value );
    }
    if ( !low.hidden ) {
        addLabel( &lpc, diagram->attributesModel()->mapToSource( low.index ), 0,
                            PositionPoints( lowPoint ), Position::South, Position::South, low.value );
    }
    if ( !close.hidden ) {
        addLabel( &lpc, diagram->attributesModel()->mapToSource( close.index ), 0,
                            PositionPoints( rightClosePoint ), Position::South, Position::South, close.value );
    }
    paintDataValueTextsAndMarkers( context, lpc, false );
}
/**
  * 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 );
}
Exemplo n.º 3
0
bool StockBarAttributes::operator==( const StockBarAttributes& r ) const
{
    return candlestickWidth() == r.candlestickWidth() &&
           tickLength() == r.tickLength();
}