qwt_plot_zoomer.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_plot_zoomer.h"
00011 #include "qwt_plot.h"
00012 #include "qwt_scale_div.h"
00013 #include "qwt_scale_map.h"
00014 #include "qwt_interval.h"
00015 #include "qwt_picker_machine.h"
00016 
00017 #include <qstack.h>
00018 
00019 static QwtInterval qwtExpandedZoomInterval( double v1, double v2,
00020     double minRange, const QwtTransform* transform )
00021 {
00022     double min = v1;
00023     double max = v2;
00024 
00025     if ( max - min < minRange )
00026     {
00027         min = 0.5 * ( min + max - minRange );
00028         max = min + minRange;
00029 
00030         if ( transform )
00031         {
00032             // f.e the logarithmic scale doesn't allow values
00033             // outside [QwtLogTransform::LogMin/QwtLogTransform::LogMax]
00034 
00035             double minBounded = transform->bounded( min );
00036             double maxBounded = transform->bounded( max );
00037 
00038             if ( minBounded != min )
00039             {
00040                 maxBounded = transform->bounded( minBounded + minRange );
00041             }
00042             else if ( maxBounded != max )
00043             {
00044                 minBounded = transform->bounded( maxBounded - minRange );
00045             }
00046 
00047             min = minBounded;
00048             max = maxBounded;
00049         }
00050     }
00051 
00052     return QwtInterval( min, max );
00053 }
00054 
00055 static QRectF qwtExpandedZoomRect( const QRectF &zoomRect, const QSizeF &minSize,
00056     const QwtTransform* transformX, const QwtTransform* transformY )
00057 {
00058     QRectF r = zoomRect;
00059 
00060     if ( minSize.width() > r.width() )
00061     {
00062         const QwtInterval intv = qwtExpandedZoomInterval(
00063             r.left(), r.right(), minSize.width(), transformX );
00064 
00065         r.setLeft( intv.minValue() );
00066         r.setRight( intv.maxValue() );
00067     }
00068 
00069     if ( minSize.height() > r.height() )
00070     {
00071         const QwtInterval intv = qwtExpandedZoomInterval(
00072             zoomRect.top(), zoomRect.bottom(), minSize.height(), transformY );
00073 
00074         r.setTop( intv.minValue() );
00075         r.setBottom( intv.maxValue() );
00076     }
00077 
00078     return r;
00079 }
00080 
00081 class QwtPlotZoomer::PrivateData
00082 {
00083 public:
00084     uint zoomRectIndex;
00085     QStack<QRectF> zoomStack;
00086 
00087     int maxStackDepth;
00088 };
00089 
00109 QwtPlotZoomer::QwtPlotZoomer( QWidget *canvas, bool doReplot ):
00110     QwtPlotPicker( canvas )
00111 {
00112     if ( canvas )
00113         init( doReplot );
00114 }
00115 
00133 QwtPlotZoomer::QwtPlotZoomer( int xAxis, int yAxis,
00134         QWidget *canvas, bool doReplot ):
00135     QwtPlotPicker( xAxis, yAxis, canvas )
00136 {
00137     if ( canvas )
00138         init( doReplot );
00139 }
00140 
00142 void QwtPlotZoomer::init( bool doReplot )
00143 {
00144     d_data = new PrivateData;
00145 
00146     d_data->maxStackDepth = -1;
00147 
00148     setTrackerMode( ActiveOnly );
00149     setRubberBand( RectRubberBand );
00150     setStateMachine( new QwtPickerDragRectMachine() );
00151 
00152     if ( doReplot && plot() )
00153         plot()->replot();
00154 
00155     setZoomBase( scaleRect() );
00156 }
00157 
00158 QwtPlotZoomer::~QwtPlotZoomer()
00159 {
00160     delete d_data;
00161 }
00162 
00174 void QwtPlotZoomer::setMaxStackDepth( int depth )
00175 {
00176     d_data->maxStackDepth = depth;
00177 
00178     if ( depth >= 0 )
00179     {
00180         // unzoom if the current depth is below d_data->maxStackDepth
00181 
00182         const int zoomOut =
00183             d_data->zoomStack.count() - 1 - depth; // -1 for the zoom base
00184 
00185         if ( zoomOut > 0 )
00186         {
00187             zoom( -zoomOut );
00188             for ( int i = d_data->zoomStack.count() - 1;
00189                 i > int( d_data->zoomRectIndex ); i-- )
00190             {
00191                 ( void )d_data->zoomStack.pop(); // remove trailing rects
00192             }
00193         }
00194     }
00195 }
00196 
00201 int QwtPlotZoomer::maxStackDepth() const
00202 {
00203     return d_data->maxStackDepth;
00204 }
00205 
00212 const QStack<QRectF> &QwtPlotZoomer::zoomStack() const
00213 {
00214     return d_data->zoomStack;
00215 }
00216 
00221 QRectF QwtPlotZoomer::zoomBase() const
00222 {
00223     return d_data->zoomStack[0];
00224 }
00225 
00235 void QwtPlotZoomer::setZoomBase( bool doReplot )
00236 {
00237     QwtPlot *plt = plot();
00238     if ( plt == NULL )
00239         return;
00240 
00241     if ( doReplot )
00242         plt->replot();
00243 
00244     d_data->zoomStack.clear();
00245     d_data->zoomStack.push( scaleRect() );
00246     d_data->zoomRectIndex = 0;
00247 
00248     rescale();
00249 }
00250 
00261 void QwtPlotZoomer::setZoomBase( const QRectF &base )
00262 {
00263     const QwtPlot *plt = plot();
00264     if ( !plt )
00265         return;
00266 
00267     const QRectF sRect = scaleRect();
00268     const QRectF bRect = base | sRect;
00269 
00270     d_data->zoomStack.clear();
00271     d_data->zoomStack.push( bRect );
00272     d_data->zoomRectIndex = 0;
00273 
00274     if ( base != sRect )
00275     {
00276         d_data->zoomStack.push( sRect );
00277         d_data->zoomRectIndex++;
00278     }
00279 
00280     rescale();
00281 }
00282 
00287 QRectF QwtPlotZoomer::zoomRect() const
00288 {
00289     return d_data->zoomStack[d_data->zoomRectIndex];
00290 }
00291 
00295 uint QwtPlotZoomer::zoomRectIndex() const
00296 {
00297     return d_data->zoomRectIndex;
00298 }
00299 
00310 void QwtPlotZoomer::zoom( const QRectF &rect )
00311 {
00312     if ( d_data->maxStackDepth >= 0 &&
00313             int( d_data->zoomRectIndex ) >= d_data->maxStackDepth )
00314     {
00315         return;
00316     }
00317 
00318     const QRectF zoomRect = rect.normalized();
00319     if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
00320     {
00321         for ( uint i = d_data->zoomStack.count() - 1;
00322            i > d_data->zoomRectIndex; i-- )
00323         {
00324             ( void )d_data->zoomStack.pop();
00325         }
00326 
00327         d_data->zoomStack.push( zoomRect );
00328         d_data->zoomRectIndex++;
00329 
00330         rescale();
00331 
00332         Q_EMIT zoomed( zoomRect );
00333     }
00334 }
00335 
00347 void QwtPlotZoomer::zoom( int offset )
00348 {
00349     int newIndex;
00350 
00351     if ( offset == 0 )
00352     {
00353         newIndex = 0;
00354     }
00355     else
00356     {
00357         newIndex = d_data->zoomRectIndex + offset;
00358         newIndex = qBound( 0, newIndex, d_data->zoomStack.count() - 1 );
00359     }
00360 
00361     if ( newIndex != static_cast<int>( d_data->zoomRectIndex ) )
00362     {
00363         d_data->zoomRectIndex = newIndex;
00364         rescale();
00365         Q_EMIT zoomed( zoomRect() );
00366     }
00367 }
00368 
00383 void QwtPlotZoomer::setZoomStack(
00384     const QStack<QRectF> &zoomStack, int zoomRectIndex )
00385 {
00386     if ( zoomStack.isEmpty() )
00387         return;
00388 
00389     if ( d_data->maxStackDepth >= 0 &&
00390         zoomStack.count() > d_data->maxStackDepth )
00391     {
00392         return;
00393     }
00394 
00395     if ( zoomRectIndex < 0 || zoomRectIndex > zoomStack.count() )
00396         zoomRectIndex = zoomStack.count() - 1;
00397 
00398     const bool doRescale = zoomStack[zoomRectIndex] != zoomRect();
00399 
00400     d_data->zoomStack = zoomStack;
00401     d_data->zoomRectIndex = uint( zoomRectIndex );
00402 
00403     if ( doRescale )
00404     {
00405         rescale();
00406         Q_EMIT zoomed( zoomRect() );
00407     }
00408 }
00409 
00416 void QwtPlotZoomer::rescale()
00417 {
00418     QwtPlot *plt = plot();
00419     if ( !plt )
00420         return;
00421 
00422     const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
00423     if ( rect != scaleRect() )
00424     {
00425         const bool doReplot = plt->autoReplot();
00426         plt->setAutoReplot( false );
00427 
00428         double x1 = rect.left();
00429         double x2 = rect.right();
00430         if ( !plt->axisScaleDiv( xAxis() ).isIncreasing() )
00431             qSwap( x1, x2 );
00432 
00433         plt->setAxisScale( xAxis(), x1, x2 );
00434 
00435         double y1 = rect.top();
00436         double y2 = rect.bottom();
00437         if ( !plt->axisScaleDiv( yAxis() ).isIncreasing() )
00438             qSwap( y1, y2 );
00439 
00440         plt->setAxisScale( yAxis(), y1, y2 );
00441 
00442         plt->setAutoReplot( doReplot );
00443 
00444         plt->replot();
00445     }
00446 }
00447 
00455 void QwtPlotZoomer::setAxis( int xAxis, int yAxis )
00456 {
00457     if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
00458     {
00459         QwtPlotPicker::setAxis( xAxis, yAxis );
00460         setZoomBase( scaleRect() );
00461     }
00462 }
00463 
00474 void QwtPlotZoomer::widgetMouseReleaseEvent( QMouseEvent *me )
00475 {
00476     if ( mouseMatch( MouseSelect2, me ) )
00477         zoom( 0 );
00478     else if ( mouseMatch( MouseSelect3, me ) )
00479         zoom( -1 );
00480     else if ( mouseMatch( MouseSelect6, me ) )
00481         zoom( +1 );
00482     else
00483         QwtPlotPicker::widgetMouseReleaseEvent( me );
00484 }
00485 
00497 void QwtPlotZoomer::widgetKeyPressEvent( QKeyEvent *ke )
00498 {
00499     if ( !isActive() )
00500     {
00501         if ( keyMatch( KeyUndo, ke ) )
00502             zoom( -1 );
00503         else if ( keyMatch( KeyRedo, ke ) )
00504             zoom( +1 );
00505         else if ( keyMatch( KeyHome, ke ) )
00506             zoom( 0 );
00507     }
00508 
00509     QwtPlotPicker::widgetKeyPressEvent( ke );
00510 }
00511 
00520 void QwtPlotZoomer::moveBy( double dx, double dy )
00521 {
00522     const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
00523     moveTo( QPointF( rect.left() + dx, rect.top() + dy ) );
00524 }
00525 
00534 void QwtPlotZoomer::moveTo( const QPointF &pos )
00535 {
00536     double x = pos.x();
00537     double y = pos.y();
00538 
00539     if ( x < zoomBase().left() )
00540         x = zoomBase().left();
00541     if ( x > zoomBase().right() - zoomRect().width() )
00542         x = zoomBase().right() - zoomRect().width();
00543 
00544     if ( y < zoomBase().top() )
00545         y = zoomBase().top();
00546     if ( y > zoomBase().bottom() - zoomRect().height() )
00547         y = zoomBase().bottom() - zoomRect().height();
00548 
00549     if ( x != zoomRect().left() || y != zoomRect().top() )
00550     {
00551         d_data->zoomStack[d_data->zoomRectIndex].moveTo( x, y );
00552         rescale();
00553     }
00554 }
00555 
00567 bool QwtPlotZoomer::accept( QPolygon &pa ) const
00568 {
00569     if ( pa.count() < 2 )
00570         return false;
00571 
00572     QRect rect = QRect( pa.first(), pa.last() );
00573     rect = rect.normalized();
00574 
00575     const int minSize = 2;
00576     if ( rect.width() < minSize && rect.height() < minSize )
00577         return false;
00578 
00579     const int minZoomSize = 11;
00580 
00581     const QPoint center = rect.center();
00582     rect.setSize( rect.size().expandedTo( QSize( minZoomSize, minZoomSize ) ) );
00583     rect.moveCenter( center );
00584 
00585     pa.resize( 2 );
00586     pa[0] = rect.topLeft();
00587     pa[1] = rect.bottomRight();
00588 
00589     return true;
00590 }
00591 
00597 QSizeF QwtPlotZoomer::minZoomSize() const
00598 {
00599     return QSizeF( d_data->zoomStack[0].width() / 10e4,
00600         d_data->zoomStack[0].height() / 10e4 );
00601 }
00602 
00609 void QwtPlotZoomer::begin()
00610 {
00611     if ( d_data->maxStackDepth >= 0 )
00612     {
00613         if ( d_data->zoomRectIndex >= uint( d_data->maxStackDepth ) )
00614             return;
00615     }
00616 
00617     const QSizeF minSize = minZoomSize();
00618     if ( minSize.isValid() )
00619     {
00620         const QSizeF sz =
00621             d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
00622 
00623         if ( minSize.width() >= sz.width() &&
00624             minSize.height() >= sz.height() )
00625         {
00626             return;
00627         }
00628     }
00629 
00630     QwtPlotPicker::begin();
00631 }
00632 
00643 bool QwtPlotZoomer::end( bool ok )
00644 {
00645     ok = QwtPlotPicker::end( ok );
00646     if ( !ok )
00647         return false;
00648 
00649     QwtPlot *plot = QwtPlotZoomer::plot();
00650     if ( !plot )
00651         return false;
00652 
00653     const QPolygon &pa = selection();
00654     if ( pa.count() < 2 )
00655         return false;
00656 
00657     QRect rect = QRect( pa.first(), pa.last() );
00658     rect = rect.normalized();
00659 
00660     const QwtScaleMap xMap = plot->canvasMap( xAxis() );
00661     const QwtScaleMap yMap = plot->canvasMap( yAxis() );
00662 
00663     QRectF zoomRect = QwtScaleMap::invTransform( xMap, yMap, rect ).normalized();
00664 
00665     zoomRect = qwtExpandedZoomRect( zoomRect, minZoomSize(),
00666         xMap.transformation(), yMap.transformation() );
00667 
00668     zoom( zoomRect );
00669 
00670     return true;
00671 }


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