source: ntrip/trunk/BNC/qwt/qwt_plot_intervalcurve.cpp@ 7040

Last change on this file since 7040 was 4271, checked in by mervart, 12 years ago
File size: 13.9 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_intervalcurve.h"
11#include "qwt_interval_symbol.h"
12#include "qwt_scale_map.h"
13#include "qwt_clipper.h"
14#include "qwt_painter.h"
15
16#include <qpainter.h>
17
18static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample,
19 double xMin, double xMax, double yMin, double yMax )
20{
21 const double y = sample.value;
22 const double x1 = sample.interval.minValue();
23 const double x2 = sample.interval.maxValue();
24
25 const bool isOffScreen = ( y < yMin ) || ( y > yMax )
26 || ( x1 < xMin && x2 < xMin ) || ( x1 > yMax && x2 > xMax );
27
28 return !isOffScreen;
29}
30
31static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample,
32 double xMin, double xMax, double yMin, double yMax )
33{
34 const double x = sample.value;
35 const double y1 = sample.interval.minValue();
36 const double y2 = sample.interval.maxValue();
37
38 const bool isOffScreen = ( x < xMin ) || ( x > xMax )
39 || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax );
40
41 return !isOffScreen;
42}
43
44class QwtPlotIntervalCurve::PrivateData
45{
46public:
47 PrivateData():
48 style( Tube ),
49 symbol( NULL ),
50 pen( Qt::black ),
51 brush( Qt::white )
52 {
53 paintAttributes = QwtPlotIntervalCurve::ClipPolygons;
54 paintAttributes |= QwtPlotIntervalCurve::ClipSymbol;
55
56 pen.setCapStyle( Qt::FlatCap );
57 }
58
59 ~PrivateData()
60 {
61 delete symbol;
62 }
63
64 CurveStyle style;
65 const QwtIntervalSymbol *symbol;
66
67 QPen pen;
68 QBrush brush;
69
70 QwtPlotIntervalCurve::PaintAttributes paintAttributes;
71};
72
73/*!
74 Constructor
75 \param title Title of the curve
76*/
77QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ):
78 QwtPlotSeriesItem<QwtIntervalSample>( title )
79{
80 init();
81}
82
83/*!
84 Constructor
85 \param title Title of the curve
86*/
87QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ):
88 QwtPlotSeriesItem<QwtIntervalSample>( QwtText( title ) )
89{
90 init();
91}
92
93//! Destructor
94QwtPlotIntervalCurve::~QwtPlotIntervalCurve()
95{
96 delete d_data;
97}
98
99//! Initialize internal members
100void QwtPlotIntervalCurve::init()
101{
102 setItemAttribute( QwtPlotItem::Legend, true );
103 setItemAttribute( QwtPlotItem::AutoScale, true );
104
105 d_data = new PrivateData;
106 d_series = new QwtIntervalSeriesData();
107
108 setZ( 19.0 );
109}
110
111//! \return QwtPlotItem::Rtti_PlotIntervalCurve
112int QwtPlotIntervalCurve::rtti() const
113{
114 return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve;
115}
116
117/*!
118 Specify an attribute how to draw the curve
119
120 \param attribute Paint attribute
121 \param on On/Off
122 \sa testPaintAttribute()
123*/
124void QwtPlotIntervalCurve::setPaintAttribute(
125 PaintAttribute attribute, bool on )
126{
127 if ( on )
128 d_data->paintAttributes |= attribute;
129 else
130 d_data->paintAttributes &= ~attribute;
131}
132
133/*!
134 \brief Return the current paint attributes
135 \sa PaintAttribute, setPaintAttribute()
136*/
137bool QwtPlotIntervalCurve::testPaintAttribute(
138 PaintAttribute attribute ) const
139{
140 return ( d_data->paintAttributes & attribute );
141}
142
143/*!
144 Initialize data with an array of samples.
145 \param samples Vector of samples
146*/
147void QwtPlotIntervalCurve::setSamples(
148 const QVector<QwtIntervalSample> &samples )
149{
150 delete d_series;
151 d_series = new QwtIntervalSeriesData( samples );
152 itemChanged();
153}
154
155/*!
156 Set the curve's drawing style
157
158 \param style Curve style
159 \sa CurveStyle, style()
160*/
161void QwtPlotIntervalCurve::setStyle( CurveStyle style )
162{
163 if ( style != d_data->style )
164 {
165 d_data->style = style;
166 itemChanged();
167 }
168}
169
170/*!
171 \brief Return the current style
172 \sa setStyle()
173*/
174QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const
175{
176 return d_data->style;
177}
178
179/*!
180 Assign a symbol.
181
182 \param symbol Symbol
183 \sa symbol()
184*/
185void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol )
186{
187 if ( symbol != d_data->symbol )
188 {
189 delete d_data->symbol;
190 d_data->symbol = symbol;
191 itemChanged();
192 }
193}
194
195/*!
196 \return Current symbol or NULL, when no symbol has been assigned
197 \sa setSymbol()
198*/
199const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const
200{
201 return d_data->symbol;
202}
203
204/*!
205 \brief Assign a pen
206 \param pen New pen
207 \sa pen(), brush()
208*/
209void QwtPlotIntervalCurve::setPen( const QPen &pen )
210{
211 if ( pen != d_data->pen )
212 {
213 d_data->pen = pen;
214 itemChanged();
215 }
216}
217
218/*!
219 \brief Return the pen used to draw the lines
220 \sa setPen(), brush()
221*/
222const QPen& QwtPlotIntervalCurve::pen() const
223{
224 return d_data->pen;
225}
226
227/*!
228 Assign a brush.
229
230 The brush is used to fill the area in Tube style().
231
232 \param brush Brush
233 \sa brush(), pen(), setStyle(), CurveStyle
234*/
235void QwtPlotIntervalCurve::setBrush( const QBrush &brush )
236{
237 if ( brush != d_data->brush )
238 {
239 d_data->brush = brush;
240 itemChanged();
241 }
242}
243
244/*!
245 \return Brush used to fill the area in Tube style()
246 \sa setBrush(), setStyle(), CurveStyle
247*/
248const QBrush& QwtPlotIntervalCurve::brush() const
249{
250 return d_data->brush;
251}
252
253/*!
254 \return Bounding rectangle of all samples.
255 For an empty series the rectangle is invalid.
256*/
257QRectF QwtPlotIntervalCurve::boundingRect() const
258{
259 QRectF rect = QwtPlotSeriesItem<QwtIntervalSample>::boundingRect();
260 if ( rect.isValid() && orientation() == Qt::Vertical )
261 rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
262
263 return rect;
264}
265
266/*!
267 Draw a subset of the samples
268
269 \param painter Painter
270 \param xMap Maps x-values into pixel coordinates.
271 \param yMap Maps y-values into pixel coordinates.
272 \param canvasRect Contents rect of the canvas
273 \param from Index of the first sample to be painted
274 \param to Index of the last sample to be painted. If to < 0 the
275 series will be painted to its last sample.
276
277 \sa drawTube(), drawSymbols()
278*/
279void QwtPlotIntervalCurve::drawSeries( QPainter *painter,
280 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
281 const QRectF &canvasRect, int from, int to ) const
282{
283 if ( to < 0 )
284 to = dataSize() - 1;
285
286 if ( from < 0 )
287 from = 0;
288
289 if ( from > to )
290 return;
291
292 switch ( d_data->style )
293 {
294 case Tube:
295 drawTube( painter, xMap, yMap, canvasRect, from, to );
296 break;
297
298 case NoCurve:
299 default:
300 break;
301 }
302
303 if ( d_data->symbol &&
304 ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
305 {
306 drawSymbols( painter, *d_data->symbol,
307 xMap, yMap, canvasRect, from, to );
308 }
309}
310
311/*!
312 Draw a tube
313
314 Builds 2 curves from the upper and lower limits of the intervals
315 and draws them with the pen(). The area between the curves is
316 filled with the brush().
317
318 \param painter Painter
319 \param xMap Maps x-values into pixel coordinates.
320 \param yMap Maps y-values into pixel coordinates.
321 \param canvasRect Contents rect of the canvas
322 \param from Index of the first sample to be painted
323 \param to Index of the last sample to be painted. If to < 0 the
324 series will be painted to its last sample.
325
326 \sa drawSeries(), drawSymbols()
327*/
328void QwtPlotIntervalCurve::drawTube( QPainter *painter,
329 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
330 const QRectF &canvasRect, int from, int to ) const
331{
332 const bool doAlign = QwtPainter::roundingAlignment( painter );
333
334 painter->save();
335
336 const size_t size = to - from + 1;
337 QPolygonF polygon( 2 * size );
338 QPointF *points = polygon.data();
339
340 for ( uint i = 0; i < size; i++ )
341 {
342 QPointF &minValue = points[i];
343 QPointF &maxValue = points[2 * size - 1 - i];
344
345 const QwtIntervalSample intervalSample = sample( from + i );
346 if ( orientation() == Qt::Vertical )
347 {
348 double x = xMap.transform( intervalSample.value );
349 double y1 = yMap.transform( intervalSample.interval.minValue() );
350 double y2 = yMap.transform( intervalSample.interval.maxValue() );
351 if ( doAlign )
352 {
353 x = qRound( x );
354 y1 = qRound( y1 );
355 y2 = qRound( y2 );
356 }
357
358 minValue.rx() = x;
359 minValue.ry() = y1;
360 maxValue.rx() = x;
361 maxValue.ry() = y2;
362 }
363 else
364 {
365 double y = yMap.transform( intervalSample.value );
366 double x1 = xMap.transform( intervalSample.interval.minValue() );
367 double x2 = xMap.transform( intervalSample.interval.maxValue() );
368 if ( doAlign )
369 {
370 y = qRound( y );
371 x1 = qRound( x1 );
372 x2 = qRound( x2 );
373 }
374
375 minValue.rx() = x1;
376 minValue.ry() = y;
377 maxValue.rx() = x2;
378 maxValue.ry() = y;
379 }
380 }
381
382 if ( d_data->brush.style() != Qt::NoBrush )
383 {
384 painter->setPen( QPen( Qt::NoPen ) );
385 painter->setBrush( d_data->brush );
386
387 if ( d_data->paintAttributes & ClipPolygons )
388 {
389 const qreal m = 1.0;
390 const QPolygonF p = QwtClipper::clipPolygonF(
391 canvasRect.adjusted(-m, -m, m, m), polygon, true );
392
393 QwtPainter::drawPolygon( painter, p );
394 }
395 else
396 {
397 QwtPainter::drawPolygon( painter, polygon );
398 }
399 }
400
401 if ( d_data->pen.style() != Qt::NoPen )
402 {
403 painter->setPen( d_data->pen );
404 painter->setBrush( Qt::NoBrush );
405
406 if ( d_data->paintAttributes & ClipPolygons )
407 {
408 qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
409 const QRectF clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
410
411 QPolygonF p;
412
413 p.resize( size );
414 qMemCopy( p.data(), points, size * sizeof( QPointF ) );
415 p = QwtClipper::clipPolygonF( canvasRect, p );
416 QwtPainter::drawPolyline( painter, p );
417
418 p.resize( size );
419 qMemCopy( p.data(), points + size, size * sizeof( QPointF ) );
420 p = QwtClipper::clipPolygonF( canvasRect, p );
421 QwtPainter::drawPolyline( painter, p );
422 }
423 else
424 {
425 QwtPainter::drawPolyline( painter, points, size );
426 QwtPainter::drawPolyline( painter, points + size, size );
427 }
428 }
429
430 painter->restore();
431}
432
433/*!
434 Draw symbols for a subset of the samples
435
436 \param painter Painter
437 \param symbol Interval symbol
438 \param xMap x map
439 \param yMap y map
440 \param canvasRect Contents rect of the canvas
441 \param from Index of the first sample to be painted
442 \param to Index of the last sample to be painted
443
444 \sa setSymbol(), drawSeries(), drawTube()
445*/
446void QwtPlotIntervalCurve::drawSymbols(
447 QPainter *painter, const QwtIntervalSymbol &symbol,
448 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
449 const QRectF &canvasRect, int from, int to ) const
450{
451 painter->save();
452
453 QPen pen = symbol.pen();
454 pen.setCapStyle( Qt::FlatCap );
455
456 painter->setPen( pen );
457 painter->setBrush( symbol.brush() );
458
459 const QRectF &tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect);
460
461 const double xMin = tr.left();
462 const double xMax = tr.right();
463 const double yMin = tr.top();
464 const double yMax = tr.bottom();
465
466 const bool doClip = d_data->paintAttributes & ClipPolygons;
467
468 for ( int i = from; i <= to; i++ )
469 {
470 const QwtIntervalSample s = sample( i );
471
472 if ( orientation() == Qt::Vertical )
473 {
474 if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) )
475 {
476 const double x = xMap.transform( s.value );
477 const double y1 = yMap.transform( s.interval.minValue() );
478 const double y2 = yMap.transform( s.interval.maxValue() );
479
480 symbol.draw( painter, orientation(),
481 QPointF( x, y1 ), QPointF( x, y2 ) );
482 }
483 }
484 else
485 {
486 if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) )
487 {
488 const double y = yMap.transform( s.value );
489 const double x1 = xMap.transform( s.interval.minValue() );
490 const double x2 = xMap.transform( s.interval.maxValue() );
491
492 symbol.draw( painter, orientation(),
493 QPointF( x1, y ), QPointF( x2, y ) );
494 }
495 }
496 }
497
498 painter->restore();
499}
500
501/*!
502 In case of Tibe stale() a plain rectangle is painted without a pen filled
503 the brush(). If a symbol is assigned it is painted cebtered into rect.
504
505 \param painter Painter
506 \param rect Bounding rectangle for the identifier
507*/
508
509void QwtPlotIntervalCurve::drawLegendIdentifier(
510 QPainter *painter, const QRectF &rect ) const
511{
512 const double dim = qMin( rect.width(), rect.height() );
513
514 QSizeF size( dim, dim );
515
516 QRectF r( 0, 0, size.width(), size.height() );
517 r.moveCenter( rect.center() );
518
519 if ( d_data->style == Tube )
520 {
521 painter->fillRect( r, d_data->brush );
522 }
523
524 if ( d_data->symbol &&
525 ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
526 {
527 QPen pen = d_data->symbol->pen();
528 pen.setWidthF( pen.widthF() );
529 pen.setCapStyle( Qt::FlatCap );
530
531 painter->setPen( pen );
532 painter->setBrush( d_data->symbol->brush() );
533
534 if ( orientation() == Qt::Vertical )
535 {
536 d_data->symbol->draw( painter, orientation(),
537 QPointF( r.center().x(), r.top() ),
538 QPointF( r.center().x(), r.bottom() - 1 ) );
539 }
540 else
541 {
542 d_data->symbol->draw( painter, orientation(),
543 QPointF( r.left(), r.center().y() ),
544 QPointF( r.right() - 1, r.center().y() ) );
545 }
546 }
547}
Note: See TracBrowser for help on using the repository browser.