Skip to content
Snippets Groups Projects
qwt_round_scale_draw.cpp 8.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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_round_scale_draw.h"
    
    pixhawk's avatar
    pixhawk committed
    #include "qwt_painter.h"
    #include "qwt_scale_div.h"
    #include "qwt_scale_map.h"
    
    Bryant's avatar
    Bryant committed
    #include "qwt_math.h"
    #include <qpen.h>
    #include <qpainter.h>
    #include <qfontmetrics.h>
    #include <qmath.h>
    
    pixhawk's avatar
    pixhawk committed
    
    class QwtRoundScaleDraw::PrivateData
    {
    public:
        PrivateData():
    
    Bryant's avatar
    Bryant committed
            center( 50.0, 50.0 ),
            radius( 50.0 ),
            startAngle( -135.0 ),
            endAngle( 135.0 )
        {
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        QPointF center;
        double radius;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        double startAngle;
        double endAngle;
    
    pixhawk's avatar
    pixhawk committed
    };
    
    /*!
      \brief Constructor
    
      The range of the scale is initialized to [0, 100],
      The center is set to (50, 50) with a radius of 50.
      The angle range is set to [-135, 135].
    */
    QwtRoundScaleDraw::QwtRoundScaleDraw()
    {
        d_data = new QwtRoundScaleDraw::PrivateData;
    
    
    Bryant's avatar
    Bryant committed
        setRadius( 50 );
        scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    //! Destructor
    QwtRoundScaleDraw::~QwtRoundScaleDraw()
    {
        delete d_data;
    }
    
    /*!
      Change of radius the scale
    
      Radius is the radius of the backbone without ticks and labels.
    
      \param radius New Radius
    
    Bryant's avatar
    Bryant committed
      \sa moveCenter()
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRoundScaleDraw::setRadius( double radius )
    
    pixhawk's avatar
    pixhawk committed
    {
        d_data->radius = radius;
    }
    
    /*!
    
      Get the radius
    
    pixhawk's avatar
    pixhawk committed
    
      Radius is the radius of the backbone without ticks and labels.
    
    
    Bryant's avatar
    Bryant committed
      \return Radius of the scale
    
    pixhawk's avatar
    pixhawk committed
      \sa setRadius(), extent()
    */
    
    Bryant's avatar
    Bryant committed
    double QwtRoundScaleDraw::radius() const
    
    pixhawk's avatar
    pixhawk committed
    {
        return d_data->radius;
    }
    
    /*!
       Move the center of the scale draw, leaving the radius unchanged
    
       \param center New center
    
    Bryant's avatar
    Bryant committed
       \sa setRadius()
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRoundScaleDraw::moveCenter( const QPointF &center )
    
    pixhawk's avatar
    pixhawk committed
    {
        d_data->center = center;
    }
    
    //! Get the center of the scale
    
    Bryant's avatar
    Bryant committed
    QPointF QwtRoundScaleDraw::center() const
    
    pixhawk's avatar
    pixhawk committed
    {
        return d_data->center;
    }
    
    /*!
      \brief Adjust the baseline circle segment for round scales.
    
      The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2).
      The default setting is [ -135, 135 ].
      An angle of 0 degrees corresponds to the 12 o'clock position,
      and positive angles count in a clockwise direction.
      \param angle1
      \param angle2 boundaries of the angle interval in degrees.
    
      \warning <ul>
    
    pixhawk's avatar
    pixhawk committed
      <li>The angle range is limited to [-360, 360] degrees. Angles exceeding
          this range will be clipped.
    
    Bryant's avatar
    Bryant committed
      <li>For angles more or equal than 360 degrees above or below min(angle1, angle2),
    
    pixhawk's avatar
    pixhawk committed
          scale marks will not be drawn.
    
    Bryant's avatar
    Bryant committed
      <li>If you need a counterclockwise scale, use QwtScaleDiv::setInterval()
    
    pixhawk's avatar
    pixhawk committed
      </ul>
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRoundScaleDraw::setAngleRange( double angle1, double angle2 )
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
    #if 0
        angle1 = qBound( -360.0, angle1, 360.0 );
        angle2 = qBound( -360.0, angle2, 360.0 );
    #endif
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        d_data->startAngle = angle1;
        d_data->endAngle = angle2;
    
    Bryant's avatar
    Bryant committed
        if ( d_data->startAngle == d_data->endAngle )
        {
    
    pixhawk's avatar
    pixhawk committed
            d_data->startAngle -= 1;
            d_data->endAngle += 1;
        }
    
    Bryant's avatar
    Bryant committed
        scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Draws the label for a major scale tick
    
       \param painter Painter
       \param value Value
    
       \sa drawTick(), drawBackbone()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRoundScaleDraw::drawLabel( QPainter *painter, double value ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        const QwtText label = tickLabel( painter->font(), value );
    
    pixhawk's avatar
    pixhawk committed
        if ( label.isEmpty() )
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const double tval = scaleMap().transform( value );
        if ( ( tval >= d_data->startAngle + 360.0 )
            || ( tval <= d_data->startAngle - 360.0 ) )
        {
    
    pixhawk's avatar
    pixhawk committed
        }
    
        double radius = d_data->radius;
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
            hasComponent( QwtAbstractScaleDraw::Backbone ) )
        {
    
    pixhawk's avatar
    pixhawk committed
            radius += spacing();
        }
    
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
            radius += tickLength( QwtScaleDiv::MajorTick );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QSizeF sz = label.textSize( painter->font() );
        const double arc = qwtRadians( tval );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const double x = d_data->center.x() +
            ( radius + sz.width() / 2.0 ) * qSin( arc );
        const double y = d_data->center.y() -
            ( radius + sz.height() / 2.0 ) * qCos( arc );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QRectF r( x - sz.width() / 2, y - sz.height() / 2,
            sz.width(), sz.height() );
        label.draw( painter, r );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Draw a tick
    
    pixhawk's avatar
    pixhawk committed
       \param painter Painter
       \param value Value of the tick
       \param len Lenght of the tick
    
    
       \sa drawBackbone(), drawLabel()
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRoundScaleDraw::drawTick( QPainter *painter, double value, double len ) const
    
    pixhawk's avatar
    pixhawk committed
    {
        if ( len <= 0 )
            return;
    
    
    Bryant's avatar
    Bryant committed
        const double tval = scaleMap().transform( value );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const double cx = d_data->center.x();
        const double cy = d_data->center.y();
        const double radius = d_data->radius;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        if ( ( tval < d_data->startAngle + 360.0 )
            || ( tval > d_data->startAngle - 360.0 ) )
        {
            const double arc = qwtRadians( tval );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
            const double sinArc = qSin( arc );
            const double cosArc = qCos( arc );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
            const double x1 = cx + radius * sinArc;
            const double x2 = cx + ( radius + len ) * sinArc;
            const double y1 = cy - radius * cosArc;
            const double y2 = cy - ( radius + len ) * cosArc;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
            QwtPainter::drawLine( painter, x1, y1, x2, y2 );
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    /*!
       Draws the baseline of the scale
       \param painter Painter
    
       \sa drawTick(), drawLabel()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRoundScaleDraw::drawBackbone( QPainter *painter ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        const double deg1 = scaleMap().p1();
        const double deg2 = scaleMap().p2();
    
        const int a1 = qRound( qMin( deg1, deg2 ) - 90 );
        const int a2 = qRound( qMax( deg1, deg2 ) - 90 );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const double radius = d_data->radius;
        const double x = d_data->center.x() - radius;
        const double y = d_data->center.y() - radius;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        painter->drawArc( QRectF( x, y, 2 * radius, 2 * radius ),
            -a2 * 16, ( a2 - a1 + 1 ) * 16 );          // counterclockwise
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Calculate the extent of the scale
    
    
    Bryant's avatar
    Bryant committed
       The extent is the distance between the baseline to the outermost
    
       pixel of the scale draw. radius() + extent() is an upper limit
    
    pixhawk's avatar
    pixhawk committed
       for the radius of the bounding circle.
    
       \param font Font used for painting the labels
    
    Bryant's avatar
    Bryant committed
       \return Calculated extent
    
    pixhawk's avatar
    pixhawk committed
    
       \sa setMinimumExtent(), minimumExtent()
    
    Bryant's avatar
    Bryant committed
       \warning The implemented algorithm is not too smart and
    
    pixhawk's avatar
    pixhawk committed
                calculates only an upper limit, that might be a
                few pixels too large
    */
    
    Bryant's avatar
    Bryant committed
    double QwtRoundScaleDraw::extent( const QFont &font ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        double d = 0.0;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
        {
    
    pixhawk's avatar
    pixhawk committed
            const QwtScaleDiv &sd = scaleDiv();
    
    Bryant's avatar
    Bryant committed
            const QList<double> &ticks = sd.ticks( QwtScaleDiv::MajorTick );
            for ( int i = 0; i < ticks.count(); i++ )
            {
    
    pixhawk's avatar
    pixhawk committed
                const double value = ticks[i];
    
    Bryant's avatar
    Bryant committed
                if ( !sd.contains( value ) )
    
    pixhawk's avatar
    pixhawk committed
                    continue;
    
    
    Bryant's avatar
    Bryant committed
                const QwtText label = tickLabel( font, value );
    
    pixhawk's avatar
    pixhawk committed
                if ( label.isEmpty() )
                    continue;
    
    Bryant's avatar
    Bryant committed
                const double tval = scaleMap().transform( value );
                if ( ( tval < d_data->startAngle + 360 )
                    && ( tval > d_data->startAngle - 360 ) )
                {
                    const double arc = qwtRadians( tval );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                    const QSizeF sz = label.textSize( font );
                    const double off = qMax( sz.width(), sz.height() );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                    double x = off * qSin( arc );
                    double y = off * qCos( arc );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                    const double dist = qSqrt( x * x + y * y );
    
    pixhawk's avatar
    pixhawk committed
                    if ( dist > d )
                        d = dist;
                }
            }
        }
    
    
    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
        if ( hasComponent( QwtAbstractScaleDraw::Labels ) &&
            ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
                hasComponent( QwtAbstractScaleDraw::Backbone ) ) )
        {
    
    pixhawk's avatar
    pixhawk committed
            d += spacing();
        }
    
    
    Bryant's avatar
    Bryant committed
        d = qMax( d, minimumExtent() );
    
    pixhawk's avatar
    pixhawk committed
    
        return d;
    }