source: ntrip/trunk/BNC/qwt/qwt_polar_spectrogram.cpp

Last change on this file was 11158, checked in by stuerze, 4 days ago

deprecated functions exchanged libs updated

File size: 12.3 KB
Line 
1/******************************************************************************
2 * QwtPolar Widget Library
3 * Copyright (C) 2008 Uwe Rathmann
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the Qwt License, Version 1.0
7 *****************************************************************************/
8
9#include "qwt_polar_spectrogram.h"
10#include "qwt_polar.h"
11#include "qwt_polar_plot.h"
12#include "qwt_color_map.h"
13#include "qwt_scale_map.h"
14#include "qwt_raster_data.h"
15#include "qwt_math.h"
16#include "qwt_clipper.h"
17
18#include <qpainter.h>
19#include <qpainterpath.h>
20#include <qthread.h>
21#include <qfuture.h>
22#include <qtconcurrentrun.h>
23#include <qnumeric.h>
24
25class QwtPolarSpectrogram::TileInfo
26{
27 public:
28 QPoint imagePos;
29 QRect rect;
30 QImage* image;
31};
32
33class QwtPolarSpectrogram::PrivateData
34{
35 public:
36 PrivateData()
37 : data( NULL )
38 {
39 colorMap = new QwtLinearColorMap();
40 }
41
42 ~PrivateData()
43 {
44 delete data;
45 delete colorMap;
46 }
47
48 QwtRasterData* data;
49 QwtColorMap* colorMap;
50
51 QwtPolarSpectrogram::PaintAttributes paintAttributes;
52};
53
54//! Constructor
55QwtPolarSpectrogram::QwtPolarSpectrogram()
56 : QwtPolarItem( QwtText( "Spectrogram" ) )
57{
58 m_data = new PrivateData;
59
60 setItemAttribute( QwtPolarItem::AutoScale );
61 setItemAttribute( QwtPolarItem::Legend, false );
62
63 setZ( 20.0 );
64}
65
66//! Destructor
67QwtPolarSpectrogram::~QwtPolarSpectrogram()
68{
69 delete m_data;
70}
71
72//! \return QwtPolarItem::Rtti_PolarSpectrogram
73int QwtPolarSpectrogram::rtti() const
74{
75 return QwtPolarItem::Rtti_PolarSpectrogram;
76}
77
78/*!
79 Set the data to be displayed
80
81 \param data Spectrogram Data
82 \sa data()
83
84 \warning QwtRasterData::initRaster() is called each time before the
85 image is rendered, but without any useful parameters.
86 Also QwtRasterData::rasterHint() is not used.
87 */
88void QwtPolarSpectrogram::setData( QwtRasterData* data )
89{
90 if ( data != m_data->data )
91 {
92 delete m_data->data;
93 m_data->data = data;
94
95 itemChanged();
96 }
97}
98
99/*!
100 \return Spectrogram data
101 \sa setData()
102 */
103const QwtRasterData* QwtPolarSpectrogram::data() const
104{
105 return m_data->data;
106}
107
108/*!
109 Change the color map
110
111 Often it is useful to display the mapping between intensities and
112 colors as an additional plot axis, showing a color bar.
113
114 \param colorMap Color Map
115
116 \sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
117 QwtScaleWidget::setColorMap()
118 */
119void QwtPolarSpectrogram::setColorMap( QwtColorMap* colorMap )
120{
121 if ( m_data->colorMap != colorMap )
122 {
123 delete m_data->colorMap;
124 m_data->colorMap = colorMap;
125 }
126
127 itemChanged();
128}
129
130/*!
131 \return Color Map used for mapping the intensity values to colors
132 \sa setColorMap()
133 */
134const QwtColorMap* QwtPolarSpectrogram::colorMap() const
135{
136 return m_data->colorMap;
137}
138
139/*!
140 Specify an attribute how to draw the curve
141
142 \param attribute Paint attribute
143 \param on On/Off
144 \sa testPaintAttribute()
145 */
146void QwtPolarSpectrogram::setPaintAttribute( PaintAttribute attribute, bool on )
147{
148 if ( on )
149 m_data->paintAttributes |= attribute;
150 else
151 m_data->paintAttributes &= ~attribute;
152}
153
154/*!
155 \param attribute Paint attribute
156 \return True, when attribute has been set
157 \sa setPaintAttribute()
158 */
159bool QwtPolarSpectrogram::testPaintAttribute( PaintAttribute attribute ) const
160{
161 return ( m_data->paintAttributes & attribute );
162}
163
164/*!
165 Draw the spectrogram
166
167 \param painter Painter
168 \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
169 \param radialMap Maps radius values into painter coordinates.
170 \param pole Position of the pole in painter coordinates
171 \param radius Radius of the complete plot area in painter coordinates
172 \param canvasRect Contents rect of the canvas in painter coordinates
173 */
174void QwtPolarSpectrogram::draw( QPainter* painter,
175 const QwtScaleMap& azimuthMap, const QwtScaleMap& radialMap,
176 const QPointF& pole, double,
177 const QRectF& canvasRect ) const
178{
179 const QRectF plotRect = plot()->plotRect( canvasRect.toRect() );
180 QRect imageRect = canvasRect.toRect();
181
182 painter->save();
183
184 painter->setClipRect( canvasRect );
185
186 QPainterPath clipPathCanvas;
187 clipPathCanvas.addEllipse( plotRect );
188 painter->setClipPath( clipPathCanvas, Qt::IntersectClip );
189
190 imageRect &= plotRect.toAlignedRect(); // outer rect
191
192 const QwtInterval radialInterval = boundingInterval( QwtPolar::ScaleRadius );
193 if ( radialInterval.isValid() )
194 {
195 const double radius = radialMap.transform( radialInterval.maxValue() ) -
196 radialMap.transform( radialInterval.minValue() );
197
198 QRectF clipRect( 0, 0, 2 * radius, 2 * radius );
199 clipRect.moveCenter( pole );
200
201 imageRect &= clipRect.toRect(); // inner rect, we don't have points outside
202
203 QPainterPath clipPathRadial;
204 clipPathRadial.addEllipse( clipRect );
205 painter->setClipPath( clipPathRadial, Qt::IntersectClip );
206 }
207
208 const QImage image = renderImage( azimuthMap, radialMap, pole, imageRect );
209 painter->drawImage( imageRect, image );
210
211 painter->restore();
212}
213
214/*!
215 \brief Render an image from the data and color map.
216
217 The area is translated into a rect of the paint device.
218 For each pixel of this rect the intensity is mapped
219 into a color.
220
221 \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
222 \param radialMap Maps radius values into painter coordinates.
223 \param pole Position of the pole in painter coordinates
224 \param rect Target rectangle of the image in painter coordinates
225
226 \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending
227 on the color map.
228
229 \sa QwtRasterData::intensity(), QwtColorMap::rgb(),
230 QwtColorMap::colorIndex()
231 */
232QImage QwtPolarSpectrogram::renderImage(
233 const QwtScaleMap& azimuthMap, const QwtScaleMap& radialMap,
234 const QPointF& pole, const QRect& rect ) const
235{
236 if ( m_data->data == NULL || m_data->colorMap == NULL )
237 return QImage();
238
239 QImage image( rect.size(), m_data->colorMap->format() == QwtColorMap::RGB
240 ? QImage::Format_ARGB32 : QImage::Format_Indexed8 );
241
242 const QwtInterval intensityRange = m_data->data->interval( Qt::ZAxis );
243 if ( !intensityRange.isValid() )
244 return image;
245
246 if ( m_data->colorMap->format() == QwtColorMap::Indexed )
247 image.setColorTable( m_data->colorMap->colorTable256() );
248
249 /*
250 For the moment we only announce the composition of the image by
251 calling initRaster(), but we don't pass any useful parameters.
252 ( How to map rect into something, that is useful to initialize a matrix
253 of values in polar coordinates ? )
254 */
255 m_data->data->initRaster( QRectF(), QSize() );
256
257
258#if !defined( QT_NO_QFUTURE )
259 uint numThreads = renderThreadCount();
260
261 if ( numThreads <= 0 )
262 numThreads = QThread::idealThreadCount();
263
264 if ( numThreads <= 0 )
265 numThreads = 1;
266
267 const int numRows = rect.height() / numThreads;
268
269
270 QVector< TileInfo > tileInfos;
271 for ( uint i = 0; i < numThreads; i++ )
272 {
273 QRect tile( rect.x(), rect.y() + i * numRows, rect.width(), numRows );
274 if ( i == numThreads - 1 )
275 tile.setHeight( rect.height() - i * numRows );
276
277 TileInfo tileInfo;
278 tileInfo.imagePos = rect.topLeft();
279 tileInfo.rect = tile;
280 tileInfo.image = &image;
281
282 tileInfos += tileInfo;
283 }
284
285 QVector< QFuture< void > > futures;
286 for ( int i = 0; i < tileInfos.size(); i++ )
287 {
288 if ( i == tileInfos.size() - 1 )
289 {
290 renderTileInfo( azimuthMap, radialMap, pole, &tileInfos[i] );
291 }
292 else
293 {
294 futures += QtConcurrent::run(
295#if QT_VERSION >= 0x060000
296 &QwtPolarSpectrogram::renderTileInfo, this,
297#else
298 this, &QwtPolarSpectrogram::renderTileInfo,
299#endif
300 azimuthMap, radialMap, pole, &tileInfos[i] );
301 }
302 }
303
304 for ( int i = 0; i < futures.size(); i++ )
305 futures[i].waitForFinished();
306
307#else
308 renderTile( azimuthMap, radialMap, pole, rect.topLeft(), rect, &image );
309#endif
310
311 m_data->data->discardRaster();
312
313 return image;
314}
315
316void QwtPolarSpectrogram::renderTileInfo(
317 const QwtScaleMap& azimuthMap, const QwtScaleMap& radialMap,
318 const QPointF& pole, TileInfo* tileInfo ) const
319{
320 renderTile( azimuthMap, radialMap, pole,
321 tileInfo->imagePos, tileInfo->rect, tileInfo->image );
322}
323
324/*!
325 \brief Render a sub-rectangle of an image
326
327 renderTile() is called by renderImage() to render different parts
328 of the image by concurrent threads.
329
330 \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
331 \param radialMap Maps radius values into painter coordinates.
332 \param pole Position of the pole in painter coordinates
333 \param imagePos Top/left position of the image in painter coordinates
334 \param tile Sub-rectangle of the tile in painter coordinates
335 \param image Image to be rendered
336
337 \sa setRenderThreadCount()
338 \note renderTile needs to be reentrant
339 */
340void QwtPolarSpectrogram::renderTile(
341 const QwtScaleMap& azimuthMap, const QwtScaleMap& radialMap,
342 const QPointF& pole, const QPoint& imagePos,
343 const QRect& tile, QImage* image ) const
344{
345 const QwtInterval intensityRange = m_data->data->interval( Qt::ZAxis );
346 if ( !intensityRange.isValid() )
347 return;
348
349 const bool doFastAtan = testPaintAttribute( ApproximatedAtan );
350
351 const int y0 = imagePos.y();
352 const int y1 = tile.top();
353 const int y2 = tile.bottom();
354
355 const int x0 = imagePos.x();
356 const int x1 = tile.left();
357 const int x2 = tile.right();
358
359 if ( m_data->colorMap->format() == QwtColorMap::RGB )
360 {
361 for ( int y = y1; y <= y2; y++ )
362 {
363 const double dy = pole.y() - y;
364 const double dy2 = qwtSqr( dy );
365
366 QRgb* line = reinterpret_cast< QRgb* >( image->scanLine( y - y0 ) );
367 line += x1 - x0;
368
369 for ( int x = x1; x <= x2; x++ )
370 {
371 const double dx = x - pole.x();
372
373 double a = doFastAtan ? qwtFastAtan2( dy, dx ) : qAtan2( dy, dx );
374
375 if ( a < 0.0 )
376 a += 2 * M_PI;
377
378 if ( a < azimuthMap.p1() )
379 a += 2 * M_PI;
380
381 const double r = qSqrt( qwtSqr( dx ) + dy2 );
382
383 const double azimuth = azimuthMap.invTransform( a );
384 const double radius = radialMap.invTransform( r );
385
386 const double value = m_data->data->value( azimuth, radius );
387 if ( qIsNaN( value ) )
388 {
389 *line++ = 0u;
390 }
391 else
392 {
393 *line++ = m_data->colorMap->rgb( intensityRange, value );
394 }
395 }
396 }
397 }
398 else if ( m_data->colorMap->format() == QwtColorMap::Indexed )
399 {
400 for ( int y = y1; y <= y2; y++ )
401 {
402 const double dy = pole.y() - y;
403 const double dy2 = qwtSqr( dy );
404
405 unsigned char* line = image->scanLine( y - y0 );
406 line += x1 - x0;
407 for ( int x = x1; x <= x2; x++ )
408 {
409 const double dx = x - pole.x();
410
411 double a = doFastAtan ? qwtFastAtan2( dy, dx ) : qAtan2( dy, dx );
412 if ( a < 0.0 )
413 a += 2 * M_PI;
414 if ( a < azimuthMap.p1() )
415 a += 2 * M_PI;
416
417 const double r = qSqrt( qwtSqr( dx ) + dy2 );
418
419 const double azimuth = azimuthMap.invTransform( a );
420 const double radius = radialMap.invTransform( r );
421
422 const double value = m_data->data->value( azimuth, radius );
423
424 const uint index = m_data->colorMap->colorIndex( 256, intensityRange, value );
425 *line++ = static_cast< unsigned char >( index );
426 }
427 }
428 }
429}
430
431/*!
432 Interval, that is necessary to display the item
433 This interval can be useful for operations like clipping or autoscaling
434
435 \param scaleId Scale index
436 \return bounding interval ( == position )
437
438 \sa position()
439 */
440QwtInterval QwtPolarSpectrogram::boundingInterval( int scaleId ) const
441{
442 if ( scaleId == QwtPolar::ScaleRadius )
443 return m_data->data->interval( Qt::YAxis );
444
445 return QwtPolarItem::boundingInterval( scaleId );
446}
Note: See TracBrowser for help on using the repository browser.