# qwt_plot_zoomer.cpp

    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 // vim: expandtab
    00011
    00012 #include <math.h>
    00013 #include "qwt_plot.h"
    00014 #include "qwt_plot_canvas.h"
    00015 #include "qwt_plot_zoomer.h"
    00016 #include "qwt_scale_div.h"
    00017 #if QT_VERSION < 0x040000
    00018 typedef QValueStack<QwtDoubleRect> QwtZoomStack;
    00019 #else
    00020 typedef QStack<QwtDoubleRect> QwtZoomStack;
    00021 #endif
    00022
    00023 class QwtPlotZoomer::PrivateData
    00024 {
    00025 public:
    00026     uint zoomRectIndex;
    00027     QwtZoomStack zoomStack;
    00028
    00029     int maxStackDepth;
    00030 };
    00031
    00051 QwtPlotZoomer::QwtPlotZoomer(QwtPlotCanvas *canvas, bool doReplot):
    00052     QwtPlotPicker(canvas)
    00053 {
    00054     if ( canvas )
    00055         init(RectSelection & ClickSelection, ActiveOnly, doReplot);
    00056 }
    00057
    00075 QwtPlotZoomer::QwtPlotZoomer(int xAxis, int yAxis,
    00076         QwtPlotCanvas *canvas, bool doReplot):
    00077     QwtPlotPicker(xAxis, yAxis, canvas)
    00078 {
    00079     if ( canvas )
    00080         init(RectSelection & ClickSelection, ActiveOnly, doReplot);
    00081 }
    00082
    00103 QwtPlotZoomer::QwtPlotZoomer(int xAxis, int yAxis, int selectionFlags,
    00104         DisplayMode trackerMode, QwtPlotCanvas *canvas, bool doReplot):
    00105     QwtPlotPicker(xAxis, yAxis, canvas)
    00106 {
    00107     if ( canvas )
    00108         init(selectionFlags, trackerMode, doReplot);
    00109 }
    00110
    00112 void QwtPlotZoomer::init(int selectionFlags,
    00113     DisplayMode trackerMode, bool doReplot)
    00114 {
    00115     d_data = new PrivateData;
    00116
    00117     d_data->maxStackDepth = -1;
    00118
    00119     setSelectionFlags(selectionFlags);
    00120     setTrackerMode(trackerMode);
    00121     setRubberBand(RectRubberBand);
    00122
    00123     if ( doReplot && plot() )
    00124         plot()->replot();
    00125
    00126     setZoomBase(scaleRect());
    00127 }
    00128
    00129 QwtPlotZoomer::~QwtPlotZoomer()
    00130 {
    00131     delete d_data;
    00132 }
    00133
    00145 void QwtPlotZoomer::setMaxStackDepth(int depth)
    00146 {
    00147     d_data->maxStackDepth = depth;
    00148
    00149     if ( depth >= 0 )
    00150     {
    00151         // unzoom if the current depth is below d_data->maxStackDepth
    00152
    00153         const int zoomOut =
    00154             int(d_data->zoomStack.count()) - 1 - depth; // -1 for the zoom base
    00155
    00156         if ( zoomOut > 0 )
    00157         {
    00158             zoom(-zoomOut);
    00159             for ( int i = int(d_data->zoomStack.count()) - 1;
    00160                 i > int(d_data->zoomRectIndex); i-- )
    00161             {
    00162                 (void)d_data->zoomStack.pop(); // remove trailing rects
    00163             }
    00164         }
    00165     }
    00166 }
    00167
    00172 int QwtPlotZoomer::maxStackDepth() const
    00173 {
    00174     return d_data->maxStackDepth;
    00175 }
    00176
    00183 const QwtZoomStack &QwtPlotZoomer::zoomStack() const
    00184 {
    00185     return d_data->zoomStack;
    00186 }
    00187
    00192 QwtDoubleRect QwtPlotZoomer::zoomBase() const
    00193 {
    00194     return d_data->zoomStack[0];
    00195 }
    00196
    00206 void QwtPlotZoomer::setZoomBase(bool doReplot)
    00207 {
    00208     QwtPlot *plt = plot();
    00209     if ( plt == NULL )
    00210         return;
    00211
    00212     if ( doReplot )
    00213         plt->replot();
    00214
    00215     d_data->zoomStack.clear();
    00216     d_data->zoomStack.push(scaleRect());
    00217     d_data->zoomRectIndex = 0;
    00218
    00219     rescale();
    00220 }
    00221
    00232 void QwtPlotZoomer::setZoomBase(const QwtDoubleRect &base)
    00233 {
    00234     const QwtPlot *plt = plot();
    00235     if ( !plt )
    00236         return;
    00237
    00238     const QwtDoubleRect sRect = scaleRect();
    00239     const QwtDoubleRect bRect = base | sRect;
    00240
    00241     d_data->zoomStack.clear();
    00242     d_data->zoomStack.push(bRect);
    00243     d_data->zoomRectIndex = 0;
    00244
    00245     if ( base != sRect )
    00246     {
    00247         d_data->zoomStack.push(sRect);
    00248         d_data->zoomRectIndex++;
    00249     }
    00250
    00251     rescale();
    00252 }
    00253
    00259 QwtDoubleRect QwtPlotZoomer::zoomRect() const
    00260 {
    00261     return d_data->zoomStack[d_data->zoomRectIndex];
    00262 }
    00263
    00267 uint QwtPlotZoomer::zoomRectIndex() const
    00268 {
    00269     return d_data->zoomRectIndex;
    00270 }
    00271
    00283 void QwtPlotZoomer::zoom(const QwtDoubleRect &rect)
    00284 {
    00285     if ( d_data->maxStackDepth >= 0 &&
    00286         int(d_data->zoomRectIndex) >= d_data->maxStackDepth )
    00287     {
    00288         return;
    00289     }
    00290
    00291     const QwtDoubleRect zoomRect = d_data->zoomStack[0] & rect.normalized();
    00292     if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
    00293     {
    00294         for ( uint i = int(d_data->zoomStack.count()) - 1;
    00295            i > d_data->zoomRectIndex; i-- )
    00296         {
    00297             (void)d_data->zoomStack.pop();
    00298         }
    00299
    00300         d_data->zoomStack.push(zoomRect);
    00301         d_data->zoomRectIndex++;
    00302
    00303         rescale();
    00304
    00305         emit zoomed(zoomRect);
    00306     }
    00307 }
    00308
    00320 void QwtPlotZoomer::zoom(int offset)
    00321 {
    00322     if ( offset == 0 )
    00323         d_data->zoomRectIndex = 0;
    00324     else
    00325     {
    00326         int newIndex = d_data->zoomRectIndex + offset;
    00327         newIndex = qwtMax(0, newIndex);
    00328         newIndex = qwtMin(int(d_data->zoomStack.count()) - 1, newIndex);
    00329
    00330         d_data->zoomRectIndex = uint(newIndex);
    00331     }
    00332
    00333     rescale();
    00334
    00335     emit zoomed(zoomRect());
    00336 }
    00337
    00352 void QwtPlotZoomer::setZoomStack(
    00353     const QwtZoomStack &zoomStack, int zoomRectIndex)
    00354 {
    00355     if ( zoomStack.isEmpty() )
    00356         return;
    00357
    00358     if ( d_data->maxStackDepth >= 0 &&
    00359         int(zoomStack.count()) > d_data->maxStackDepth )
    00360     {
    00361         return;
    00362     }
    00363
    00364     if ( zoomRectIndex < 0 || zoomRectIndex > int(zoomStack.count()) )
    00365         zoomRectIndex = zoomStack.count() - 1;
    00366
    00367     const bool doRescale = zoomStack[zoomRectIndex] != zoomRect();
    00368
    00369     d_data->zoomStack = zoomStack;
    00370     d_data->zoomRectIndex = uint(zoomRectIndex);
    00371
    00372     if ( doRescale )
    00373     {
    00374         rescale();
    00375         emit zoomed(zoomRect());
    00376     }
    00377 }
    00378
    00385 void QwtPlotZoomer::rescale()
    00386 {
    00387     QwtPlot *plt = plot();
    00388     if ( !plt )
    00389         return;
    00390
    00391     const QwtDoubleRect &rect = d_data->zoomStack[d_data->zoomRectIndex];
    00392     if ( rect != scaleRect() )
    00393     {
    00394         const bool doReplot = plt->autoReplot();
    00395         plt->setAutoReplot(false);
    00396
    00397         double x1 = rect.left();
    00398         double x2 = rect.right();
    00399         if ( plt->axisScaleDiv(xAxis())->lBound() >
    00400             plt->axisScaleDiv(xAxis())->hBound() )
    00401         {
    00402             qSwap(x1, x2);
    00403         }
    00404
    00405         plt->setAxisScale(xAxis(), x1, x2);
    00406
    00407         double y1 = rect.top();
    00408         double y2 = rect.bottom();
    00409         if ( plt->axisScaleDiv(yAxis())->lBound() >
    00410             plt->axisScaleDiv(yAxis())->hBound() )
    00411         {
    00412             qSwap(y1, y2);
    00413         }
    00414         plt->setAxisScale(yAxis(), y1, y2);
    00415
    00416         plt->setAutoReplot(doReplot);
    00417
    00418         plt->replot();
    00419     }
    00420 }
    00421
    00429 void QwtPlotZoomer::setAxis(int xAxis, int yAxis)
    00430 {
    00431     if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
    00432     {
    00433         QwtPlotPicker::setAxis(xAxis, yAxis);
    00434         setZoomBase(scaleRect());
    00435     }
    00436 }
    00437
    00448 void QwtPlotZoomer::widgetMouseReleaseEvent(QMouseEvent *me)
    00449 {
    00450     if ( mouseMatch(MouseSelect2, me) )
    00451         zoom(0);
    00452     else if ( mouseMatch(MouseSelect3, me) )
    00453         zoom(-1);
    00454     else if ( mouseMatch(MouseSelect6, me) )
    00455         zoom(+1);
    00456     else
    00457         QwtPlotPicker::widgetMouseReleaseEvent(me);
    00458 }
    00459
    00471 void QwtPlotZoomer::widgetKeyPressEvent(QKeyEvent *ke)
    00472 {
    00473     if ( !isActive() )
    00474     {
    00475         if ( keyMatch(KeyUndo, ke) )
    00476             zoom(-1);
    00477         else if ( keyMatch(KeyRedo, ke) )
    00478             zoom(+1);
    00479         else if ( keyMatch(KeyHome, ke) )
    00480             zoom(0);
    00481     }
    00482
    00483     QwtPlotPicker::widgetKeyPressEvent(ke);
    00484 }
    00485
    00494 void QwtPlotZoomer::moveBy(double dx, double dy)
    00495 {
    00496     const QwtDoubleRect &rect = d_data->zoomStack[d_data->zoomRectIndex];
    00497     move(rect.left() + dx, rect.top() + dy);
    00498 }
    00499
    00509 void QwtPlotZoomer::move(double x, double y)
    00510 {
    00511     if ( x < zoomBase().left() )
    00512         x = zoomBase().left();
    00513     if ( x > zoomBase().right() - zoomRect().width() )
    00514         x = zoomBase().right() - zoomRect().width();
    00515
    00516     if ( y < zoomBase().top() )
    00517         y = zoomBase().top();
    00518     if ( y > zoomBase().bottom() - zoomRect().height() )
    00519         y = zoomBase().bottom() - zoomRect().height();
    00520
    00521     if ( x != zoomRect().left() || y != zoomRect().top() )
    00522     {
    00523         d_data->zoomStack[d_data->zoomRectIndex].moveTo(x, y);
    00524         rescale();
    00525     }
    00526 }
    00527
    00539 bool QwtPlotZoomer::accept(QwtPolygon &pa) const
    00540 {
    00541     if ( pa.count() < 2 )
    00542         return false;
    00543
    00544     QRect rect = QRect(pa[0], pa[int(pa.count()) - 1]);
    00545 #if QT_VERSION < 0x040000
    00546     rect = rect.normalize();
    00547 #else
    00548     rect = rect.normalized();
    00549 #endif
    00550
    00551     const int minSize = 2;
    00552     if (rect.width() < minSize && rect.height() < minSize )
    00553         return false;
    00554
    00555     const int minZoomSize = 11;
    00556
    00557     const QPoint center = rect.center();
    00558     rect.setSize(rect.size().expandedTo(QSize(minZoomSize, minZoomSize)));
    00559     rect.moveCenter(center);
    00560
    00561     pa.resize(2);
    00562     pa[0] = rect.topLeft();
    00563     pa[1] = rect.bottomRight();
    00564
    00565     return true;
    00566 }
    00567
    00573 QwtDoubleSize QwtPlotZoomer::minZoomSize() const
    00574 {
    00575     return QwtDoubleSize(
    00576         d_data->zoomStack[0].width() / 10e4,
    00577         d_data->zoomStack[0].height() / 10e4
    00578     );
    00579 }
    00580
    00587 void QwtPlotZoomer::begin()
    00588 {
    00589     if ( d_data->maxStackDepth >= 0 )
    00590     {
    00591         if ( d_data->zoomRectIndex >= uint(d_data->maxStackDepth) )
    00592             return;
    00593     }
    00594
    00595     const QwtDoubleSize minSize = minZoomSize();
    00596     if ( minSize.isValid() )
    00597     {
    00598         const QwtDoubleSize sz =
    00599             d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
    00600
    00601         if ( minSize.width() >= sz.width() &&
    00602             minSize.height() >= sz.height() )
    00603         {
    00604             return;
    00605         }
    00606     }
    00607
    00608     QwtPlotPicker::begin();
    00609 }
    00610
    00617 bool QwtPlotZoomer::end(bool ok)
    00618 {
    00619     ok = QwtPlotPicker::end(ok);
    00620     if (!ok)
    00621         return false;
    00622
    00623     QwtPlot *plot = QwtPlotZoomer::plot();
    00624     if ( !plot )
    00625         return false;
    00626
    00627     const QwtPolygon &pa = selection();
    00628     if ( pa.count() < 2 )
    00629         return false;
    00630
    00631     QRect rect = QRect(pa[0], pa[int(pa.count() - 1)]);
    00632 #if QT_VERSION < 0x040000
    00633     rect = rect.normalize();
    00634 #else
    00635     rect = rect.normalized();
    00636 #endif
    00637
    00638
    00639     QwtDoubleRect zoomRect = invTransform(rect).normalized();
    00640
    00641     const QwtDoublePoint center = zoomRect.center();
    00642     zoomRect.setSize(zoomRect.size().expandedTo(minZoomSize()));
    00643     zoomRect.moveCenter(center);
    00644
    00645     zoom(zoomRect);
    00646
    00647     return true;
    00648 }
    00649
    00661 void QwtPlotZoomer::setSelectionFlags(int flags)
    00662 {
    00663     // we accept only rects
    00664     flags &= ~(PointSelection | PolygonSelection);
    00665     flags |= RectSelection;
    00666
    00667     QwtPlotPicker::setSelectionFlags(flags);
    00668 }