Skip to content
qwt_scale_draw.cpp 22.1 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
pixhawk's avatar
pixhawk committed
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
#include "qwt_scale_draw.h"
pixhawk's avatar
pixhawk committed
#include "qwt_scale_div.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
#include "qwt_math.h"
#include "qwt_painter.h"
#include <qpen.h>
#include <qpainter.h>
#include <qmath.h>
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
#if QT_VERSION < 0x040601
#define qFastSin(x) qSin(x)
#define qFastCos(x) qCos(x)
pixhawk's avatar
pixhawk committed
#endif

class QwtScaleDraw::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        len( 0 ),
        alignment( QwtScaleDraw::BottomScale ),
        labelAlignment( 0 ),
        labelRotation( 0.0 )
    {
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    QPointF pos;
    double len;
pixhawk's avatar
pixhawk committed

    Alignment alignment;

    Qt::Alignment labelAlignment;
    double labelRotation;
};

/*!
  \brief Constructor

  The range of the scale is initialized to [0, 100],
  The position is at (0, 0) with a length of 100.
  The orientation is QwtAbstractScaleDraw::Bottom.
*/
QwtScaleDraw::QwtScaleDraw()
{
    d_data = new QwtScaleDraw::PrivateData;
Bryant's avatar
Bryant committed
    setLength( 100 );
pixhawk's avatar
pixhawk committed
}

//! Destructor
QwtScaleDraw::~QwtScaleDraw()
{
    delete d_data;
}

pixhawk's avatar
pixhawk committed
   Return alignment of the scale
   \sa setAlignment()
Bryant's avatar
Bryant committed
   \return Alignment of the scale
pixhawk's avatar
pixhawk committed
*/
QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
pixhawk's avatar
pixhawk committed
{
    return d_data->alignment;
pixhawk's avatar
pixhawk committed
}

/*!
   Set the alignment of the scale

Bryant's avatar
Bryant committed
   \param align Alignment of the scale

pixhawk's avatar
pixhawk committed
   The default alignment is QwtScaleDraw::BottomScale
   \sa alignment()
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::setAlignment( Alignment align )
pixhawk's avatar
pixhawk committed
{
    d_data->alignment = align;
}

/*!
  Return the orientation

  TopScale, BottomScale are horizontal (Qt::Horizontal) scales,
  LeftScale, RightScale are vertical (Qt::Vertical) scales.

Bryant's avatar
Bryant committed
  \return Orientation of the scale

pixhawk's avatar
pixhawk committed
  \sa alignment()
*/
Qt::Orientation QwtScaleDraw::orientation() const
{
Bryant's avatar
Bryant committed
    switch ( d_data->alignment )
    {
        case TopScale:
        case BottomScale:
            return Qt::Horizontal;
        case LeftScale:
        case RightScale:
        default:
            return Qt::Vertical;
pixhawk's avatar
pixhawk committed
    }
}

