Skip to content
qwt_round_scale_draw.cpp 8.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_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;
}