Skip to content
qwt_plot_layout.cpp 42.9 KiB
Newer Older
Bryant's avatar
Bryant committed

  \param options Layout options
  \param canvasRect Geometry of the canvas ( IN/OUT )
  \param scaleRect Geometries of the scales ( IN/OUT )

  \sa Options
pixhawk's avatar
pixhawk committed
*/

Bryant's avatar
Bryant committed
void QwtPlotLayout::alignScales( Options options,
    QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const
pixhawk's avatar
pixhawk committed
{
    int backboneOffset[QwtPlot::axisCnt];
Bryant's avatar
Bryant committed
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
pixhawk's avatar
pixhawk committed
        backboneOffset[axis] = 0;
Bryant's avatar
Bryant committed

        if ( !d_data->alignCanvasToScales[axis] )
        {
pixhawk's avatar
pixhawk committed
            backboneOffset[axis] += d_data->canvasMargin[axis];
Bryant's avatar
Bryant committed
        }

        if ( !( options & IgnoreFrames ) )
        {
            backboneOffset[axis] += 
                d_data->layoutData.canvas.contentsMargins[axis];
        }
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
pixhawk's avatar
pixhawk committed
        if ( !scaleRect[axis].isValid() )
            continue;

        const int startDist = d_data->layoutData.scale[axis].start;
        const int endDist = d_data->layoutData.scale[axis].end;

Bryant's avatar
Bryant committed
        QRectF &axisRect = scaleRect[axis];
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
        {
            const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft];
            const int leftOffset =
pixhawk's avatar
pixhawk committed
                backboneOffset[QwtPlot::yLeft] - startDist;

Bryant's avatar
Bryant committed
            if ( leftScaleRect.isValid() )
            {
                const double dx = leftOffset + leftScaleRect.width();
                if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 )
                {
                    /*
                      The axis needs more space than the width
                      of the left scale.
                     */
                    const double cLeft = canvasRect.left(); // qreal -> double
                    canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) );
                }
                else
                {
                    const double minLeft = leftScaleRect.left();
                    const double left = axisRect.left() + leftOffset;
                    axisRect.setLeft( qMax( left, minLeft ) );
                }
            }
            else
            {
                if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 )
                {
                    canvasRect.setLeft( qMax( canvasRect.left(),
                        axisRect.left() - leftOffset ) );
                }
                else
                {
pixhawk's avatar
pixhawk committed
                    if ( leftOffset > 0 )
Bryant's avatar
Bryant committed
                        axisRect.setLeft( axisRect.left() + leftOffset );
pixhawk's avatar
pixhawk committed
                }
            }

Bryant's avatar
Bryant committed
            const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight];
            const int rightOffset =
pixhawk's avatar
pixhawk committed
                backboneOffset[QwtPlot::yRight] - endDist + 1;

Bryant's avatar
Bryant committed
            if ( rightScaleRect.isValid() )
            {
                const double dx = rightOffset + rightScaleRect.width();
                if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 )
                {
                    /*
                      The axis needs more space than the width
                      of the right scale.
                     */
                    const double cRight = canvasRect.right(); // qreal -> double
                    canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) );
                }   

                const double maxRight = rightScaleRect.right();
                const double right = axisRect.right() - rightOffset;
                axisRect.setRight( qMin( right, maxRight ) );
            }
            else
            {
                if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 )
                {
                    canvasRect.setRight( qMin( canvasRect.right(),
                        axisRect.right() + rightOffset ) );
                }
                else
                {
pixhawk's avatar
pixhawk committed
                    if ( rightOffset > 0 )
Bryant's avatar
Bryant committed
                        axisRect.setRight( axisRect.right() - rightOffset );
pixhawk's avatar
pixhawk committed
                }
            }
Bryant's avatar
Bryant committed
        }
        else // QwtPlot::yLeft, QwtPlot::yRight
        {
            const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom];
            const int bottomOffset =
pixhawk's avatar
pixhawk committed
                backboneOffset[QwtPlot::xBottom] - endDist + 1;

Bryant's avatar
Bryant committed
            if ( bottomScaleRect.isValid() )
            {
                const double dy = bottomOffset + bottomScaleRect.height();
                if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 )
                {
                    /*
                      The axis needs more space than the height
                      of the bottom scale.
                     */
                    const double cBottom = canvasRect.bottom(); // qreal -> double
                    canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) );
                }
                else
                {
                    const double maxBottom = bottomScaleRect.top() +
                        d_data->layoutData.scale[QwtPlot::xBottom].tickOffset;
                    const double bottom = axisRect.bottom() - bottomOffset;
                    axisRect.setBottom( qMin( bottom, maxBottom ) );
                }
            }
            else
            {
                if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 )
                {
                    canvasRect.setBottom( qMin( canvasRect.bottom(),
                        axisRect.bottom() + bottomOffset ) );
                }
                else
                {
pixhawk's avatar
pixhawk committed
                    if ( bottomOffset > 0 )
Bryant's avatar
Bryant committed
                        axisRect.setBottom( axisRect.bottom() - bottomOffset );
pixhawk's avatar
pixhawk committed
                }
            }
