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.h"
11 #include "qwt_math.h"
12 #include "qwt_scale_widget.h"
13 #include "qwt_scale_div.h"
14 #include "qwt_scale_engine.h"
15 
16 class QwtPlot::AxisData
17 {
18 public:
19     bool isEnabled;
20     bool doAutoScale;
21 
22     double minValue;
23     double maxValue;
24     double stepSize;
25 
26     int maxMajor;
27     int maxMinor;
28 
29     bool isValid;
30 
31     QwtScaleDiv scaleDiv;
32     QwtScaleEngine *scaleEngine;
33     QwtScaleWidget *scaleWidget;
34 };
35 
36 //! Initialize axes
initAxesData()37 void QwtPlot::initAxesData()
38 {
39     int axisId;
40 
41     for ( axisId = 0; axisId < axisCnt; axisId++ )
42         d_axisData[axisId] = new AxisData;
43 
44     d_axisData[yLeft]->scaleWidget =
45         new QwtScaleWidget( QwtScaleDraw::LeftScale, this );
46     d_axisData[yRight]->scaleWidget =
47         new QwtScaleWidget( QwtScaleDraw::RightScale, this );
48     d_axisData[xTop]->scaleWidget =
49         new QwtScaleWidget( QwtScaleDraw::TopScale, this );
50     d_axisData[xBottom]->scaleWidget =
51         new QwtScaleWidget( QwtScaleDraw::BottomScale, this );
52 
53     d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" );
54     d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" );
55     d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" );
56     d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" );
57 
58 #if 1
59     // better find the font sizes from the application font
60     QFont fscl( fontInfo().family(), 10 );
61     QFont fttl( fontInfo().family(), 12, QFont::Bold );
62 #endif
63 
64     for ( axisId = 0; axisId < axisCnt; axisId++ )
65     {
66         AxisData &d = *d_axisData[axisId];
67 
68         d.scaleEngine = new QwtLinearScaleEngine;
69 
70         d.scaleWidget->setTransformation(
71             d.scaleEngine->transformation() );
72 
73         d.scaleWidget->setFont( fscl );
74         d.scaleWidget->setMargin( 2 );
75 
76         QwtText text = d.scaleWidget->title();
77         text.setFont( fttl );
78         d.scaleWidget->setTitle( text );
79 
80         d.doAutoScale = true;
81 
82         d.minValue = 0.0;
83         d.maxValue = 1000.0;
84         d.stepSize = 0.0;
85 
86         d.maxMinor = 5;
87         d.maxMajor = 8;
88 
89 
90         d.isValid = false;
91     }
92 
93     d_axisData[yLeft]->isEnabled = true;
94     d_axisData[yRight]->isEnabled = false;
95     d_axisData[xBottom]->isEnabled = true;
96     d_axisData[xTop]->isEnabled = false;
97 }
98 
deleteAxesData()99 void QwtPlot::deleteAxesData()
100 {
101     for ( int axisId = 0; axisId < axisCnt; axisId++ )
102     {
103         delete d_axisData[axisId]->scaleEngine;
104         delete d_axisData[axisId];
105         d_axisData[axisId] = NULL;
106     }
107 }
108 
109 /*!
110   \return Scale widget of the specified axis, or NULL if axisId is invalid.
111   \param axisId Axis index
112 */
axisWidget(int axisId) const113 const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const
114 {
115     if ( axisValid( axisId ) )
116         return d_axisData[axisId]->scaleWidget;
117 
118     return NULL;
119 }
120 
121 /*!
122   \return Scale widget of the specified axis, or NULL if axisId is invalid.
123   \param axisId Axis index
124 */
axisWidget(int axisId)125 QwtScaleWidget *QwtPlot::axisWidget( int axisId )
126 {
127     if ( axisValid( axisId ) )
128         return d_axisData[axisId]->scaleWidget;
129 
130     return NULL;
131 }
132 
133 /*!
134   Change the scale engine for an axis
135 
136   \param axisId Axis index
137   \param scaleEngine Scale engine
138 
139   \sa axisScaleEngine()
140 */
setAxisScaleEngine(int axisId,QwtScaleEngine * scaleEngine)141 void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine )
142 {
143     if ( axisValid( axisId ) && scaleEngine != NULL )
144     {
145         AxisData &d = *d_axisData[axisId];
146 
147         delete d.scaleEngine;
148         d.scaleEngine = scaleEngine;
149 
150         d_axisData[axisId]->scaleWidget->setTransformation(
151             scaleEngine->transformation() );
152 
153         d.isValid = false;
154 
155         autoRefresh();
156     }
157 }
158 
159 /*!
160   \param axisId Axis index
161   \return Scale engine for a specific axis
162 */
axisScaleEngine(int axisId)163 QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId )
164 {
165     if ( axisValid( axisId ) )
166         return d_axisData[axisId]->scaleEngine;
167     else
168         return NULL;
169 }
170 
171 /*!
172   \param axisId Axis index
173   \return Scale engine for a specific axis
174 */
axisScaleEngine(int axisId) const175 const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const
176 {
177     if ( axisValid( axisId ) )
178         return d_axisData[axisId]->scaleEngine;
179     else
180         return NULL;
181 }
182 /*!
183   \return \c True, if autoscaling is enabled
184   \param axisId Axis index
185 */
axisAutoScale(int axisId) const186 bool QwtPlot::axisAutoScale( int axisId ) const
187 {
188     if ( axisValid( axisId ) )
189         return d_axisData[axisId]->doAutoScale;
190     else
191         return false;
192 }
193 
194 /*!
195   \return \c True, if a specified axis is enabled
196   \param axisId Axis index
197 */
axisEnabled(int axisId) const198 bool QwtPlot::axisEnabled( int axisId ) const
199 {
200     if ( axisValid( axisId ) )
201         return d_axisData[axisId]->isEnabled;
202     else
203         return false;
204 }
205 
206 /*!
207   \return The font of the scale labels for a specified axis
208   \param axisId Axis index
209 */
axisFont(int axisId) const210 QFont QwtPlot::axisFont( int axisId ) const
211 {
212     if ( axisValid( axisId ) )
213         return axisWidget( axisId )->font();
214     else
215         return QFont();
216 
217 }
218 
219 /*!
220   \return The maximum number of major ticks for a specified axis
221   \param axisId Axis index
222   \sa setAxisMaxMajor(), QwtScaleEngine::divideScale()
223 */
axisMaxMajor(int axisId) const224 int QwtPlot::axisMaxMajor( int axisId ) const
225 {
226     if ( axisValid( axisId ) )
227         return d_axisData[axisId]->maxMajor;
228     else
229         return 0;
230 }
231 
232 /*!
233   \return the maximum number of minor ticks for a specified axis
234   \param axisId Axis index
235   \sa setAxisMaxMinor(), QwtScaleEngine::divideScale()
236 */
axisMaxMinor(int axisId) const237 int QwtPlot::axisMaxMinor( int axisId ) const
238 {
239     if ( axisValid( axisId ) )
240         return d_axisData[axisId]->maxMinor;
241     else
242         return 0;
243 }
244 
245 /*!
246   \brief Return the scale division of a specified axis
247 
248   axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound()
249   are the current limits of the axis scale.
250 
251   \param axisId Axis index
252   \return Scale division
253 
254   \sa QwtScaleDiv, setAxisScaleDiv(), QwtScaleEngine::divideScale()
255 */
axisScaleDiv(int axisId) const256 const QwtScaleDiv &QwtPlot::axisScaleDiv( int axisId ) const
257 {
258     return d_axisData[axisId]->scaleDiv;
259 }
260 
261 /*!
262   \brief Return the scale draw of a specified axis
263 
264   \param axisId Axis index
265   \return Specified scaleDraw for axis, or NULL if axis is invalid.
266 */
axisScaleDraw(int axisId) const267 const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const
268 {
269     if ( !axisValid( axisId ) )
270         return NULL;
271 
272     return axisWidget( axisId )->scaleDraw();
273 }
274 
275 /*!
276   \brief Return the scale draw of a specified axis
277 
278   \param axisId Axis index
279   \return Specified scaleDraw for axis, or NULL if axis is invalid.
280 */
axisScaleDraw(int axisId)281 QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId )
282 {
283     if ( !axisValid( axisId ) )
284         return NULL;
285 
286     return axisWidget( axisId )->scaleDraw();
287 }
288 
289 /*!
290   \brief Return the step size parameter that has been set in setAxisScale.
291 
292   This doesn't need to be the step size of the current scale.
293 
294   \param axisId Axis index
295   \return step size parameter value
296 
297    \sa setAxisScale(), QwtScaleEngine::divideScale()
298 */
axisStepSize(int axisId) const299 double QwtPlot::axisStepSize( int axisId ) const
300 {
301     if ( !axisValid( axisId ) )
302         return 0;
303 
304     return d_axisData[axisId]->stepSize;
305 }
306 
307 /*!
308   \brief Return the current interval of the specified axis
309 
310   This is only a convenience function for axisScaleDiv( axisId )->interval();
311 
312   \param axisId Axis index
313   \return Scale interval
314 
315   \sa QwtScaleDiv, axisScaleDiv()
316 */
axisInterval(int axisId) const317 QwtInterval QwtPlot::axisInterval( int axisId ) const
318 {
319     if ( !axisValid( axisId ) )
320         return QwtInterval();
321 
322     return d_axisData[axisId]->scaleDiv.interval();
323 }
324 
325 /*!
326   \return Title of a specified axis
327   \param axisId Axis index
328 */
axisTitle(int axisId) const329 QwtText QwtPlot::axisTitle( int axisId ) const
330 {
331     if ( axisValid( axisId ) )
332         return axisWidget( axisId )->title();
333     else
334         return QwtText();
335 }
336 
337 /*!
338   \brief Enable or disable a specified axis
339 
340   When an axis is disabled, this only means that it is not
341   visible on the screen. Curves, markers and can be attached
342   to disabled axes, and transformation of screen coordinates
343   into values works as normal.
344 
345   Only xBottom and yLeft are enabled by default.
346 
347   \param axisId Axis index
348   \param tf \c true (enabled) or \c false (disabled)
349 */
enableAxis(int axisId,bool tf)350 void QwtPlot::enableAxis( int axisId, bool tf )
351 {
352     if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled )
353     {
354         d_axisData[axisId]->isEnabled = tf;
355         updateLayout();
356     }
357 }
358 
359 /*!
360   Transform the x or y coordinate of a position in the
361   drawing region into a value.
362 
363   \param axisId Axis index
364   \param pos position
365 
366   \return Position as axis coordinate
367 
368   \warning The position can be an x or a y coordinate,
369            depending on the specified axis.
370 */
invTransform(int axisId,int pos) const371 double QwtPlot::invTransform( int axisId, int pos ) const
372 {
373     if ( axisValid( axisId ) )
374         return( canvasMap( axisId ).invTransform( pos ) );
375     else
376         return 0.0;
377 }
378 
379 
380 /*!
381   \brief Transform a value into a coordinate in the plotting region
382 
383   \param axisId Axis index
384   \param value value
385   \return X or Y coordinate in the plotting region corresponding
386           to the value.
387 */
transform(int axisId,double value) const388 double QwtPlot::transform( int axisId, double value ) const
389 {
390     if ( axisValid( axisId ) )
391         return( canvasMap( axisId ).transform( value ) );
392     else
393         return 0.0;
394 }
395 
396 /*!
397   \brief Change the font of an axis
398 
399   \param axisId Axis index
400   \param font Font
401   \warning This function changes the font of the tick labels,
402            not of the axis title.
403 */
setAxisFont(int axisId,const QFont & font)404 void QwtPlot::setAxisFont( int axisId, const QFont &font )
405 {
406     if ( axisValid( axisId ) )
407         axisWidget( axisId )->setFont( font );
408 }
409 
410 /*!
411   \brief Enable autoscaling for a specified axis
412 
413   This member function is used to switch back to autoscaling mode
414   after a fixed scale has been set. Autoscaling is enabled by default.
415 
416   \param axisId Axis index
417   \param on On/Off
418   \sa setAxisScale(), setAxisScaleDiv(), updateAxes()
419 
420   \note The autoscaling flag has no effect until updateAxes() is executed
421         ( called by replot() ).
422 */
setAxisAutoScale(int axisId,bool on)423 void QwtPlot::setAxisAutoScale( int axisId, bool on )
424 {
425     if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) )
426     {
427         d_axisData[axisId]->doAutoScale = on;
428         autoRefresh();
429     }
430 }
431 
432 /*!
433   \brief Disable autoscaling and specify a fixed scale for a selected axis.
434 
435   In updateAxes() the scale engine calculates a scale division from the
436   specified parameters, that will be assigned to the scale widget. So
437   updates of the scale widget usually happen delayed with the next replot.
438 
439   \param axisId Axis index
440   \param min Minimum of the scale
441   \param max Maximum of the scale
442   \param stepSize Major step size. If <code>step == 0</code>, the step size is
443                   calculated automatically using the maxMajor setting.
444 
445   \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize(), QwtScaleEngine::divideScale()
446 */
setAxisScale(int axisId,double min,double max,double stepSize)447 void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize )
448 {
449     if ( axisValid( axisId ) )
450     {
451         AxisData &d = *d_axisData[axisId];
452 
453         d.doAutoScale = false;
454         d.isValid = false;
455 
456         d.minValue = min;
457         d.maxValue = max;
458         d.stepSize = stepSize;
459 
460         autoRefresh();
461     }
462 }
463 
464 /*!
465   \brief Disable autoscaling and specify a fixed scale for a selected axis.
466 
467   The scale division will be stored locally only until the next call
468   of updateAxes(). So updates of the scale widget usually happen delayed with
469   the next replot.
470 
471   \param axisId Axis index
472   \param scaleDiv Scale division
473 
474   \sa setAxisScale(), setAxisAutoScale()
475 */
setAxisScaleDiv(int axisId,const QwtScaleDiv & scaleDiv)476 void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv )
477 {
478     if ( axisValid( axisId ) )
479     {
480         AxisData &d = *d_axisData[axisId];
481 
482         d.doAutoScale = false;
483         d.scaleDiv = scaleDiv;
484         d.isValid = true;
485 
486         autoRefresh();
487     }
488 }
489 
490 /*!
491   \brief Set a scale draw
492 
493   \param axisId Axis index
494   \param scaleDraw Object responsible for drawing scales.
495 
496   By passing scaleDraw it is possible to extend QwtScaleDraw
497   functionality and let it take place in QwtPlot. Please note
498   that scaleDraw has to be created with new and will be deleted
499   by the corresponding QwtScale member ( like a child object ).
500 
501   \sa QwtScaleDraw, QwtScaleWidget
502   \warning The attributes of scaleDraw will be overwritten by those of the
503            previous QwtScaleDraw.
504 */
505 
setAxisScaleDraw(int axisId,QwtScaleDraw * scaleDraw)506 void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw )
507 {
508     if ( axisValid( axisId ) )
509     {
510         axisWidget( axisId )->setScaleDraw( scaleDraw );
511         autoRefresh();
512     }
513 }
514 
515 /*!
516   Change the alignment of the tick labels
517 
518   \param axisId Axis index
519   \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
520 
521   \sa QwtScaleDraw::setLabelAlignment()
522 */
setAxisLabelAlignment(int axisId,Qt::Alignment alignment)523 void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment )
524 {
525     if ( axisValid( axisId ) )
526         axisWidget( axisId )->setLabelAlignment( alignment );
527 }
528 
529 /*!
530   Rotate all tick labels
531 
532   \param axisId Axis index
533   \param rotation Angle in degrees. When changing the label rotation,
534                   the label alignment might be adjusted too.
535 
536   \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment()
537 */
setAxisLabelRotation(int axisId,double rotation)538 void QwtPlot::setAxisLabelRotation( int axisId, double rotation )
539 {
540     if ( axisValid( axisId ) )
541         axisWidget( axisId )->setLabelRotation( rotation );
542 }
543 
544 /*!
545   Set the maximum number of minor scale intervals for a specified axis
546 
547   \param axisId Axis index
548   \param maxMinor Maximum number of minor steps
549 
550   \sa axisMaxMinor()
551 */
setAxisMaxMinor(int axisId,int maxMinor)552 void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor )
553 {
554     if ( axisValid( axisId ) )
555     {
556         maxMinor = qBound( 0, maxMinor, 100 );
557 
558         AxisData &d = *d_axisData[axisId];
559         if ( maxMinor != d.maxMinor )
560         {
561             d.maxMinor = maxMinor;
562             d.isValid = false;
563             autoRefresh();
564         }
565     }
566 }
567 
568 /*!
569   Set the maximum number of major scale intervals for a specified axis
570 
571   \param axisId Axis index
572   \param maxMajor Maximum number of major steps
573 
574   \sa axisMaxMajor()
575 */
setAxisMaxMajor(int axisId,int maxMajor)576 void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor )
577 {
578     if ( axisValid( axisId ) )
579     {
580         maxMajor = qBound( 1, maxMajor, 10000 );
581 
582         AxisData &d = *d_axisData[axisId];
583         if ( maxMajor != d.maxMajor )
584         {
585             d.maxMajor = maxMajor;
586             d.isValid = false;
587             autoRefresh();
588         }
589     }
590 }
591 
592 /*!
593   \brief Change the title of a specified axis
594 
595   \param axisId Axis index
596   \param title axis title
597 */
setAxisTitle(int axisId,const QString & title)598 void QwtPlot::setAxisTitle( int axisId, const QString &title )
599 {
600     if ( axisValid( axisId ) )
601         axisWidget( axisId )->setTitle( title );
602 }
603 
604 /*!
605   \brief Change the title of a specified axis
606 
607   \param axisId Axis index
608   \param title Axis title
609 */
setAxisTitle(int axisId,const QwtText & title)610 void QwtPlot::setAxisTitle( int axisId, const QwtText &title )
611 {
612     if ( axisValid( axisId ) )
613         axisWidget( axisId )->setTitle( title );
614 }
615 
616 /*!
617   \brief Rebuild the axes scales
618 
619   In case of autoscaling the boundaries of a scale are calculated
620   from the bounding rectangles of all plot items, having the
621   QwtPlotItem::AutoScale flag enabled ( QwtScaleEngine::autoScale() ).
622   Then a scale division is calculated ( QwtScaleEngine::didvideScale() )
623   and assigned to scale widget.
624 
625   When the scale boundaries have been assigned with setAxisScale() a
626   scale division is calculated ( QwtScaleEngine::didvideScale() )
627   for this interval and assigned to the scale widget.
628 
629   When the scale has been set explicitly by setAxisScaleDiv() the
630   locally stored scale division gets assigned to the scale widget.
631 
632   The scale widget indicates modifications by emitting a
633   QwtScaleWidget::scaleDivChanged() signal.
634 
635   updateAxes() is usually called by replot().
636 
637   \sa setAxisAutoScale(), setAxisScale(), setAxisScaleDiv(), replot()
638       QwtPlotItem::boundingRect()
639  */
updateAxes()640 void QwtPlot::updateAxes()
641 {
642     // Find bounding interval of the item data
643     // for all axes, where autoscaling is enabled
644 
645     QwtInterval intv[axisCnt];
646 
647     const QwtPlotItemList& itmList = itemList();
648 
649     QwtPlotItemIterator it;
650     for ( it = itmList.begin(); it != itmList.end(); ++it )
651     {
652         const QwtPlotItem *item = *it;
653 
654         if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) )
655             continue;
656 
657         if ( !item->isVisible() )
658             continue;
659 
660         if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) )
661         {
662             const QRectF rect = item->boundingRect();
663 
664             if ( rect.width() >= 0.0 )
665                 intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() );
666 
667             if ( rect.height() >= 0.0 )
668                 intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() );
669         }
670     }
671 
672     // Adjust scales
673 
674     for ( int axisId = 0; axisId < axisCnt; axisId++ )
675     {
676         AxisData &d = *d_axisData[axisId];
677 
678         double minValue = d.minValue;
679         double maxValue = d.maxValue;
680         double stepSize = d.stepSize;
681 
682         if ( d.doAutoScale && intv[axisId].isValid() )
683         {
684             d.isValid = false;
685 
686             minValue = intv[axisId].minValue();
687             maxValue = intv[axisId].maxValue();
688 
689             d.scaleEngine->autoScale( d.maxMajor,
690                 minValue, maxValue, stepSize );
691         }
692         if ( !d.isValid )
693         {
694             d.scaleDiv = d.scaleEngine->divideScale(
695                 minValue, maxValue,
696                 d.maxMajor, d.maxMinor, stepSize );
697             d.isValid = true;
698         }
699 
700         QwtScaleWidget *scaleWidget = axisWidget( axisId );
701         scaleWidget->setScaleDiv( d.scaleDiv );
702 
703         int startDist, endDist;
704         scaleWidget->getBorderDistHint( startDist, endDist );
705         scaleWidget->setBorderDist( startDist, endDist );
706     }
707 
708     for ( it = itmList.begin(); it != itmList.end(); ++it )
709     {
710         QwtPlotItem *item = *it;
711         if ( item->testItemInterest( QwtPlotItem::ScaleInterest ) )
712         {
713             item->updateScaleDiv( axisScaleDiv( item->xAxis() ),
714                 axisScaleDiv( item->yAxis() ) );
715         }
716     }
717 }
718 
719