diff --git a/example/example.pro b/example/example.pro index 7884189..e7093f5 100644 --- a/example/example.pro +++ b/example/example.pro @@ -11,4 +11,5 @@ SUBDIRS += linechart \ axischart \ multichart \ gdpbarchart \ - presenterchart + presenterchart \ + splinechart diff --git a/example/splinechart/main.cpp b/example/splinechart/main.cpp new file mode 100644 index 0000000..8aea394 --- /dev/null +++ b/example/splinechart/main.cpp @@ -0,0 +1,11 @@ +#include +#include "splinewidget.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + SplineWidget w; + w.show(); + + return a.exec(); +} diff --git a/example/splinechart/splinechart.pro b/example/splinechart/splinechart.pro new file mode 100644 index 0000000..5c800ad --- /dev/null +++ b/example/splinechart/splinechart.pro @@ -0,0 +1,20 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2012-02-29T12:37:46 +# +#------------------------------------------------- + +!include( ../example.pri ) { + error( "Couldn't find the example.pri file!" ) +} + +QT += core gui + +TARGET = splinechart +TEMPLATE = app + + +SOURCES += main.cpp\ + splinewidget.cpp + +HEADERS += splinewidget.h diff --git a/example/splinechart/splinewidget.cpp b/example/splinechart/splinewidget.cpp new file mode 100644 index 0000000..abef9c1 --- /dev/null +++ b/example/splinechart/splinewidget.cpp @@ -0,0 +1,53 @@ +#include "splinewidget.h" +#include "qchartview.h" +#include "qsplineseries.h" +#include "qlinechartseries.h" +#include + +QTCOMMERCIALCHART_USE_NAMESPACE + +SplineWidget::SplineWidget(QWidget *parent) + : QWidget(parent) +{ + //create QSplineSeries + QSplineSeries* series = new QSplineSeries(this); + series->addData(QPointF(150, 100)); + series->addData(QPointF(200, 180)); + series->addData(QPointF(240, 130)); + series->addData(QPointF(270, 120)); + series->addData(QPointF(310, 120)); + series->addData(QPointF(420, 160)); + series->addData(QPointF(535, 250)); + + series->calculateControlPoints(); + + QLineChartSeries* lineSeries = new QLineChartSeries; + for (int i = 0; i < series->count() - 1; i++) + { + lineSeries->add(series->at(i).x(), series->at(i).y()); + lineSeries->add(series->controlPoint(2*i).x(), series->controlPoint(2*i).y()); + lineSeries->add(series->controlPoint(2*i + 1).x(), series->controlPoint(2*i + 1).y()); + } + +// QLineChartSeries* lineSeries2 = new QLineChartSeries; +// lineSeries2->add(10, 50); +// lineSeries2->add(30, 15); +// lineSeries2->add(60, 40); +// lineSeries2->add(90, 70); +// lineSeries2->add(100, 20); + + //create chart view + QChartView* chart = new QChartView; + chart->setMinimumSize(800,600); +// chart->setGeometry(50, 50, 400, 300); + chart->addSeries(series); + + QGridLayout* mainLayout = new QGridLayout; + mainLayout->addWidget(chart); + setLayout(mainLayout); +} + +SplineWidget::~SplineWidget() +{ + +} diff --git a/example/splinechart/splinewidget.h b/example/splinechart/splinewidget.h new file mode 100644 index 0000000..2dfae26 --- /dev/null +++ b/example/splinechart/splinewidget.h @@ -0,0 +1,15 @@ +#ifndef SPLINEWIDGET_H +#define SPLINEWIDGET_H + +#include + +class SplineWidget : public QWidget +{ + Q_OBJECT + +public: + SplineWidget(QWidget *parent = 0); + ~SplineWidget(); +}; + +#endif // SPLINEWIDGET_H diff --git a/src/chartdataset.cpp b/src/chartdataset.cpp index 1fbdd18..a825d5b 100644 --- a/src/chartdataset.cpp +++ b/src/chartdataset.cpp @@ -7,14 +7,15 @@ #include "qpercentbarchartseries.h" #include "qpieseries.h" #include "qscatterseries.h" +#include "qsplineseries.h" QTCOMMERCIALCHART_BEGIN_NAMESPACE ChartDataSet::ChartDataSet(QObject *parent):QObject(parent), -m_axisX(new QChartAxis(this)), -m_axisY(new QChartAxis(this)), -m_domainIndex(0), -m_axisXInitialized(false) + m_axisX(new QChartAxis(this)), + m_axisY(new QChartAxis(this)), + m_domainIndex(0), + m_axisXInitialized(false) { } @@ -25,11 +26,11 @@ ChartDataSet::~ChartDataSet() const Domain ChartDataSet::domain(QChartAxis *axisY) const { - int i = m_domainMap.count(axisY); - if(i == 0){ - return Domain(); - } - i = i - m_domainIndex -1; + int i = m_domainMap.count(axisY); + if(i == 0){ + return Domain(); + } + i = i - m_domainIndex -1; return m_domainMap.values(axisY).at(i); } @@ -55,79 +56,94 @@ void ChartDataSet::addSeries(QChartSeries* series, QChartAxis *axisY) switch(series->type()) { - case QChartSeries::SeriesTypeLine: { - - QLineChartSeries* xyseries = static_cast(series); - - for (int i = 0; i < xyseries->count(); i++) - { - qreal x = xyseries->x(i); - qreal y = xyseries->y(i); - domain.m_minX = qMin(domain.m_minX,x); - domain.m_minY = qMin(domain.m_minY,y); - domain.m_maxX = qMax(domain.m_maxX,x); - domain.m_maxY = qMax(domain.m_maxY,y); - } - break; - } - case QChartSeries::SeriesTypeBar: { - qDebug() << "QChartSeries::SeriesTypeBar"; - QBarChartSeries* barSeries = static_cast(series); - qreal x = barSeries->countCategories(); - qreal y = barSeries->max(); + case QChartSeries::SeriesTypeLine: { + + QLineChartSeries* xyseries = static_cast(series); + + for (int i = 0; i < xyseries->count(); i++) + { + qreal x = xyseries->x(i); + qreal y = xyseries->y(i); domain.m_minX = qMin(domain.m_minX,x); domain.m_minY = qMin(domain.m_minY,y); domain.m_maxX = qMax(domain.m_maxX,x); domain.m_maxY = qMax(domain.m_maxY,y); - break; } - case QChartSeries::SeriesTypeStackedBar: { + break; + } + case QChartSeries::SeriesTypeBar: { + qDebug() << "QChartSeries::SeriesTypeBar"; + QBarChartSeries* barSeries = static_cast(series); + qreal x = barSeries->countCategories(); + qreal y = barSeries->max(); + domain.m_minX = qMin(domain.m_minX,x); + domain.m_minY = qMin(domain.m_minY,y); + domain.m_maxX = qMax(domain.m_maxX,x); + domain.m_maxY = qMax(domain.m_maxY,y); + break; + } + case QChartSeries::SeriesTypeStackedBar: { qDebug() << "QChartSeries::SeriesTypeStackedBar"; - QStackedBarChartSeries* stackedBarSeries = static_cast(series); - qreal x = stackedBarSeries->countCategories(); - qreal y = stackedBarSeries->maxCategorySum(); - domain.m_minX = qMin(domain.m_minX,x); - domain.m_minY = qMin(domain.m_minY,y); - domain.m_maxX = qMax(domain.m_maxX,x); - domain.m_maxY = qMax(domain.m_maxY,y); - break; - } - case QChartSeries::SeriesTypePercentBar: { + QStackedBarChartSeries* stackedBarSeries = static_cast(series); + qreal x = stackedBarSeries->countCategories(); + qreal y = stackedBarSeries->maxCategorySum(); + domain.m_minX = qMin(domain.m_minX,x); + domain.m_minY = qMin(domain.m_minY,y); + domain.m_maxX = qMax(domain.m_maxX,x); + domain.m_maxY = qMax(domain.m_maxY,y); + break; + } + case QChartSeries::SeriesTypePercentBar: { qDebug() << "QChartSeries::SeriesTypePercentBar"; - QPercentBarChartSeries* percentBarSeries = static_cast(series); - qreal x = percentBarSeries->countCategories(); - domain.m_minX = qMin(domain.m_minX,x); - domain.m_minY = 0; - domain.m_maxX = qMax(domain.m_maxX,x); - domain.m_maxY = 100; - break; - } + QPercentBarChartSeries* percentBarSeries = static_cast(series); + qreal x = percentBarSeries->countCategories(); + domain.m_minX = qMin(domain.m_minX,x); + domain.m_minY = 0; + domain.m_maxX = qMax(domain.m_maxX,x); + domain.m_maxY = 100; + break; + } - case QChartSeries::SeriesTypePie: { - QPieSeries *pieSeries = static_cast(series); - // TODO: domain stuff - break; - } + case QChartSeries::SeriesTypePie: { + QPieSeries *pieSeries = static_cast(series); + // TODO: domain stuff + break; + } - case QChartSeries::SeriesTypeScatter: { - QScatterSeries *scatterSeries = qobject_cast(series); - Q_ASSERT(scatterSeries); - foreach (QPointF point, scatterSeries->data()) { - domain.m_minX = qMin(domain.m_minX, point.x()); - domain.m_maxX = qMax(domain.m_maxX, point.x()); - domain.m_minY = qMin(domain.m_minY, point.y()); - domain.m_maxY = qMax(domain.m_maxY, point.y()); - } - break; + case QChartSeries::SeriesTypeScatter: { + QScatterSeries *scatterSeries = qobject_cast(series); + Q_ASSERT(scatterSeries); + foreach (QPointF point, scatterSeries->data()) { + domain.m_minX = qMin(domain.m_minX, point.x()); + domain.m_maxX = qMax(domain.m_maxX, point.x()); + domain.m_minY = qMin(domain.m_minY, point.y()); + domain.m_maxY = qMax(domain.m_maxY, point.y()); } + break; + } - default: { - qDebug()<<__FUNCTION__<<"type" << series->type()<<"not supported"; - return; - break; + case QChartSeries::SeriesTypeSpline: { + QSplineSeries* splineSeries = static_cast(series); + + for (int i = 0; i < splineSeries->count(); i++) + { + qreal x = splineSeries->at(i).x(); + qreal y = splineSeries->at(i).y(); + domain.m_minX = qMin(domain.m_minX,x); + domain.m_minY = qMin(domain.m_minY,y); + domain.m_maxX = qMax(domain.m_maxX,x); + domain.m_maxY = qMax(domain.m_maxY,y); } + break; + } + + default: { + qDebug()<<__FUNCTION__<<"type" << series->type()<<"not supported"; + return; + break; + } } @@ -172,7 +188,7 @@ void ChartDataSet::removeSeries(QChartSeries* series) emit axisRemoved(axis); m_domainMap.remove(axis); if(axis != m_axisY) - delete axis; + delete axis; } series->setParent(0); break; @@ -194,7 +210,7 @@ void ChartDataSet::removeAllSeries() m_domainMap.remove(axis); emit axisRemoved(axis); if(axis != m_axisY) delete axis; - } + } m_domainIndex=0; } diff --git a/src/chartpresenter.cpp b/src/chartpresenter.cpp index 18282f7..5986593 100644 --- a/src/chartpresenter.cpp +++ b/src/chartpresenter.cpp @@ -25,13 +25,13 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE ChartPresenter::ChartPresenter(QChart* chart,ChartDataSet* dataset):QObject(chart), -m_chart(chart), -m_dataset(dataset), -m_chartTheme(0), -m_marginSize(0), -m_rect(QRectF(QPoint(0,0),m_chart->size())) + m_chart(chart), + m_dataset(dataset), + m_chartTheme(0), + m_marginSize(0), + m_rect(QRectF(QPoint(0,0),m_chart->size())) { - createConnections(); + createConnections(); setChartTheme(QChart::ChartThemeDefault); } @@ -60,7 +60,7 @@ QRectF ChartPresenter::geometry() const void ChartPresenter::handleGeometryChanged() { m_rect = QRectF(QPoint(0,0),m_chart->size()); - m_rect.adjust(m_marginSize,m_marginSize, -m_marginSize, -m_marginSize); + // m_rect.adjust(m_marginSize,m_marginSize, -m_marginSize, -m_marginSize); Q_ASSERT(m_rect.isValid()); emit geometryChanged(m_rect); } @@ -80,9 +80,9 @@ void ChartPresenter::handleAxisAdded(QChartAxis* axis) AxisItem* item ; if(axis==m_dataset->axisX()){ - item = new AxisItem(AxisItem::X_AXIS,m_chart); + item = new AxisItem(AxisItem::X_AXIS,m_chart); }else{ - item = new AxisItem(AxisItem::Y_AXIS,m_chart); + item = new AxisItem(AxisItem::Y_AXIS,m_chart); } QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); QObject::connect(axis,SIGNAL(update(QChartAxis*)),item,SLOT(handleAxisUpdate(QChartAxis*))); @@ -105,74 +105,77 @@ void ChartPresenter::handleSeriesAdded(QChartSeries* series) { switch(series->type()) { - case QChartSeries::SeriesTypeLine: { - QLineChartSeries* lineSeries = static_cast(series); - LineChartItem* item = new LineChartAnimationItem(this,lineSeries,m_chart); - m_chartTheme->decorate(item,lineSeries,m_chartItems.count()); - QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); - QObject::connect(lineSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); - m_chartItems.insert(series,item); - break; - } - - case QChartSeries::SeriesTypeBar: { - QBarChartSeries* barSeries = static_cast(series); - BarPresenter* item = new BarPresenter(barSeries,m_chart); - m_chartTheme->decorate(item,barSeries,m_chartItems.count()); - QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); - QObject::connect(barSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); - m_chartItems.insert(series,item); - // m_axisXItem->setVisible(false); - break; - } - - case QChartSeries::SeriesTypeStackedBar: { - - QStackedBarChartSeries* stackedBarSeries = static_cast(series); - StackedBarPresenter* item = new StackedBarPresenter(stackedBarSeries,m_chart); - m_chartTheme->decorate(item,stackedBarSeries,m_chartItems.count()); - QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); - QObject::connect(stackedBarSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); - m_chartItems.insert(series,item); - break; - } - - case QChartSeries::SeriesTypePercentBar: { - - QPercentBarChartSeries* percentBarSeries = static_cast(series); - PercentBarPresenter* item = new PercentBarPresenter(percentBarSeries,m_chart); - m_chartTheme->decorate(item,percentBarSeries ,m_chartItems.count()); - QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); - QObject::connect(percentBarSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); - m_chartItems.insert(series,item); - break; - } - case QChartSeries::SeriesTypeScatter: { - QScatterSeries *scatterSeries = qobject_cast(series); - ScatterPresenter *scatterPresenter = new ScatterPresenter(scatterSeries, m_chart); - QObject::connect(this, SIGNAL(geometryChanged(const QRectF&)), - scatterPresenter, SLOT(handleGeometryChanged(const QRectF&))); - m_chartTheme->decorate(scatterPresenter, scatterSeries, m_chartItems.count()); - m_chartItems.insert(scatterSeries, scatterPresenter); - break; - } - case QChartSeries::SeriesTypePie: { - QPieSeries *s = qobject_cast(series); - PiePresenter* pie = new PiePresenter(m_chart, s); - m_chartTheme->decorate(pie, s, m_chartItems.count()); - QObject::connect(this, SIGNAL(geometryChanged(const QRectF&)), pie, SLOT(handleGeometryChanged(const QRectF&))); - m_chartItems.insert(series, pie); - break; - } + case QChartSeries::SeriesTypeLine: { + QLineChartSeries* lineSeries = static_cast(series); + LineChartItem* item = new LineChartAnimationItem(this,lineSeries,m_chart); + m_chartTheme->decorate(item,lineSeries,m_chartItems.count()); + QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); + QObject::connect(lineSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); + m_chartItems.insert(series,item); + break; + } + + case QChartSeries::SeriesTypeBar: { + QBarChartSeries* barSeries = static_cast(series); + BarPresenter* item = new BarPresenter(barSeries,m_chart); + m_chartTheme->decorate(item,barSeries,m_chartItems.count()); + QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); + QObject::connect(barSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); + m_chartItems.insert(series,item); + // m_axisXItem->setVisible(false); + break; + } + + case QChartSeries::SeriesTypeStackedBar: { + + QStackedBarChartSeries* stackedBarSeries = static_cast(series); + StackedBarPresenter* item = new StackedBarPresenter(stackedBarSeries,m_chart); + m_chartTheme->decorate(item,stackedBarSeries,m_chartItems.count()); + QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); + QObject::connect(stackedBarSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); + m_chartItems.insert(series,item); + break; + } + + case QChartSeries::SeriesTypePercentBar: { + + QPercentBarChartSeries* percentBarSeries = static_cast(series); + PercentBarPresenter* item = new PercentBarPresenter(percentBarSeries,m_chart); + m_chartTheme->decorate(item,percentBarSeries ,m_chartItems.count()); + QObject::connect(this,SIGNAL(geometryChanged(const QRectF&)),item,SLOT(handleGeometryChanged(const QRectF&))); + QObject::connect(percentBarSeries,SIGNAL(changed(int)),item,SLOT(handleModelChanged(int))); + m_chartItems.insert(series,item); + break; + } + case QChartSeries::SeriesTypeScatter: { + QScatterSeries *scatterSeries = qobject_cast(series); + ScatterPresenter *scatterPresenter = new ScatterPresenter(scatterSeries, m_chart); + QObject::connect(this, SIGNAL(geometryChanged(const QRectF&)), + scatterPresenter, SLOT(handleGeometryChanged(const QRectF&))); + m_chartTheme->decorate(scatterPresenter, scatterSeries, m_chartItems.count()); + m_chartItems.insert(scatterSeries, scatterPresenter); + break; + } + case QChartSeries::SeriesTypePie: { + QPieSeries *s = qobject_cast(series); + PiePresenter* pie = new PiePresenter(m_chart, s); + m_chartTheme->decorate(pie, s, m_chartItems.count()); + QObject::connect(this, SIGNAL(geometryChanged(const QRectF&)), pie, SLOT(handleGeometryChanged(const QRectF&))); + m_chartItems.insert(series, pie); + break; + } case QChartSeries::SeriesTypeSpline: { QSplineSeries* splineSeries = qobject_cast(series); - SplinePresenter* splinePresenter = new SplinePresenter + SplinePresenter* splinePresenter = new SplinePresenter(splineSeries, m_chart); + QObject::connect(this, SIGNAL(geometryChanged(const QRectF&)), splinePresenter, SLOT(handleGeometryChanged(const QRectF&))); + m_chartTheme->decorate(splinePresenter, splineSeries, m_chartItems.count()); + m_chartItems.insert(splineSeries, splinePresenter); + break; + } + default: { + qDebug()<< "Series type" << series->type() << "not implemented."; break; } - default: { - qDebug()<< "Series type" << series->type() << "not implemented."; - break; - } } if(m_rect.isValid()) emit geometryChanged(m_rect); @@ -180,8 +183,8 @@ void ChartPresenter::handleSeriesAdded(QChartSeries* series) void ChartPresenter::handleSeriesRemoved(QChartSeries* series) { - ChartItem* item = m_chartItems.take(series); - delete item; + ChartItem* item = m_chartItems.take(series); + delete item; } void ChartPresenter::handleSeriesChanged(QChartSeries* series) @@ -217,8 +220,8 @@ void ChartPresenter::setChartTheme(QChart::ChartTheme theme) QMapIterator j(m_axisItems); while (j.hasNext()) { - j.next(); - m_chartTheme->decorate(j.key(),j.value()); + j.next(); + m_chartTheme->decorate(j.key(),j.value()); } } diff --git a/src/charttheme.cpp b/src/charttheme.cpp index 1ea3d29..d04b826 100644 --- a/src/charttheme.cpp +++ b/src/charttheme.cpp @@ -12,6 +12,7 @@ #include "qscatterseries.h" #include "qpieseries.h" #include "qpieslice.h" +#include "qsplineseries.h" //items #include "axisitem_p.h" @@ -21,6 +22,7 @@ #include "percentbarpresenter.h" #include "scatterpresenter_p.h" #include "piepresenter.h" +#include "splinepresenter_p.h" //themes #include "chartthemevanilla_p.h" @@ -226,4 +228,21 @@ void ChartTheme::decorate(QChartAxis* axis,AxisItem* item) axis->setShadesOpacity(0.5); } +void ChartTheme::decorate(SplinePresenter* presenter, QSplineSeries* series, int count) +{ + Q_ASSERT(presenter); + Q_ASSERT(series); + +// QColor color = m_seriesColor.at(count % m_seriesColor.size()); + // TODO: define alpha in the theme? or in the series? + //color.setAlpha(120); + +// QBrush brush(color, Qt::SolidPattern); +// presenter->m_markerBrush = brush; + +// QPen pen(brush, 3); +// pen.setColor(color); +// presenter->m_markerPen = pen; +} + QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/charttheme_p.h b/src/charttheme_p.h index 86eeb20..259d0e4 100644 --- a/src/charttheme_p.h +++ b/src/charttheme_p.h @@ -21,6 +21,8 @@ class QScatterSeries; class ScatterPresenter; class PiePresenter; class QPieSeries; +class SplinePresenter; +class QSplineSeries; class ChartTheme { @@ -38,6 +40,7 @@ public: void decorate(ScatterPresenter* presenter, QScatterSeries* series, int count); void decorate(PiePresenter* item, QPieSeries* series, int count); void decorate(QChartAxis* axis,AxisItem* item); + void decorate(SplinePresenter* presenter, QSplineSeries* series, int count); protected: QChart::ChartTheme m_id; diff --git a/src/splinechart/qsplineseries.cpp b/src/splinechart/qsplineseries.cpp index 828e01c..2c63fd4 100644 --- a/src/splinechart/qsplineseries.cpp +++ b/src/splinechart/qsplineseries.cpp @@ -1,100 +1,110 @@ #include "qsplineseries.h" +QTCOMMERCIALCHART_BEGIN_NAMESPACE + QSplineSeries::QSplineSeries(QObject *parent) : - QObject(parent) + QChartSeries(parent) { } QSplineSeries& QSplineSeries::operator << (const QPointF &value) { - d->m_data.append(value); - emit changed(); +// d->m_data.append(value); + m_data.append(value); +// emit changed(); return *this; } -void QSplineSeries::GetCurveControlPoints() - { +void QSplineSeries::addData(QPointF value) +{ + m_data.append(value); +} + +void QSplineSeries::calculateControlPoints() +{ + + // Based on http://www.codeproject.com/Articles/31859/Draw-a-Smooth-Curve-through-a-Set-of-2D-Points-wit + // CPOL Licence + int n = m_data.size() - 1; - if (n < 1) - throw new ArgumentException - ("At least two knot points required", "knots"); - if (n == 1) - { // Special case: Bezier curve should be a straight line. - firstControlPoints = new Point[1]; - // 3P1 = 2P0 + P3 - firstControlPoints[0].X = (2 * knots[0].X + knots[1].X) / 3; - firstControlPoints[0].Y = (2 * knots[0].Y + knots[1].Y) / 3; - - secondControlPoints = new Point[1]; - // P2 = 2P1 P0 - secondControlPoints[0].X = 2 * - firstControlPoints[0].X - knots[0].X; - secondControlPoints[0].Y = 2 * - firstControlPoints[0].Y - knots[0].Y; - return; - } - - // Calculate first Bezier control points - // Right hand side vector - double[] rhs = new double[n]; - - // Set right hand side X values - for (int i = 1; i < n - 1; ++i) - rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X; - rhs[0] = knots[0].X + 2 * knots[1].X; - rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0; - // Get first control points X-values - double[] x = GetFirstControlPoints(rhs); - - // Set right hand side Y values - for (int i = 1; i < n - 1; ++i) - rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y; - rhs[0] = knots[0].Y + 2 * knots[1].Y; - rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0; - // Get first control points Y-values - double[] y = GetFirstControlPoints(rhs); - - // Fill output arrays. - firstControlPoints = new Point[n]; - secondControlPoints = new Point[n]; - for (int i = 0; i < n; ++i) - { - // First control point - firstControlPoints[i] = new Point(x[i], y[i]); - // Second control point - if (i < n - 1) - secondControlPoints[i] = new Point(2 * knots - [i + 1].X - x[i + 1], 2 * - knots[i + 1].Y - y[i + 1]); - else - secondControlPoints[i] = new Point((knots - [n].X + x[n - 1]) / 2, - (knots[n].Y + y[n - 1]) / 2); - } + if (n == 1) + { // Special case: Bezier curve should be a straight line. + // firstControlPoints = new Point[1]; + // 3P1 = 2P0 + P3 + m_controlPoints.append(QPointF((2 * m_data[0].x() + m_data[1].x()) / 3, (2 * m_data[0].y() + m_data[1].y()) / 3)); + + // P2 = 2P1 P0 + m_controlPoints.append(QPointF(2 * m_controlPoints[0].x() - m_data[0].x(), 2 * m_controlPoints[0].y() - m_data[0].y())); + return; } - /// - /// Solves a tridiagonal system for one of coordinates (x or y) - /// of first Bezier control points. - /// - /// Right hand side vector. - /// Solution vector. -void GetFirstControlPoints(qreal[] rhs) + // Calculate first Bezier control points + // Right hand side vector + // Set of equations for P0 to Pn points. + // + // | 2 1 0 0 ... 0 0 0 ... 0 0 0 | | P1_1 | | P0 + 2 * P1 | + // | 1 4 1 0 ... 0 0 0 ... 0 0 0 | | P1_2 | | 4 * P1 + 2 * P2 | + // | 0 1 4 1 ... 0 0 0 ... 0 0 0 | | P1_3 | | 4 * P2 + 2 * P3 | + // | . . . . . . . . . . . . | | ... | | ... | + // | 0 0 0 0 ... 1 4 1 ... 0 0 0 | * | P1_i | = | 4 * P(i-1) + 2 * Pi | + // | . . . . . . . . . . . . | | ... | | ... | + // | 0 0 0 0 0 0 0 0 ... 1 4 1 | | P1_(n-1)| | 4 * P(n-2) + 2 * P(n-1) | + // | 0 0 0 0 0 0 0 0 ... 0 2 7 | | P1_n | | 8 * P(n-1) + Pn | + // + QList rhs; + rhs.append(m_data[0].x() + 2 * m_data[1].x()); + + // Set right hand side X values + for (int i = 1; i < m_data.size() - 1; ++i) + rhs.append(4 * m_data[i].x() + 2 * m_data[i + 1].x()); + + rhs.append((8 * m_data[n - 1].x() + m_data[n].x()) / 2.0); + // Get first control points X-values + QList x = getFirstControlPoints(rhs); + rhs[0] = m_data[0].y() + 2 * m_data[1].y(); + + // Set right hand side Y values + for (int i = 1; i < m_data.size() - 1; ++i) + rhs[i] = 4 * m_data[i].y() + 2 * m_data[i + 1].y(); + + rhs[n - 1] = (8 * m_data[n - 1].y() + m_data[n].y()) / 2.0; + // Get first control points Y-values + QList y = getFirstControlPoints(rhs); + + // Fill output arrays. + // firstControlPoints = new Point[n]; + // secondControlPoints = new Point[n]; + for (int i = 0; i < m_data.size(); ++i) { - int n = rhs.Length; - double[] x = new double[n]; // Solution vector. - double[] tmp = new double[n]; // Temp workspace. - - double b = 2.0; - x[0] = rhs[0] / b; - for (int i = 1; i < n; i++) // Decomposition and forward substitution. - { - tmp[i] = 1 / b; - b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; - x[i] = (rhs[i] - x[i - 1]) / b; - } - for (int i = 1; i < n; i++) - x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. - - return x; + // First control point + m_controlPoints.append(QPointF(x[i], y[i])); + // Second control point + if (i < n - 1) + m_controlPoints.append(QPointF(2 * m_data[i + 1].x() - x[i + 1], 2 * m_data[i + 1].y() - y[i + 1])); + else + m_controlPoints.append(QPointF((m_data[n].x() + x[n - 1]) / 2, (m_data[n].y() + y[n - 1]) / 2)); } +} + +QList QSplineSeries::getFirstControlPoints(QList rhs) +{ + QList x; // Solution vector. + QList tmp; // Temp workspace. + + qreal b = 2.0; + x.append(rhs[0] / b); + tmp.append(0); + for (int i = 1; i < rhs.size(); i++) // Decomposition and forward substitution. + { + tmp.append(1 / b); + b = (i < rhs.size() - 1 ? 4.0 : 3.5) - tmp[i]; + x.append((rhs[i] - x[i - 1]) / b); + } + for (int i = 1; i < rhs.size(); i++) + x[rhs.size() - i - 1] -= tmp[rhs.size() - i] * x[rhs.size() - i]; // Backsubstitution. + + return x; +} +#include "moc_qsplineseries.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/splinechart/qsplineseries.h b/src/splinechart/qsplineseries.h index 9623679..0439b21 100644 --- a/src/splinechart/qsplineseries.h +++ b/src/splinechart/qsplineseries.h @@ -1,7 +1,11 @@ #ifndef QSPLINESERIES_H #define QSPLINESERIES_H +#include "qchartglobal.h" +#include #include "qchartseries.h" +#include +#include QTCOMMERCIALCHART_BEGIN_NAMESPACE @@ -15,6 +19,12 @@ class QSplineSeries : public QChartSeries void addData(QPointF value); QSplineSeries& operator << (const QPointF &value); void calculateControlPoints(); + QList getFirstControlPoints(QList rhs); + + int count() const { return m_data.size(); } + + QPointF at(int index) const { return m_data[index]; } + QPointF controlPoint(int index) const { return m_controlPoints[index]; } signals: diff --git a/src/splinechart/splinechart.pri b/src/splinechart/splinechart.pri index 541dd82..c2ff5c8 100644 --- a/src/splinechart/splinechart.pri +++ b/src/splinechart/splinechart.pri @@ -3,13 +3,10 @@ DEPENDPATH += $$PWD SOURCES += \ $$PWD/qsplineseries.cpp \ - splinechart/splinepresenter.cpp + $$PWD/splinepresenter.cpp -PRIVATE_HEADERS += +PRIVATE_HEADERS += \ + $$PWD/splinepresenter_p.h PUBLIC_HEADERS += \ $$PWD/qsplineseries.h - -HEADERS += \ - splinechart/qsplineseries.h \ - splinechart/splinepresenter_p.h diff --git a/src/splinechart/splinepresenter.cpp b/src/splinechart/splinepresenter.cpp index 987000c..b70ef0c 100644 --- a/src/splinechart/splinepresenter.cpp +++ b/src/splinechart/splinepresenter.cpp @@ -1,16 +1,54 @@ #include "splinepresenter_p.h" +#include -SplinePresenter::SplinePresenter(QObject *parent) : - QObject(parent) +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +SplinePresenter::SplinePresenter(QSplineSeries* series, QGraphicsObject *parent) : + ChartItem(parent),m_series(series),m_boundingRect() { + if (parent) + m_boundingRect = parent->boundingRect(); + else + m_boundingRect = QRectF(10,50, 250, 250); } void SplinePresenter::handleGeometryChanged(const QRectF&) { - // + update(); } void SplinePresenter::handleDomainChanged(const Domain& domain) { // } + +void SplinePresenter::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(widget); + Q_UNUSED(option); + painter->save(); + + painter->setPen(Qt::SolidLine); + + QPainterPath splinePath; + splinePath.moveTo(m_series->at(0)); + for (int i = 0; i < m_series->count() - 1; i++) + { + painter->setPen(Qt::red); + splinePath.cubicTo(m_series->controlPoint(2 * i), m_series->controlPoint(2 * i + 1), m_series->at(i + 1)); + painter->drawEllipse(m_series->at(i), 4, 4); + + painter->setPen(Qt::blue); + painter->drawLine(m_series->at(i), m_series->controlPoint(2 * i)); + painter->drawLine(m_series->at(i + 1), m_series->controlPoint(2 * i + 1)); + painter->drawEllipse(m_series->controlPoint(2 * i), 4, 4); + painter->drawEllipse(m_series->controlPoint(2 * i + 1), 4, 4); + } + painter->setPen(Qt::red); + painter->drawPath(splinePath); + painter->restore(); +} + +#include "moc_splinepresenter_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/splinechart/splinepresenter_p.h b/src/splinechart/splinepresenter_p.h index 67595cd..21132b6 100644 --- a/src/splinechart/splinepresenter_p.h +++ b/src/splinechart/splinepresenter_p.h @@ -3,6 +3,7 @@ #include "chartitem_p.h" #include +#include "qsplineseries.h" QTCOMMERCIALCHART_BEGIN_NAMESPACE @@ -10,14 +11,20 @@ class SplinePresenter : public QObject, public ChartItem { Q_OBJECT public: - SplinePresenter(QObject *parent = 0); + SplinePresenter(QSplineSeries* series, QGraphicsObject *parent = 0); - void handleGeometryChanged(const QRectF&); - void handleDomainChanged(const Domain& domain); + QRectF boundingRect() const { return m_boundingRect; } + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); signals: public slots: + void handleDomainChanged(const Domain& domain); + void handleGeometryChanged(const QRectF& rect); + + private: + QSplineSeries* m_series; + QRectF m_boundingRect; }; diff --git a/test/chartwidgettest/dataseriedialog.cpp b/test/chartwidgettest/dataseriedialog.cpp index 19164e9..78bd77c 100644 --- a/test/chartwidgettest/dataseriedialog.cpp +++ b/test/chartwidgettest/dataseriedialog.cpp @@ -1,7 +1,7 @@ #include "dataseriedialog.h" #include #include -#include +#include #include #include #include