00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <qpainter.h>
00013 #include "qwt_math.h"
00014 #include "qwt_painter.h"
00015 #include "qwt_scldraw.h"
00016
00017 static const double step_eps = 1.0e-6;
00018
00027 QwtScaleDraw::QwtScaleDraw():
00028 d_options(Backbone),
00029 d_hpad(4),
00030 d_vpad(4),
00031 d_medLen(6),
00032 d_majLen(8),
00033 d_minLen(4),
00034 d_minAngle(-135 * 16),
00035 d_maxAngle(135 * 16),
00036 d_fmt('g'),
00037 d_prec(4),
00038 d_fieldwidth(0),
00039 d_labelAlignment(0),
00040 d_labelRotation(0.0)
00041 {
00042 setGeometry(0,0,100,Bottom);
00043 setScale(0,100,0,0,10);
00044 }
00045
00047 QwtScaleDraw::~QwtScaleDraw()
00048 {
00049 }
00050
00051 void QwtScaleDraw::setOptions(int opt)
00052 {
00053 d_options = opt;
00054 }
00055
00056 int QwtScaleDraw::options() const
00057 {
00058 return d_options;
00059 }
00060
00073 void QwtScaleDraw::setScale(double x1, double x2, int maxMajIntv,
00074 int maxMinIntv, double step, int logscale)
00075 {
00076 d_scldiv.rebuild( x1, x2, maxMajIntv, maxMinIntv, logscale, step, FALSE );
00077 setDblRange( d_scldiv.lBound(), d_scldiv.hBound(), d_scldiv.logScale());
00078 }
00079
00080
00085 void QwtScaleDraw::setScale(const QwtScaleDiv &sd)
00086 {
00087 d_scldiv = sd;
00088 setDblRange(d_scldiv.lBound(),d_scldiv.hBound(),d_scldiv.logScale());
00089 }
00090
00095 void QwtScaleDraw::draw(QPainter *p) const
00096 {
00097 int i;
00098
00099 for (i=0; i< d_scldiv.majCnt(); i++)
00100 {
00101 const double val = d_scldiv.majMark(i);
00102 drawTick(p, val, d_majLen);
00103 drawLabel(p, val);
00104 }
00105
00106 if (d_scldiv.logScale())
00107 {
00108 for (i=0; i< d_scldiv.minCnt(); i++)
00109 drawTick(p, d_scldiv.minMark(i), d_minLen);
00110 }
00111 else
00112 {
00113 const int kmax = d_scldiv.majCnt() - 1;
00114 if (kmax > 0)
00115 {
00116 double majTick = d_scldiv.majMark(0);
00117 double hval = majTick - 0.5 * d_scldiv.majStep();
00118
00119 int k = 0;
00120 for (i=0; i< d_scldiv.minCnt(); i++)
00121 {
00122 const double val = d_scldiv.minMark(i);
00123 if (val > majTick)
00124 {
00125 if (k < kmax)
00126 {
00127 k++;
00128 majTick = d_scldiv.majMark(k);
00129 }
00130 else
00131 {
00132 majTick += d_scldiv.majMark(kmax) + d_scldiv.majStep();
00133 }
00134 hval = majTick - 0.5 * d_scldiv.majStep();
00135
00136 }
00137 if (qwtAbs(val-hval) < step_eps * d_scldiv.majStep())
00138 drawTick(p, val, d_medLen);
00139 else
00140 drawTick(p, val, d_minLen);
00141 }
00142 }
00143 }
00144
00145 if ( options() & Backbone )
00146 drawBackbone(p);
00147 }
00148
00149
00151 void QwtScaleDraw::drawTick(QPainter *p, double val, int len) const
00152 {
00153 if ( len <= 0 )
00154 return;
00155
00156 const int tval = transform(val);
00157
00158 switch(d_orient)
00159 {
00160 case Left:
00161 QwtPainter::drawLine(p, d_xorg, tval, d_xorg - len, tval);
00162 break;
00163
00164 case Right:
00165 QwtPainter::drawLine(p, d_xorg, tval, d_xorg + len, tval);
00166 break;
00167
00168 case Bottom:
00169 QwtPainter::drawLine(p, tval, d_yorg, tval, d_yorg + len);
00170 break;
00171
00172 case Top:
00173 QwtPainter::drawLine(p, tval, d_yorg, tval, d_yorg - len);
00174 break;
00175
00176 case Round:
00177 if ((tval <= d_minAngle + 359 * 16)
00178 || (tval >= d_minAngle - 359 * 16))
00179 {
00180 const double arc = double(tval) / 16.0 * M_PI / 180.0;
00181 const int x1 = qwtInt(d_xCenter + sin(arc) * d_radius);
00182 const int x2 = qwtInt(d_xCenter + sin(arc)
00183 * (d_radius + double(len)));
00184 const int y1 = qwtInt(d_yCenter - cos(arc) * d_radius);
00185 const int y2 = qwtInt(d_yCenter - cos(arc)
00186 * (d_radius + double(len)));
00187
00188 QwtPainter::drawLine(p, x1, y1, x2, y2);
00189 }
00190 break;
00191 }
00192 }
00193
00195 void QwtScaleDraw::drawLabel(QPainter *p, double val) const
00196 {
00197 QPoint pos;
00198 int alignment;
00199 double rotation;
00200 labelPlacement(QFontMetrics(p->font()), val, pos, alignment, rotation);
00201
00202 if ( alignment )
00203 {
00204 const QString txt = label(val);
00205
00206 QWMatrix m = labelWorldMatrix(QFontMetrics(p->font()),
00207 pos, alignment, rotation, txt);
00208
00209 p->save();
00210 p->setWorldMatrix(m, TRUE);
00211 QwtPainter::drawText(p, 0, 0, txt);
00212 p->restore();
00213 }
00214 }
00215
00217 void QwtScaleDraw::labelPlacement( const QFontMetrics &fm, double val,
00218 QPoint &pos, int &alignment, double &rotation) const
00219 {
00220
00221 if ((!d_scldiv.logScale())
00222 && (qwtAbs(val) < qwtAbs(step_eps * d_scldiv.majStep())))
00223 {
00224 val = 0.0;
00225 }
00226
00227 const int tval = transform(val);
00228
00229 int x = 0;
00230 int y = 0;
00231 int align = 0;
00232
00233 switch(d_orient)
00234 {
00235 case Right:
00236 {
00237 x = d_xorg + d_majLen + d_hpad + 1;
00238 y = tval;
00239 align = d_labelAlignment;
00240 if ( align == 0 )
00241 align = Qt::AlignRight | Qt::AlignVCenter;
00242 break;
00243 }
00244 case Left:
00245 {
00246 x = d_xorg - d_majLen - d_hpad - 1;
00247 y = tval;
00248 align = d_labelAlignment;
00249 if ( align == 0 )
00250 align = Qt::AlignLeft | Qt::AlignVCenter;
00251 break;
00252 }
00253 case Bottom:
00254 {
00255 x = tval;
00256 y = d_yorg + d_majLen + d_vpad + 1;
00257 align = d_labelAlignment;
00258 if ( align == 0 )
00259 align = Qt::AlignHCenter | Qt::AlignBottom;
00260 break;
00261 }
00262 case Top:
00263 {
00264 x = tval;
00265 y = d_yorg - d_majLen - d_vpad - 1;
00266 align = d_labelAlignment;
00267 if ( align == 0 )
00268 align = Qt::AlignHCenter | Qt::AlignTop;
00269 break;
00270 }
00271 case Round:
00272 {
00273 if ((tval > d_minAngle + 359 * 16)
00274 || (tval < d_minAngle - 359 * 16))
00275 {
00276 break;
00277 }
00278
00279 const int fmh = fm.ascent() - 2;
00280 const double arc = tval / 16.0 / 360.0 * 2 * M_PI;
00281 const double radius = d_radius + d_majLen + d_vpad;
00282
00283
00284
00285
00286 double xOffset = ( radius + fmh / 2 ) * sin(arc);
00287 double yOffset = ( radius + fmh / 2 ) * cos(arc);
00288
00289 if ( qwtInt(xOffset) != 0 )
00290 {
00291
00292
00293
00294
00295
00296
00297 const int fmw = fm.width(label(val));
00298
00299 const double circleX = radius * sin(arc);
00300 if ( xOffset < 0 )
00301 xOffset = circleX - fmw / 2;
00302 else
00303 xOffset = circleX + fmw / 2;
00304 }
00305 x = qwtInt(d_xCenter + xOffset);
00306 y = qwtInt(d_yCenter - yOffset);
00307 align = Qt::AlignHCenter | Qt::AlignVCenter;
00308
00309 break;
00310 }
00311 }
00312
00313 pos = QPoint(x, y);
00314 alignment = align;
00315 rotation = d_labelRotation;
00316 }
00317
00319
00320 QWMatrix QwtScaleDraw::labelWorldMatrix(const QFontMetrics &fm,
00321 const QPoint &pos, int alignment, double rotation,
00322 const QString &txt) const
00323 {
00324 const int w = fm.boundingRect(0, 0,
00325 QCOORD_MAX, QCOORD_MAX, 0, txt).width() - 2;
00326 const int h = fm.ascent() - 2;
00327
00328 int x, y;
00329 if ( alignment & Qt::AlignLeft )
00330 x = -w;
00331 else if ( alignment & Qt::AlignRight )
00332 x = 0 - w % 2;
00333 else
00334 x = -(w / 2);
00335
00336 if ( alignment & Qt::AlignTop )
00337 y = 0;
00338 else if ( alignment & Qt::AlignBottom )
00339 y = h - 1;
00340 else
00341 y = h / 2;
00342
00343 QWMatrix m;
00344 m.translate(pos.x(), pos.y());
00345 m.rotate(rotation);
00346 m.translate(x, y);
00347
00348 return m;
00349 }
00350
00352 void QwtScaleDraw::drawBackbone(QPainter *p) const
00353 {
00354 const int bw2 = p->pen().width() / 2;
00355
00356 switch(d_orient)
00357 {
00358 case Left:
00359 QwtPainter::drawLine(p, d_xorg - bw2,
00360 d_yorg, d_xorg - bw2, d_yorg + d_len - 1);
00361 break;
00362 case Right:
00363 QwtPainter::drawLine(p, d_xorg + bw2,
00364 d_yorg, d_xorg + bw2, d_yorg + d_len - 1);
00365 break;
00366 case Top:
00367 QwtPainter::drawLine(p, d_xorg, d_yorg - bw2,
00368 d_xorg + d_len - 1, d_yorg - bw2);
00369 break;
00370 case Bottom:
00371 QwtPainter::drawLine(p, d_xorg, d_yorg + bw2,
00372 d_xorg + d_len - 1, d_yorg + bw2);
00373 break;
00374 case Round:
00375 {
00376 const int a1 = qwtMin(i1(), i2()) - 90 * 16;
00377 const int a2 = qwtMax(i1(), i2()) - 90 * 16;
00378
00379 p->drawArc(d_xorg, d_yorg, d_len, d_len,
00380 -a2, a2 - a1 + 1);
00381 break;
00382 }
00383 }
00384 }
00385
00386
00426 void QwtScaleDraw::setGeometry(int xorigin, int yorigin,
00427 int length, Orientation o)
00428 {
00429 static int minLen = 10;
00430
00431 d_xorg = xorigin;
00432 d_yorg = yorigin;
00433 d_radius = double(length) * 0.5;
00434 d_xCenter = double(xorigin) + double(length) * 0.5;
00435 d_yCenter = double(yorigin) + double(length) * 0.5;
00436
00437 if (length > minLen)
00438 d_len = length;
00439 else
00440 d_len = minLen;
00441
00442 d_orient = o;
00443
00444 switch(d_orient)
00445 {
00446 case Left:
00447 case Right:
00448 setIntRange(d_yorg + d_len - 1, d_yorg);
00449 break;
00450 case Round:
00451 setIntRange(d_minAngle, d_maxAngle);
00452 break;
00453 case Top:
00454 case Bottom:
00455 setIntRange(d_xorg, d_xorg + d_len - 1);
00456 break;
00457 }
00458 }
00459
00465 int QwtScaleDraw::maxWidth(const QPen &pen, const QFontMetrics &fm) const
00466 {
00467 int w = 0;
00468
00469 switch (d_orient)
00470 {
00471 case Left:
00472 case Right:
00473 w += pen.width() + d_majLen + d_hpad + maxLabelWidth(fm);
00474 break;
00475 case Round:
00476 w += pen.width() + d_majLen + d_vpad + maxLabelWidth(fm);
00477 break;
00478 case Top:
00479 case Bottom:
00480 w = d_len + maxLabelWidth(fm);
00481 break;
00482 }
00483 return w;
00484 }
00485
00491 int QwtScaleDraw::maxHeight(const QPen &pen, const QFontMetrics &fm) const
00492 {
00493 int h = 0;
00494
00495 switch (d_orient)
00496 {
00497 case Top:
00498 case Bottom:
00499 h = pen.width() + d_vpad + d_majLen + maxLabelHeight(fm);
00500 break;
00501 case Left:
00502 case Right:
00503 h = d_len + maxLabelHeight(fm);
00504 break;
00505 case Round:
00506 h = d_vpad + d_majLen + (fm.ascent() - 2);
00507 break;
00508 }
00509
00510 return h;
00511 }
00512
00522 void QwtScaleDraw::setLabelRotation(double rotation)
00523 {
00524 d_labelRotation = rotation;
00525 }
00526
00531 double QwtScaleDraw::labelRotation() const
00532 {
00533 return d_labelRotation;
00534 }
00535
00555 void QwtScaleDraw::setLabelAlignment(int alignment)
00556 {
00557 d_labelAlignment = alignment;
00558 }
00559
00564 int QwtScaleDraw::labelAlignment() const
00565 {
00566 return d_labelAlignment;
00567 }
00568
00587 void QwtScaleDraw::setAngleRange(double angle1, double angle2)
00588 {
00589 angle1 = qwtLim(angle1, -360.0, 360.0);
00590 angle2 = qwtLim(angle2, -360.0, 360.0);
00591
00592 int amin = int(floor (qwtMin(angle1, angle2) * 16.0 + 0.5));
00593 int amax = int(floor (qwtMax(angle1, angle2) * 16.0 + 0.5));
00594
00595 if (amin == amax)
00596 {
00597 amin -= 1;
00598 amax += 1;
00599 }
00600
00601 d_minAngle = amin;
00602 d_maxAngle = amax;
00603 setIntRange(d_minAngle, d_maxAngle);
00604 }
00605
00616 void QwtScaleDraw::setLabelFormat(char f, int prec, int fieldwidth)
00617 {
00618 d_fmt = f;
00619 d_prec = prec;
00620 d_fieldwidth = fieldwidth;
00621 }
00622
00631 void QwtScaleDraw::labelFormat(char &f, int &prec, int &fieldwidth) const
00632 {
00633 f = d_fmt;
00634 prec = d_prec;
00635 fieldwidth = d_fieldwidth;
00636 }
00637
00641 void QwtScaleDraw::setTickLength(unsigned int minLen,
00642 unsigned int medLen, unsigned int majLen)
00643 {
00644 const unsigned int maxTickLen = 1000;
00645
00646 d_minLen = QMIN(minLen, maxTickLen);
00647 d_medLen = QMIN(medLen, maxTickLen);
00648 d_majLen = QMIN(majLen, maxTickLen);
00649 }
00650
00655 void QwtScaleDraw::tickLength(unsigned int &minLen,
00656 unsigned int &medLen, unsigned int &majLen) const
00657 {
00658 minLen = d_minLen;
00659 medLen = d_medLen;
00660 majLen = d_majLen;
00661 }
00662
00667 unsigned int QwtScaleDraw::majTickLength() const
00668 {
00669 return d_majLen;
00670 }
00671
00676 int QwtScaleDraw::maxLabelWidth(const QFontMetrics &fm) const
00677 {
00678 int maxWidth = 0;
00679
00680 for (int i = 0; i < d_scldiv.majCnt(); i++)
00681 {
00682 double val = d_scldiv.majMark(i);
00683
00684
00685
00686 if ((!d_scldiv.logScale())
00687 && (qwtAbs(val) < step_eps * qwtAbs(d_scldiv.majStep())))
00688 {
00689 val = 0.0;
00690 }
00691
00692 const int w = labelBoundingRect(fm, val).width();
00693 if ( w > maxWidth )
00694 maxWidth = w;
00695 }
00696
00697 return maxWidth;
00698 }
00699
00704 int QwtScaleDraw::maxLabelHeight(const QFontMetrics &fm) const
00705 {
00706 int maxHeight = 0;
00707
00708 for (int i = 0; i < d_scldiv.majCnt(); i++)
00709 {
00710 double val = d_scldiv.majMark(i);
00711
00712
00713
00714 if ((!d_scldiv.logScale())
00715 && (qwtAbs(val) < step_eps * qwtAbs(d_scldiv.majStep())))
00716 {
00717 val = 0.0;
00718 }
00719
00720 const int h = labelBoundingRect(fm, val).height();
00721 if ( h > maxHeight )
00722 maxHeight = h;
00723 }
00724
00725 return maxHeight;
00726 }
00727
00733 QRect QwtScaleDraw::labelBoundingRect(
00734 const QFontMetrics &fm, double val) const
00735 {
00736 QString zeroString;
00737 if ( d_fieldwidth > 0 )
00738 zeroString.fill('0', d_fieldwidth);
00739
00740 const QString lbl = label(val);
00741
00742 const QString &txt = fm.width(zeroString) > fm.width(lbl)
00743 ? zeroString : lbl;
00744 if ( txt.isEmpty() )
00745 return QRect(0, 0, 0, 0);
00746
00747 QRect br;
00748
00749 QPoint pos;
00750 int alignment;
00751 double rotation;
00752
00753 labelPlacement(fm, val, pos, alignment, rotation);
00754 if ( alignment )
00755 {
00756
00757 const int w = fm.boundingRect(0, 0,
00758 QCOORD_MAX, QCOORD_MAX, 0, txt).width();
00759 const int h = -(fm.ascent() - 2);
00760
00761 QWMatrix m = labelWorldMatrix(fm, pos, alignment, rotation, txt);
00762 br = QwtPainter::map(m, QRect(0, 0, w, h));
00763 br.moveBy(-pos.x(), -pos.y());
00764 }
00765
00766 return br;
00767 }
00768
00779 void QwtScaleDraw::minBorderDist(const QFontMetrics &fm,
00780 int &start, int &end ) const
00781 {
00782 const QRect labelRectMin = labelBoundingRect(fm, d_scldiv.majMark(0));
00783 const QRect labelRectMax = labelBoundingRect(fm,
00784 d_scldiv.majMark(d_scldiv.majCnt() - 1));
00785
00786 start = 0;
00787 end = 0;
00788
00789 if ( d_scldiv.majCnt() > 0 )
00790 {
00791 switch (d_orient)
00792 {
00793 case Left:
00794 case Right:
00795 end = -labelRectMin.y();
00796 start = labelRectMax.height() + labelRectMax.y();
00797 break;
00798 case Top:
00799 case Bottom:
00800 start = -labelRectMin.x();
00801 end = labelRectMax.width() + labelRectMax.x();
00802 break;
00803 case Round:
00804 start = labelRectMin.width();
00805 end = labelRectMax.width();
00806 break;
00807 }
00808 }
00809 }
00810
00820 int QwtScaleDraw::minLabelDist(const QFontMetrics &fm) const
00821 {
00822 if ( d_orient == Round )
00823 return 0;
00824
00825 const bool vertical = (d_orient == Left || d_orient == Right);
00826
00827 QRect bRect1, bRect2;
00828 int maxDist = 0;
00829 for ( int i = 0; i < d_scldiv.majCnt() - 1; i++ )
00830 {
00831 if ( i == 0 )
00832 {
00833 bRect1 = labelBoundingRect(fm, d_scldiv.majMark(0));
00834 if ( vertical )
00835 {
00836 bRect1.setRect(-bRect1.bottom(), 0,
00837 bRect1.height(), bRect1.width());
00838 }
00839 }
00840 else
00841 bRect1 = bRect2;
00842
00843 bRect2 = labelBoundingRect(fm, d_scldiv.majMark(i + 1));
00844 if ( vertical )
00845 {
00846 bRect2.setRect(-bRect2.bottom(), 0,
00847 bRect2.height(), bRect2.width());
00848 }
00849
00850 int dist = fm.leading();
00851 if ( bRect1.right() > 0 )
00852 dist += bRect1.right();
00853 if ( bRect2.left() < 0 )
00854 dist += -bRect2.left();
00855
00856 if ( dist > maxDist )
00857 maxDist = dist;
00858 }
00859
00860 double angle = d_labelRotation / 180.0 * M_PI;
00861 if ( vertical )
00862 angle += M_PI / 2;
00863
00864 if ( sin(angle) == 0.0 )
00865 return maxDist;
00866
00867 const int fmHeight = fm.ascent() - 2;
00868
00869
00870
00871
00872
00873 int labelDist = (int)(fmHeight / sin(angle) * cos(angle));
00874 if ( labelDist < 0 )
00875 labelDist = -labelDist;
00876
00877
00878 labelDist++;
00879
00880
00881
00882 if ( labelDist > maxDist )
00883 labelDist = maxDist;
00884
00885
00886
00887
00888 if ( labelDist < fmHeight )
00889 labelDist = fmHeight;
00890
00891 return labelDist;
00892 }
00893
00900 int QwtScaleDraw::minHeight( const QPen &pen, const QFontMetrics &fm ) const
00901 {
00902 const int pw = QMAX( 1, pen.width() );
00903
00904 int h = 0;
00905 switch ( d_orient )
00906 {
00907 case Left:
00908 case Right:
00909 {
00910 int bottomDist, topDist;
00911 minBorderDist(fm, bottomDist, topDist);
00912
00913 h = bottomDist + topDist +
00914 minLabelDist(fm) * (d_scldiv.majCnt() - 1);
00915
00916 int th = 2 * (d_scldiv.majCnt() + d_scldiv.minCnt()) * pw;
00917 if ( th > h )
00918 h = th;
00919 break;
00920 }
00921 case Round:
00922
00923 h = pw + d_vpad + d_majLen + maxLabelWidth(fm);
00924 break;
00925 case Top:
00926 case Bottom:
00927 h = pw + d_vpad + d_majLen + maxLabelHeight(fm);
00928 break;
00929 }
00930 return h;
00931 }
00932
00939 int QwtScaleDraw::minWidth( const QPen &pen, const QFontMetrics &fm ) const
00940 {
00941 const int pw = QMAX( 1, pen.width() );
00942
00943 int w = 0;
00944
00945 switch(d_orient)
00946 {
00947 case Left:
00948 case Right:
00949 {
00950 w = pw + d_hpad + d_majLen + maxLabelWidth(fm);
00951 break;
00952 }
00953 case Round:
00954 {
00955 w = pw + d_vpad + d_majLen + maxLabelWidth(fm);
00956 break;
00957 }
00958 case Top:
00959 case Bottom:
00960 {
00961 int leftDist, rightDist;
00962 minBorderDist(fm, leftDist, rightDist);
00963
00964 w = leftDist + rightDist +
00965 minLabelDist(fm) * (d_scldiv.majCnt() - 1);
00966
00967 int tw = 2 * (d_scldiv.majCnt() + d_scldiv.minCnt()) * pw;
00968 if ( tw > w )
00969 w = tw;
00970
00971 break;
00972 }
00973 }
00974 return w;
00975 }
00976
00985 QString QwtScaleDraw::label(double value) const
00986 {
00987 #if 1
00988 if ( value == -0 )
00989 value = 0;
00990 #endif
00991
00992 QString fmt;
00993 fmt.sprintf("%%%d.%d%c", d_fieldwidth, d_prec, d_fmt);
00994
00995 QString text;
00996 text.sprintf(fmt, value);
00997
00998 return text;
00999 }
01000
01002 int QwtScaleDraw::x() const
01003 {
01004 return d_xorg;
01005 }
01006
01008 int QwtScaleDraw::y() const
01009 {
01010 return d_yorg;
01011 }
01013 int QwtScaleDraw::length() const
01014 {
01015 return d_len;
01016 }
01017
01019 QwtScaleDraw::Orientation QwtScaleDraw::orientation() const
01020 {
01021 return d_orient;
01022 }