source: ntrip/trunk/BNC/qwt/qwt_plot_rescaler.cpp@ 8963

Last change on this file since 8963 was 8127, checked in by stoecker, 7 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 15.0 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_scale_div.h"
13#include "qwt_interval.h"
14#include "qwt_plot_canvas.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( QWidget *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 that 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
270QWidget *QwtPlotRescaler::canvas()
271{
272 return qobject_cast<QWidget *>( parent() );
273}
274
275//! \return plot canvas
276const QWidget *QwtPlotRescaler::canvas() const
277{
278 return qobject_cast<const QWidget *>( parent() );
279}
280
281//! \return plot widget
282QwtPlot *QwtPlotRescaler::plot()
283{
284 QWidget *w = canvas();
285 if ( w )
286 w = w->parentWidget();
287
288 return qobject_cast<QwtPlot *>( w );
289}
290
291//! \return plot widget
292const QwtPlot *QwtPlotRescaler::plot() const
293{
294 const QWidget *w = canvas();
295 if ( w )
296 w = w->parentWidget();
297
298 return qobject_cast<const QwtPlot *>( w );
299}
300
301//! Event filter for the plot canvas
302bool QwtPlotRescaler::eventFilter( QObject *object, QEvent *event )
303{
304 if ( object && object == canvas() )
305 {
306 switch ( event->type() )
307 {
308 case QEvent::Resize:
309 {
310 canvasResizeEvent( static_cast<QResizeEvent *>( event ) );
311 break;
312 }
313 case QEvent::PolishRequest:
314 {
315 rescale();
316 break;
317 }
318 default:;
319 }
320 }
321
322 return false;
323}
324
325/*!
326 Event handler for resize events of the plot canvas
327
328 \param event Resize event
329 \sa rescale()
330*/
331void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
332{
333 int left, top, right, bottom;
334 canvas()->getContentsMargins( &left, &top, &right, &bottom );
335
336 const QSize marginSize( left + right, top + bottom );
337
338 const QSize newSize = event->size() - marginSize;
339 const QSize oldSize = event->oldSize() - marginSize;
340
341 rescale( oldSize, newSize );
342}
343
344//! Adjust the plot axes scales
345void QwtPlotRescaler::rescale() const
346{
347 const QSize size = canvas()->contentsRect().size();
348 rescale( size, size );
349}
350
351/*!
352 Adjust the plot axes scales
353
354 \param oldSize Previous size of the canvas
355 \param newSize New size of the canvas
356*/
357void QwtPlotRescaler::rescale(
358 const QSize &oldSize, const QSize &newSize ) const
359{
360 if ( newSize.isEmpty() )
361 return;
362
363 QwtInterval intervals[QwtPlot::axisCnt];
364 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
365 intervals[axis] = interval( axis );
366
367 const int refAxis = referenceAxis();
368 intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
369
370 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
371 {
372 if ( aspectRatio( axis ) > 0.0 && axis != refAxis )
373 intervals[axis] = syncScale( axis, intervals[refAxis], newSize );
374 }
375
376 updateScales( intervals );
377}
378
379/*!
380 Calculate the new scale interval of a plot axis
381
382 \param axis Axis index ( see QwtPlot::AxisId )
383 \param oldSize Previous size of the canvas
384 \param newSize New size of the canvas
385
386 \return Calculated new interval for the axis
387*/
388QwtInterval QwtPlotRescaler::expandScale( int axis,
389 const QSize &oldSize, const QSize &newSize ) const
390{
391 const QwtInterval oldInterval = interval( axis );
392
393 QwtInterval expanded = oldInterval;
394 switch ( rescalePolicy() )
395 {
396 case Fixed:
397 {
398 break; // do nothing
399 }
400 case Expanding:
401 {
402 if ( !oldSize.isEmpty() )
403 {
404 double width = oldInterval.width();
405 if ( orientation( axis ) == Qt::Horizontal )
406 width *= double( newSize.width() ) / oldSize.width();
407 else
408 width *= double( newSize.height() ) / oldSize.height();
409
410 expanded = expandInterval( oldInterval,
411 width, expandingDirection( axis ) );
412 }
413 break;
414 }
415 case Fitting:
416 {
417 double dist = 0.0;
418 for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ )
419 {
420 const double d = pixelDist( ax, newSize );
421 if ( d > dist )
422 dist = d;
423 }
424 if ( dist > 0.0 )
425 {
426 double width;
427 if ( orientation( axis ) == Qt::Horizontal )
428 width = newSize.width() * dist;
429 else
430 width = newSize.height() * dist;
431
432 expanded = expandInterval( intervalHint( axis ),
433 width, expandingDirection( axis ) );
434 }
435 break;
436 }
437 }
438
439 return expanded;
440}
441
442/*!
443 Synchronize an axis scale according to the scale of the reference axis
444
445 \param axis Axis index ( see QwtPlot::AxisId )
446 \param reference Interval of the reference axis
447 \param size Size of the canvas
448
449 \return New interval for axis
450*/
451QwtInterval QwtPlotRescaler::syncScale( int axis,
452 const QwtInterval& reference, const QSize &size ) const
453{
454 double dist;
455 if ( orientation( referenceAxis() ) == Qt::Horizontal )
456 dist = reference.width() / size.width();
457 else
458 dist = reference.width() / size.height();
459
460 if ( orientation( axis ) == Qt::Horizontal )
461 dist *= size.width();
462 else
463 dist *= size.height();
464
465 dist /= aspectRatio( axis );
466
467 QwtInterval intv;
468 if ( rescalePolicy() == Fitting )
469 intv = intervalHint( axis );
470 else
471 intv = interval( axis );
472
473 intv = expandInterval( intv, dist, expandingDirection( axis ) );
474
475 return intv;
476}
477
478/*!
479 \return Orientation of an axis
480 \param axis Axis index ( see QwtPlot::AxisId )
481*/
482Qt::Orientation QwtPlotRescaler::orientation( int axis ) const
483{
484 if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
485 return Qt::Vertical;
486
487 return Qt::Horizontal;
488}
489
490/*!
491 \param axis Axis index ( see QwtPlot::AxisId )
492 \return Normalized interval of an axis
493*/
494QwtInterval QwtPlotRescaler::interval( int axis ) const
495{
496 if ( axis < 0 || axis >= QwtPlot::axisCnt )
497 return QwtInterval();
498
499 return plot()->axisScaleDiv( axis ).interval().normalized();
500}
501
502/*!
503 Expand the interval
504
505 \param interval Interval to be expanded
506 \param width Distance to be added to the interval
507 \param direction Direction of the expand operation
508
509 \return Expanded interval
510*/
511QwtInterval QwtPlotRescaler::expandInterval(
512 const QwtInterval &interval, double width,
513 ExpandingDirection direction ) const
514{
515 QwtInterval expanded = interval;
516
517 switch ( direction )
518 {
519 case ExpandUp:
520 expanded.setMinValue( interval.minValue() );
521 expanded.setMaxValue( interval.minValue() + width );
522 break;
523
524 case ExpandDown:
525 expanded.setMaxValue( interval.maxValue() );
526 expanded.setMinValue( interval.maxValue() - width );
527 break;
528
529 case ExpandBoth:
530 default:
531 expanded.setMinValue( interval.minValue() +
532 interval.width() / 2.0 - width / 2.0 );
533 expanded.setMaxValue( expanded.minValue() + width );
534 }
535 return expanded;
536}
537
538double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const
539{
540 const QwtInterval intv = intervalHint( axis );
541
542 double dist = 0.0;
543 if ( !intv.isNull() )
544 {
545 if ( axis == referenceAxis() )
546 dist = intv.width();
547 else
548 {
549 const double r = aspectRatio( axis );
550 if ( r > 0.0 )
551 dist = intv.width() * r;
552 }
553 }
554
555 if ( dist > 0.0 )
556 {
557 if ( orientation( axis ) == Qt::Horizontal )
558 dist /= size.width();
559 else
560 dist /= size.height();
561 }
562
563 return dist;
564}
565
566/*!
567 Update the axes scales
568
569 \param intervals Scale intervals
570*/
571void QwtPlotRescaler::updateScales(
572 QwtInterval intervals[QwtPlot::axisCnt] ) const
573{
574 if ( d_data->inReplot >= 5 )
575 {
576 return;
577 }
578
579 QwtPlot *plt = const_cast<QwtPlot *>( plot() );
580
581 const bool doReplot = plt->autoReplot();
582 plt->setAutoReplot( false );
583
584 for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
585 {
586 if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 )
587 {
588 double v1 = intervals[axis].minValue();
589 double v2 = intervals[axis].maxValue();
590
591 if ( !plt->axisScaleDiv( axis ).isIncreasing() )
592 qSwap( v1, v2 );
593
594 if ( d_data->inReplot >= 1 )
595 d_data->axisData[axis].scaleDiv = plt->axisScaleDiv( axis );
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 QwtPlotCanvas *canvas = qobject_cast<QwtPlotCanvas *>( plt->canvas() );
613
614 bool immediatePaint = false;
615 if ( canvas )
616 {
617 immediatePaint = canvas->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
618 canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
619 }
620
621 plt->setAutoReplot( doReplot );
622
623 d_data->inReplot++;
624 plt->replot();
625 d_data->inReplot--;
626
627 if ( canvas && immediatePaint )
628 {
629 canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true );
630 }
631}
Note: See TracBrowser for help on using the repository browser.