Bryant's avatar
Bryant committed
            const QRectF &topScaleRect = scaleRect[QwtPlot::xTop];
pixhawk's avatar
pixhawk committed
            const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;

Bryant's avatar
Bryant committed
            if ( topScaleRect.isValid() )
            {
                const double dy = topOffset + topScaleRect.height();
                if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 )
                {
                    /*
                      The axis needs more space than the height
                      of the top scale.
                     */
                    const double cTop = canvasRect.top(); // qreal -> double
                    canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) );
                }
                else
                {
                    const double minTop = topScaleRect.bottom() -
                        d_data->layoutData.scale[QwtPlot::xTop].tickOffset;
                    const double top = axisRect.top() + topOffset;
                    axisRect.setTop( qMax( top, minTop ) );
                }
            }
            else
            {
                if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 )
                {
                    canvasRect.setTop( qMax( canvasRect.top(),
                        axisRect.top() - topOffset ) );
                }
                else
                {
pixhawk's avatar
pixhawk committed
                    if ( topOffset > 0 )
Bryant's avatar
Bryant committed
                        axisRect.setTop( axisRect.top() + topOffset );
Bryant's avatar
Bryant committed
    /*
      The canvas has been aligned to the scale with largest
      border distances. Now we have to realign the other scale.
     */


    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
        QRectF &sRect = scaleRect[axis];

        if ( !sRect.isValid() )
            continue;

        if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
        {
            if ( d_data->alignCanvasToScales[QwtPlot::yLeft] )
            {
                double y = canvasRect.left() - d_data->layoutData.scale[axis].start;
                if ( !( options & IgnoreFrames ) )
                    y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ];

                sRect.setLeft( y );
            }
            if ( d_data->alignCanvasToScales[QwtPlot::yRight] )
            {
                double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end;
                if ( !( options & IgnoreFrames ) )
                    y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ];

                sRect.setRight( y );
            }

            if ( d_data->alignCanvasToScales[ axis ] )
            {
                if ( axis == QwtPlot::xTop )
                    sRect.setBottom( canvasRect.top() );
                else
                    sRect.setTop( canvasRect.bottom() );
pixhawk's avatar
pixhawk committed
            }
        }