/*!
  \brief Determine the minimum border distance

  This member function returns the minimum space
  needed to draw the mark labels at the scale's endpoints.

  \param font Font
  \param start Start border distance
  \param end End border distance
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::getBorderDistHint( 
    const QFont &font, int &start, int &end ) const
pixhawk's avatar
pixhawk committed
{
    start = 0;
    end = 0;
Bryant's avatar
Bryant committed
    if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
    if ( ticks.count() == 0 )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    // Find the ticks, that are mapped to the borders.
    // minTick is the tick, that is mapped to the top/left-most position
    // in widget coordinates.

    double minTick = ticks[0];
    double minPos = scaleMap().transform( minTick );
    double maxTick = minTick;
    double maxPos = minPos;

    for ( int i = 1; i < ticks.count(); i++ )
    {
        const double tickPos = scaleMap().transform( ticks[i] );
        if ( tickPos < minPos )
        {
            minTick = ticks[i];
            minPos = tickPos;
        }
        if ( tickPos > scaleMap().transform( maxTick ) )
        {
            maxTick = ticks[i];
            maxPos = tickPos;
        }
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double e = 0.0;
    double s = 0.0;
pixhawk's avatar
pixhawk committed
    if ( orientation() == Qt::Vertical )
Bryant's avatar
Bryant committed
    {
        s = -labelRect( font, minTick ).top();
        s -= qAbs( minPos - qRound( scaleMap().p2() ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        e = labelRect( font, maxTick ).bottom();
        e -= qAbs( maxPos - scaleMap().p1() );
    }
pixhawk's avatar
pixhawk committed
    else
Bryant's avatar
Bryant committed
    {
        s = -labelRect( font, minTick ).left();
        s -= qAbs( minPos - scaleMap().p1() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        e = labelRect( font, maxTick ).right();
        e -= qAbs( maxPos - scaleMap().p2() );
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( s < 0.0 )
        s = 0.0;
    if ( e < 0.0 )
        e = 0.0;

    start = qCeil( s );
    end = qCeil( e );
pixhawk's avatar
pixhawk committed
}

/*!
  Determine the minimum distance between two labels, that is necessary
  that the texts don't overlap.

  \param font Font
  \return The maximum width of a label

  \sa getBorderDistHint()
*/

