00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_plot_rescaler.h"
00011 #include "qwt_plot.h"
00012 #include "qwt_scale_div.h"
00013 #include "qwt_interval.h"
00014 #include "qwt_plot_canvas.h"
00015 #include <qevent.h>
00016 #include <qalgorithms.h>
00017
00018 class QwtPlotRescaler::AxisData
00019 {
00020 public:
00021 AxisData():
00022 aspectRatio( 1.0 ),
00023 expandingDirection( QwtPlotRescaler::ExpandUp )
00024 {
00025 }
00026
00027 double aspectRatio;
00028 QwtInterval intervalHint;
00029 QwtPlotRescaler::ExpandingDirection expandingDirection;
00030 mutable QwtScaleDiv scaleDiv;
00031 };
00032
00033 class QwtPlotRescaler::PrivateData
00034 {
00035 public:
00036 PrivateData():
00037 referenceAxis( QwtPlot::xBottom ),
00038 rescalePolicy( QwtPlotRescaler::Expanding ),
00039 isEnabled( false ),
00040 inReplot( 0 )
00041 {
00042 }
00043
00044 int referenceAxis;
00045 RescalePolicy rescalePolicy;
00046 QwtPlotRescaler::AxisData axisData[QwtPlot::axisCnt];
00047 bool isEnabled;
00048
00049 mutable int inReplot;
00050 };
00051
00061 QwtPlotRescaler::QwtPlotRescaler( QWidget *canvas,
00062 int referenceAxis, RescalePolicy policy ):
00063 QObject( canvas )
00064 {
00065 d_data = new PrivateData;
00066 d_data->referenceAxis = referenceAxis;
00067 d_data->rescalePolicy = policy;
00068
00069 setEnabled( true );
00070 }
00071
00073 QwtPlotRescaler::~QwtPlotRescaler()
00074 {
00075 delete d_data;
00076 }
00077
00087 void QwtPlotRescaler::setEnabled( bool on )
00088 {
00089 if ( d_data->isEnabled != on )
00090 {
00091 d_data->isEnabled = on;
00092
00093 QWidget *w = canvas();
00094 if ( w )
00095 {
00096 if ( d_data->isEnabled )
00097 w->installEventFilter( this );
00098 else
00099 w->removeEventFilter( this );
00100 }
00101 }
00102 }
00103
00108 bool QwtPlotRescaler::isEnabled() const
00109 {
00110 return d_data->isEnabled;
00111 }
00112
00119 void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy )
00120 {
00121 d_data->rescalePolicy = policy;
00122 }
00123
00128 QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const
00129 {
00130 return d_data->rescalePolicy;
00131 }
00132
00139 void QwtPlotRescaler::setReferenceAxis( int axis )
00140 {
00141 d_data->referenceAxis = axis;
00142 }
00143
00148 int QwtPlotRescaler::referenceAxis() const
00149 {
00150 return d_data->referenceAxis;
00151 }
00152
00159 void QwtPlotRescaler::setExpandingDirection(
00160 ExpandingDirection direction )
00161 {
00162 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00163 setExpandingDirection( axis, direction );
00164 }
00165
00173 void QwtPlotRescaler::setExpandingDirection(
00174 int axis, ExpandingDirection direction )
00175 {
00176 if ( axis >= 0 && axis < QwtPlot::axisCnt )
00177 d_data->axisData[axis].expandingDirection = direction;
00178 }
00179
00186 QwtPlotRescaler::ExpandingDirection
00187 QwtPlotRescaler::expandingDirection( int axis ) const
00188 {
00189 if ( axis >= 0 && axis < QwtPlot::axisCnt )
00190 return d_data->axisData[axis].expandingDirection;
00191
00192 return ExpandBoth;
00193 }
00194
00202 void QwtPlotRescaler::setAspectRatio( double ratio )
00203 {
00204 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00205 setAspectRatio( axis, ratio );
00206 }
00207
00216 void QwtPlotRescaler::setAspectRatio( int axis, double ratio )
00217 {
00218 if ( ratio < 0.0 )
00219 ratio = 0.0;
00220
00221 if ( axis >= 0 && axis < QwtPlot::axisCnt )
00222 d_data->axisData[axis].aspectRatio = ratio;
00223 }
00224
00231 double QwtPlotRescaler::aspectRatio( int axis ) const
00232 {
00233 if ( axis >= 0 && axis < QwtPlot::axisCnt )
00234 return d_data->axisData[axis].aspectRatio;
00235
00236 return 0.0;
00237 }
00238
00249 void QwtPlotRescaler::setIntervalHint( int axis,
00250 const QwtInterval &interval )
00251 {
00252 if ( axis >= 0 && axis < QwtPlot::axisCnt )
00253 d_data->axisData[axis].intervalHint = interval;
00254 }
00255
00261 QwtInterval QwtPlotRescaler::intervalHint( int axis ) const
00262 {
00263 if ( axis >= 0 && axis < QwtPlot::axisCnt )
00264 return d_data->axisData[axis].intervalHint;
00265
00266 return QwtInterval();
00267 }
00268
00270 QWidget *QwtPlotRescaler::canvas()
00271 {
00272 return qobject_cast<QWidget *>( parent() );
00273 }
00274
00276 const QWidget *QwtPlotRescaler::canvas() const
00277 {
00278 return qobject_cast<const QWidget *>( parent() );
00279 }
00280
00282 QwtPlot *QwtPlotRescaler::plot()
00283 {
00284 QWidget *w = canvas();
00285 if ( w )
00286 w = w->parentWidget();
00287
00288 return qobject_cast<QwtPlot *>( w );
00289 }
00290
00292 const QwtPlot *QwtPlotRescaler::plot() const
00293 {
00294 const QWidget *w = canvas();
00295 if ( w )
00296 w = w->parentWidget();
00297
00298 return qobject_cast<const QwtPlot *>( w );
00299 }
00300
00302 bool QwtPlotRescaler::eventFilter( QObject *object, QEvent *event )
00303 {
00304 if ( object && object == canvas() )
00305 {
00306 switch ( event->type() )
00307 {
00308 case QEvent::Resize:
00309 {
00310 canvasResizeEvent( static_cast<QResizeEvent *>( event ) );
00311 break;
00312 }
00313 case QEvent::PolishRequest:
00314 {
00315 rescale();
00316 break;
00317 }
00318 default:;
00319 }
00320 }
00321
00322 return false;
00323 }
00324
00331 void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
00332 {
00333 int left, top, right, bottom;
00334 canvas()->getContentsMargins( &left, &top, &right, &bottom );
00335
00336 const QSize marginSize( left + right, top + bottom );
00337
00338 const QSize newSize = event->size() - marginSize;
00339 const QSize oldSize = event->oldSize() - marginSize;
00340
00341 rescale( oldSize, newSize );
00342 }
00343
00345 void QwtPlotRescaler::rescale() const
00346 {
00347 const QSize size = canvas()->contentsRect().size();
00348 rescale( size, size );
00349 }
00350
00357 void QwtPlotRescaler::rescale(
00358 const QSize &oldSize, const QSize &newSize ) const
00359 {
00360 if ( newSize.isEmpty() )
00361 return;
00362
00363 QwtInterval intervals[QwtPlot::axisCnt];
00364 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00365 intervals[axis] = interval( axis );
00366
00367 const int refAxis = referenceAxis();
00368 intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
00369
00370 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00371 {
00372 if ( aspectRatio( axis ) > 0.0 && axis != refAxis )
00373 intervals[axis] = syncScale( axis, intervals[refAxis], newSize );
00374 }
00375
00376 updateScales( intervals );
00377 }
00378
00388 QwtInterval QwtPlotRescaler::expandScale( int axis,
00389 const QSize &oldSize, const QSize &newSize ) const
00390 {
00391 const QwtInterval oldInterval = interval( axis );
00392
00393 QwtInterval expanded = oldInterval;
00394 switch ( rescalePolicy() )
00395 {
00396 case Fixed:
00397 {
00398 break;
00399 }
00400 case Expanding:
00401 {
00402 if ( !oldSize.isEmpty() )
00403 {
00404 double width = oldInterval.width();
00405 if ( orientation( axis ) == Qt::Horizontal )
00406 width *= double( newSize.width() ) / oldSize.width();
00407 else
00408 width *= double( newSize.height() ) / oldSize.height();
00409
00410 expanded = expandInterval( oldInterval,
00411 width, expandingDirection( axis ) );
00412 }
00413 break;
00414 }
00415 case Fitting:
00416 {
00417 double dist = 0.0;
00418 for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ )
00419 {
00420 const double d = pixelDist( ax, newSize );
00421 if ( d > dist )
00422 dist = d;
00423 }
00424 if ( dist > 0.0 )
00425 {
00426 double width;
00427 if ( orientation( axis ) == Qt::Horizontal )
00428 width = newSize.width() * dist;
00429 else
00430 width = newSize.height() * dist;
00431
00432 expanded = expandInterval( intervalHint( axis ),
00433 width, expandingDirection( axis ) );
00434 }
00435 break;
00436 }
00437 }
00438
00439 return expanded;
00440 }
00441
00451 QwtInterval QwtPlotRescaler::syncScale( int axis,
00452 const QwtInterval& reference, const QSize &size ) const
00453 {
00454 double dist;
00455 if ( orientation( referenceAxis() ) == Qt::Horizontal )
00456 dist = reference.width() / size.width();
00457 else
00458 dist = reference.width() / size.height();
00459
00460 if ( orientation( axis ) == Qt::Horizontal )
00461 dist *= size.width();
00462 else
00463 dist *= size.height();
00464
00465 dist /= aspectRatio( axis );
00466
00467 QwtInterval intv;
00468 if ( rescalePolicy() == Fitting )
00469 intv = intervalHint( axis );
00470 else
00471 intv = interval( axis );
00472
00473 intv = expandInterval( intv, dist, expandingDirection( axis ) );
00474
00475 return intv;
00476 }
00477
00482 Qt::Orientation QwtPlotRescaler::orientation( int axis ) const
00483 {
00484 if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
00485 return Qt::Vertical;
00486
00487 return Qt::Horizontal;
00488 }
00489
00494 QwtInterval QwtPlotRescaler::interval( int axis ) const
00495 {
00496 if ( axis < 0 || axis >= QwtPlot::axisCnt )
00497 return QwtInterval();
00498
00499 return plot()->axisScaleDiv( axis ).interval().normalized();
00500 }
00501
00511 QwtInterval QwtPlotRescaler::expandInterval(
00512 const QwtInterval &interval, double width,
00513 ExpandingDirection direction ) const
00514 {
00515 QwtInterval expanded = interval;
00516
00517 switch ( direction )
00518 {
00519 case ExpandUp:
00520 expanded.setMinValue( interval.minValue() );
00521 expanded.setMaxValue( interval.minValue() + width );
00522 break;
00523
00524 case ExpandDown:
00525 expanded.setMaxValue( interval.maxValue() );
00526 expanded.setMinValue( interval.maxValue() - width );
00527 break;
00528
00529 case ExpandBoth:
00530 default:
00531 expanded.setMinValue( interval.minValue() +
00532 interval.width() / 2.0 - width / 2.0 );
00533 expanded.setMaxValue( expanded.minValue() + width );
00534 }
00535 return expanded;
00536 }
00537
00538 double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const
00539 {
00540 const QwtInterval intv = intervalHint( axis );
00541
00542 double dist = 0.0;
00543 if ( !intv.isNull() )
00544 {
00545 if ( axis == referenceAxis() )
00546 dist = intv.width();
00547 else
00548 {
00549 const double r = aspectRatio( axis );
00550 if ( r > 0.0 )
00551 dist = intv.width() * r;
00552 }
00553 }
00554
00555 if ( dist > 0.0 )
00556 {
00557 if ( orientation( axis ) == Qt::Horizontal )
00558 dist /= size.width();
00559 else
00560 dist /= size.height();
00561 }
00562
00563 return dist;
00564 }
00565
00571 void QwtPlotRescaler::updateScales(
00572 QwtInterval intervals[QwtPlot::axisCnt] ) const
00573 {
00574 if ( d_data->inReplot >= 5 )
00575 {
00576 return;
00577 }
00578
00579 QwtPlot *plt = const_cast<QwtPlot *>( plot() );
00580
00581 const bool doReplot = plt->autoReplot();
00582 plt->setAutoReplot( false );
00583
00584 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
00585 {
00586 if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 )
00587 {
00588 double v1 = intervals[axis].minValue();
00589 double v2 = intervals[axis].maxValue();
00590
00591 if ( !plt->axisScaleDiv( axis ).isIncreasing() )
00592 qSwap( v1, v2 );
00593
00594 if ( d_data->inReplot >= 1 )
00595 d_data->axisData[axis].scaleDiv = plt->axisScaleDiv( axis );
00596
00597 if ( d_data->inReplot >= 2 )
00598 {
00599 QList<double> ticks[QwtScaleDiv::NTickTypes];
00600 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
00601 ticks[i] = d_data->axisData[axis].scaleDiv.ticks( i );
00602
00603 plt->setAxisScaleDiv( axis, QwtScaleDiv( v1, v2, ticks ) );
00604 }
00605 else
00606 {
00607 plt->setAxisScale( axis, v1, v2 );
00608 }
00609 }
00610 }
00611
00612 QwtPlotCanvas *canvas = qobject_cast<QwtPlotCanvas *>( plt->canvas() );
00613
00614 bool immediatePaint = false;
00615 if ( canvas )
00616 {
00617 immediatePaint = canvas->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
00618 canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
00619 }
00620
00621 plt->setAutoReplot( doReplot );
00622
00623 d_data->inReplot++;
00624 plt->replot();
00625 d_data->inReplot--;
00626
00627 if ( canvas && immediatePaint )
00628 {
00629 canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true );
00630 }
00631 }