Bryant's avatar
Bryant committed
        else
        {
            if ( d_data->alignCanvasToScales[QwtPlot::xTop] )
            {
                double x = canvasRect.top() - d_data->layoutData.scale[axis].start;
                if ( !( options & IgnoreFrames ) )
                    x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ];

                sRect.setTop( x );
            }
            if ( d_data->alignCanvasToScales[QwtPlot::xBottom] )
            {
                double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end;
                if ( !( options & IgnoreFrames ) )
                    x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ];

                sRect.setBottom( x );
            }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            if ( d_data->alignCanvasToScales[ axis ] )
            {
                if ( axis == QwtPlot::yLeft )
                    sRect.setRight( canvasRect.left() );
                else
                    sRect.setLeft( canvasRect.right() );
  \brief Recalculate the geometry of all components.
pixhawk's avatar
pixhawk committed

  \param plot Plot to be layout
Bryant's avatar
Bryant committed
  \param plotRect Rectangle where to place the components
  \param options Layout options
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa invalidate(), titleRect(), footerRect()
pixhawk's avatar
pixhawk committed
      legendRect(), scaleRect(), canvasRect()
*/
Bryant's avatar
Bryant committed
void QwtPlotLayout::activate( const QwtPlot *plot,
    const QRectF &plotRect, Options options )
pixhawk's avatar
pixhawk committed
{
    invalidate();

Bryant's avatar
Bryant committed
    QRectF rect( plotRect );  // undistributed rest of the plot rect
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    // We extract all layout relevant parameters from the widgets,
    // and save them to d_data->layoutData.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    d_data->layoutData.init( plot, rect );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( !( options & IgnoreLegend )
        && plot->legend() && !plot->legend()->isEmpty() )
    {
        d_data->legendRect = layoutLegend( options, rect );
pixhawk's avatar
pixhawk committed

        // subtract d_data->legendRect from rect

Bryant's avatar
Bryant committed
        const QRegion region( rect.toRect() );
        rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        switch ( d_data->legendPos )
        {
            case QwtPlot::LeftLegend:
Bryant's avatar
Bryant committed
                rect.setLeft( rect.left() + d_data->spacing );
                break;
            case QwtPlot::RightLegend:
Bryant's avatar
Bryant committed
                rect.setRight( rect.right() - d_data->spacing );
                break;
            case QwtPlot::TopLegend:
Bryant's avatar
Bryant committed
                rect.setTop( rect.top() + d_data->spacing );
                break;
            case QwtPlot::BottomLegend:
Bryant's avatar
Bryant committed
                rect.setBottom( rect.bottom() - d_data->spacing );
pixhawk's avatar
pixhawk committed
        }
    }

    /*
     +---+-----------+---+
     |       Title       |
     +---+-----------+---+
     |   |   Axis    |   |
     +---+-----------+---+
     | A |           | A |
     | x |  Canvas   | x |
     | i |           | i |
     | s |           | s |
     +---+-----------+---+
     |   |   Axis    |   |
     +---+-----------+---+
Bryant's avatar
Bryant committed
     |      Footer       |
     +---+-----------+---+
pixhawk's avatar
pixhawk committed
    */

Bryant's avatar
Bryant committed
    // title, footer and axes include text labels. The height of each
pixhawk's avatar
pixhawk committed
    // label depends on its line breaks, that depend on the width
    // for the label. A line break in a horizontal text will reduce
    // the available width for vertical texts and vice versa.
Bryant's avatar
Bryant committed
    // expandLineBreaks finds the height/width for title, footer and axes
pixhawk's avatar
pixhawk committed
    // including all line breaks.

Bryant's avatar
Bryant committed
    int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt];
    expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( dimTitle > 0 )
    {
        d_data->titleRect.setRect(
            rect.left(), rect.top(), rect.width(), dimTitle );

        rect.setTop( d_data->titleRect.bottom() + d_data->spacing );
pixhawk's avatar
pixhawk committed

        if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
Bryant's avatar
Bryant committed
            d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
        {
pixhawk's avatar
pixhawk committed
            // if only one of the y axes is missing we align
            // the title centered to the canvas

Bryant's avatar
Bryant committed
            d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
            d_data->titleRect.setWidth( rect.width()
                - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( dimFooter > 0 )
    {
        d_data->footerRect.setRect(
            rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter );

        rect.setBottom( d_data->footerRect.top() - d_data->spacing );

        if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
            d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
        {
            // if only one of the y axes is missing we align
            // the footer centered to the canvas

            d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
            d_data->footerRect.setWidth( rect.width()
                - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
        }
pixhawk's avatar
pixhawk committed
    }

    d_data->canvasRect.setRect(
        rect.x() + dimAxes[QwtPlot::yLeft],
        rect.y() + dimAxes[QwtPlot::xTop],
        rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
Bryant's avatar
Bryant committed
        rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
pixhawk's avatar
pixhawk committed
        // set the rects for the axes

Bryant's avatar
Bryant committed
        if ( dimAxes[axis] )
        {
pixhawk's avatar
pixhawk committed
            int dim = dimAxes[axis];
Bryant's avatar
Bryant committed
            QRectF &scaleRect = d_data->scaleRect[axis];
pixhawk's avatar
pixhawk committed

            scaleRect = d_data->canvasRect;
Bryant's avatar
Bryant committed
            switch ( axis )
            {
                case QwtPlot::yLeft:
                    scaleRect.setX( d_data->canvasRect.left() - dim );
                    scaleRect.setWidth( dim );
                    break;
                case QwtPlot::yRight:
                    scaleRect.setX( d_data->canvasRect.right() );
                    scaleRect.setWidth( dim );
                    break;
                case QwtPlot::xBottom:
                    scaleRect.setY( d_data->canvasRect.bottom() );
                    scaleRect.setHeight( dim );
                    break;
                case QwtPlot::xTop:
                    scaleRect.setY( d_data->canvasRect.top() - dim );
                    scaleRect.setHeight( dim );
                    break;
pixhawk's avatar
pixhawk committed
            }
            scaleRect = scaleRect.normalized();
        }
    }

    // +---+-----------+---+
    // |  <-   Axis   ->   |
    // +-^-+-----------+-^-+
    // | | |           | | |
    // |   |           |   |
    // | A |           | A |
    // | x |  Canvas   | x |
    // | i |           | i |
    // | s |           | s |
    // |   |           |   |
    // | | |           | | |
    // +-V-+-----------+-V-+
    // |   <-  Axis   ->   |
    // +---+-----------+---+

    // The ticks of the axes - not the labels above - should
    // be aligned to the canvas. So we try to use the empty
    // corners to extend the axes, so that the label texts
    // left/right of the min/max ticks are moved into them.
Bryant's avatar
Bryant committed
    alignScales( options, d_data->canvasRect, d_data->scaleRect );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( !d_data->legendRect.isEmpty() )
    {
pixhawk's avatar
pixhawk committed
        // We prefer to align the legend to the canvas - not to
        // the complete plot - if possible.

Bryant's avatar
Bryant committed
        d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect );
pixhawk's avatar
pixhawk committed
    }
}