source: ntrip/trunk/BNC/qwt/qwt_plot_multi_barchart.cpp@ 9639

Last change on this file since 9639 was 9383, checked in by stoecker, 4 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

File size: 18.4 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_multi_barchart.h"
11#include "qwt_scale_map.h"
12#include "qwt_column_symbol.h"
13#include "qwt_painter.h"
14#include <qpainter.h>
15#include <qpalette.h>
16#include <qmap.h>
17
18inline static bool qwtIsIncreasing(
19 const QwtScaleMap &map, const QVector<double> &values )
20{
21 bool isInverting = map.isInverting();
22
23 for ( int i = 0; i < values.size(); i++ )
24 {
25 const double y = values[ i ];
26 if ( y != 0.0 )
27 return ( map.isInverting() != ( y > 0.0 ) );
28 }
29
30 return !isInverting;
31}
32
33class QwtPlotMultiBarChart::PrivateData
34{
35public:
36 PrivateData():
37 style( QwtPlotMultiBarChart::Grouped )
38 {
39 }
40
41 QwtPlotMultiBarChart::ChartStyle style;
42 QList<QwtText> barTitles;
43 QMap<int, QwtColumnSymbol *> symbolMap;
44};
45
46/*!
47 Constructor
48 \param title Title of the chart
49*/
50QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QwtText &title ):
51 QwtPlotAbstractBarChart( title )
52{
53 init();
54}
55
56/*!
57 Constructor
58 \param title Title of the chart
59*/
60QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QString &title ):
61 QwtPlotAbstractBarChart( QwtText( title ) )
62{
63 init();
64}
65
66//! Destructor
67QwtPlotMultiBarChart::~QwtPlotMultiBarChart()
68{
69 resetSymbolMap();
70 delete d_data;
71}
72
73void QwtPlotMultiBarChart::init()
74{
75 d_data = new PrivateData;
76 setData( new QwtSetSeriesData() );
77}
78
79//! \return QwtPlotItem::Rtti_PlotBarChart
80int QwtPlotMultiBarChart::rtti() const
81{
82 return QwtPlotItem::Rtti_PlotMultiBarChart;
83}
84
85/*!
86 Initialize data with an array of samples.
87 \param samples Vector of points
88*/
89void QwtPlotMultiBarChart::setSamples(
90 const QVector<QwtSetSample> &samples )
91{
92 setData( new QwtSetSeriesData( samples ) );
93}
94
95/*!
96 Initialize data with an array of samples.
97 \param samples Vector of points
98*/
99void QwtPlotMultiBarChart::setSamples(
100 const QVector< QVector<double> > &samples )
101{
102 QVector<QwtSetSample> s;
103 for ( int i = 0; i < samples.size(); i++ )
104 s += QwtSetSample( i, samples[ i ] );
105
106 setData( new QwtSetSeriesData( s ) );
107}
108
109/*!
110 Assign a series of samples
111
112 setSamples() is just a wrapper for setData() without any additional
113 value - beside that it is easier to find for the developer.
114
115 \param data Data
116 \warning The item takes ownership of the data object, deleting
117 it when its not used anymore.
118*/
119void QwtPlotMultiBarChart::setSamples(
120 QwtSeriesData<QwtSetSample> *data )
121{
122 setData( data );
123}
124
125/*!
126 \brief Set the titles for the bars
127
128 The titles are used for the legend.
129
130 \param titles Bar titles
131
132 \sa barTitles(), legendData()
133 */
134void QwtPlotMultiBarChart::setBarTitles( const QList<QwtText> &titles )
135{
136 d_data->barTitles = titles;
137 itemChanged();
138}
139
140/*!
141 \return Bar titles
142 \sa setBarTitles(), legendData()
143 */
144QList<QwtText> QwtPlotMultiBarChart::barTitles() const
145{
146 return d_data->barTitles;
147}
148
149/*!
150 \brief Add a symbol to the symbol map
151
152 Assign a default symbol for drawing the bar representing all values
153 with the same index in a set.
154
155 \param valueIndex Index of a value in a set
156 \param symbol Symbol used for drawing a bar
157
158 \sa symbol(), resetSymbolMap(), specialSymbol()
159*/
160void QwtPlotMultiBarChart::setSymbol( int valueIndex, QwtColumnSymbol *symbol )
161{
162 if ( valueIndex < 0 )
163 return;
164
165 QMap<int, QwtColumnSymbol *>::iterator it =
166 d_data->symbolMap.find(valueIndex);
167 if ( it == d_data->symbolMap.end() )
168 {
169 if ( symbol != NULL )
170 {
171 d_data->symbolMap.insert( valueIndex, symbol );
172
173 legendChanged();
174 itemChanged();
175 }
176 }
177 else
178 {
179 if ( symbol != it.value() )
180 {
181 delete it.value();
182
183 if ( symbol == NULL )
184 {
185 d_data->symbolMap.remove( valueIndex );
186 }
187 else
188 {
189 it.value() = symbol;
190 }
191
192 legendChanged();
193 itemChanged();
194 }
195 }
196}
197
198/*!
199 Find a symbol in the symbol map
200
201 \param valueIndex Index of a value in a set
202 \return The symbol, that had been set by setSymbol() or NULL.
203
204 \sa setSymbol(), specialSymbol(), drawBar()
205*/
206const QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) const
207{
208 QMap<int, QwtColumnSymbol *>::const_iterator it =
209 d_data->symbolMap.constFind( valueIndex );
210
211 return ( it == d_data->symbolMap.constEnd() ) ? NULL : it.value();
212}
213
214/*!
215 Find a symbol in the symbol map
216
217 \param valueIndex Index of a value in a set
218 \return The symbol, that had been set by setSymbol() or NULL.
219
220 \sa setSymbol(), specialSymbol(), drawBar()
221*/
222QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex )
223{
224 QMap<int, QwtColumnSymbol *>::const_iterator it =
225 d_data->symbolMap.constFind( valueIndex );
226
227 return ( it == d_data->symbolMap.constEnd() ) ? NULL : it.value();
228}
229
230/*!
231 Remove all symbols from the symbol map
232 */
233void QwtPlotMultiBarChart::resetSymbolMap()
234{
235 qDeleteAll( d_data->symbolMap );
236 d_data->symbolMap.clear();
237}
238
239/*!
240 \brief Create a symbol for special values
241
242 Usually the symbols for displaying a bar are set by setSymbols() and
243 common for all sets. By overloading specialSymbol() it is possible to
244 create a temporary symbol() for displaying a special value.
245
246 The symbol has to be created by new each time specialSymbol() is
247 called. As soon as the symbol is painted this symbol gets deleted.
248
249 When no symbol ( NULL ) is returned, the value will be displayed
250 with the standard symbol that is used for all symbols with the same
251 valueIndex.
252
253 \param sampleIndex Index of the sample
254 \param valueIndex Index of the value in the set
255
256 \return NULL, meaning that the value is not special
257
258 */
259QwtColumnSymbol *QwtPlotMultiBarChart::specialSymbol(
260 int sampleIndex, int valueIndex ) const
261{
262 Q_UNUSED( sampleIndex );
263 Q_UNUSED( valueIndex );
264
265 return NULL;
266}
267
268/*!
269 Set the style of the chart
270
271 \param style Chart style
272 \sa style()
273 */
274void QwtPlotMultiBarChart::setStyle( ChartStyle style )
275{
276 if ( style != d_data->style )
277 {
278 d_data->style = style;
279
280 legendChanged();
281 itemChanged();
282 }
283}
284
285/*!
286 \return Style of the chart
287 \sa setStyle()
288 */
289QwtPlotMultiBarChart::ChartStyle QwtPlotMultiBarChart::style() const
290{
291 return d_data->style;
292}
293
294/*!
295 \return Bounding rectangle of all samples.
296 For an empty series the rectangle is invalid.
297*/
298QRectF QwtPlotMultiBarChart::boundingRect() const
299{
300 const size_t numSamples = dataSize();
301
302 if ( numSamples == 0 )
303 return QwtPlotSeriesItem::boundingRect();
304
305 const double baseLine = baseline();
306
307 QRectF rect;
308
309 if ( d_data->style != QwtPlotMultiBarChart::Stacked )
310 {
311 rect = QwtPlotSeriesItem::boundingRect();
312
313 if ( rect.height() >= 0 )
314 {
315 if ( rect.bottom() < baseLine )
316 rect.setBottom( baseLine );
317 if ( rect.top() > baseLine )
318 rect.setTop( baseLine );
319 }
320 }
321 else
322 {
323 double xMin, xMax, yMin, yMax;
324
325 xMin = xMax = 0.0;
326 yMin = yMax = baseLine;
327
328 const QwtSeriesData<QwtSetSample> *series = data();
329
330 for ( size_t i = 0; i < numSamples; i++ )
331 {
332 const QwtSetSample sample = series->sample( i );
333 if ( i == 0 )
334 {
335 xMin = xMax = sample.value;
336 }
337 else
338 {
339 xMin = qMin( xMin, sample.value );
340 xMax = qMax( xMax, sample.value );
341 }
342
343 const double y = baseLine + sample.added();
344
345 yMin = qMin( yMin, y );
346 yMax = qMax( yMax, y );
347 }
348 rect.setRect( xMin, yMin, xMax - xMin, yMax - yMin );
349 }
350
351 if ( orientation() == Qt::Horizontal )
352 rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
353
354 return rect;
355}
356
357/*!
358 Draw an interval of the bar chart
359
360 \param painter Painter
361 \param xMap Maps x-values into pixel coordinates.
362 \param yMap Maps y-values into pixel coordinates.
363 \param canvasRect Contents rectangle of the canvas
364 \param from Index of the first point to be painted
365 \param to Index of the last point to be painted. If to < 0 the
366 curve will be painted to its last point.
367
368 \sa drawSymbols()
369*/
370void QwtPlotMultiBarChart::drawSeries( QPainter *painter,
371 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
372 const QRectF &canvasRect, int from, int to ) const
373{
374 if ( to < 0 )
375 to = dataSize() - 1;
376
377 if ( from < 0 )
378 from = 0;
379
380 if ( from > to )
381 return;
382
383
384 const QRectF br = data()->boundingRect();
385 const QwtInterval interval( br.left(), br.right() );
386
387 painter->save();
388
389 for ( int i = from; i <= to; i++ )
390 {
391 drawSample( painter, xMap, yMap,
392 canvasRect, interval, i, sample( i ) );
393 }
394
395 painter->restore();
396}
397
398/*!
399 Draw a sample
400
401 \param painter Painter
402 \param xMap x map
403 \param yMap y map
404 \param canvasRect Contents rectangle of the canvas
405 \param boundingInterval Bounding interval of sample values
406 \param index Index of the sample to be painted
407 \param sample Sample value
408
409 \sa drawSeries()
410*/
411void QwtPlotMultiBarChart::drawSample( QPainter *painter,
412 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
413 const QRectF &canvasRect, const QwtInterval &boundingInterval,
414 int index, const QwtSetSample& sample ) const
415{
416 if ( sample.set.size() <= 0 )
417 return;
418
419 double sampleW;
420
421 if ( orientation() == Qt::Horizontal )
422 {
423 sampleW = sampleWidth( yMap, canvasRect.height(),
424 boundingInterval.width(), sample.value );
425 }
426 else
427 {
428 sampleW = sampleWidth( xMap, canvasRect.width(),
429 boundingInterval.width(), sample.value );
430 }
431
432 if ( d_data->style == Stacked )
433 {
434 drawStackedBars( painter, xMap, yMap,
435 canvasRect, index, sampleW, sample );
436 }
437 else
438 {
439 drawGroupedBars( painter, xMap, yMap,
440 canvasRect, index, sampleW, sample );
441 }
442}
443
444/*!
445 Draw a grouped sample
446
447 \param painter Painter
448 \param xMap x map
449 \param yMap y map
450 \param canvasRect Contents rectangle of the canvas
451 \param index Index of the sample to be painted
452 \param sampleWidth Boundng width for all bars of the smaple
453 \param sample Sample
454
455 \sa drawSeries(), sampleWidth()
456*/
457void QwtPlotMultiBarChart::drawGroupedBars( QPainter *painter,
458 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
459 const QRectF &canvasRect, int index, double sampleWidth,
460 const QwtSetSample& sample ) const
461{
462 Q_UNUSED( canvasRect );
463
464 const int numBars = sample.set.size();
465 if ( numBars == 0 )
466 return;
467
468 if ( orientation() == Qt::Vertical )
469 {
470 const double barWidth = sampleWidth / numBars;
471
472 const double y1 = yMap.transform( baseline() );
473 const double x0 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
474
475 for ( int i = 0; i < numBars; i++ )
476 {
477 const double x1 = x0 + i * barWidth;
478 const double x2 = x1 + barWidth;
479
480 const double y2 = yMap.transform( sample.set[i] );
481
482 QwtColumnRect barRect;
483 barRect.direction = ( y1 < y2 ) ?
484 QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
485
486 barRect.hInterval = QwtInterval( x1, x2 ).normalized();
487 if ( i != 0 )
488 barRect.hInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
489
490 barRect.vInterval = QwtInterval( y1, y2 ).normalized();
491
492 drawBar( painter, index, i, barRect );
493 }
494 }
495 else
496 {
497 const double barHeight = sampleWidth / numBars;
498
499 const double x1 = xMap.transform( baseline() );
500 const double y0 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
501
502 for ( int i = 0; i < numBars; i++ )
503 {
504 double y1 = y0 + i * barHeight;
505 double y2 = y1 + barHeight;
506
507 double x2 = xMap.transform( sample.set[i] );
508
509 QwtColumnRect barRect;
510 barRect.direction = x1 < x2 ?
511 QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
512
513 barRect.hInterval = QwtInterval( x1, x2 ).normalized();
514
515 barRect.vInterval = QwtInterval( y1, y2 );
516 if ( i != 0 )
517 barRect.vInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
518
519 drawBar( painter, index, i, barRect );
520 }
521 }
522}
523
524/*!
525 Draw a stacked sample
526
527 \param painter Painter
528 \param xMap x map
529 \param yMap y map
530 \param canvasRect Contents rectangle of the canvas
531 \param index Index of the sample to be painted
532 \param sampleWidth Width of the bars
533 \param sample Sample
534
535 \sa drawSeries(), sampleWidth()
536*/
537void QwtPlotMultiBarChart::drawStackedBars( QPainter *painter,
538 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
539 const QRectF &canvasRect, int index,
540 double sampleWidth, const QwtSetSample& sample ) const
541{
542 Q_UNUSED( canvasRect ); // clipping the bars ?
543
544 const int numBars = sample.set.size();
545 if ( numBars == 0 )
546 return;
547
548 QwtInterval::BorderFlag borderFlags = QwtInterval::IncludeBorders;
549
550 if ( orientation() == Qt::Vertical )
551 {
552 const double x1 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
553 const double x2 = x1 + sampleWidth;
554
555 const bool increasing = qwtIsIncreasing( yMap, sample.set );
556
557 QwtColumnRect bar;
558 bar.direction = increasing ?
559 QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
560
561 bar.hInterval = QwtInterval( x1, x2 ).normalized();
562
563 double sum = baseline();
564
565 for ( int i = 0; i < numBars; i++ )
566 {
567 const double si = sample.set[ i ];
568 if ( si == 0.0 )
569 continue;
570
571 const double y1 = yMap.transform( sum );
572 const double y2 = yMap.transform( sum + si );
573
574 if ( ( y2 > y1 ) != increasing )
575 {
576 // stacked bars need to be in the same direction
577 continue;
578 }
579
580 bar.vInterval = QwtInterval( y1, y2 ).normalized();
581 bar.vInterval.setBorderFlags( borderFlags );
582
583 drawBar( painter, index, i, bar );
584
585 sum += si;
586
587 if ( increasing )
588 borderFlags = QwtInterval::ExcludeMinimum;
589 else
590 borderFlags = QwtInterval::ExcludeMaximum;
591 }
592 }
593 else
594 {
595 const double y1 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
596 const double y2 = y1 + sampleWidth;
597
598 const bool increasing = qwtIsIncreasing( xMap, sample.set );
599
600 QwtColumnRect bar;
601 bar.direction = increasing ?
602 QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
603 bar.vInterval = QwtInterval( y1, y2 ).normalized();
604
605 double sum = baseline();
606
607 for ( int i = 0; i < sample.set.size(); i++ )
608 {
609 const double si = sample.set[ i ];
610 if ( si == 0.0 )
611 continue;
612
613 const double x1 = xMap.transform( sum );
614 const double x2 = xMap.transform( sum + si );
615
616 if ( ( x2 > x1 ) != increasing )
617 {
618 // stacked bars need to be in the same direction
619 continue;
620 }
621
622 bar.hInterval = QwtInterval( x1, x2 ).normalized();
623 bar.hInterval.setBorderFlags( borderFlags );
624
625 drawBar( painter, index, i, bar );
626
627 sum += si;
628
629 if ( increasing )
630 borderFlags = QwtInterval::ExcludeMinimum;
631 else
632 borderFlags = QwtInterval::ExcludeMaximum;
633 }
634 }
635}
636
637/*!
638 Draw a bar
639
640 \param painter Painter
641 \param sampleIndex Index of the sample - might be -1 when the
642 bar is painted for the legend
643 \param valueIndex Index of a value in a set
644 \param rect Directed target rectangle for the bar
645
646 \sa drawSeries()
647*/
648void QwtPlotMultiBarChart::drawBar( QPainter *painter,
649 int sampleIndex, int valueIndex, const QwtColumnRect &rect ) const
650{
651 const QwtColumnSymbol *specialSym = NULL;
652 if ( sampleIndex >= 0 )
653 specialSym = specialSymbol( sampleIndex, valueIndex );
654
655 const QwtColumnSymbol *sym = specialSym;
656 if ( sym == NULL )
657 sym = symbol( valueIndex );
658
659 if ( sym )
660 {
661 sym->draw( painter, rect );
662 }
663 else
664 {
665 // we build a temporary default symbol
666 QwtColumnSymbol columnSymbol( QwtColumnSymbol::Box );
667 columnSymbol.setLineWidth( 1 );
668 columnSymbol.setFrameStyle( QwtColumnSymbol::Plain );
669 columnSymbol.draw( painter, rect );
670 }
671
672 delete specialSym;
673}
674
675/*!
676 \return Information to be displayed on the legend
677
678 The chart is represented by a list of entries - one for each bar title.
679 Each element contains a bar title and an icon showing its corresponding bar.
680
681 \sa barTitles(), legendIcon(), legendIconSize()
682*/
683QList<QwtLegendData> QwtPlotMultiBarChart::legendData() const
684{
685 QList<QwtLegendData> list;
686
687 for ( int i = 0; i < d_data->barTitles.size(); i++ )
688 {
689 QwtLegendData data;
690
691 QVariant titleValue;
692 qVariantSetValue( titleValue, d_data->barTitles[i] );
693 data.setValue( QwtLegendData::TitleRole, titleValue );
694
695 if ( !legendIconSize().isEmpty() )
696 {
697 QVariant iconValue;
698 qVariantSetValue( iconValue,
699 legendIcon( i, legendIconSize() ) );
700
701 data.setValue( QwtLegendData::IconRole, iconValue );
702 }
703
704 list += data;
705 }
706
707 return list;
708}
709
710/*!
711 \return Icon for representing a bar on the legend
712
713 \param index Index of the bar
714 \param size Icon size
715
716 \return An icon showing a bar
717 \sa drawBar(), legendData()
718 */
719QwtGraphic QwtPlotMultiBarChart::legendIcon( int index,
720 const QSizeF &size ) const
721{
722 QwtColumnRect column;
723 column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
724 column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
725
726 QwtGraphic icon;
727 icon.setDefaultSize( size );
728 icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
729
730 QPainter painter( &icon );
731 painter.setRenderHint( QPainter::Antialiasing,
732 testRenderHint( QwtPlotItem::RenderAntialiased ) );
733
734 drawBar( &painter, -1, index, column );
735
736 return icon;
737}
738
Note: See TracBrowser for help on using the repository browser.