qt_demoe/third/3rd_qwt/qwt_plot_rescaler.cpp

632 lines
15 KiB
C++

/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 Uwe Rathmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
#include "qwt_plot_rescaler.h"
#include "qwt_plot.h"
#include "qwt_scale_div.h"
#include "qwt_interval.h"
#include "qwt_plot_canvas.h"
#include <qevent.h>
#include <qalgorithms.h>
class QwtPlotRescaler::AxisData
{
public:
AxisData():
aspectRatio( 1.0 ),
expandingDirection( QwtPlotRescaler::ExpandUp )
{
}
double aspectRatio;
QwtInterval intervalHint;
QwtPlotRescaler::ExpandingDirection expandingDirection;
mutable QwtScaleDiv scaleDiv;
};
class QwtPlotRescaler::PrivateData
{
public:
PrivateData():
referenceAxis( QwtPlot::xBottom ),
rescalePolicy( QwtPlotRescaler::Expanding ),
isEnabled( false ),
inReplot( 0 )
{
}
int referenceAxis;
RescalePolicy rescalePolicy;
QwtPlotRescaler::AxisData axisData[QwtPlot::axisCnt];
bool isEnabled;
mutable int inReplot;
};
/*!
Constructor
\param canvas Canvas
\param referenceAxis Reference axis, see RescalePolicy
\param policy Rescale policy
\sa setRescalePolicy(), setReferenceAxis()
*/
QwtPlotRescaler::QwtPlotRescaler( QWidget *canvas,
int referenceAxis, RescalePolicy policy ):
QObject( canvas )
{
d_data = new PrivateData;
d_data->referenceAxis = referenceAxis;
d_data->rescalePolicy = policy;
setEnabled( true );
}
//! Destructor
QwtPlotRescaler::~QwtPlotRescaler()
{
delete d_data;
}
/*!
\brief En/disable the rescaler
When enabled is true an event filter is installed for
the canvas, otherwise the event filter is removed.
\param on true or false
\sa isEnabled(), eventFilter()
*/
void QwtPlotRescaler::setEnabled( bool on )
{
if ( d_data->isEnabled != on )
{
d_data->isEnabled = on;
QWidget *w = canvas();
if ( w )
{
if ( d_data->isEnabled )
w->installEventFilter( this );
else
w->removeEventFilter( this );
}
}
}
/*!
\return true when enabled, false otherwise
\sa setEnabled, eventFilter()
*/
bool QwtPlotRescaler::isEnabled() const
{
return d_data->isEnabled;
}
/*!
Change the rescale policy
\param policy Rescale policy
\sa rescalePolicy()
*/
void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy )
{
d_data->rescalePolicy = policy;
}
/*!
\return Rescale policy
\sa setRescalePolicy()
*/
QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const
{
return d_data->rescalePolicy;
}
/*!
Set the reference axis ( see RescalePolicy )
\param axis Axis index ( QwtPlot::Axis )
\sa referenceAxis()
*/
void QwtPlotRescaler::setReferenceAxis( int axis )
{
d_data->referenceAxis = axis;
}
/*!
\return Reference axis ( see RescalePolicy )
\sa setReferenceAxis()
*/
int QwtPlotRescaler::referenceAxis() const
{
return d_data->referenceAxis;
}
/*!
Set the direction in which all axis should be expanded
\param direction Direction
\sa expandingDirection()
*/
void QwtPlotRescaler::setExpandingDirection(
ExpandingDirection direction )
{
for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
setExpandingDirection( axis, direction );
}
/*!
Set the direction in which an axis should be expanded
\param axis Axis index ( see QwtPlot::AxisId )
\param direction Direction
\sa expandingDirection()
*/
void QwtPlotRescaler::setExpandingDirection(
int axis, ExpandingDirection direction )
{
if ( axis >= 0 && axis < QwtPlot::axisCnt )
d_data->axisData[axis].expandingDirection = direction;
}
/*!
\return Direction in which an axis should be expanded
\param axis Axis index ( see QwtPlot::AxisId )
\sa setExpandingDirection()
*/
QwtPlotRescaler::ExpandingDirection
QwtPlotRescaler::expandingDirection( int axis ) const
{
if ( axis >= 0 && axis < QwtPlot::axisCnt )
return d_data->axisData[axis].expandingDirection;
return ExpandBoth;
}
/*!
Set the aspect ratio between the scale of the reference axis
and the other scales. The default ratio is 1.0
\param ratio Aspect ratio
\sa aspectRatio()
*/
void QwtPlotRescaler::setAspectRatio( double ratio )
{
for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
setAspectRatio( axis, ratio );
}
/*!
Set the aspect ratio between the scale of the reference axis
and another scale. The default ratio is 1.0
\param axis Axis index ( see QwtPlot::AxisId )
\param ratio Aspect ratio
\sa aspectRatio()
*/
void QwtPlotRescaler::setAspectRatio( int axis, double ratio )
{
if ( ratio < 0.0 )
ratio = 0.0;
if ( axis >= 0 && axis < QwtPlot::axisCnt )
d_data->axisData[axis].aspectRatio = ratio;
}
/*!
\return Aspect ratio between an axis and the reference axis.
\param axis Axis index ( see QwtPlot::AxisId )
\sa setAspectRatio()
*/
double QwtPlotRescaler::aspectRatio( int axis ) const
{
if ( axis >= 0 && axis < QwtPlot::axisCnt )
return d_data->axisData[axis].aspectRatio;
return 0.0;
}
/*!
Set an interval hint for an axis
In Fitting mode, the hint is used as minimal interval
that always needs to be displayed.
\param axis Axis, see QwtPlot::Axis
\param interval Axis
\sa intervalHint(), RescalePolicy
*/
void QwtPlotRescaler::setIntervalHint( int axis,
const QwtInterval &interval )
{
if ( axis >= 0 && axis < QwtPlot::axisCnt )
d_data->axisData[axis].intervalHint = interval;
}
/*!
\param axis Axis, see QwtPlot::Axis
\return Interval hint
\sa setIntervalHint(), RescalePolicy
*/
QwtInterval QwtPlotRescaler::intervalHint( int axis ) const
{
if ( axis >= 0 && axis < QwtPlot::axisCnt )
return d_data->axisData[axis].intervalHint;
return QwtInterval();
}
//! \return plot canvas
QWidget *QwtPlotRescaler::canvas()
{
return qobject_cast<QWidget *>( parent() );
}
//! \return plot canvas
const QWidget *QwtPlotRescaler::canvas() const
{
return qobject_cast<const QWidget *>( parent() );
}
//! \return plot widget
QwtPlot *QwtPlotRescaler::plot()
{
QWidget *w = canvas();
if ( w )
w = w->parentWidget();
return qobject_cast<QwtPlot *>( w );
}
//! \return plot widget
const QwtPlot *QwtPlotRescaler::plot() const
{
const QWidget *w = canvas();
if ( w )
w = w->parentWidget();
return qobject_cast<const QwtPlot *>( w );
}
//! Event filter for the plot canvas
bool QwtPlotRescaler::eventFilter( QObject *object, QEvent *event )
{
if ( object && object == canvas() )
{
switch ( event->type() )
{
case QEvent::Resize:
{
canvasResizeEvent( static_cast<QResizeEvent *>( event ) );
break;
}
case QEvent::PolishRequest:
{
rescale();
break;
}
default:;
}
}
return false;
}
/*!
Event handler for resize events of the plot canvas
\param event Resize event
\sa rescale()
*/
void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
{
int left, top, right, bottom;
canvas()->getContentsMargins( &left, &top, &right, &bottom );
const QSize marginSize( left + right, top + bottom );
const QSize newSize = event->size() - marginSize;
const QSize oldSize = event->oldSize() - marginSize;
rescale( oldSize, newSize );
}
//! Adjust the plot axes scales
void QwtPlotRescaler::rescale() const
{
const QSize size = canvas()->contentsRect().size();
rescale( size, size );
}
/*!
Adjust the plot axes scales
\param oldSize Previous size of the canvas
\param newSize New size of the canvas
*/
void QwtPlotRescaler::rescale(
const QSize &oldSize, const QSize &newSize ) const
{
if ( newSize.isEmpty() )
return;
QwtInterval intervals[QwtPlot::axisCnt];
for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
intervals[axis] = interval( axis );
const int refAxis = referenceAxis();
intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
{
if ( aspectRatio( axis ) > 0.0 && axis != refAxis )
intervals[axis] = syncScale( axis, intervals[refAxis], newSize );
}
updateScales( intervals );
}
/*!
Calculate the new scale interval of a plot axis
\param axis Axis index ( see QwtPlot::AxisId )
\param oldSize Previous size of the canvas
\param newSize New size of the canvas
\return Calculated new interval for the axis
*/
QwtInterval QwtPlotRescaler::expandScale( int axis,
const QSize &oldSize, const QSize &newSize ) const
{
const QwtInterval oldInterval = interval( axis );
QwtInterval expanded = oldInterval;
switch ( rescalePolicy() )
{
case Fixed:
{
break; // do nothing
}
case Expanding:
{
if ( !oldSize.isEmpty() )
{
double width = oldInterval.width();
if ( orientation( axis ) == Qt::Horizontal )
width *= double( newSize.width() ) / oldSize.width();
else
width *= double( newSize.height() ) / oldSize.height();
expanded = expandInterval( oldInterval,
width, expandingDirection( axis ) );
}
break;
}
case Fitting:
{
double dist = 0.0;
for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ )
{
const double d = pixelDist( ax, newSize );
if ( d > dist )
dist = d;
}
if ( dist > 0.0 )
{
double width;
if ( orientation( axis ) == Qt::Horizontal )
width = newSize.width() * dist;
else
width = newSize.height() * dist;
expanded = expandInterval( intervalHint( axis ),
width, expandingDirection( axis ) );
}
break;
}
}
return expanded;
}
/*!
Synchronize an axis scale according to the scale of the reference axis
\param axis Axis index ( see QwtPlot::AxisId )
\param reference Interval of the reference axis
\param size Size of the canvas
\return New interval for axis
*/
QwtInterval QwtPlotRescaler::syncScale( int axis,
const QwtInterval& reference, const QSize &size ) const
{
double dist;
if ( orientation( referenceAxis() ) == Qt::Horizontal )
dist = reference.width() / size.width();
else
dist = reference.width() / size.height();
if ( orientation( axis ) == Qt::Horizontal )
dist *= size.width();
else
dist *= size.height();
dist /= aspectRatio( axis );
QwtInterval intv;
if ( rescalePolicy() == Fitting )
intv = intervalHint( axis );
else
intv = interval( axis );
intv = expandInterval( intv, dist, expandingDirection( axis ) );
return intv;
}
/*!
\return Orientation of an axis
\param axis Axis index ( see QwtPlot::AxisId )
*/
Qt::Orientation QwtPlotRescaler::orientation( int axis ) const
{
if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
return Qt::Vertical;
return Qt::Horizontal;
}
/*!
\param axis Axis index ( see QwtPlot::AxisId )
\return Normalized interval of an axis
*/
QwtInterval QwtPlotRescaler::interval( int axis ) const
{
if ( axis < 0 || axis >= QwtPlot::axisCnt )
return QwtInterval();
return plot()->axisScaleDiv( axis ).interval().normalized();
}
/*!
Expand the interval
\param interval Interval to be expanded
\param width Distance to be added to the interval
\param direction Direction of the expand operation
\return Expanded interval
*/
QwtInterval QwtPlotRescaler::expandInterval(
const QwtInterval &interval, double width,
ExpandingDirection direction ) const
{
QwtInterval expanded = interval;
switch ( direction )
{
case ExpandUp:
expanded.setMinValue( interval.minValue() );
expanded.setMaxValue( interval.minValue() + width );
break;
case ExpandDown:
expanded.setMaxValue( interval.maxValue() );
expanded.setMinValue( interval.maxValue() - width );
break;
case ExpandBoth:
default:
expanded.setMinValue( interval.minValue() +
interval.width() / 2.0 - width / 2.0 );
expanded.setMaxValue( expanded.minValue() + width );
}
return expanded;
}
double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const
{
const QwtInterval intv = intervalHint( axis );
double dist = 0.0;
if ( !intv.isNull() )
{
if ( axis == referenceAxis() )
dist = intv.width();
else
{
const double r = aspectRatio( axis );
if ( r > 0.0 )
dist = intv.width() * r;
}
}
if ( dist > 0.0 )
{
if ( orientation( axis ) == Qt::Horizontal )
dist /= size.width();
else
dist /= size.height();
}
return dist;
}
/*!
Update the axes scales
\param intervals Scale intervals
*/
void QwtPlotRescaler::updateScales(
QwtInterval intervals[QwtPlot::axisCnt] ) const
{
if ( d_data->inReplot >= 5 )
{
return;
}
QwtPlot *plt = const_cast<QwtPlot *>( plot() );
const bool doReplot = plt->autoReplot();
plt->setAutoReplot( false );
for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
{
if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 )
{
double v1 = intervals[axis].minValue();
double v2 = intervals[axis].maxValue();
if ( !plt->axisScaleDiv( axis ).isIncreasing() )
qSwap( v1, v2 );
if ( d_data->inReplot >= 1 )
d_data->axisData[axis].scaleDiv = plt->axisScaleDiv( axis );
if ( d_data->inReplot >= 2 )
{
QList<double> ticks[QwtScaleDiv::NTickTypes];
for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
ticks[i] = d_data->axisData[axis].scaleDiv.ticks( i );
plt->setAxisScaleDiv( axis, QwtScaleDiv( v1, v2, ticks ) );
}
else
{
plt->setAxisScale( axis, v1, v2 );
}
}
}
QwtPlotCanvas *canvas = qobject_cast<QwtPlotCanvas *>( plt->canvas() );
bool immediatePaint = false;
if ( canvas )
{
immediatePaint = canvas->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
}
plt->setAutoReplot( doReplot );
d_data->inReplot++;
plt->replot();
d_data->inReplot--;
if ( canvas && immediatePaint )
{
canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true );
}
}