123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904 |
- /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
- * Qwt Widget Library
- * Copyright (C) 1997 Josef Wilgen
- * Copyright (C) 2002 Uwe Rathmann
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the Qwt License, Version 1.0
- *****************************************************************************/
- #include "qwt_scale_draw.h"
- #include "qwt_scale_div.h"
- #include "qwt_scale_map.h"
- #include "qwt_math.h"
- #include "qwt_painter.h"
- #include <qpen.h>
- #include <qpainter.h>
- #include <qmath.h>
- class QwtScaleDraw::PrivateData
- {
- public:
- PrivateData():
- len( 0 ),
- alignment( QwtScaleDraw::BottomScale ),
- labelAlignment( 0 ),
- labelRotation( 0.0 )
- {
- }
- QPointF pos;
- double len;
- 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;
- setLength( 100 );
- }
- //! Destructor
- QwtScaleDraw::~QwtScaleDraw()
- {
- delete d_data;
- }
- /*!
- Return alignment of the scale
- \sa setAlignment()
- */
- QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
- {
- return d_data->alignment;
- }
- /*!
- Set the alignment of the scale
- The default alignment is QwtScaleDraw::BottomScale
- \sa alignment()
- */
- void QwtScaleDraw::setAlignment( Alignment align )
- {
- d_data->alignment = align;
- }
- /*!
- Return the orientation
- TopScale, BottomScale are horizontal (Qt::Horizontal) scales,
- LeftScale, RightScale are vertical (Qt::Vertical) scales.
- \sa alignment()
- */
- Qt::Orientation QwtScaleDraw::orientation() const
- {
- switch ( d_data->alignment )
- {
- case TopScale:
- case BottomScale:
- return Qt::Horizontal;
- case LeftScale:
- case RightScale:
- default:
- return Qt::Vertical;
- }
- }
- /*!
- \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
- */
- void QwtScaleDraw::getBorderDistHint( const QFont &font,
- int &start, int &end ) const
- {
- start = 0;
- end = 0;
- if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
- return;
- const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
- if ( ticks.count() == 0 )
- return;
- // 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 = map().transform( minTick );
- double maxTick = minTick;
- double maxPos = minPos;
- for ( uint i = 1; i < ( uint )ticks.count(); i++ )
- {
- const double tickPos = map().transform( ticks[i] );
- if ( tickPos < minPos )
- {
- minTick = ticks[i];
- minPos = tickPos;
- }
- if ( tickPos > map().transform( maxTick ) )
- {
- maxTick = ticks[i];
- maxPos = tickPos;
- }
- }
- double e = 0.0;
- double s = 0.0;
- if ( orientation() == Qt::Vertical )
- {
- s = -labelRect( font, minTick ).top();
- s -= qAbs( minPos - qRound( map().p2() ) );
- e = labelRect( font, maxTick ).bottom();
- e -= qAbs( maxPos - map().p1() );
- }
- else
- {
- s = -labelRect( font, minTick ).left();
- s -= qAbs( minPos - map().p1() );
- e = labelRect( font, maxTick ).right();
- e -= qAbs( maxPos - map().p2() );
- }
- if ( s < 0.0 )
- s = 0.0;
- if ( e < 0.0 )
- e = 0.0;
- start = qCeil( s );
- end = qCeil( e );
- }
- /*!
- 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()
- */
- int QwtScaleDraw::minLabelDist( const QFont &font ) const
- {
- if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
- return 0;
- const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
- if ( ticks.count() == 0 )
- return 0;
- const QFontMetrics fm( font );
- const bool vertical = ( orientation() == Qt::Vertical );
- QRectF bRect1;
- QRectF bRect2 = labelRect( font, ticks[0] );
- if ( vertical )
- {
- bRect2.setRect( -bRect2.bottom(), 0, bRect2.height(), bRect2.width() );
- }
- int maxDist = 0;
- for ( uint i = 1; i < ( uint )ticks.count(); i++ )
- {
- bRect1 = bRect2;
- bRect2 = labelRect( font, ticks[i] );
- if ( vertical )
- {
- bRect2.setRect( -bRect2.bottom(), 0,
- bRect2.height(), bRect2.width() );
- }
- int dist = fm.leading(); // space between the labels
- if ( bRect1.right() > 0 )
- dist += bRect1.right();
- if ( bRect2.left() < 0 )
- dist += -bRect2.left();
- if ( dist > maxDist )
- maxDist = dist;
- }
- double angle = labelRotation() / 180.0 * M_PI;
- if ( vertical )
- angle += M_PI / 2;
- if ( qSin( angle ) == 0.0 )
- return maxDist;
- const int fmHeight = fm.ascent() - 2;
- // The distance we need until there is
- // the height of the label font. This height is needed
- // for the neighbour labal.
- int labelDist = ( int )( fmHeight / qSin( angle ) * qCos( angle ) );
- if ( labelDist < 0 )
- labelDist = -labelDist;
- // The cast above floored labelDist. We want to ceil.
- labelDist++;
- // For text orientations close to the scale orientation
- if ( labelDist > maxDist )
- labelDist = maxDist;
- // For text orientations close to the opposite of the
- // scale orientation
- if ( labelDist < fmHeight )
- labelDist = fmHeight;
- return labelDist;
- }
- /*!
- 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
- \sa minLength()
- */
- double QwtScaleDraw::extent( const QFont &font ) const
- {
- double d = 0;
- if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
- {
- if ( orientation() == Qt::Vertical )
- d = maxLabelWidth( font );
- else
- d = maxLabelHeight( font );
- if ( d > 0 )
- d += spacing();
- }
- if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
- {
- d += majTickLength();
- }
- if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
- {
- const double pw = qMax( 1, penWidth() ); // penwidth can be zero
- d += pw;
- }
- d = qMax( d, minimumExtent() );
- return d;
- }
- /*!
- Calculate the minimum length that is needed to draw the scale
- \param font Font used for painting the labels
- \sa extent()
- */
- int QwtScaleDraw::minLength( const QFont &font ) const
- {
- int startDist, endDist;
- getBorderDistHint( font, startDist, endDist );
- const QwtScaleDiv &sd = scaleDiv();
- const uint minorCount =
- sd.ticks( QwtScaleDiv::MinorTick ).count() +
- sd.ticks( QwtScaleDiv::MediumTick ).count();
- const uint majorCount =
- sd.ticks( QwtScaleDiv::MajorTick ).count();
- int lengthForLabels = 0;
- if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
- {
- if ( majorCount >= 2 )
- lengthForLabels = minLabelDist( font ) * ( majorCount - 1 );
- }
- int lengthForTicks = 0;
- if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
- {
- const double pw = qMax( 1, penWidth() ); // penwidth can be zero
- lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
- }
- return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
- }
- /*!
- Find the position, where to paint a label
- The position has a distance of majTickLength() + spacing() + 1
- from the backbone. The direction depends on the alignment()
- \param value Value
- */
- QPointF QwtScaleDraw::labelPosition( double value ) const
- {
- const double tval = map().transform( value );
- double dist = spacing();
- if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
- dist += qMax( 1, penWidth() );
- if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
- dist += majTickLength();
- 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;
- }
- }
- return QPointF( px, py );
- }
- /*!
- Draw a tick
- \param painter Painter
- \param value Value of the tick
- \param len Lenght of the tick
- \sa drawBackbone(), drawLabel()
- */
- void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const
- {
- if ( len <= 0 )
- return;
- const bool roundingAlignment = QwtPainter::roundingAlignment( painter );
- QwtScaleMap scaleMap = map();
- QPointF pos = d_data->pos;
- double tval = scaleMap.transform( value );
- if ( roundingAlignment )
- tval = qRound( tval );
- const int pw = penWidth();
- int a = 0;
- if ( pw > 1 && roundingAlignment )
- a = 1;
- switch ( alignment() )
- {
- case LeftScale:
- {
- double x1 = pos.x() + a;
- double x2 = pos.x() + a - pw - len;
- if ( roundingAlignment )
- {
- x1 = qRound( x1 );
- x2 = qRound( x2 );
- }
- 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;
- }
- }
- }
- /*!
- Draws the baseline of the scale
- \param painter Painter
- \sa drawTick(), drawLabel()
- */
- void QwtScaleDraw::drawBackbone( QPainter *painter ) const
- {
- 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;
- }
- }
- }
- /*!
- \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
- 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.
- 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.
- 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
- Scale marks and labels are drawn
- below the backbone.
- </dl>
- \param pos Origin of the scale
- \sa pos(), setLength()
- */
- void QwtScaleDraw::move( const QPointF &pos )
- {
- d_data->pos = pos;
- updateMap();
- }
- /*!
- \return Origin of the scale
- \sa move(), length()
- */
- QPointF QwtScaleDraw::pos() const
- {
- return d_data->pos;
- }
- /*!
- Set the length of the backbone.
- The length doesn't include the space needed for
- overlapping labels.
- \sa move(), minLabelDist()
- */
- void QwtScaleDraw::setLength( double length )
- {
- if ( length >= 0 && length < 10 )
- length = 10;
- if ( length < 0 && length > -10 )
- length = -10;
- d_data->len = length;
- updateMap();
- }
- /*!
- \return the length of the backbone
- \sa setLength(), pos()
- */
- double QwtScaleDraw::length() const
- {
- return d_data->len;
- }
- /*!
- Draws the label for a major scale tick
- \param painter Painter
- \param value Value
- \sa drawTick(), drawBackbone(), boundingLabelRect()
- */
- void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const
- {
- QwtText lbl = tickLabel( painter->font(), value );
- if ( lbl.isEmpty() )
- return;
- QPointF pos = labelPosition( value );
- QSizeF labelSize = lbl.textSize( painter->font() );
- const QTransform transform = labelTransformation( pos, labelSize );
- painter->save();
- painter->setWorldTransform( transform, true );
- lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
- painter->restore();
- }
- /*!
- Find the bounding rect for the label. The coordinates of
- the rect are absolute coordinates ( calculated from pos() ).
- in direction of the tick.
- \param font Font used for painting
- \param value Value
- \sa labelRect()
- */
- QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const
- {
- QwtText lbl = tickLabel( font, value );
- if ( lbl.isEmpty() )
- return QRect();
- const QPointF pos = labelPosition( value );
- QSizeF labelSize = lbl.textSize( font );
- const QTransform transform = labelTransformation( pos, labelSize );
- return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
- }
- /*!
- Calculate the transformation that is needed to paint a label
- depending on its alignment and rotation.
- \param pos Position where to paint the label
- \param size Size of the label
- \sa setLabelAlignment(), setLabelRotation()
- */
- QTransform QwtScaleDraw::labelTransformation(
- const QPointF &pos, const QSizeF &size ) const
- {
- QTransform transform;
- transform.translate( pos.x(), pos.y() );
- transform.rotate( labelRotation() );
- int flags = labelAlignment();
- 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;
- }
- }
- }
- double x, y;
- if ( flags & Qt::AlignLeft )
- x = -size.width();
- else if ( flags & Qt::AlignRight )
- x = 0.0;
- else // Qt::AlignHCenter
- x = -( 0.5 * size.width() );
- if ( flags & Qt::AlignTop )
- y = -size.height();
- else if ( flags & Qt::AlignBottom )
- y = 0;
- else // Qt::AlignVCenter
- y = -( 0.5 * size.height() );
- transform.translate( x, y );
- return transform;
- }
- /*!
- Find the bounding rect for the label. The coordinates of
- the rect are relative to spacing + ticklength from the backbone
- in direction of the tick.
- \param font Font used for painting
- \param value Value
- */
- QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const
- {
- QwtText lbl = tickLabel( font, value );
- if ( lbl.isEmpty() )
- return QRectF( 0.0, 0.0, 0.0, 0.0 );
- const QPointF pos = labelPosition( value );
- const QSizeF labelSize = lbl.textSize( font );
- const QTransform transform = labelTransformation( pos, labelSize );
- QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
- br.translate( -pos.x(), -pos.y() );
- return br;
- }
- /*!
- Calculate the size that is needed to draw a label
- \param font Label font
- \param value Value
- */
- QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const
- {
- return labelRect( font, value ).size();
- }
- /*!
- 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().
- */
- void QwtScaleDraw::setLabelRotation( double rotation )
- {
- d_data->labelRotation = rotation;
- }
- /*!
- \return the label rotation
- \sa setLabelRotation(), labelAlignment()
- */
- double QwtScaleDraw::labelRotation() const
- {
- return d_data->labelRotation;
- }
- /*!
- \brief Change the label flags
- Labels are aligned to the point ticklength + spacing away from the backbone.
- 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:
- 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
- Changing the alignment is often necessary for rotated labels.
- \param alignment Or'd Qt::AlignmentFlags <see qnamespace.h>
- \sa setLabelRotation(), labelRotation(), labelAlignment()
- \warning The various alignments might be confusing.
- The alignment of the label is not the alignment
- of the scale and is not the alignment of the flags
- (QwtText::flags()) returned from QwtAbstractScaleDraw::label().
- */
- void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
- {
- d_data->labelAlignment = alignment;
- }
- /*!
- \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
- */
- int QwtScaleDraw::maxLabelWidth( const QFont &font ) const
- {
- int maxWidth = 0;
- const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
- for ( uint i = 0; i < ( uint )ticks.count(); i++ )
- {
- const double v = ticks[i];
- if ( scaleDiv().contains( v ) )
- {
- const int w = labelSize( font, ticks[i] ).width();
- if ( w > maxWidth )
- maxWidth = w;
- }
- }
- return maxWidth;
- }
- /*!
- \param font Font
- \return the maximum height of a label
- */
- int QwtScaleDraw::maxLabelHeight( const QFont &font ) const
- {
- int maxHeight = 0;
- const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
- for ( uint i = 0; i < ( uint )ticks.count(); i++ )
- {
- const double v = ticks[i];
- if ( scaleDiv().contains( v ) )
- {
- const int h = labelSize( font, ticks[i] ).height();
- if ( h > maxHeight )
- maxHeight = h;
- }
- }
- return maxHeight;
- }
- void QwtScaleDraw::updateMap()
- {
- const QPointF pos = d_data->pos;
- double len = d_data->len;
- QwtScaleMap &sm = scaleMap();
- if ( orientation() == Qt::Vertical )
- sm.setPaintInterval( pos.y() + len, pos.y() );
- else
- sm.setPaintInterval( pos.x(), pos.x() + len );
- }
|