00001
00002
00003
00004
00005
00006
00007
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
00134
00135
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();
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 );
00242 if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
00243 return qCeil( maxDist );
00244
00245 const int fmHeight = fm.ascent() - 2;
00246
00247
00248
00249
00250
00251 double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
00252 if ( labelDist < 0 )
00253 labelDist = -labelDist;
00254
00255
00256
00257 if ( labelDist > maxDist )
00258 labelDist = maxDist;
00259
00260
00261
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() );
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() );
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
00502
00503
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
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
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
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 }