00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_plot_zoomer.h"
00011 #include "qwt_plot.h"
00012 #include "qwt_scale_div.h"
00013 #include "qwt_picker_machine.h"
00014 #include <qalgorithms.h>
00015
00016 static QwtInterval qwtExpandedZoomInterval( double v1, double v2,
00017 double minRange, const QwtTransform* transform )
00018 {
00019 double min = v1;
00020 double max = v2;
00021
00022 if ( max - min < minRange )
00023 {
00024 min = 0.5 * ( min + max - minRange );
00025 max = min + minRange;
00026
00027 if ( transform )
00028 {
00029
00030
00031
00032 double minBounded = transform->bounded( min );
00033 double maxBounded = transform->bounded( max );
00034
00035 if ( minBounded != min )
00036 {
00037 maxBounded = transform->bounded( minBounded + minRange );
00038 }
00039 else if ( maxBounded != max )
00040 {
00041 minBounded = transform->bounded( maxBounded - minRange );
00042 }
00043
00044 min = minBounded;
00045 max = maxBounded;
00046 }
00047 }
00048
00049 return QwtInterval( min, max );
00050 }
00051
00052 static QRectF qwtExpandedZoomRect( const QRectF &zoomRect, const QSizeF &minSize,
00053 const QwtTransform* transformX, const QwtTransform* transformY )
00054 {
00055 QRectF r = zoomRect;
00056
00057 if ( minSize.width() > r.width() )
00058 {
00059 const QwtInterval intv = qwtExpandedZoomInterval(
00060 r.left(), r.right(), minSize.width(), transformX );
00061
00062 r.setLeft( intv.minValue() );
00063 r.setRight( intv.maxValue() );
00064 }
00065
00066 if ( minSize.height() > r.height() )
00067 {
00068 const QwtInterval intv = qwtExpandedZoomInterval(
00069 zoomRect.top(), zoomRect.bottom(), minSize.height(), transformY );
00070
00071 r.setTop( intv.minValue() );
00072 r.setBottom( intv.maxValue() );
00073 }
00074
00075 return r;
00076 }
00077
00078 class QwtPlotZoomer::PrivateData
00079 {
00080 public:
00081 uint zoomRectIndex;
00082 QStack<QRectF> zoomStack;
00083
00084 int maxStackDepth;
00085 };
00086
00106 QwtPlotZoomer::QwtPlotZoomer( QWidget *canvas, bool doReplot ):
00107 QwtPlotPicker( canvas )
00108 {
00109 if ( canvas )
00110 init( doReplot );
00111 }
00112
00130 QwtPlotZoomer::QwtPlotZoomer( int xAxis, int yAxis,
00131 QWidget *canvas, bool doReplot ):
00132 QwtPlotPicker( xAxis, yAxis, canvas )
00133 {
00134 if ( canvas )
00135 init( doReplot );
00136 }
00137
00139 void QwtPlotZoomer::init( bool doReplot )
00140 {
00141 d_data = new PrivateData;
00142
00143 d_data->maxStackDepth = -1;
00144
00145 setTrackerMode( ActiveOnly );
00146 setRubberBand( RectRubberBand );
00147 setStateMachine( new QwtPickerDragRectMachine() );
00148
00149 if ( doReplot && plot() )
00150 plot()->replot();
00151
00152 setZoomBase( scaleRect() );
00153 }
00154
00155 QwtPlotZoomer::~QwtPlotZoomer()
00156 {
00157 delete d_data;
00158 }
00159
00171 void QwtPlotZoomer::setMaxStackDepth( int depth )
00172 {
00173 d_data->maxStackDepth = depth;
00174
00175 if ( depth >= 0 )
00176 {
00177
00178
00179 const int zoomOut =
00180 int( d_data->zoomStack.count() ) - 1 - depth;
00181
00182 if ( zoomOut > 0 )
00183 {
00184 zoom( -zoomOut );
00185 for ( int i = int( d_data->zoomStack.count() ) - 1;
00186 i > int( d_data->zoomRectIndex ); i-- )
00187 {
00188 ( void )d_data->zoomStack.pop();
00189 }
00190 }
00191 }
00192 }
00193
00198 int QwtPlotZoomer::maxStackDepth() const
00199 {
00200 return d_data->maxStackDepth;
00201 }
00202
00209 const QStack<QRectF> &QwtPlotZoomer::zoomStack() const
00210 {
00211 return d_data->zoomStack;
00212 }
00213
00218 QRectF QwtPlotZoomer::zoomBase() const
00219 {
00220 return d_data->zoomStack[0];
00221 }
00222
00232 void QwtPlotZoomer::setZoomBase( bool doReplot )
00233 {
00234 QwtPlot *plt = plot();
00235 if ( plt == NULL )
00236 return;
00237
00238 if ( doReplot )
00239 plt->replot();
00240
00241 d_data->zoomStack.clear();
00242 d_data->zoomStack.push( scaleRect() );
00243 d_data->zoomRectIndex = 0;
00244
00245 rescale();
00246 }
00247
00258 void QwtPlotZoomer::setZoomBase( const QRectF &base )
00259 {
00260 const QwtPlot *plt = plot();
00261 if ( !plt )
00262 return;
00263
00264 const QRectF sRect = scaleRect();
00265 const QRectF bRect = base | sRect;
00266
00267 d_data->zoomStack.clear();
00268 d_data->zoomStack.push( bRect );
00269 d_data->zoomRectIndex = 0;
00270
00271 if ( base != sRect )
00272 {
00273 d_data->zoomStack.push( sRect );
00274 d_data->zoomRectIndex++;
00275 }
00276
00277 rescale();
00278 }
00279
00284 QRectF QwtPlotZoomer::zoomRect() const
00285 {
00286 return d_data->zoomStack[d_data->zoomRectIndex];
00287 }
00288
00292 uint QwtPlotZoomer::zoomRectIndex() const
00293 {
00294 return d_data->zoomRectIndex;
00295 }
00296
00307 void QwtPlotZoomer::zoom( const QRectF &rect )
00308 {
00309 if ( d_data->maxStackDepth >= 0 &&
00310 int( d_data->zoomRectIndex ) >= d_data->maxStackDepth )
00311 {
00312 return;
00313 }
00314
00315 const QRectF zoomRect = rect.normalized();
00316 if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
00317 {
00318 for ( uint i = int( d_data->zoomStack.count() ) - 1;
00319 i > d_data->zoomRectIndex; i-- )
00320 {
00321 ( void )d_data->zoomStack.pop();
00322 }
00323
00324 d_data->zoomStack.push( zoomRect );
00325 d_data->zoomRectIndex++;
00326
00327 rescale();
00328
00329 Q_EMIT zoomed( zoomRect );
00330 }
00331 }
00332
00344 void QwtPlotZoomer::zoom( int offset )
00345 {
00346 int newIndex;
00347
00348 if ( offset == 0 )
00349 {
00350 newIndex = 0;
00351 }
00352 else
00353 {
00354 newIndex = d_data->zoomRectIndex + offset;
00355 newIndex = qBound( 0, newIndex, d_data->zoomStack.count() - 1 );
00356 }
00357
00358 if ( newIndex != static_cast<int>( d_data->zoomRectIndex ) )
00359 {
00360 d_data->zoomRectIndex = newIndex;
00361 rescale();
00362 Q_EMIT zoomed( zoomRect() );
00363 }
00364 }
00365
00380 void QwtPlotZoomer::setZoomStack(
00381 const QStack<QRectF> &zoomStack, int zoomRectIndex )
00382 {
00383 if ( zoomStack.isEmpty() )
00384 return;
00385
00386 if ( d_data->maxStackDepth >= 0 &&
00387 int( zoomStack.count() ) > d_data->maxStackDepth )
00388 {
00389 return;
00390 }
00391
00392 if ( zoomRectIndex < 0 || zoomRectIndex > int( zoomStack.count() ) )
00393 zoomRectIndex = zoomStack.count() - 1;
00394
00395 const bool doRescale = zoomStack[zoomRectIndex] != zoomRect();
00396
00397 d_data->zoomStack = zoomStack;
00398 d_data->zoomRectIndex = uint( zoomRectIndex );
00399
00400 if ( doRescale )
00401 {
00402 rescale();
00403 Q_EMIT zoomed( zoomRect() );
00404 }
00405 }
00406
00413 void QwtPlotZoomer::rescale()
00414 {
00415 QwtPlot *plt = plot();
00416 if ( !plt )
00417 return;
00418
00419 const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
00420 if ( rect != scaleRect() )
00421 {
00422 const bool doReplot = plt->autoReplot();
00423 plt->setAutoReplot( false );
00424
00425 double x1 = rect.left();
00426 double x2 = rect.right();
00427 if ( !plt->axisScaleDiv( xAxis() ).isIncreasing() )
00428 qSwap( x1, x2 );
00429
00430 plt->setAxisScale( xAxis(), x1, x2 );
00431
00432 double y1 = rect.top();
00433 double y2 = rect.bottom();
00434 if ( !plt->axisScaleDiv( yAxis() ).isIncreasing() )
00435 qSwap( y1, y2 );
00436
00437 plt->setAxisScale( yAxis(), y1, y2 );
00438
00439 plt->setAutoReplot( doReplot );
00440
00441 plt->replot();
00442 }
00443 }
00444
00452 void QwtPlotZoomer::setAxis( int xAxis, int yAxis )
00453 {
00454 if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
00455 {
00456 QwtPlotPicker::setAxis( xAxis, yAxis );
00457 setZoomBase( scaleRect() );
00458 }
00459 }
00460
00471 void QwtPlotZoomer::widgetMouseReleaseEvent( QMouseEvent *me )
00472 {
00473 if ( mouseMatch( MouseSelect2, me ) )
00474 zoom( 0 );
00475 else if ( mouseMatch( MouseSelect3, me ) )
00476 zoom( -1 );
00477 else if ( mouseMatch( MouseSelect6, me ) )
00478 zoom( +1 );
00479 else
00480 QwtPlotPicker::widgetMouseReleaseEvent( me );
00481 }
00482
00494 void QwtPlotZoomer::widgetKeyPressEvent( QKeyEvent *ke )
00495 {
00496 if ( !isActive() )
00497 {
00498 if ( keyMatch( KeyUndo, ke ) )
00499 zoom( -1 );
00500 else if ( keyMatch( KeyRedo, ke ) )
00501 zoom( +1 );
00502 else if ( keyMatch( KeyHome, ke ) )
00503 zoom( 0 );
00504 }
00505
00506 QwtPlotPicker::widgetKeyPressEvent( ke );
00507 }
00508
00517 void QwtPlotZoomer::moveBy( double dx, double dy )
00518 {
00519 const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
00520 moveTo( QPointF( rect.left() + dx, rect.top() + dy ) );
00521 }
00522
00531 void QwtPlotZoomer::moveTo( const QPointF &pos )
00532 {
00533 double x = pos.x();
00534 double y = pos.y();
00535
00536 if ( x < zoomBase().left() )
00537 x = zoomBase().left();
00538 if ( x > zoomBase().right() - zoomRect().width() )
00539 x = zoomBase().right() - zoomRect().width();
00540
00541 if ( y < zoomBase().top() )
00542 y = zoomBase().top();
00543 if ( y > zoomBase().bottom() - zoomRect().height() )
00544 y = zoomBase().bottom() - zoomRect().height();
00545
00546 if ( x != zoomRect().left() || y != zoomRect().top() )
00547 {
00548 d_data->zoomStack[d_data->zoomRectIndex].moveTo( x, y );
00549 rescale();
00550 }
00551 }
00552
00564 bool QwtPlotZoomer::accept( QPolygon &pa ) const
00565 {
00566 if ( pa.count() < 2 )
00567 return false;
00568
00569 QRect rect = QRect( pa[0], pa[int( pa.count() ) - 1] );
00570 rect = rect.normalized();
00571
00572 const int minSize = 2;
00573 if ( rect.width() < minSize && rect.height() < minSize )
00574 return false;
00575
00576 const int minZoomSize = 11;
00577
00578 const QPoint center = rect.center();
00579 rect.setSize( rect.size().expandedTo( QSize( minZoomSize, minZoomSize ) ) );
00580 rect.moveCenter( center );
00581
00582 pa.resize( 2 );
00583 pa[0] = rect.topLeft();
00584 pa[1] = rect.bottomRight();
00585
00586 return true;
00587 }
00588
00594 QSizeF QwtPlotZoomer::minZoomSize() const
00595 {
00596 return QSizeF( d_data->zoomStack[0].width() / 10e4,
00597 d_data->zoomStack[0].height() / 10e4 );
00598 }
00599
00606 void QwtPlotZoomer::begin()
00607 {
00608 if ( d_data->maxStackDepth >= 0 )
00609 {
00610 if ( d_data->zoomRectIndex >= uint( d_data->maxStackDepth ) )
00611 return;
00612 }
00613
00614 const QSizeF minSize = minZoomSize();
00615 if ( minSize.isValid() )
00616 {
00617 const QSizeF sz =
00618 d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
00619
00620 if ( minSize.width() >= sz.width() &&
00621 minSize.height() >= sz.height() )
00622 {
00623 return;
00624 }
00625 }
00626
00627 QwtPlotPicker::begin();
00628 }
00629
00640 bool QwtPlotZoomer::end( bool ok )
00641 {
00642 ok = QwtPlotPicker::end( ok );
00643 if ( !ok )
00644 return false;
00645
00646 QwtPlot *plot = QwtPlotZoomer::plot();
00647 if ( !plot )
00648 return false;
00649
00650 const QPolygon &pa = selection();
00651 if ( pa.count() < 2 )
00652 return false;
00653
00654 QRect rect = QRect( pa[0], pa[int( pa.count() - 1 )] );
00655 rect = rect.normalized();
00656
00657 const QwtScaleMap xMap = plot->canvasMap( xAxis() );
00658 const QwtScaleMap yMap = plot->canvasMap( yAxis() );
00659
00660 QRectF zoomRect = QwtScaleMap::invTransform( xMap, yMap, rect ).normalized();
00661
00662 zoomRect = qwtExpandedZoomRect( zoomRect, minZoomSize(),
00663 xMap.transformation(), yMap.transformation() );
00664
00665 zoom( zoomRect );
00666
00667 return true;
00668 }