##// END OF EJS Templates
improvements in execution time, colorbar ok, more work to do on axes,
improvements in execution time, colorbar ok, more work to do on axes,

File last commit:

r3:ab1c9ba54a31 default
r3:ab1c9ba54a31 default
Show More
qcolormapseries.cpp
631 lines | 16.6 KiB | text/x-c | CppLexer
#include "qcolormapseries.h"
#include <private/qcolormapseries_p.h>
#include <private/abstractdomain_p.h>
#include <QtCharts/QValueAxis>
#include <private/colormapchart_p.h>
#include <QtCharts/QColorMapLegendMarker>
#include <private/charthelpers_p.h>
#include <private/qchart_p.h>
#include <QtGui/QPainter>
//#include <algorithm>
#include <cmath>
#include "qcolorbaraxis.h"
QT_CHARTS_BEGIN_NAMESPACE
/*!
\internal
Constructs empty series object which is a child of \a parent.
When series object is added to QChart instance ownerships is transferred.
*/
QColorMapSeries::QColorMapSeries(QObject *parent)
: QAbstractSeries(*new QColorMapSeriesPrivate(this), parent)
{
}
/*!
\internal
Constructs empty series object which is a child of \a parent.
When series object is added to QChart instance ownerships is transferred.
*/
QColorMapSeries::QColorMapSeries(QColorMapSeriesPrivate &d, QObject *parent)
: QAbstractSeries(d, parent)
{
}
/*!
Destroys the object. Series added to QChart instances are owned by those,
and are destroyed when QChart instances are destroyed.
*/
QColorMapSeries::~QColorMapSeries()
{
}
/*!
\fn virtual SeriesType QBoxPlotSeries::type() const
\brief Returns type of series.
\sa QAbstractSeries, SeriesType
*/
QAbstractSeries::SeriesType QColorMapSeries::type() const
{
return QAbstractSeries::SeriesTypeColorMap;
}
/*!
Adds data part \a dataPart to the series.\n
If \a copy is true, adds a copy of the data part instead.
*/
void QColorMapSeries::append(ColorMapDataPart* dataPart, bool copy)
{
Q_D(QColorMapSeries);
if(copy)
d->m_dataParts << new ColorMapDataPart(dataPart);
else
d->m_dataParts << dataPart;
d->recomputeDataRange();
emit dataPartAdded(d->m_dataParts.count() - 1);
}
/*!
This is an overloaded function.\n
Adds data parts \a dataParts to the series.\n
If \a copy is true, adds a copy of the data part instead.
*/
void QColorMapSeries::append(const QList<ColorMapDataPart*> &dataParts, bool copy)
{
foreach (ColorMapDataPart* dataPart , dataParts)
append(dataPart,copy);
}
/*!
Returns number of data parts within series.
*/
int QColorMapSeries::count() const
{
Q_D(const QColorMapSeries);
return d->m_dataParts.count();
}
/*!
Stream operator for adding a data part \a point to the series.
\sa append()
*/
QColorMapSeries &QColorMapSeries::operator <<(const ColorMapDataPart &dataPart)
{
append(new ColorMapDataPart(dataPart));
return *this;
}
/*!
Stream operator for adding a vector of data parts \a dataParts to the series.
\sa append()
*/
QColorMapSeries &QColorMapSeries::operator <<(const QList<ColorMapDataPart*> &dataParts)
{
append(dataParts);
return *this;
}
/*!
Returns a ColorMapData part containing a uniform grid of data points to be mapped in a ColorMapChart.\n
The rectangle of data returned is determined by \a width and \height,\n
from the position \a xpos , \a ypos (starting at the top left corner of the plot area).\n
When there are more points than pixels, \a strategy is applied to determine which to choose.
*/
QVector<double> *QColorMapSeries::getUniformGrid(int xpos, int ypos,int width, int height, QColorMapSeries::Strategy strategy)
{
Q_D(QColorMapSeries);
return d->getUniformGrid(xpos, ypos,width,height, strategy);
}
//void QColorMapSeries::attachAxis(QAbstractAxis *axis)
//{
// axis = static_cast<QColorBarAxis*>();
// if(axis)
// {
// }
// else
// {
// QAbstractSeries::attachAxis(axis);
// }
//}
/*!
Sets \a pen used for drawing points on the chart. If the pen is not defined, the
pen from chart theme is used.
\sa QChart::setTheme()
*/
void QColorMapSeries::setPen(const QPen &pen)
{
Q_D(QColorMapSeries);
if (d->m_pen != pen)
{
//bool emitColorChanged = d->m_pen.color() != pen.color();
d->m_pen = pen;
emit d->updated();
// if (emitColorChanged)
// emit colorChanged(pen.color());
emit penChanged(pen);
}
}
QPen QColorMapSeries::pen() const
{
Q_D(const QColorMapSeries);
if (d->m_pen == QChartPrivate::defaultPen())
return QPen();
else
return d->m_pen;
}
/*!
Sets \a brush used for drawing points on the chart. If the brush is not defined, brush
from chart theme setting is used.
\sa QChart::setTheme()
*/
void QColorMapSeries::setBrush(const QBrush &brush)
{
Q_D(QColorMapSeries);
if (d->m_brush != brush)
{
d->m_brush = brush;
emit d->updated();
}
}
QBrush QColorMapSeries::brush() const
{
Q_D(const QColorMapSeries);
if (d->m_brush == QChartPrivate::defaultBrush())
return QBrush();
else
return d->m_brush;
}
//void QColorMapSeries::setColor(const QColor &color)
//{
// QPen p = pen();
// if (p.color() != color)
// {
// p.setColor(color);
// setPen(p);
// }
//}
//QColor QColorMapSeries::color() const
//{
// return pen().color();
//}
//void QColorMapSeries::setPointsVisible(bool visible)
//{
// Q_D(QColorMapSeries);
// if (d->m_pointsVisible != visible)
// {
// d->m_pointsVisible = visible;
// emit d->updated();
// }
//}
//bool QColorMapSeries::pointsVisible() const
//{
// Q_D(const QColorMapSeries);
// return d->m_pointsVisible;
//}
//void QColorMapSeries::setPointLabelsFormat(const QString &format)
//{
// Q_D(QColorMapSeries);
// if (d->m_pointLabelsFormat != format)
// {
// d->m_pointLabelsFormat = format;
// emit pointLabelsFormatChanged(format);
// }
//}
//QString QColorMapSeries::pointLabelsFormat() const
//{
// Q_D(const QColorMapSeries);
// return d->m_pointLabelsFormat;
//}
//void QColorMapSeries::setPointLabelsVisible(bool visible)
//{
// Q_D(QColorMapSeries);
// if (d->m_pointLabelsVisible != visible)
// {
// d->m_pointLabelsVisible = visible;
// emit pointLabelsVisibilityChanged(visible);
// }
//}
//bool QColorMapSeries::pointLabelsVisible() const
//{
// Q_D(const QColorMapSeries);
// return d->m_pointLabelsVisible;
//}
//void QColorMapSeries::setPointLabelsFont(const QFont &font)
//{
// Q_D(QColorMapSeries);
// if (d->m_pointLabelsFont != font) {
// d->m_pointLabelsFont = font;
// emit pointLabelsFontChanged(font);
// }
//}
//QFont QColorMapSeries::pointLabelsFont() const
//{
// Q_D(const QColorMapSeries);
// return d->m_pointLabelsFont;
//}
//void QColorMapSeries::setPointLabelsColor(const QColor &color)
//{
// Q_D(QColorMapSeries);
// if (d->m_pointLabelsColor != color) {
// d->m_pointLabelsColor = color;
// emit pointLabelsColorChanged(color);
// }
//}
//QColor QColorMapSeries::pointLabelsColor() const
//{
// Q_D(const QColorMapSeries);
// if (d->m_pointLabelsColor == QChartPrivate::defaultPen().color())
// return QPen().color();
// else
// return d->m_pointLabelsColor;
//}
//void QColorMapSeries::setPointLabelsClipping(bool enabled)
//{
// Q_D(QColorMapSeries);
// if (d->m_pointLabelsClipping != enabled) {
// d->m_pointLabelsClipping = enabled;
// emit pointLabelsClippingChanged(enabled);
// }
//}
//bool QColorMapSeries::pointLabelsClipping() const
//{
// Q_D(const QColorMapSeries);
// return d->m_pointLabelsClipping;
//}
/*!
Returns the minimum value of the series on the X axis.
*/
double QColorMapSeries::minX()
{
Q_D(QColorMapSeries);
return d->m_minX;
}
/*!
Returns the minimum value of the series on the Y axis.
*/
double QColorMapSeries::minY()
{
Q_D(QColorMapSeries);
return d->m_minY;
}
/*!
Returns the minimum value of the series on the Z axis.
*/
double QColorMapSeries::minZ()
{
Q_D(QColorMapSeries);
return d->m_minZ;
}
/*!
Returns the maximum value of the series on the X axis.
*/
double QColorMapSeries::maxX()
{
Q_D(QColorMapSeries);
return d->m_maxX;
}
/*!
Returns the maximum value of the series on the Y axis.
*/
double QColorMapSeries::maxY()
{
Q_D(QColorMapSeries);
return d->m_maxY;
}
/*!
Returns the maximum value of the series on the Z axis.
*/
double QColorMapSeries::maxZ()
{
Q_D(QColorMapSeries);
return d->m_maxZ;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QColorMapSeriesPrivate::QColorMapSeriesPrivate(QColorMapSeries *q)
: QAbstractSeriesPrivate(q),
m_pen(QChartPrivate::defaultPen()),
m_brush(QChartPrivate::defaultBrush()),
m_pointsVisible(false),
m_pointLabelsFormat(QLatin1String("@xPoint, @yPoint")), //TODO : change
m_pointLabelsVisible(false),
m_pointLabelsFont(QChartPrivate::defaultFont()),
m_pointLabelsColor(QChartPrivate::defaultPen().color()),
m_pointLabelsClipping(true)
{
}
void QColorMapSeriesPrivate::initializeGraphics(QGraphicsItem* parent)
{
Q_Q(QColorMapSeries);
ColorMapChart *colormap = new ColorMapChart(q,parent);
m_item.reset(colormap);
QAbstractSeriesPrivate::initializeGraphics(parent);
}
void QColorMapSeriesPrivate::initializeDomain()
{
domain()->setRange(m_minX, m_maxX, m_minY, m_maxY);
}
void QColorMapSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced)
{
// Q_Q(QColorMapSeries);
// const QList<QGradient> gradients = theme->seriesGradients();
// const QList<QColor> colors = theme->seriesColors();
// if (forced || QChartPrivate::defaultPen() == m_pen) {
// QPen pen;
// pen.setColor(ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.0));
// pen.setWidthF(2);
// q->setPen(pen);
// }
// if (forced || QChartPrivate::defaultBrush() == m_brush) {
// QBrush brush(colors.at(index % colors.size()));
// q->setBrush(brush);
// }
// if (forced || QChartPrivate::defaultPen().color() == m_pointLabelsColor) {
// QColor color = theme->labelBrush().color();
// q->setPointLabelsColor(color);
// }
}
QList<QLegendMarker*> QColorMapSeriesPrivate::createLegendMarkers(QLegend* legend)
{
Q_Q(QColorMapSeries);
QList<QLegendMarker*> list;
return list << new QColorMapLegendMarker(q,legend);
}
void QColorMapSeriesPrivate::initializeAxes()
{
}
QAbstractAxis::AxisType QColorMapSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
{
Q_UNUSED(orientation);
return QAbstractAxis::AxisTypeValue;
}
QAbstractAxis* QColorMapSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
{
Q_UNUSED(orientation);
return new QValueAxis;
}
QVector<double> *QColorMapSeriesPrivate::getUniformGrid(int xpos, int ypos, int width, int height, QColorMapSeries::Strategy strategy)
{
QVector<double> *grid = new QVector<double>(width*height);
double dx = (m_maxX - m_minX)/(double)width;
double dy = (m_maxY - m_minY)/(double)height;
int x=0;
int y=0;
QVector<Point3D> cluster;
cluster.reserve(height*width/1000);
for (auto cell= grid->begin();cell<grid->end();++cell)
{
cluster.resize(0);
this->buildCluster(x,y,dx,dy,cluster);
if(strategy == QColorMapSeries::LastPixel)
*cell=this->clusterStrategyLast(cluster);
else if(strategy == QColorMapSeries::MeanPixel)
*cell=this->clusterStrategyMean(cluster);
else if(strategy == QColorMapSeries::MedianPixel)
*cell=this->clusterStrategyMedian(cluster);
if(x<width-1)
{
x++;
}
else
{
x=0;
y++;
}
}
return grid;
}
/*!
Recompute the data range on X, Y and Z axes each time a ColorMapDataPart is added to the series.\n
The minimum distance between 2 adjacent values on X and Y axis is added to the maximum on X and Y respectively\n
in order to take account of the width of the last element.
*/
void QColorMapSeriesPrivate::recomputeDataRange()
{
//TODO : if non empty
m_minX = m_dataParts.first()->timesSeries().first();
m_maxX = m_dataParts.last()->timesSeries().last();
m_minY = m_dataParts.last()->ySeries().last();
m_maxY = m_dataParts.last()->ySeries().first();
m_minZ = m_dataParts.first()->dataSeries().first();
m_maxZ = m_dataParts.last()->dataSeries().last();
double minDeltaX=m_maxX-m_minX;
double minDeltaY=m_minY-m_maxY;
foreach(ColorMapDataPart* datapart, m_dataParts)
{
m_minY = qMin(datapart->ySeries().first(),m_minY);
m_maxY = qMax(datapart->ySeries().last(),m_maxY);
for(int i=1;i<datapart->timesSeries().size();i++)
{
double newDeltaX=datapart->timesSeries()[i]-datapart->timesSeries()[i-1];
minDeltaX=qMin(minDeltaX,newDeltaX);
}
for(int i=1;i<datapart->ySeries().size();i++)
{
double newDeltaY=datapart->ySeries()[i]-datapart->ySeries()[i-1];
minDeltaY=qMin(minDeltaY,newDeltaY);
}
for (int i = 0; i < datapart->dataSeries().count(); i++)
{
double z = datapart->dataSeries().at(i);
m_minZ = qMin(m_minZ, z);
m_maxZ = qMax(m_maxZ, z);
}
}
m_maxX+=minDeltaX;
m_maxY+=minDeltaY;
}
/*!
Returns the last value (bottom right Z value) of a \a cluster of points passed as argument.
*/
double QColorMapSeriesPrivate::clusterStrategyLast(QVector<Point3D>& cluster)
{
if(!cluster.isEmpty())
return cluster.last().value();
return 0;
}
/*!
Returns the mean value (mean Z value) of a \a cluster of points passed as argument.
*/
double QColorMapSeriesPrivate::clusterStrategyMean(QVector<Point3D>& cluster)
{
if(!cluster.isEmpty())
{
double sum=0;
int count =0;
for(int i =0;i<cluster.size();i++)
{
if(!isnan(Point3D(cluster.at(i)).value()))
{
sum+= Point3D(cluster.at(i)).value();
count++;
}
}
return (sum/count);
}
return 0;
}
/*!
Returns the median value (median Z value) of a \a cluster of points passed as argument.
*/
double QColorMapSeriesPrivate::clusterStrategyMedian(QVector<Point3D>& cluster)
{
if(!cluster.isEmpty())
{
QVector<double> *copy = new QVector<double>();
for(int i =0;i<cluster.size();i++)
{
if(!isnan(Point3D(cluster.at(i)).value()))
copy->append(Point3D(cluster.at(i)).value());
}
if(!copy->isEmpty())
{
std::sort(copy->begin(), copy->end());
if(copy->count() & 1) // odd
{
int middle = (copy->count()+1)/2;
return copy->at(middle-1);
}
else //even
{
int middle = copy->count()/2;
return (copy->at(middle-1)+copy->at(middle))/2;
}
}
}
return 0;
}
/*!
Computes which data points correspond to the given position and returns the in a \a cluster.
*/
void QColorMapSeriesPrivate::buildCluster(int xpos, int ypos, double dx, double dy, QVector<Point3D>& cluster)
{
foreach(ColorMapDataPart *dataPart, m_dataParts)
{
QPair<int,int> xRange = dataPart->getRange(dataPart->timesSeries(),m_minX+xpos*dx,m_minX+(xpos+1)*dx);
QPair<int,int> yRange = dataPart->getRange(dataPart->ySeries(),m_maxY-(ypos+1)*dy,m_maxY-ypos*dy);
int timeSeriesSize = dataPart->timesSeries().size();
if(xRange.first != xRange.second && yRange.first != yRange.second)
{
for(int i =xRange.first+1;i<xRange.second;i++)
{
qreal xval=dataPart->timesSeries()[i];
for(int j=yRange.first+1;j<yRange.second;j++)
{
qreal yval=dataPart->ySeries()[j];
qreal val=dataPart->dataSeries()[ i + (timeSeriesSize * j)];
cluster.append(Point3D(xval,yval,val));
}
}
}
}
}
void QColorMapSeriesPrivate::initializeAnimations(QtCharts::QChart::AnimationOptions options,
int duration, QEasingCurve &curve)
{
// ColorMapChart *item = static_cast<ColorMapChart *>(m_item.data());
// Q_ASSERT(item);
// if (item->animation())
// item->animation()->stopAndDestroyLater();
// if (options.testFlag(QChart::SeriesAnimations))
// item->setAnimation(new XYAnimation(item, duration, curve));
// else
// item->setAnimation(0);
QAbstractSeriesPrivate::initializeAnimations(options, duration, curve);
}
#include "moc_qcolormapseries.cpp"
#include "moc_qcolormapseries_p.cpp"
QT_CHARTS_END_NAMESPACE