# 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
    00181 const QwtZoomStack &QwtPlotZoomer::zoomStack() const
    00182 {
    00183     return d_data->zoomStack;
    00184 }
    00185
    00190 QwtDoubleRect QwtPlotZoomer::zoomBase() const
    00191 {
    00192     return d_data->zoomStack[0];
    00193 }
    00194
    00208 void QwtPlotZoomer::setZoomBase()
    00209 {
    00210     const QwtPlot *plt = plot();
    00211     if ( !plt )
    00212         return;
    00213
    00214     d_data->zoomStack.clear();
    00215     d_data->zoomStack.push(scaleRect());
    00216     d_data->zoomRectIndex = 0;
    00217
    00218     rescale();
    00219 }
    00220
    00231 void QwtPlotZoomer::setZoomBase(const QwtDoubleRect &base)
    00232 {
    00233     const QwtPlot *plt = plot();
    00234     if ( !plt )
    00235         return;
    00236
    00237     const QwtDoubleRect sRect = scaleRect();
    00238     const QwtDoubleRect bRect = base | sRect;
    00239
    00240     d_data->zoomStack.clear();
    00241     d_data->zoomStack.push(bRect);
    00242     d_data->zoomRectIndex = 0;
    00243
    00244     if ( base != sRect )
    00245     {
    00246         d_data->zoomStack.push(sRect);
    00247         d_data->zoomRectIndex++;
    00248     }
    00249
    00250     rescale();
    00251 }
    00252
    00258 QwtDoubleRect QwtPlotZoomer::zoomRect() const
    00259 {
    00260     return d_data->zoomStack[d_data->zoomRectIndex];
    00261 }
    00262
    00266 uint QwtPlotZoomer::zoomRectIndex() const
    00267 {
    00268     return d_data->zoomRectIndex;
    00269 }
    00270
    00282 void QwtPlotZoomer::zoom(const QwtDoubleRect &rect)
    00283 {
    00284     if ( d_data->maxStackDepth >= 0 &&
    00285         int(d_data->zoomRectIndex) >= d_data->maxStackDepth )
    00286     {
    00287         return;
    00288     }
    00289
    00290     const QwtDoubleRect zoomRect = d_data->zoomStack[0] & rect.normalized();
    00291     if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
    00292     {
    00293         for ( uint i = int(d_data->zoomStack.count()) - 1;
    00294            i > d_data->zoomRectIndex; i-- )
    00295         {
    00296             (void)d_data->zoomStack.pop();
    00297         }
    00298
    00299         d_data->zoomStack.push(zoomRect);
    00300         d_data->zoomRectIndex++;
    00301
    00302         rescale();
    00303
    00304         emit zoomed(zoomRect);
    00305     }
    00306 }
    00307
    00319 void QwtPlotZoomer::zoom(int offset)
    00320 {
    00321     if ( offset == 0 )
    00322         d_data->zoomRectIndex = 0;
    00323     else
    00324     {
    00325         int newIndex = d_data->zoomRectIndex + offset;
    00326         newIndex = qwtMax(0, newIndex);
    00327         newIndex = qwtMin(int(d_data->zoomStack.count()) - 1, newIndex);
    00328
    00329         d_data->zoomRectIndex = uint(newIndex);
    00330     }
    00331
    00332     rescale();
    00333
    00334     emit zoomed(zoomRect());
    00335 }
    00336
    00343 void QwtPlotZoomer::rescale()
    00344 {
    00345     QwtPlot *plt = plot();
    00346     if ( !plt )
    00347         return;
    00348
    00349     const QwtDoubleRect &rect = d_data->zoomStack[d_data->zoomRectIndex];
    00350     if ( rect != scaleRect() )
    00351     {
    00352         const bool doReplot = plt->autoReplot();
    00353         plt->setAutoReplot(false);
    00354
    00355         double x1 = rect.left();
    00356         double x2 = rect.right();
    00357         if ( plt->axisScaleDiv(xAxis())->lBound() >
    00358             plt->axisScaleDiv(xAxis())->hBound() )
    00359         {
    00360             qSwap(x1, x2);
    00361         }
    00362
    00363         plt->setAxisScale(xAxis(), x1, x2);
    00364
    00365         double y1 = rect.top();
    00366         double y2 = rect.bottom();
    00367         if ( plt->axisScaleDiv(yAxis())->lBound() >
    00368             plt->axisScaleDiv(yAxis())->hBound() )
    00369         {
    00370             qSwap(y1, y2);
    00371         }
    00372         plt->setAxisScale(yAxis(), y1, y2);
    00373
    00374         plt->setAutoReplot(doReplot);
    00375
    00376         plt->replot();
    00377     }
    00378 }
    00379
    00387 void QwtPlotZoomer::setAxis(int xAxis, int yAxis)
    00388 {
    00389     if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
    00390     {
    00391         QwtPlotPicker::setAxis(xAxis, yAxis);
    00392         setZoomBase(scaleRect());
    00393     }
    00394 }
    00395
    00406 void QwtPlotZoomer::widgetMouseReleaseEvent(QMouseEvent *me)
    00407 {
    00408     if ( mouseMatch(MouseSelect2, me) )
    00409         zoom(0);
    00410     else if ( mouseMatch(MouseSelect3, me) )
    00411         zoom(-1);
    00412     else if ( mouseMatch(MouseSelect6, me) )
    00413         zoom(+1);
    00414     else
    00415         QwtPlotPicker::widgetMouseReleaseEvent(me);
    00416 }
    00417
    00429 void QwtPlotZoomer::widgetKeyPressEvent(QKeyEvent *ke)
    00430 {
    00431     if ( !isActive() )
    00432     {
    00433         if ( keyMatch(KeyUndo, ke) )
    00434             zoom(-1);
    00435         else if ( keyMatch(KeyRedo, ke) )
    00436             zoom(+1);
    00437         else if ( keyMatch(KeyHome, ke) )
    00438             zoom(0);
    00439     }
    00440
    00441     QwtPlotPicker::widgetKeyPressEvent(ke);
    00442 }
    00443
    00452 void QwtPlotZoomer::moveBy(double dx, double dy)
    00453 {
    00454     const QwtDoubleRect &rect = d_data->zoomStack[d_data->zoomRectIndex];
    00455     move(rect.left() + dx, rect.top() + dy);
    00456 }
    00457
    00467 void QwtPlotZoomer::move(double x, double y)
    00468 {
    00469     if ( x < zoomBase().left() )
    00470         x = zoomBase().left();
    00471     if ( x > zoomBase().right() - zoomRect().width() )
    00472         x = zoomBase().right() - zoomRect().width();
    00473
    00474     if ( y < zoomBase().top() )
    00475         y = zoomBase().top();
    00476     if ( y > zoomBase().bottom() - zoomRect().height() )
    00477         y = zoomBase().bottom() - zoomRect().height();
    00478
    00479     if ( x != zoomRect().left() || y != zoomRect().top() )
    00480     {
    00481         d_data->zoomStack[d_data->zoomRectIndex].moveTo(x, y);
    00482         rescale();
    00483     }
    00484 }
    00485
    00497 bool QwtPlotZoomer::accept(QwtPolygon &pa) const
    00498 {
    00499     if ( pa.count() < 2 )
    00500         return false;
    00501
    00502     QRect rect = QRect(pa[0], pa[int(pa.count()) - 1]);
    00503 #if QT_VERSION < 0x040000
    00504     rect = rect.normalize();
    00505 #else
    00506     rect = rect.normalized();
    00507 #endif
    00508
    00509     const int minSize = 2;
    00510     if (rect.width() < minSize && rect.height() < minSize )
    00511         return false;
    00512
    00513     const int minZoomSize = 11;
    00514
    00515     const QPoint center = rect.center();
    00516     rect.setSize(rect.size().expandedTo(QSize(minZoomSize, minZoomSize)));
    00517     rect.moveCenter(center);
    00518
    00519     pa.resize(2);
    00520     pa[0] = rect.topLeft();
    00521     pa[1] = rect.bottomRight();
    00522
    00523     return true;
    00524 }
    00525
    00531 QwtDoubleSize QwtPlotZoomer::minZoomSize() const
    00532 {
    00533     return QwtDoubleSize(
    00534         d_data->zoomStack[0].width() / 10e4,
    00535         d_data->zoomStack[0].height() / 10e4
    00536     );
    00537 }
    00538
    00545 void QwtPlotZoomer::begin()
    00546 {
    00547     if ( d_data->maxStackDepth >= 0 )
    00548     {
    00549         if ( d_data->zoomRectIndex >= uint(d_data->maxStackDepth) )
    00550             return;
    00551     }
    00552
    00553     const QwtDoubleSize minSize = minZoomSize();
    00554     if ( minSize.isValid() )
    00555     {
    00556         const QwtDoubleSize sz =
    00557             d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
    00558
    00559         if ( minSize.width() >= sz.width() &&
    00560             minSize.height() >= sz.height() )
    00561         {
    00562             return;
    00563         }
    00564     }
    00565
    00566     QwtPlotPicker::begin();
    00567 }
    00568
    00575 bool QwtPlotZoomer::end(bool ok)
    00576 {
    00577     ok = QwtPlotPicker::end(ok);
    00578     if (!ok)
    00579         return false;
    00580
    00581     QwtPlot *plot = QwtPlotZoomer::plot();
    00582     if ( !plot )
    00583         return false;
    00584
    00585     const QwtPolygon &pa = selection();
    00586     if ( pa.count() < 2 )
    00587         return false;
    00588
    00589     QRect rect = QRect(pa[0], pa[int(pa.count() - 1)]);
    00590 #if QT_VERSION < 0x040000
    00591     rect = rect.normalize();
    00592 #else
    00593     rect = rect.normalized();
    00594 #endif
    00595
    00596
    00597     QwtDoubleRect zoomRect = invTransform(rect).normalized();
    00598
    00599     const QwtDoublePoint center = zoomRect.center();
    00600     zoomRect.setSize(zoomRect.size().expandedTo(minZoomSize()));
    00601     zoomRect.moveCenter(center);
    00602
    00603     zoom(zoomRect);
    00604
    00605     return true;
    00606 }
    00607
    00619 void QwtPlotZoomer::setSelectionFlags(int flags)
    00620 {
    00621     // we accept only rects
    00622     flags &= ~(PointSelection | PolygonSelection);
    00623     flags |= RectSelection;
    00624
    00625     QwtPlotPicker::setSelectionFlags(flags);
    00626 }