Bryant's avatar
Bryant committed
int QwtScaleDraw::minLabelDist( const QFont &font ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
pixhawk's avatar
pixhawk committed
        return 0;

Bryant's avatar
Bryant committed
    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
    if ( ticks.isEmpty() )
pixhawk's avatar
pixhawk committed
        return 0;

Bryant's avatar
Bryant committed
    const QFontMetrics fm( font );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const bool vertical = ( orientation() == Qt::Vertical );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QRectF bRect1;
    QRectF bRect2 = labelRect( font, ticks[0] );
    if ( vertical )
    {
        bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    double maxDist = 0.0;

    for ( int i = 1; i < ticks.count(); i++ )
    {
pixhawk's avatar
pixhawk committed
        bRect1 = bRect2;
Bryant's avatar
Bryant committed
        bRect2 = labelRect( font, ticks[i] );
        if ( vertical )
        {
            bRect2.setRect( -bRect2.bottom(), 0.0,
                bRect2.height(), bRect2.width() );
pixhawk's avatar
pixhawk committed
        }

Bryant's avatar
Bryant committed
        double dist = fm.leading(); // space between the labels
pixhawk's avatar
pixhawk committed
        if ( bRect1.right() > 0 )
            dist += bRect1.right();
        if ( bRect2.left() < 0 )
            dist += -bRect2.left();

        if ( dist > maxDist )
            maxDist = dist;
    }

Bryant's avatar
Bryant committed
    double angle = qwtRadians( labelRotation() ); 
pixhawk's avatar
pixhawk committed
    if ( vertical )
        angle += M_PI / 2;

Bryant's avatar
Bryant committed
    const double sinA = qFastSin( angle ); // qreal -> double
    if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
        return qCeil( maxDist );
pixhawk's avatar
pixhawk committed

    const int fmHeight = fm.ascent() - 2;
pixhawk's avatar
pixhawk committed

    // The distance we need until there is
    // the height of the label font. This height is needed
Bryant's avatar
Bryant committed
    // for the neighbored label.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
pixhawk's avatar
pixhawk committed
    if ( labelDist < 0 )
        labelDist = -labelDist;

    // For text orientations close to the scale orientation
pixhawk's avatar
pixhawk committed

    if ( labelDist > maxDist )
        labelDist = maxDist;

    // For text orientations close to the opposite of the
pixhawk's avatar
pixhawk committed
    // scale orientation

    if ( labelDist < fmHeight )
        labelDist = fmHeight;

Bryant's avatar
Bryant committed
    return qCeil( labelDist );
pixhawk's avatar
pixhawk committed
}

/*!
   Calculate the width/height that is needed for a
   vertical/horizontal scale.

   The extent is calculated from the pen width of the backbone,
   the major tick length, the spacing and the maximum width/height
   of the labels.

   \param font Font used for painting the labels
Bryant's avatar
Bryant committed
   \return Extent
pixhawk's avatar
pixhawk committed

   \sa minLength()
*/
Bryant's avatar
Bryant committed
double QwtScaleDraw::extent( const QFont &font ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double d = 0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
    {
pixhawk's avatar
pixhawk committed
        if ( orientation() == Qt::Vertical )
Bryant's avatar
Bryant committed
            d = maxLabelWidth( font );
pixhawk's avatar
pixhawk committed
        else
Bryant's avatar
Bryant committed
            d = maxLabelHeight( font );
pixhawk's avatar
pixhawk committed

        if ( d > 0 )
            d += spacing();
    }

Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
    {
        d += maxTickLength();
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
    {
        const double pw = qMax( 1, penWidth() );  // pen width can be zero
pixhawk's avatar
pixhawk committed
        d += pw;
    }

Bryant's avatar
Bryant committed
    d = qMax( d, minimumExtent() );
pixhawk's avatar
pixhawk committed
    return d;
}

/*!
   Calculate the minimum length that is needed to draw the scale

   \param font Font used for painting the labels
Bryant's avatar
Bryant committed
   \return Minimum length that is needed to draw the scale
pixhawk's avatar
pixhawk committed

   \sa extent()
*/
Bryant's avatar
Bryant committed
int QwtScaleDraw::minLength( const QFont &font ) const
pixhawk's avatar
pixhawk committed
{
    int startDist, endDist;
Bryant's avatar
Bryant committed
    getBorderDistHint( font, startDist, endDist );
pixhawk's avatar
pixhawk committed

    const QwtScaleDiv &sd = scaleDiv();

    const uint minorCount =
Bryant's avatar
Bryant committed
        sd.ticks( QwtScaleDiv::MinorTick ).count() +
        sd.ticks( QwtScaleDiv::MediumTick ).count();
pixhawk's avatar
pixhawk committed
    const uint majorCount =
Bryant's avatar
Bryant committed
        sd.ticks( QwtScaleDiv::MajorTick ).count();
pixhawk's avatar
pixhawk committed

    int lengthForLabels = 0;
Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
        lengthForLabels = minLabelDist( font ) * majorCount;
pixhawk's avatar
pixhawk committed

    int lengthForTicks = 0;
Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
    {
        const double pw = qMax( 1, penWidth() );  // penwidth can be zero
        lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
pixhawk's avatar
pixhawk committed
}

/*!
   Find the position, where to paint a label

Bryant's avatar
Bryant committed
   The position has a distance that depends on the length of the ticks 
   in direction of the alignment().
pixhawk's avatar
pixhawk committed

   \param value Value
Bryant's avatar
Bryant committed
   \return Position, where to paint a label
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QPointF QwtScaleDraw::labelPosition( double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const double tval = scaleMap().transform( value );
    double dist = spacing();
    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
        dist += qMax( 1, penWidth() );

    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
        dist += tickLength( QwtScaleDiv::MajorTick );

    double px = 0;
    double py = 0;

    switch ( alignment() )
    {
        case RightScale:
        {
            px = d_data->pos.x() + dist;
            py = tval;
            break;
        }
        case LeftScale:
        {
            px = d_data->pos.x() - dist;
            py = tval;
            break;
        }
        case BottomScale:
        {
            px = tval;
            py = d_data->pos.y() + dist;
            break;
        }
        case TopScale:
        {
            px = tval;
            py = d_data->pos.y() - dist;
            break;
        }
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    return QPointF( px, py );
pixhawk's avatar
pixhawk committed
}

/*!
   Draw a tick

   \param painter Painter
   \param value Value of the tick
Bryant's avatar
Bryant committed
   \param len Length of the tick
pixhawk's avatar
pixhawk committed

   \sa drawBackbone(), drawLabel()
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const
pixhawk's avatar
pixhawk committed
{
    if ( len <= 0 )
        return;

Bryant's avatar
Bryant committed
    const bool roundingAlignment = QwtPainter::roundingAlignment( painter );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QPointF pos = d_data->pos;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double tval = scaleMap().transform( value );
    if ( roundingAlignment )
        tval = qRound( tval );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const int pw = penWidth();
    int a = 0;
    if ( pw > 1 && roundingAlignment )
        a = 1;
Bryant's avatar
Bryant committed
    switch ( alignment() )
    {
        case LeftScale:
        {
            double x1 = pos.x() + a;
            double x2 = pos.x() + a - pw - len;
            if ( roundingAlignment )
            {
                x1 = qRound( x1 );
                x2 = qRound( x2 );
            }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            QwtPainter::drawLine( painter, x1, tval, x2, tval );
            break;
        }

        case RightScale:
        {
            double x1 = pos.x();
            double x2 = pos.x() + pw + len;
            if ( roundingAlignment )
            {
                x1 = qRound( x1 );
                x2 = qRound( x2 );
            }

            QwtPainter::drawLine( painter, x1, tval, x2, tval );
            break;
        }

        case BottomScale:
        {
            double y1 = pos.y();
            double y2 = pos.y() + pw + len;
            if ( roundingAlignment )
            {
                y1 = qRound( y1 );
                y2 = qRound( y2 );
            }

            QwtPainter::drawLine( painter, tval, y1, tval, y2 );
            break;
        }

        case TopScale:
        {
            double y1 = pos.y() + a;
            double y2 = pos.y() - pw - len + a;
            if ( roundingAlignment )
            {
                y1 = qRound( y1 );
                y2 = qRound( y2 );
            }

            QwtPainter::drawLine( painter, tval, y1, tval, y2 );
            break;
        }
pixhawk's avatar
pixhawk committed
    }
}

pixhawk's avatar
pixhawk committed
   Draws the baseline of the scale
   \param painter Painter

   \sa drawTick(), drawLabel()
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::drawBackbone( QPainter *painter ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const bool doAlign = QwtPainter::roundingAlignment( painter );

    const QPointF &pos = d_data->pos;
    const double len = d_data->len;
    const int pw = qMax( penWidth(), 1 );

    // pos indicates a border not the center of the backbone line
    // so we need to shift its position depending on the pen width
    // and the alignment of the scale

    double off;
    if ( doAlign )
    {
        if ( alignment() == LeftScale || alignment() == TopScale )
            off = ( pw - 1 ) / 2;
        else
            off = pw / 2;
    }
    else
    {
        off = 0.5 * penWidth();
    }

    switch ( alignment() )
    {
        case LeftScale:
        {
            double x = pos.x() - off;
            if ( doAlign )
                x = qRound( x );

            QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
            break;
        }
        case RightScale:
        {
            double x = pos.x() + off;
            if ( doAlign )
                x = qRound( x );

            QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
            break;
        }
        case TopScale:
        {
            double y = pos.y() - off;
            if ( doAlign )
                y = qRound( y );

            QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
            break;
        }
        case BottomScale:
        {
            double y = pos.y() + off;
            if ( doAlign )
                y = qRound( y );

            QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
            break;
        }
pixhawk's avatar
pixhawk committed
    }
}

/*!
  \brief Move the position of the scale

  The meaning of the parameter pos depends on the alignment:
  <dl>
  <dt>QwtScaleDraw::LeftScale
  <dd>The origin is the topmost point of the
      backbone. The backbone is a vertical line.
      Scale marks and labels are drawn
pixhawk's avatar
pixhawk committed
      at the left of the backbone.
  <dt>QwtScaleDraw::RightScale
  <dd>The origin is the topmost point of the
      backbone. The backbone is a vertical line.
pixhawk's avatar
pixhawk committed
      Scale marks and labels are drawn
      at the right of the backbone.
  <dt>QwtScaleDraw::TopScale
  <dd>The origin is the leftmost point of the
      backbone. The backbone is a horizontal line.
pixhawk's avatar
pixhawk committed
      Scale marks and labels are drawn
      above the backbone.
  <dt>QwtScaleDraw::BottomScale
  <dd>The origin is the leftmost point of the
      backbone. The backbone is a horizontal line
pixhawk's avatar
pixhawk committed
      Scale marks and labels are drawn
      below the backbone.
  </dl>

  \param pos Origin of the scale

  \sa pos(), setLength()
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::move( const QPointF &pos )
pixhawk's avatar
pixhawk committed
{
    d_data->pos = pos;
    updateMap();
}

pixhawk's avatar
pixhawk committed
   \return Origin of the scale
   \sa move(), length()
*/
Bryant's avatar
Bryant committed
QPointF QwtScaleDraw::pos() const
pixhawk's avatar
pixhawk committed
{
    return d_data->pos;
}

/*!
  Set the length of the backbone.
pixhawk's avatar
pixhawk committed
  The length doesn't include the space needed for
  overlapping labels.

Bryant's avatar
Bryant committed
  \param length Length of the backbone

pixhawk's avatar
pixhawk committed
  \sa move(), minLabelDist()
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::setLength( double length )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
#if 1
pixhawk's avatar
pixhawk committed
    if ( length >= 0 && length < 10 )
        length = 10;
Bryant's avatar
Bryant committed

    // why should we accept negative lengths ???
pixhawk's avatar
pixhawk committed
    if ( length < 0 && length > -10 )
        length = -10;
Bryant's avatar
Bryant committed
#else
    length = qMax( length, 10 );
#endif
pixhawk's avatar
pixhawk committed
    d_data->len = length;
    updateMap();
}

pixhawk's avatar
pixhawk committed
   \return the length of the backbone
   \sa setLength(), pos()
*/
Bryant's avatar
Bryant committed
double QwtScaleDraw::length() const
pixhawk's avatar
pixhawk committed
{
    return d_data->len;
}

pixhawk's avatar
pixhawk committed
   Draws the label for a major scale tick

   \param painter Painter
   \param value Value

   \sa drawTick(), drawBackbone(), boundingLabelRect()
*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtText lbl = tickLabel( painter->font(), value );
pixhawk's avatar
pixhawk committed
    if ( lbl.isEmpty() )
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QPointF pos = labelPosition( value );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QSizeF labelSize = lbl.textSize( painter->font() );
Bryant's avatar
Bryant committed
    const QTransform transform = labelTransformation( pos, labelSize );
pixhawk's avatar
pixhawk committed

    painter->save();
Bryant's avatar
Bryant committed
    painter->setWorldTransform( transform, true );

    lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
pixhawk's avatar
pixhawk committed

    painter->restore();
}

/*!
Bryant's avatar
Bryant committed
  \brief Find the bounding rectangle for the label.

  The coordinates of the rectangle are absolute ( calculated from pos() ).
pixhawk's avatar
pixhawk committed
  in direction of the tick.

  \param font Font used for painting
  \param value Value

Bryant's avatar
Bryant committed
  \return Bounding rectangle
pixhawk's avatar
pixhawk committed
  \sa labelRect()
*/
Bryant's avatar
Bryant committed
QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtText lbl = tickLabel( font, value );
pixhawk's avatar
pixhawk committed
    if ( lbl.isEmpty() )
        return QRect();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QPointF pos = labelPosition( value );
    QSizeF labelSize = lbl.textSize( font );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QTransform transform = labelTransformation( pos, labelSize );
    return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
   Calculate the transformation that is needed to paint a label
pixhawk's avatar
pixhawk committed
   depending on its alignment and rotation.

   \param pos Position where to paint the label
   \param size Size of the label

Bryant's avatar
Bryant committed
   \return Transformation matrix
pixhawk's avatar
pixhawk committed
   \sa setLabelAlignment(), setLabelRotation()
*/
Bryant's avatar
Bryant committed
QTransform QwtScaleDraw::labelTransformation(
    const QPointF &pos, const QSizeF &size ) const
Bryant's avatar
Bryant committed
    QTransform transform;
    transform.translate( pos.x(), pos.y() );
    transform.rotate( labelRotation() );
pixhawk's avatar
pixhawk committed
    int flags = labelAlignment();
Bryant's avatar
Bryant committed
    if ( flags == 0 )
    {
        switch ( alignment() )
        {
            case RightScale:
            {
                if ( flags == 0 )
                    flags = Qt::AlignRight | Qt::AlignVCenter;
                break;
            }
            case LeftScale:
            {
                if ( flags == 0 )
                    flags = Qt::AlignLeft | Qt::AlignVCenter;
                break;
            }
            case BottomScale:
            {
                if ( flags == 0 )
                    flags = Qt::AlignHCenter | Qt::AlignBottom;
                break;
            }
            case TopScale:
            {
                if ( flags == 0 )
                    flags = Qt::AlignHCenter | Qt::AlignTop;
                break;
            }
pixhawk's avatar
pixhawk committed
        }
    }

Bryant's avatar
Bryant committed
    double x, y;
pixhawk's avatar
pixhawk committed
    if ( flags & Qt::AlignLeft )
Bryant's avatar
Bryant committed
        x = -size.width();
pixhawk's avatar
pixhawk committed
    else if ( flags & Qt::AlignRight )
Bryant's avatar
Bryant committed
        x = 0.0;
pixhawk's avatar
pixhawk committed
    else // Qt::AlignHCenter
Bryant's avatar
Bryant committed
        x = -( 0.5 * size.width() );
pixhawk's avatar
pixhawk committed
    if ( flags & Qt::AlignTop )
Bryant's avatar
Bryant committed
        y = -size.height();
pixhawk's avatar
pixhawk committed
    else if ( flags & Qt::AlignBottom )
Bryant's avatar
Bryant committed
        y = 0;
pixhawk's avatar
pixhawk committed
    else // Qt::AlignVCenter
Bryant's avatar
Bryant committed
        y = -( 0.5 * size.height() );
Bryant's avatar
Bryant committed
    transform.translate( x, y );
Bryant's avatar
Bryant committed
    return transform;
pixhawk's avatar
pixhawk committed

/*!
Bryant's avatar
Bryant committed
  Find the bounding rectangle for the label. The coordinates of
  the rectangle are relative to spacing + tick length from the backbone
pixhawk's avatar
pixhawk committed
  in direction of the tick.

  \param font Font used for painting
  \param value Value
Bryant's avatar
Bryant committed

   \return Bounding rectangle that is needed to draw a label
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const
Bryant's avatar
Bryant committed
    QwtText lbl = tickLabel( font, value );
pixhawk's avatar
pixhawk committed
    if ( lbl.isEmpty() )
Bryant's avatar
Bryant committed
        return QRectF( 0.0, 0.0, 0.0, 0.0 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QPointF pos = labelPosition( value );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QSizeF labelSize = lbl.textSize( font );
    const QTransform transform = labelTransformation( pos, labelSize );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
    br.translate( -pos.x(), -pos.y() );
pixhawk's avatar
pixhawk committed

    return br;
}

/*!
   Calculate the size that is needed to draw a label

   \param font Label font
   \param value Value
Bryant's avatar
Bryant committed

   \return Size that is needed to draw a label
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return labelRect( font, value ).size();
pixhawk's avatar
pixhawk committed
}

/*!
  Rotate all labels.

  When changing the rotation, it might be necessary to
  adjust the label flags too. Finding a useful combination is
  often the result of try and error.

  \param rotation Angle in degrees. When changing the label rotation,
                  the label flags often needs to be adjusted too.

  \sa setLabelAlignment(), labelRotation(), labelAlignment().

*/
Bryant's avatar
Bryant committed
void QwtScaleDraw::setLabelRotation( double rotation )
pixhawk's avatar
pixhawk committed
{
    d_data->labelRotation = rotation;
}

/*!
  \return the label rotation
  \sa setLabelRotation(), labelAlignment()
*/
double QwtScaleDraw::labelRotation() const
{
    return d_data->labelRotation;
}

/*!
  \brief Change the label flags

Bryant's avatar
Bryant committed
  Labels are aligned to the point tick length + spacing away from the backbone.
pixhawk's avatar
pixhawk committed

  The alignment is relative to the orientation of the label text.
  In case of an flags of 0 the label will be aligned
  depending on the orientation of the scale:

pixhawk's avatar
pixhawk committed
      QwtScaleDraw::TopScale: Qt::AlignHCenter | Qt::AlignTop\n
      QwtScaleDraw::BottomScale: Qt::AlignHCenter | Qt::AlignBottom\n
      QwtScaleDraw::LeftScale: Qt::AlignLeft | Qt::AlignVCenter\n
      QwtScaleDraw::RightScale: Qt::AlignRight | Qt::AlignVCenter\n
pixhawk's avatar
pixhawk committed
  Changing the alignment is often necessary for rotated labels.
Bryant's avatar
Bryant committed
  \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
pixhawk's avatar
pixhawk committed

  \sa setLabelRotation(), labelRotation(), labelAlignment()
  \warning The various alignments might be confusing.
pixhawk's avatar
pixhawk committed
           The alignment of the label is not the alignment
           of the scale and is not the alignment of the flags
Bryant's avatar
Bryant committed
           ( QwtText::flags() ) returned from QwtAbstractScaleDraw::label().
Bryant's avatar
Bryant committed
void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
pixhawk's avatar
pixhawk committed
{
    d_data->labelAlignment = alignment;
pixhawk's avatar
pixhawk committed

/*!
  \return the label flags
  \sa setLabelAlignment(), labelRotation()
*/
Qt::Alignment QwtScaleDraw::labelAlignment() const
{
    return d_data->labelAlignment;
}

/*!
  \param font Font
  \return the maximum width of a label
*/
Bryant's avatar
Bryant committed
int QwtScaleDraw::maxLabelWidth( const QFont &font ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double maxWidth = 0.0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
    for ( int i = 0; i < ticks.count(); i++ )
    {
pixhawk's avatar
pixhawk committed
        const double v = ticks[i];
Bryant's avatar
Bryant committed
        if ( scaleDiv().contains( v ) )
        {
            const double w = labelSize( font, ticks[i] ).width();
pixhawk's avatar
pixhawk committed
            if ( w > maxWidth )
                maxWidth = w;
        }
    }

Bryant's avatar
Bryant committed
    return qCeil( maxWidth );
pixhawk's avatar
pixhawk committed
}

/*!
  \param font Font
  \return the maximum height of a label
*/
Bryant's avatar
Bryant committed
int QwtScaleDraw::maxLabelHeight( const QFont &font ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double maxHeight = 0.0;
Bryant's avatar
Bryant committed
    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
    for ( int i = 0; i < ticks.count(); i++ )
    {
pixhawk's avatar
pixhawk committed
        const double v = ticks[i];
Bryant's avatar
Bryant committed
        if ( scaleDiv().contains( v ) )
        {
            const double h = labelSize( font, ticks[i] ).height();
pixhawk's avatar
pixhawk committed
            if ( h > maxHeight )
Bryant's avatar
Bryant committed
    return qCeil( maxHeight );
pixhawk's avatar
pixhawk committed

void QwtScaleDraw::updateMap()
{
Bryant's avatar
Bryant committed
    const QPointF pos = d_data->pos;
    double len = d_data->len;

pixhawk's avatar
pixhawk committed
    QwtScaleMap &sm = scaleMap();
    if ( orientation() == Qt::Vertical )
Bryant's avatar
Bryant committed
        sm.setPaintInterval( pos.y() + len, pos.y() );
pixhawk's avatar
pixhawk committed
    else
Bryant's avatar
Bryant committed
        sm.setPaintInterval( pos.x(), pos.x() + len );
pixhawk's avatar
pixhawk committed
}