source: ntrip/branches/BNC_2.11.0/qwt/qwt_plot_rescaler.cpp@ 6214

Last change on this file since 6214 was 4271, checked in by mervart, 12 years ago
File size: 14.8 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_plot_rescaler.h"
11#include "qwt_plot.h"
12#include "qwt_plot_canvas.h"
13#include "qwt_scale_div.h"
14#include "qwt_interval.h"
15#include <qevent.h>
16#include <qalgorithms.h>
17
18class QwtPlotRescaler::AxisData
19{
20public:
21 AxisData():
22 aspectRatio( 1.0 ),
23 expandingDirection( QwtPlotRescaler::ExpandUp )
24 {
25 }
26
27 double aspectRatio;
28 QwtInterval intervalHint;
29 QwtPlotRescaler::ExpandingDirection expandingDirection;
30 mutable QwtScaleDiv scaleDiv;
31};
32
33class QwtPlotRescaler::PrivateData
34{
35public:
36 PrivateData():
37 referenceAxis( QwtPlot::xBottom ),
38 rescalePolicy( QwtPlotRescaler::Expanding ),
39 isEnabled( false ),
40 inReplot( 0 )
41 {
42 }
43
44 int referenceAxis;
45 RescalePolicy rescalePolicy;
46 QwtPlotRescaler::AxisData axisData[QwtPlot::axisCnt];
47 bool isEnabled;
48
49 mutable int inReplot;
50};
51
52/*!
53 Constructor
54
55 \param canvas Canvas
56 \param referenceAxis Reference axis, see RescalePolicy
57 \param policy Rescale policy
58
59 \sa setRescalePolicy(), setReferenceAxis()
60*/
61QwtPlotRescaler::QwtPlotRescaler( QwtPlotCanvas *canvas,
62 int referenceAxis, RescalePolicy policy ):
63 QObject( canvas )
64{
65 d_data = new PrivateData;
66 d_data->referenceAxis = referenceAxis;
67 d_data->rescalePolicy = policy;
68
69 setEnabled( true );
70}
71
72//! Destructor
73QwtPlotRescaler::~QwtPlotRescaler()
74{
75 delete d_data;
76}
77
78/*!
79 \brief En/disable the rescaler
80
81 When enabled is true an event filter is installed for
82 the canvas, otherwise the event filter is removed.
83
84 \param on true or false
85 \sa isEnabled(), eventFilter()
86*/
87void QwtPlotRescaler::setEnabled( bool on )
88{
89 if ( d_data->isEnabled != on )
90 {
91 d_data->isEnabled = on;
92
93 QWidget *w = canvas();
94 if ( w )
95 {
96 if ( d_data->isEnabled )
97 w->installEventFilter( this );
98 else
99 w->removeEventFilter( this );
100 }
101 }
102}
103
104/*!
105 \return true when enabled, false otherwise
106 \sa setEnabled, eventFilter()
107*/
108bool QwtPlotRescaler::isEnabled() const
109{
110 return d_data->isEnabled;
111}
112
113/*!
114 Change the rescale policy
115
116 \param policy Rescale policy
117 \sa rescalePolicy()
118*/
119void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy )
120{
121 d_data->rescalePolicy = policy;
122}
123
124/*!
125 \return Rescale policy
126 \sa setRescalePolicy()
127*/
128QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const
129{
130 return d_data->rescalePolicy;
131}
132
133/*!
134 Set the reference axis ( see RescalePolicy )
135
136 \param axis Axis index ( QwtPlot::Axis )
137 \sa referenceAxis()
138*/
139void QwtPlotRescaler::setReferenceAxis( int axis )
140{
141 d_data->referenceAxis = axis;
142}
143
144/*!
145 \return Reference axis ( see RescalePolicy )
146 \sa setReferenceAxis()
147*/
148int QwtPlotRescaler::referenceAxis() const
149{
150 return d_data->referenceAxis;
151}
152
153/*!
154 Set the direction in which all axis should be expanded
155
156 \param direction Direction
157 \sa expandingDirection()
158*/
159void QwtPlotRescaler::setExpandingDirection(
160 ExpandingDirection direction )
161{
162 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
163 setExpandingDirection( axis, direction );
164}
165
166/*!
167 Set the direction in which an axis should be expanded
168
169 \param axis Axis index ( see QwtPlot::AxisId )
170 \param direction Direction
171 \sa expandingDirection()
172*/
173void QwtPlotRescaler::setExpandingDirection(
174 int axis, ExpandingDirection direction )
175{
176 if ( axis >= 0 && axis < QwtPlot::axisCnt )
177 d_data->axisData[axis].expandingDirection = direction;
178}
179
180/*!
181 Return direction in which an axis should be expanded
182
183 \param axis Axis index ( see QwtPlot::AxisId )
184 \sa setExpandingDirection()
185*/
186QwtPlotRescaler::ExpandingDirection
187QwtPlotRescaler::expandingDirection( int axis ) const
188{
189 if ( axis >= 0 && axis < QwtPlot::axisCnt )
190 return d_data->axisData[axis].expandingDirection;
191
192 return ExpandBoth;
193}
194
195/*!
196 Set the aspect ratio between the scale of the reference axis
197 and the other scales. The default ratio is 1.0
198
199 \param ratio Aspect ratio
200 \sa aspectRatio()
201*/
202void QwtPlotRescaler::setAspectRatio( double ratio )
203{
204 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
205 setAspectRatio( axis, ratio );
206}
207
208/*!
209 Set the aspect ratio between the scale of the reference axis
210 and another scale. The default ratio is 1.0
211
212 \param axis Axis index ( see QwtPlot::AxisId )
213 \param ratio Aspect ratio
214 \sa aspectRatio()
215*/
216void QwtPlotRescaler::setAspectRatio( int axis, double ratio )
217{
218 if ( ratio < 0.0 )
219 ratio = 0.0;
220
221 if ( axis >= 0 && axis < QwtPlot::axisCnt )
222 d_data->axisData[axis].aspectRatio = ratio;
223}
224
225/*!
226 Return aspect ratio between an axis and the reference axis.
227
228 \param axis Axis index ( see QwtPlot::AxisId )
229 \sa setAspectRatio()
230*/
231double QwtPlotRescaler::aspectRatio( int axis ) const
232{
233 if ( axis >= 0 && axis < QwtPlot::axisCnt )
234 return d_data->axisData[axis].aspectRatio;
235
236 return 0.0;
237}
238
239/*!
240 Set an interval hint for an axis
241
242 In Fitting mode, the hint is used as minimal interval
243 taht always needs to be displayed.
244
245 \param axis Axis, see QwtPlot::Axis
246 \param interval Axis
247 \sa intervalHint(), RescalePolicy
248*/
249void QwtPlotRescaler::setIntervalHint( int axis,
250 const QwtInterval &interval )
251{
252 if ( axis >= 0 && axis < QwtPlot::axisCnt )
253 d_data->axisData[axis].intervalHint = interval;
254}
255
256/*!
257 \param axis Axis, see QwtPlot::Axis
258 \return Interval hint
259 \sa setIntervalHint(), RescalePolicy
260*/
261QwtInterval QwtPlotRescaler::intervalHint( int axis ) const
262{
263 if ( axis >= 0 && axis < QwtPlot::axisCnt )
264 return d_data->axisData[axis].intervalHint;
265
266 return QwtInterval();
267}
268
269//! \return plot canvas
270QwtPlotCanvas *QwtPlotRescaler::canvas()
271{
272 return qobject_cast<QwtPlotCanvas *>( parent() );
273}
274
275//! \return plot canvas
276const QwtPlotCanvas *QwtPlotRescaler::canvas() const
277{
278 return qobject_cast<const QwtPlotCanvas *>( parent() );
279}
280
281//! \return plot widget
282QwtPlot *QwtPlotRescaler::plot()
283{
284 QwtPlotCanvas *w = canvas();
285 if ( w )
286 return w->plot();
287
288 return NULL;
289}
290
291//! \return plot widget
292const QwtPlot *QwtPlotRescaler::plot() const
293{
294 const QwtPlotCanvas *w = canvas();
295 if ( w )
296 return w->plot();
297
298 return NULL;
299}
300
301//! Event filter for the plot canvas
302bool QwtPlotRescaler::eventFilter( QObject *o, QEvent *e )
303{
304 if ( o && o == canvas() )
305 {
306 switch ( e->type() )
307 {
308 case QEvent::Resize:
309 canvasResizeEvent( ( QResizeEvent * )e );
310 break;
311 case QEvent::PolishRequest:
312 rescale();
313 break;
314 default:;
315 }
316 }
317
318 return false;
319}
320
321/*!
322 Event handler for resize events of the plot canvas
323
324 \param event Resize event
325 \sa rescale()
326*/
327void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
328{
329 const int fw = 2 * canvas()->frameWidth();
330 const QSize newSize = event->size() - QSize( fw, fw );
331 const QSize oldSize = event->oldSize() - QSize( fw, fw );
332
333 rescale( oldSize, newSize );
334}
335
336//! Adjust the plot axes scales
337void QwtPlotRescaler::rescale() const
338{
339 const QSize size = canvas()->contentsRect().size();
340 rescale( size, size );
341}
342
343/*!
344 Adjust the plot axes scales
345
346 \param oldSize Previous size of the canvas
347 \param newSize New size of the canvas
348*/
349void QwtPlotRescaler::rescale(
350 const QSize &oldSize, const QSize &newSize ) const
351{
352 if ( newSize.isEmpty() )
353 return;
354
355 QwtInterval intervals[QwtPlot::axisCnt];
356 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
357 intervals[axis] = interval( axis );
358
359 const int refAxis = referenceAxis();
360 intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
361
362 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
363 {
364 if ( aspectRatio( axis ) > 0.0 && axis != refAxis )
365 intervals[axis] = syncScale( axis, intervals[refAxis], newSize );
366 }
367
368 updateScales( intervals );
369}
370
371/*!
372 Calculate the new scale interval of a plot axis
373
374 \param axis Axis index ( see QwtPlot::AxisId )
375 \param oldSize Previous size of the canvas
376 \param newSize New size of the canvas
377
378 \return Calculated new interval for the axis
379*/
380QwtInterval QwtPlotRescaler::expandScale( int axis,
381 const QSize &oldSize, const QSize &newSize ) const
382{
383 const QwtInterval oldInterval = interval( axis );
384
385 QwtInterval expanded = oldInterval;
386 switch ( rescalePolicy() )
387 {
388 case Fixed:
389 {
390 break; // do nothing
391 }
392 case Expanding:
393 {
394 if ( !oldSize.isEmpty() )
395 {
396 double width = oldInterval.width();
397 if ( orientation( axis ) == Qt::Horizontal )
398 width *= double( newSize.width() ) / oldSize.width();
399 else
400 width *= double( newSize.height() ) / oldSize.height();
401
402 expanded = expandInterval( oldInterval,
403 width, expandingDirection( axis ) );
404 }
405 break;
406 }
407 case Fitting:
408 {
409 double dist = 0.0;
410 for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ )
411 {
412 const double d = pixelDist( ax, newSize );
413 if ( d > dist )
414 dist = d;
415 }
416 if ( dist > 0.0 )
417 {
418 double width;
419 if ( orientation( axis ) == Qt::Horizontal )
420 width = newSize.width() * dist;
421 else
422 width = newSize.height() * dist;
423
424 expanded = expandInterval( intervalHint( axis ),
425 width, expandingDirection( axis ) );
426 }
427 break;
428 }
429 }
430
431 return expanded;
432}
433
434/*!
435 Synchronize an axis scale according to the scale of the reference axis
436
437 \param axis Axis index ( see QwtPlot::AxisId )
438 \param reference Interval of the reference axis
439 \param size Size of the canvas
440*/
441QwtInterval QwtPlotRescaler::syncScale( int axis,
442 const QwtInterval& reference, const QSize &size ) const
443{
444 double dist;
445 if ( orientation( referenceAxis() ) == Qt::Horizontal )
446 dist = reference.width() / size.width();
447 else
448 dist = reference.width() / size.height();
449
450 if ( orientation( axis ) == Qt::Horizontal )
451 dist *= size.width();
452 else
453 dist *= size.height();
454
455 dist /= aspectRatio( axis );
456
457 QwtInterval intv;
458 if ( rescalePolicy() == Fitting )
459 intv = intervalHint( axis );
460 else
461 intv = interval( axis );
462
463 intv = expandInterval( intv, dist, expandingDirection( axis ) );
464
465 return intv;
466}
467
468/*!
469 Return orientation of an axis
470 \param axis Axis index ( see QwtPlot::AxisId )
471*/
472Qt::Orientation QwtPlotRescaler::orientation( int axis ) const
473{
474 if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
475 return Qt::Vertical;
476
477 return Qt::Horizontal;
478}
479
480/*!
481 Return interval of an axis
482 \param axis Axis index ( see QwtPlot::AxisId )
483*/
484QwtInterval QwtPlotRescaler::interval( int axis ) const
485{
486 if ( axis < 0 || axis >= QwtPlot::axisCnt )
487 return QwtInterval();
488
489 const QwtPlot *plt = plot();
490
491 const double v1 = plt->axisScaleDiv( axis )->lowerBound();
492 const double v2 = plt->axisScaleDiv( axis )->upperBound();
493
494 return QwtInterval( v1, v2 ).normalized();
495}
496
497/*!
498 Expand the interval
499
500 \param interval Interval to be expanded
501 \param width Distance to be added to the interval
502 \param direction Direction of the expand operation
503
504 \return Expanded interval
505*/
506QwtInterval QwtPlotRescaler::expandInterval(
507 const QwtInterval &interval, double width,
508 ExpandingDirection direction ) const
509{
510 QwtInterval expanded = interval;
511
512 switch ( direction )
513 {
514 case ExpandUp:
515 expanded.setMinValue( interval.minValue() );
516 expanded.setMaxValue( interval.minValue() + width );
517 break;
518
519 case ExpandDown:
520 expanded.setMaxValue( interval.maxValue() );
521 expanded.setMinValue( interval.maxValue() - width );
522 break;
523
524 case ExpandBoth:
525 default:
526 expanded.setMinValue( interval.minValue() +
527 interval.width() / 2.0 - width / 2.0 );
528 expanded.setMaxValue( expanded.minValue() + width );
529 }
530 return expanded;
531}
532
533double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const
534{
535 const QwtInterval intv = intervalHint( axis );
536
537 double dist = 0.0;
538 if ( !intv.isNull() )
539 {
540 if ( axis == referenceAxis() )
541 dist = intv.width();
542 else
543 {
544 const double r = aspectRatio( axis );
545 if ( r > 0.0 )
546 dist = intv.width() * r;
547 }
548 }
549
550 if ( dist > 0.0 )
551 {
552 if ( orientation( axis ) == Qt::Horizontal )
553 dist /= size.width();
554 else
555 dist /= size.height();
556 }
557
558 return dist;
559}
560
561/*!
562 Update the axes scales
563
564 \param intervals Scale intervals
565*/
566void QwtPlotRescaler::updateScales(
567 QwtInterval intervals[QwtPlot::axisCnt] ) const
568{
569 if ( d_data->inReplot >= 5 )
570 {
571 return;
572 }
573
574 QwtPlot *plt = const_cast<QwtPlot *>( plot() );
575
576 const bool doReplot = plt->autoReplot();
577 plt->setAutoReplot( false );
578
579 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
580 {
581 if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 )
582 {
583 double v1 = intervals[axis].minValue();
584 double v2 = intervals[axis].maxValue();
585
586 if ( plt->axisScaleDiv( axis )->lowerBound() >
587 plt->axisScaleDiv( axis )->upperBound() )
588 {
589 qSwap( v1, v2 );
590 }
591
592 if ( d_data->inReplot >= 1 )
593 {
594 d_data->axisData[axis].scaleDiv = *plt->axisScaleDiv( axis );
595 }
596
597 if ( d_data->inReplot >= 2 )
598 {
599 QList<double> ticks[QwtScaleDiv::NTickTypes];
600 for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
601 ticks[i] = d_data->axisData[axis].scaleDiv.ticks( i );
602
603 plt->setAxisScaleDiv( axis, QwtScaleDiv( v1, v2, ticks ) );
604 }
605 else
606 {
607 plt->setAxisScale( axis, v1, v2 );
608 }
609 }
610 }
611
612 const bool immediatePaint =
613 plt->canvas()->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
614 plt->canvas()->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
615
616 plt->setAutoReplot( doReplot );
617
618 d_data->inReplot++;
619 plt->replot();
620 d_data->inReplot--;
621
622 plt->canvas()->setPaintAttribute(
623 QwtPlotCanvas::ImmediatePaint, immediatePaint );
624}
Note: See TracBrowser for help on using the repository browser.