1
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
3
* Copyright (C) 1997 Josef Wilgen
4
* Copyright (C) 2002 Uwe Rathmann
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
*****************************************************************************/
13
#include "qwt_painter.h"
14
#include "qwt_double_interval.h"
15
#include "qwt_scale_map.h"
16
#include "qwt_color_map.h"
17
#include "qwt_plot_spectrogram.h"
19
#if QT_VERSION < 0x040000
20
typedef QValueVector<QRgb> QwtColorTable;
22
typedef QVector<QRgb> QwtColorTable;
25
class QwtPlotSpectrogramImage: public QImage
27
// This class hides some Qt3/Qt4 API differences
29
QwtPlotSpectrogramImage(const QSize &size, QwtColorMap::Format format):
30
#if QT_VERSION < 0x040000
31
QImage(size, format == QwtColorMap::RGB ? 32 : 8)
33
QImage(size, format == QwtColorMap::RGB
34
? QImage::Format_ARGB32 : QImage::Format_Indexed8 )
39
QwtPlotSpectrogramImage(const QImage &other):
44
void initColorTable(const QImage& other)
46
#if QT_VERSION < 0x040000
47
const unsigned int numColors = other.numColors();
49
setNumColors(numColors);
50
for ( unsigned int i = 0; i < numColors; i++ )
51
setColor(i, other.color(i));
53
setColorTable(other.colorTable());
57
#if QT_VERSION < 0x040000
59
void setColorTable(const QwtColorTable &colorTable)
61
setNumColors(colorTable.size());
62
for ( unsigned int i = 0; i < colorTable.size(); i++ )
63
setColor(i, colorTable[i]);
66
QwtColorTable colorTable() const
68
QwtColorTable table(numColors());
69
for ( int i = 0; i < numColors(); i++ )
77
class QwtPlotSpectrogram::PrivateData
80
class DummyData: public QwtRasterData
83
virtual QwtRasterData *copy() const
85
return new DummyData();
88
virtual double value(double, double) const
93
virtual QwtDoubleInterval range() const
95
return QwtDoubleInterval(0.0, 1.0);
101
data = new DummyData();
102
colorMap = new QwtLinearColorMap();
103
displayMode = ImageMode;
105
conrecAttributes = QwtRasterData::IgnoreAllVerticesOnLevel;
106
conrecAttributes |= QwtRasterData::IgnoreOutOfRange;
115
QwtColorMap *colorMap;
118
QwtValueList contourLevels;
119
QPen defaultContourPen;
120
int conrecAttributes;
124
Sets the following item attributes:
125
- QwtPlotItem::AutoScale: true
126
- QwtPlotItem::Legend: false
128
The z value is initialized by 8.0.
132
\sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
134
QwtPlotSpectrogram::QwtPlotSpectrogram(const QString &title):
135
QwtPlotRasterItem(title)
137
d_data = new PrivateData();
139
setItemAttribute(QwtPlotItem::AutoScale, true);
140
setItemAttribute(QwtPlotItem::Legend, false);
146
QwtPlotSpectrogram::~QwtPlotSpectrogram()
151
//! \return QwtPlotItem::Rtti_PlotSpectrogram
152
int QwtPlotSpectrogram::rtti() const
154
return QwtPlotItem::Rtti_PlotSpectrogram;
158
The display mode controls how the raster data will be represented.
160
\param mode Display mode
163
The default setting enables ImageMode.
165
\sa DisplayMode, displayMode()
167
void QwtPlotSpectrogram::setDisplayMode(DisplayMode mode, bool on)
169
if ( on != bool(mode & d_data->displayMode) )
172
d_data->displayMode |= mode;
174
d_data->displayMode &= ~mode;
181
The display mode controls how the raster data will be represented.
183
\param mode Display mode
184
\return true if mode is enabled
186
bool QwtPlotSpectrogram::testDisplayMode(DisplayMode mode) const
188
return (d_data->displayMode & mode);
194
Often it is useful to display the mapping between intensities and
195
colors as an additional plot axis, showing a color bar.
197
\param colorMap Color Map
199
\sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
200
QwtScaleWidget::setColorMap()
202
void QwtPlotSpectrogram::setColorMap(const QwtColorMap &colorMap)
204
delete d_data->colorMap;
205
d_data->colorMap = colorMap.copy();
212
\return Color Map used for mapping the intensity values to colors
215
const QwtColorMap &QwtPlotSpectrogram::colorMap() const
217
return *d_data->colorMap;
221
\brief Set the default pen for the contour lines
223
If the spectrogram has a valid default contour pen
224
a contour line is painted using the default contour pen.
225
Otherwise (pen.style() == Qt::NoPen) the pen is calculated
226
for each contour level using contourPen().
228
\sa defaultContourPen, contourPen
230
void QwtPlotSpectrogram::setDefaultContourPen(const QPen &pen)
232
if ( pen != d_data->defaultContourPen )
234
d_data->defaultContourPen = pen;
240
\return Default contour pen
241
\sa setDefaultContourPen
243
QPen QwtPlotSpectrogram::defaultContourPen() const
245
return d_data->defaultContourPen;
249
\brief Calculate the pen for a contour line
251
The color of the pen is the color for level calculated by the color map
253
\param level Contour level
254
\return Pen for the contour line
255
\note contourPen is only used if defaultContourPen().style() == Qt::NoPen
257
\sa setDefaultContourPen, setColorMap, setContourLevels
259
QPen QwtPlotSpectrogram::contourPen(double level) const
261
const QwtDoubleInterval intensityRange = d_data->data->range();
262
const QColor c(d_data->colorMap->rgb(intensityRange, level));
268
Modify an attribute of the CONREC algorithm, used to calculate
271
\param attribute CONREC attribute
274
\sa testConrecAttribute, renderContourLines, QwtRasterData::contourLines
276
void QwtPlotSpectrogram::setConrecAttribute(
277
QwtRasterData::ConrecAttribute attribute, bool on)
279
if ( bool(d_data->conrecAttributes & attribute) == on )
283
d_data->conrecAttributes |= attribute;
285
d_data->conrecAttributes &= ~attribute;
291
Test an attribute of the CONREC algorithm, used to calculate
294
\param attribute CONREC attribute
295
\return true, is enabled
297
\sa setConrecAttribute, renderContourLines, QwtRasterData::contourLines
299
bool QwtPlotSpectrogram::testConrecAttribute(
300
QwtRasterData::ConrecAttribute attribute) const
302
return d_data->conrecAttributes & attribute;
306
Set the levels of the contour lines
308
\param levels Values of the contour levels
309
\sa contourLevels, renderContourLines, QwtRasterData::contourLines
311
\note contourLevels returns the same levels but sorted.
313
void QwtPlotSpectrogram::setContourLevels(const QwtValueList &levels)
315
d_data->contourLevels = levels;
316
#if QT_VERSION >= 0x040000
317
qSort(d_data->contourLevels);
319
qHeapSort(d_data->contourLevels);
325
\brief Return the levels of the contour lines.
327
The levels are sorted in increasing order.
329
\sa contourLevels, renderContourLines, QwtRasterData::contourLines
331
QwtValueList QwtPlotSpectrogram::contourLevels() const
333
return d_data->contourLevels;
337
Set the data to be displayed
339
\param data Spectrogram Data
342
void QwtPlotSpectrogram::setData(const QwtRasterData &data)
345
d_data->data = data.copy();
352
\return Spectrogram data
355
const QwtRasterData &QwtPlotSpectrogram::data() const
357
return *d_data->data;
361
\return Bounding rect of the data
362
\sa QwtRasterData::boundingRect
364
QwtDoubleRect QwtPlotSpectrogram::boundingRect() const
366
return d_data->data->boundingRect();
370
\brief Returns the recommended raster for a given rect.
372
F.e the raster hint is used to limit the resolution of
373
the image that is rendered.
375
\param rect Rect for the raster hint
376
\return data().rasterHint(rect)
378
QSize QwtPlotSpectrogram::rasterHint(const QwtDoubleRect &rect) const
380
return d_data->data->rasterHint(rect);
384
\brief Render an image from the data and color map.
386
The area is translated into a rect of the paint device.
387
For each pixel of this rect the intensity is mapped
390
\param xMap X-Scale Map
391
\param yMap Y-Scale Map
392
\param area Area that should be rendered in scale coordinates.
394
\return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending
397
\sa QwtRasterData::intensity(), QwtColorMap::rgb(),
398
QwtColorMap::colorIndex()
400
QImage QwtPlotSpectrogram::renderImage(
401
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
402
const QwtDoubleRect &area) const
404
if ( area.isEmpty() )
407
QRect rect = transform(xMap, yMap, area);
409
QwtScaleMap xxMap = xMap;
410
QwtScaleMap yyMap = yMap;
412
const QSize res = d_data->data->rasterHint(area);
416
It is useless to render an image with a higher resolution
417
than the data offers. Of course someone will have to
418
scale this image later into the size of the given rect, but f.e.
419
in case of postscript this will done on the printer.
421
rect.setSize(rect.size().boundedTo(res));
424
int px2 = rect.x() + rect.width();
425
if ( xMap.p1() > xMap.p2() )
428
double sx1 = area.x();
429
double sx2 = area.x() + area.width();
430
if ( xMap.s1() > xMap.s2() )
434
int py2 = rect.y() + rect.height();
435
if ( yMap.p1() > yMap.p2() )
438
double sy1 = area.y();
439
double sy2 = area.y() + area.height();
440
if ( yMap.s1() > yMap.s2() )
443
xxMap.setPaintInterval(px1, px2);
444
xxMap.setScaleInterval(sx1, sx2);
445
yyMap.setPaintInterval(py1, py2);
446
yyMap.setScaleInterval(sy1, sy2);
449
QwtPlotSpectrogramImage image(rect.size(), d_data->colorMap->format());
451
const QwtDoubleInterval intensityRange = d_data->data->range();
452
if ( !intensityRange.isValid() )
455
d_data->data->initRaster(area, rect.size());
457
if ( d_data->colorMap->format() == QwtColorMap::RGB )
459
for ( int y = rect.top(); y <= rect.bottom(); y++ )
461
const double ty = yyMap.invTransform(y);
463
QRgb *line = (QRgb *)image.scanLine(y - rect.top());
464
for ( int x = rect.left(); x <= rect.right(); x++ )
466
const double tx = xxMap.invTransform(x);
468
*line++ = d_data->colorMap->rgb(intensityRange,
469
d_data->data->value(tx, ty));
473
else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
475
image.setColorTable(d_data->colorMap->colorTable(intensityRange));
477
for ( int y = rect.top(); y <= rect.bottom(); y++ )
479
const double ty = yyMap.invTransform(y);
481
unsigned char *line = image.scanLine(y - rect.top());
482
for ( int x = rect.left(); x <= rect.right(); x++ )
484
const double tx = xxMap.invTransform(x);
486
*line++ = d_data->colorMap->colorIndex(intensityRange,
487
d_data->data->value(tx, ty));
492
d_data->data->discardRaster();
494
// Mirror the image in case of inverted maps
496
const bool hInvert = xxMap.p1() > xxMap.p2();
497
const bool vInvert = yyMap.p1() < yyMap.p2();
498
if ( hInvert || vInvert )
501
#warning Better invert the for loops above
503
#if QT_VERSION < 0x040000
504
image = image.mirror(hInvert, vInvert);
506
image = image.mirrored(hInvert, vInvert);
514
\brief Return the raster to be used by the CONREC contour algorithm.
516
A larger size will improve the precisision of the CONREC algorithm,
517
but will slow down the time that is needed to calculate the lines.
519
The default implementation returns rect.size() / 2 bounded to
522
\param area Rect, where to calculate the contour lines
523
\param rect Rect in pixel coordinates, where to paint the contour lines
524
\return Raster to be used by the CONREC contour algorithm.
526
\note The size will be bounded to rect.size().
528
\sa drawContourLines, QwtRasterData::contourLines
530
QSize QwtPlotSpectrogram::contourRasterSize(const QwtDoubleRect &area,
531
const QRect &rect) const
533
QSize raster = rect.size() / 2;
535
const QSize rasterHint = d_data->data->rasterHint(area);
536
if ( rasterHint.isValid() )
537
raster = raster.boundedTo(rasterHint);
543
Calculate contour lines
545
\param rect Rectangle, where to calculate the contour lines
546
\param raster Raster, used by the CONREC algorithm
548
\sa contourLevels, setConrecAttribute, QwtRasterData::contourLines
550
QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
551
const QwtDoubleRect &rect, const QSize &raster) const
553
return d_data->data->contourLines(rect, raster,
554
d_data->contourLevels, d_data->conrecAttributes );
558
Paint the contour lines
560
\param painter Painter
561
\param xMap Maps x-values into pixel coordinates.
562
\param yMap Maps y-values into pixel coordinates.
563
\param contourLines Contour lines
565
\sa renderContourLines, defaultContourPen, contourPen
567
void QwtPlotSpectrogram::drawContourLines(QPainter *painter,
568
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
569
const QwtRasterData::ContourLines &contourLines) const
571
const QwtDoubleInterval intensityRange = d_data->data->range();
573
const int numLevels = (int)d_data->contourLevels.size();
574
for (int l = 0; l < numLevels; l++)
576
const double level = d_data->contourLevels[l];
578
QPen pen = defaultContourPen();
579
if ( pen.style() == Qt::NoPen )
580
pen = contourPen(level);
582
if ( pen.style() == Qt::NoPen )
585
painter->setPen(pen);
587
#if QT_VERSION >= 0x040000
588
const QPolygonF &lines = contourLines[level];
590
const QwtArray<QwtDoublePoint> &lines = contourLines[level];
592
for ( int i = 0; i < (int)lines.size(); i += 2 )
594
const QPoint p1( xMap.transform(lines[i].x()),
595
yMap.transform(lines[i].y()) );
596
const QPoint p2( xMap.transform(lines[i+1].x()),
597
yMap.transform(lines[i+1].y()) );
599
QwtPainter::drawLine(painter, p1, p2);
605
\brief Draw the spectrogram
607
\param painter Painter
608
\param xMap Maps x-values into pixel coordinates.
609
\param yMap Maps y-values into pixel coordinates.
610
\param canvasRect Contents rect of the canvas in painter coordinates
612
\sa setDisplayMode, renderImage,
613
QwtPlotRasterItem::draw, drawContourLines
616
void QwtPlotSpectrogram::draw(QPainter *painter,
617
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
618
const QRect &canvasRect) const
620
if ( d_data->displayMode & ImageMode )
621
QwtPlotRasterItem::draw(painter, xMap, yMap, canvasRect);
623
if ( d_data->displayMode & ContourMode )
625
// Add some pixels at the borders, so that
626
const int margin = 2;
627
QRect rasterRect(canvasRect.x() - margin, canvasRect.y() - margin,
628
canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin);
630
QwtDoubleRect area = invTransform(xMap, yMap, rasterRect);
632
const QwtDoubleRect br = boundingRect();
633
if ( br.isValid() && br.contains(area) )
636
rasterRect = transform(xMap, yMap, area);
639
QSize raster = contourRasterSize(area, rasterRect);
640
raster = raster.boundedTo(rasterRect.size());
641
if ( raster.isValid() )
643
const QwtRasterData::ContourLines lines =
644
renderContourLines(area, raster);
646
drawContourLines(painter, xMap, yMap, lines);