qwt_scale_draw.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 #include "qwt_scale_draw.h"
00011 #include "qwt_scale_div.h"
00012 #include "qwt_scale_map.h"
00013 #include "qwt_math.h"
00014 #include "qwt_painter.h"
00015 #include <qpen.h>
00016 #include <qpainter.h>
00017 #include <qmath.h>
00018 
00019 #if QT_VERSION < 0x040601
00020 #define qFastSin(x) qSin(x)
00021 #define qFastCos(x) qCos(x)
00022 #endif
00023 
00024 class QwtScaleDraw::PrivateData
00025 {
00026 public:
00027     PrivateData():
00028         len( 0 ),
00029         alignment( QwtScaleDraw::BottomScale ),
00030         labelAlignment( 0 ),
00031         labelRotation( 0.0 )
00032     {
00033     }
00034 
00035     QPointF pos;
00036     double len;
00037 
00038     Alignment alignment;
00039 
00040     Qt::Alignment labelAlignment;
00041     double labelRotation;
00042 };
00043 
00051 QwtScaleDraw::QwtScaleDraw()
00052 {
00053     d_data = new QwtScaleDraw::PrivateData;
00054     setLength( 100 );
00055 }
00056 
00058 QwtScaleDraw::~QwtScaleDraw()
00059 {
00060     delete d_data;
00061 }
00062 
00068 QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
00069 {
00070     return d_data->alignment;
00071 }
00072 
00081 void QwtScaleDraw::setAlignment( Alignment align )
00082 {
00083     d_data->alignment = align;
00084 }
00085 
00096 Qt::Orientation QwtScaleDraw::orientation() const
00097 {
00098     switch ( d_data->alignment )
00099     {
00100         case TopScale:
00101         case BottomScale:
00102             return Qt::Horizontal;
00103         case LeftScale:
00104         case RightScale:
00105         default:
00106             return Qt::Vertical;
00107     }
00108 }
00109 
00120 void QwtScaleDraw::getBorderDistHint( 
00121     const QFont &font, int &start, int &end ) const
00122 {
00123     start = 0;
00124     end = 1.0;
00125 
00126     if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
00127         return;
00128 
00129     const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
00130     if ( ticks.count() == 0 )
00131         return;
00132 
00133     // Find the ticks, that are mapped to the borders.
00134     // minTick is the tick, that is mapped to the top/left-most position
00135     // in widget coordinates.
00136 
00137     double minTick = ticks[0];
00138     double minPos = scaleMap().transform( minTick );
00139     double maxTick = minTick;
00140     double maxPos = minPos;
00141 
00142     for ( int i = 1; i < ticks.count(); i++ )
00143     {
00144         const double tickPos = scaleMap().transform( ticks[i] );
00145         if ( tickPos < minPos )
00146         {
00147             minTick = ticks[i];
00148             minPos = tickPos;
00149         }
00150         if ( tickPos > scaleMap().transform( maxTick ) )
00151         {
00152             maxTick = ticks[i];
00153             maxPos = tickPos;
00154         }
00155     }
00156 
00157     double e = 0.0;
00158     double s = 0.0;
00159     if ( orientation() == Qt::Vertical )
00160     {
00161         s = -labelRect( font, minTick ).top();
00162         s -= qAbs( minPos - qRound( scaleMap().p2() ) );
00163 
00164         e = labelRect( font, maxTick ).bottom();
00165         e -= qAbs( maxPos - scaleMap().p1() );
00166     }
00167     else
00168     {
00169         s = -labelRect( font, minTick ).left();
00170         s -= qAbs( minPos - scaleMap().p1() );
00171 
00172         e = labelRect( font, maxTick ).right();
00173         e -= qAbs( maxPos - scaleMap().p2() );
00174     }
00175 
00176     if ( s < 0.0 )
00177         s = 0.0;
00178     if ( e < 0.0 )
00179         e = 0.0;
00180 
00181     start = qCeil( s );
00182     end = qCeil( e );
00183 }
00184 
00195 int QwtScaleDraw::minLabelDist( const QFont &font ) const
00196 {
00197     if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
00198         return 0;
00199 
00200     const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
00201     if ( ticks.isEmpty() )
00202         return 0;
00203 
00204     const QFontMetrics fm( font );
00205 
00206     const bool vertical = ( orientation() == Qt::Vertical );
00207 
00208     QRectF bRect1;
00209     QRectF bRect2 = labelRect( font, ticks[0] );
00210     if ( vertical )
00211     {
00212         bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() );
00213     }
00214 
00215     double maxDist = 0.0;
00216 
00217     for ( int i = 1; i < ticks.count(); i++ )
00218     {
00219         bRect1 = bRect2;
00220         bRect2 = labelRect( font, ticks[i] );
00221         if ( vertical )
00222         {
00223             bRect2.setRect( -bRect2.bottom(), 0.0,
00224                 bRect2.height(), bRect2.width() );
00225         }
00226 
00227         double dist = fm.leading(); // space between the labels
00228         if ( bRect1.right() > 0 )
00229             dist += bRect1.right();
00230         if ( bRect2.left() < 0 )
00231             dist += -bRect2.left();
00232 
00233         if ( dist > maxDist )
00234             maxDist = dist;
00235     }
00236 
00237     double angle = qwtRadians( labelRotation() ); 
00238     if ( vertical )
00239         angle += M_PI / 2;
00240 
00241     const double sinA = qFastSin( angle ); // qreal -> double
00242     if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
00243         return qCeil( maxDist );
00244 
00245     const int fmHeight = fm.ascent() - 2;
00246 
00247     // The distance we need until there is
00248     // the height of the label font. This height is needed
00249     // for the neighbored label.
00250 
00251     double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
00252     if ( labelDist < 0 )
00253         labelDist = -labelDist;
00254 
00255     // For text orientations close to the scale orientation
00256 
00257     if ( labelDist > maxDist )
00258         labelDist = maxDist;
00259 
00260     // For text orientations close to the opposite of the
00261     // scale orientation
00262 
00263     if ( labelDist < fmHeight )
00264         labelDist = fmHeight;
00265 
00266     return qCeil( labelDist );
00267 }
00268 
00282 double QwtScaleDraw::extent( const QFont &font ) const
00283 {
00284     double d = 0;
00285 
00286     if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
00287     {
00288         if ( orientation() == Qt::Vertical )
00289             d = maxLabelWidth( font );
00290         else
00291             d = maxLabelHeight( font );
00292 
00293         if ( d > 0 )
00294             d += spacing();
00295     }
00296 
00297     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
00298     {
00299         d += maxTickLength();
00300     }
00301 
00302     if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
00303     {
00304         const double pw = qMax( 1, penWidth() );  // pen width can be zero
00305         d += pw;
00306     }
00307 
00308     d = qMax( d, minimumExtent() );
00309     return d;
00310 }
00311 
00320 int QwtScaleDraw::minLength( const QFont &font ) const
00321 {
00322     int startDist, endDist;
00323     getBorderDistHint( font, startDist, endDist );
00324 
00325     const QwtScaleDiv &sd = scaleDiv();
00326 
00327     const uint minorCount =
00328         sd.ticks( QwtScaleDiv::MinorTick ).count() +
00329         sd.ticks( QwtScaleDiv::MediumTick ).count();
00330     const uint majorCount =
00331         sd.ticks( QwtScaleDiv::MajorTick ).count();
00332 
00333     int lengthForLabels = 0;
00334     if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
00335         lengthForLabels = minLabelDist( font ) * majorCount;
00336 
00337     int lengthForTicks = 0;
00338     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
00339     {
00340         const double pw = qMax( 1, penWidth() );  // penwidth can be zero
00341         lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
00342     }
00343 
00344     return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
00345 }
00346 
00356 QPointF QwtScaleDraw::labelPosition( double value ) const
00357 {
00358     const double tval = scaleMap().transform( value );
00359     double dist = spacing();
00360     if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
00361         dist += qMax( 1, penWidth() );
00362 
00363     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
00364         dist += tickLength( QwtScaleDiv::MajorTick );
00365 
00366     double px = 0;
00367     double py = 0;
00368 
00369     switch ( alignment() )
00370     {
00371         case RightScale:
00372         {
00373             px = d_data->pos.x() + dist;
00374             py = tval;
00375             break;
00376         }
00377         case LeftScale:
00378         {
00379             px = d_data->pos.x() - dist;
00380             py = tval;
00381             break;
00382         }
00383         case BottomScale:
00384         {
00385             px = tval;
00386             py = d_data->pos.y() + dist;
00387             break;
00388         }
00389         case TopScale:
00390         {
00391             px = tval;
00392             py = d_data->pos.y() - dist;
00393             break;
00394         }
00395     }
00396 
00397     return QPointF( px, py );
00398 }
00399 
00409 void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const
00410 {
00411     if ( len <= 0 )
00412         return;
00413 
00414     const bool roundingAlignment = QwtPainter::roundingAlignment( painter );
00415 
00416     QPointF pos = d_data->pos;
00417 
00418     double tval = scaleMap().transform( value );
00419     if ( roundingAlignment )
00420         tval = qRound( tval );
00421 
00422     const int pw = penWidth();
00423     int a = 0;
00424     if ( pw > 1 && roundingAlignment )
00425         a = 1;
00426 
00427     switch ( alignment() )
00428     {
00429         case LeftScale:
00430         {
00431             double x1 = pos.x() + a;
00432             double x2 = pos.x() + a - pw - len;
00433             if ( roundingAlignment )
00434             {
00435                 x1 = qRound( x1 );
00436                 x2 = qRound( x2 );
00437             }
00438 
00439             QwtPainter::drawLine( painter, x1, tval, x2, tval );
00440             break;
00441         }
00442 
00443         case RightScale:
00444         {
00445             double x1 = pos.x();
00446             double x2 = pos.x() + pw + len;
00447             if ( roundingAlignment )
00448             {
00449                 x1 = qRound( x1 );
00450                 x2 = qRound( x2 );
00451             }
00452 
00453             QwtPainter::drawLine( painter, x1, tval, x2, tval );
00454             break;
00455         }
00456 
00457         case BottomScale:
00458         {
00459             double y1 = pos.y();
00460             double y2 = pos.y() + pw + len;
00461             if ( roundingAlignment )
00462             {
00463                 y1 = qRound( y1 );
00464                 y2 = qRound( y2 );
00465             }
00466 
00467             QwtPainter::drawLine( painter, tval, y1, tval, y2 );
00468             break;
00469         }
00470 
00471         case TopScale:
00472         {
00473             double y1 = pos.y() + a;
00474             double y2 = pos.y() - pw - len + a;
00475             if ( roundingAlignment )
00476             {
00477                 y1 = qRound( y1 );
00478                 y2 = qRound( y2 );
00479             }
00480 
00481             QwtPainter::drawLine( painter, tval, y1, tval, y2 );
00482             break;
00483         }
00484     }
00485 }
00486 
00493 void QwtScaleDraw::drawBackbone( QPainter *painter ) const
00494 {
00495     const bool doAlign = QwtPainter::roundingAlignment( painter );
00496 
00497     const QPointF &pos = d_data->pos;
00498     const double len = d_data->len;
00499     const int pw = qMax( penWidth(), 1 );
00500 
00501     // pos indicates a border not the center of the backbone line
00502     // so we need to shift its position depending on the pen width
00503     // and the alignment of the scale
00504 
00505     double off;
00506     if ( doAlign )
00507     {
00508         if ( alignment() == LeftScale || alignment() == TopScale )
00509             off = ( pw - 1 ) / 2;
00510         else
00511             off = pw / 2;
00512     }
00513     else
00514     {
00515         off = 0.5 * penWidth();
00516     }
00517 
00518     switch ( alignment() )
00519     {
00520         case LeftScale:
00521         {
00522             double x = pos.x() - off;
00523             if ( doAlign )
00524                 x = qRound( x );
00525 
00526             QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
00527             break;
00528         }
00529         case RightScale:
00530         {
00531             double x = pos.x() + off;
00532             if ( doAlign )
00533                 x = qRound( x );
00534 
00535             QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
00536             break;
00537         }
00538         case TopScale:
00539         {
00540             double y = pos.y() - off;
00541             if ( doAlign )
00542                 y = qRound( y );
00543 
00544             QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
00545             break;
00546         }
00547         case BottomScale:
00548         {
00549             double y = pos.y() + off;
00550             if ( doAlign )
00551                 y = qRound( y );
00552 
00553             QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
00554             break;
00555         }
00556     }
00557 }
00558 
00590 void QwtScaleDraw::move( const QPointF &pos )
00591 {
00592     d_data->pos = pos;
00593     updateMap();
00594 }
00595 
00600 QPointF QwtScaleDraw::pos() const
00601 {
00602     return d_data->pos;
00603 }
00604 
00615 void QwtScaleDraw::setLength( double length )
00616 {
00617 #if 0
00618     if ( length >= 0 && length < 10 )
00619         length = 10;
00620 
00621     // why should we accept negative lengths ???
00622     if ( length < 0 && length > -10 )
00623         length = -10;
00624 #else
00625     length = qMax( length, 10.0 );
00626 #endif
00627 
00628     d_data->len = length;
00629     updateMap();
00630 }
00631 
00636 double QwtScaleDraw::length() const
00637 {
00638     return d_data->len;
00639 }
00640 
00649 void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const
00650 {
00651     QwtText lbl = tickLabel( painter->font(), value );
00652     if ( lbl.isEmpty() )
00653         return;
00654 
00655     QPointF pos = labelPosition( value );
00656 
00657     QSizeF labelSize = lbl.textSize( painter->font() );
00658 
00659     const QTransform transform = labelTransformation( pos, labelSize );
00660 
00661     painter->save();
00662     painter->setWorldTransform( transform, true );
00663 
00664     lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
00665 
00666     painter->restore();
00667 }
00668 
00681 QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const
00682 {
00683     QwtText lbl = tickLabel( font, value );
00684     if ( lbl.isEmpty() )
00685         return QRect();
00686 
00687     const QPointF pos = labelPosition( value );
00688     QSizeF labelSize = lbl.textSize( font );
00689 
00690     const QTransform transform = labelTransformation( pos, labelSize );
00691     return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
00692 }
00693 
00704 QTransform QwtScaleDraw::labelTransformation(
00705     const QPointF &pos, const QSizeF &size ) const
00706 {
00707     QTransform transform;
00708     transform.translate( pos.x(), pos.y() );
00709     transform.rotate( labelRotation() );
00710 
00711     int flags = labelAlignment();
00712     if ( flags == 0 )
00713     {
00714         switch ( alignment() )
00715         {
00716             case RightScale:
00717             {
00718                 if ( flags == 0 )
00719                     flags = Qt::AlignRight | Qt::AlignVCenter;
00720                 break;
00721             }
00722             case LeftScale:
00723             {
00724                 if ( flags == 0 )
00725                     flags = Qt::AlignLeft | Qt::AlignVCenter;
00726                 break;
00727             }
00728             case BottomScale:
00729             {
00730                 if ( flags == 0 )
00731                     flags = Qt::AlignHCenter | Qt::AlignBottom;
00732                 break;
00733             }
00734             case TopScale:
00735             {
00736                 if ( flags == 0 )
00737                     flags = Qt::AlignHCenter | Qt::AlignTop;
00738                 break;
00739             }
00740         }
00741     }
00742 
00743     double x, y;
00744 
00745     if ( flags & Qt::AlignLeft )
00746         x = -size.width();
00747     else if ( flags & Qt::AlignRight )
00748         x = 0.0;
00749     else // Qt::AlignHCenter
00750         x = -( 0.5 * size.width() );
00751 
00752     if ( flags & Qt::AlignTop )
00753         y = -size.height();
00754     else if ( flags & Qt::AlignBottom )
00755         y = 0;
00756     else // Qt::AlignVCenter
00757         y = -( 0.5 * size.height() );
00758 
00759     transform.translate( x, y );
00760 
00761     return transform;
00762 }
00763 
00774 QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const
00775 {
00776     QwtText lbl = tickLabel( font, value );
00777     if ( lbl.isEmpty() )
00778         return QRectF( 0.0, 0.0, 0.0, 0.0 );
00779 
00780     const QPointF pos = labelPosition( value );
00781 
00782     const QSizeF labelSize = lbl.textSize( font );
00783     const QTransform transform = labelTransformation( pos, labelSize );
00784 
00785     QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
00786     br.translate( -pos.x(), -pos.y() );
00787 
00788     return br;
00789 }
00790 
00799 QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const
00800 {
00801     return labelRect( font, value ).size();
00802 }
00803 
00817 void QwtScaleDraw::setLabelRotation( double rotation )
00818 {
00819     d_data->labelRotation = rotation;
00820 }
00821 
00826 double QwtScaleDraw::labelRotation() const
00827 {
00828     return d_data->labelRotation;
00829 }
00830 
00856 void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
00857 {
00858     d_data->labelAlignment = alignment;
00859 }
00860 
00865 Qt::Alignment QwtScaleDraw::labelAlignment() const
00866 {
00867     return d_data->labelAlignment;
00868 }
00869 
00874 int QwtScaleDraw::maxLabelWidth( const QFont &font ) const
00875 {
00876     double maxWidth = 0.0;
00877 
00878     const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
00879     for ( int i = 0; i < ticks.count(); i++ )
00880     {
00881         const double v = ticks[i];
00882         if ( scaleDiv().contains( v ) )
00883         {
00884             const double w = labelSize( font, ticks[i] ).width();
00885             if ( w > maxWidth )
00886                 maxWidth = w;
00887         }
00888     }
00889 
00890     return qCeil( maxWidth );
00891 }
00892 
00897 int QwtScaleDraw::maxLabelHeight( const QFont &font ) const
00898 {
00899     double maxHeight = 0.0;
00900 
00901     const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
00902     for ( int i = 0; i < ticks.count(); i++ )
00903     {
00904         const double v = ticks[i];
00905         if ( scaleDiv().contains( v ) )
00906         {
00907             const double h = labelSize( font, ticks[i] ).height();
00908             if ( h > maxHeight )
00909                 maxHeight = h;
00910         }
00911     }
00912 
00913     return qCeil( maxHeight );
00914 }
00915 
00916 void QwtScaleDraw::updateMap()
00917 {
00918     const QPointF pos = d_data->pos;
00919     double len = d_data->len;
00920 
00921     QwtScaleMap &sm = scaleMap();
00922     if ( orientation() == Qt::Vertical )
00923         sm.setPaintInterval( pos.y() + len, pos.y() );
00924     else
00925         sm.setPaintInterval( pos.x(), pos.x() + len );
00926 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Wed Jul 3 2019 19:28:05