Git commit 7294844260c45b1363a89b786cd94248477f9938 by Stefan Gerlach. Committed on 09/04/2016 at 19:31. Pushed by sgerlach into branch 'analysis_interpolation'.
finished interpolation 1. use evaluation 2. check minimum number of points 3. fix bugs 4. documentation M +27 -4 doc/index.docbook M +9 -10 src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp M +25 -14 src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp M +0 -4 src/backend/worksheet/plots/cartesian/XYInterpolationCurve.h M +1 -1 src/kdefrontend/dockwidgets/AxisDock.cpp M +1 -1 src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp M +1 -1 src/kdefrontend/dockwidgets/XYFitCurveDock.cpp M +1 -1 src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp M +75 -14 src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp http://commits.kde.org/labplot/7294844260c45b1363a89b786cd94248477f9938 diff --git a/doc/index.docbook b/doc/index.docbook index e771d00..7791153 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -51,7 +51,7 @@ </copyright> <legalnotice>&FDLNotice;</legalnotice> -<date>2016-04-03</date> +<date>2016-04-09</date> <releaseinfo>3.1</releaseinfo> <abstract> @@ -778,15 +778,38 @@ The menu is only available when a datapicker object is selected on the <guilabel <chapter id="analysis"> <title>Analysis functions</title> - <para>&LabPlot; supports the data analysis functions nonlinear curve fitting and Fourier filter. + <para>&LabPlot; supports the data analysis functions interpolation, nonlinear curve fitting and Fourier filter. Both can be applied to any data consisting of x- and y-columns. The analysis functions can be accessed using the Analysis menu or the context menu of a worksheet.</para> <para>The newly created curves can be customized (line style, symbol style, &etc;) like any x-y-curve.</para> + <sect1 id="interpolation"> + <title>Interpolation</title> + <para> + Interpolation of data can be done with several algorithm: + </para> + <itemizedlist> + <listitem><para>Linear</para></listitem> + <listitem><para>Polynomial</para></listitem> + <listitem><para>cubic spline</para></listitem> + <listitem><para>cubic spline (periodic)</para></listitem> + <listitem><para>Akima spline</para></listitem> + <listitem><para>Akima spline (periodic)</para></listitem> + <listitem><para>Steffen spline (needs GSL >= 2.0)</para></listitem> + </itemizedlist> + <para> + The interpolating function is calculated with the given number of data points an evaluated as: + </para> + <itemizedlist> + <listitem><para>function</para></listitem> + <listitem><para>derivative</para></listitem> + <listitem><para>second derivative</para></listitem> + <listitem><para>integral (starting from zero)</para></listitem> + </itemizedlist> + </sect1> + <sect1 id="fitting"> <title>Curve fitting</title> <para> - <!-- Linear and non-linear fits to data, several fit-models are predefined and custom models with - arbitrary number of parameters can be provided --> Linear and non-linear curve fitting of data can be done with several predefined fit-models (for instance polynomial, exponential, Gaussian or custom) to data consisting of x- and y-columns with an optional weight column. With a custom model any function with unlimited number of parameters diff --git a/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp b/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp index 733ae27..16b8dc7 100644 --- a/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp @@ -38,11 +38,9 @@ #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" + #include <cmath> // isnan -/*#include "backend/gsl/ExpressionParser.h" -#include "backend/gsl/parser_extern.h" -*/ -//#include <gsl/gsl_version.h> +#include <gsl_errno.h> #include <gsl/gsl_fft_real.h> #include <gsl/gsl_fft_halfcomplex.h> #include <gsl/gsl_sf_pow_int.h> @@ -215,7 +213,7 @@ void XYFourierFilterCurvePrivate::recalculate() { return; } - //copy all valid data point for the fit to temporary vectors + //copy all valid data point for the filter to temporary vectors QVector<double> xdataVector; QVector<double> ydataVector; for (int row=0; row<xDataColumn->rowCount(); ++row) { @@ -228,7 +226,7 @@ void XYFourierFilterCurvePrivate::recalculate() { } } - //number of data points to fit + //number of data points to filter unsigned int n = ydataVector.size(); if (n == 0) { filterResult.available = true; @@ -258,12 +256,13 @@ void XYFourierFilterCurvePrivate::recalculate() { qDebug()<<"unit :"<<unit<<unit2; #endif /////////////////////////////////////////////////////////// + int status; // 1. transform gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc (n); gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc (n); - gsl_fft_real_transform (ydata, 1, n, real, work); - gsl_fft_real_wavetable_free (real); + status = gsl_fft_real_transform(ydata, 1, n, real, work); + gsl_fft_real_wavetable_free(real); // calculate index double cutindex=0, cutindex2=0; @@ -379,7 +378,7 @@ void XYFourierFilterCurvePrivate::recalculate() { // 3. back transform gsl_fft_halfcomplex_wavetable *hc = gsl_fft_halfcomplex_wavetable_alloc (n); - gsl_fft_halfcomplex_inverse (ydata, 1, n, hc, work); + status = gsl_fft_halfcomplex_inverse(ydata, 1, n, hc, work); gsl_fft_halfcomplex_wavetable_free (hc); gsl_fft_real_workspace_free (work); @@ -394,7 +393,7 @@ void XYFourierFilterCurvePrivate::recalculate() { //write the result filterResult.available = true; filterResult.valid = true; - filterResult.status = i18n("OK"); + filterResult.status = QString(gsl_strerror(status));; filterResult.elapsedTime = timer.elapsed(); //redraw the curve diff --git a/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp b/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp index 406ce9a..e73ef82 100644 --- a/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp @@ -38,10 +38,9 @@ #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" + #include <cmath> // isnan -/*#include "backend/gsl/ExpressionParser.h" -#include "backend/gsl/parser_extern.h" -*/ +#include <gsl_errno.h> #include <gsl/gsl_interp.h> #include <gsl/gsl_spline.h> @@ -213,7 +212,7 @@ void XYInterpolationCurvePrivate::recalculate() { return; } - //copy all valid data point for the fit to temporary vectors + //copy all valid data point for the interpolation to temporary vectors QVector<double> xdataVector; QVector<double> ydataVector; for (int row=0; row<xDataColumn->rowCount(); ++row) { @@ -228,10 +227,10 @@ void XYInterpolationCurvePrivate::recalculate() { //number of data points to fit unsigned int n = ydataVector.size(); - if (n == 0) { + if (n < 2) { interpolationResult.available = true; interpolationResult.valid = false; - interpolationResult.status = i18n("No data points available."); + interpolationResult.status = i18n("Not enough data points available."); emit (q->dataChanged()); sourceDataChangedSinceLastInterpolation = false; return; @@ -253,9 +252,8 @@ void XYInterpolationCurvePrivate::recalculate() { qDebug()<<"npoints ="<<npoints; #endif /////////////////////////////////////////////////////////// + int status; //TODO: - // * check minimum number of points - // * evaluate "evaluate" // * check Steffen spline with GSL 2.X gsl_interp_accel *acc = gsl_interp_accel_alloc(); @@ -279,21 +277,34 @@ void XYInterpolationCurvePrivate::recalculate() { case XYInterpolationCurve::AkimaPeriodic: spline = gsl_spline_alloc(gsl_interp_akima_periodic, n); break; -#if GSL_MAJOR_VERSION >= 2 case XYInterpolationCurve::Steffen: +#if GSL_MAJOR_VERSION >= 2 spline = gsl_spline_alloc(gsl_interp_steffen, n); - break; #endif + break; } - gsl_spline_init (spline, xdata, ydata, n); + status = gsl_spline_init (spline, xdata, ydata, n); xVector->resize(npoints); yVector->resize(npoints); - for (int i = 0; i<npoints; i++) { + for (unsigned int i = 0; i<npoints; i++) { double x = min + i*(max-min)/(npoints-1); (*xVector)[i] = x; - (*yVector)[i] = gsl_spline_eval (spline, x, acc); + switch(evaluate) { + case XYInterpolationCurve::Function: + (*yVector)[i] = gsl_spline_eval(spline, x, acc); + break; + case XYInterpolationCurve::Derivative: + (*yVector)[i] = gsl_spline_eval_deriv(spline, x, acc); + break; + case XYInterpolationCurve::Derivative2: + (*yVector)[i] = gsl_spline_eval_deriv2(spline, x, acc); + break; + case XYInterpolationCurve::Integral: + (*yVector)[i] = gsl_spline_eval_integ(spline, min, x, acc); + break; + } } gsl_spline_free(spline); @@ -304,7 +315,7 @@ void XYInterpolationCurvePrivate::recalculate() { //write the result interpolationResult.available = true; interpolationResult.valid = true; - interpolationResult.status = i18n("OK"); + interpolationResult.status = QString(gsl_strerror(status));; interpolationResult.elapsedTime = timer.elapsed(); //redraw the curve diff --git a/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.h b/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.h index bc6c9e8..cf0339c 100644 --- a/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.h +++ b/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.h @@ -37,11 +37,7 @@ class XYInterpolationCurve: public XYCurve { Q_OBJECT public: -#if GSL_MAJOR_VERSION >= 2 enum InterpolationType {Linear,Polynomial,CSpline,CSplinePeriodic,Akima,AkimaPeriodic,Steffen}; // TODO:more -#else - enum InterpolationType {Linear,Polynomial,CSpline,CSplinePeriodic,Akima,AkimaPeriodic}; // TODO:more -#endif enum InterpolationEval {Function,Derivative,Derivative2,Integral}; struct InterpolationData { diff --git a/src/kdefrontend/dockwidgets/AxisDock.cpp b/src/kdefrontend/dockwidgets/AxisDock.cpp index 42ef646..95a5b1c 100644 --- a/src/kdefrontend/dockwidgets/AxisDock.cpp +++ b/src/kdefrontend/dockwidgets/AxisDock.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - File : AxisDock.cc + File : AxisDock.cpp Project : LabPlot Description : axes widget class -------------------------------------------------------------------- diff --git a/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp index 314cc99..62e1f89 100644 --- a/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - File : XYEquationCurveDock.h + File : XYEquationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014 Alexander Semke ([email protected]) diff --git a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp index 9d1288d..a5d9445 100644 --- a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - File : XYFitCurveDock.h + File : XYFitCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014-2016 Alexander Semke ([email protected]) diff --git a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp index 8b65d24..0a70b16 100644 --- a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - File : XYFourierFilterCurveDock.h + File : XYFourierFilterCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach ([email protected]) diff --git a/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp index 269393a..bb6a540 100644 --- a/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - File : XYInterpolationCurveDock.h + File : XYInterpolationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach ([email protected]) @@ -33,6 +33,9 @@ #include "commonfrontend/widgets/TreeViewComboBox.h" #include <QMenu> #include <QWidgetAction> +#include <QStandardItemModel> +#include <gsl_interp.h> // gsl_interp types +#include <cmath> // isnan #include <QDebug> /*! @@ -102,7 +105,6 @@ void XYInterpolationCurveDock::setupGeneral() { connect( uiGeneralTab.cbEval, SIGNAL(currentIndexChanged(int)), this, SLOT(evaluateChanged(int)) ); connect( uiGeneralTab.sbPoints, SIGNAL(valueChanged(int)), this, SLOT(numberOfPointsChanged(int)) ); -// connect( uiGeneralTab.pbOptions, SIGNAL(clicked()), this, SLOT(showOptions()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); } @@ -131,6 +133,8 @@ void XYInterpolationCurveDock::initGeneralTab() { Q_ASSERT(m_interpolationCurve); XYCurveDock::setModelIndexFromColumn(cbXDataColumn, m_interpolationCurve->xDataColumn()); XYCurveDock::setModelIndexFromColumn(cbYDataColumn, m_interpolationCurve->yDataColumn()); + // update list of selectable types + xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbType->setCurrentIndex(m_interpolationData.type); this->typeChanged(m_interpolationData.type); @@ -162,11 +166,12 @@ void XYInterpolationCurveDock::setModel() { cbXDataColumn->setSelectableClasses(list); cbYDataColumn->setSelectableClasses(list); + connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); + connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); + cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); - connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); - connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); XYCurveDock::setModel(); } @@ -205,9 +210,6 @@ void XYInterpolationCurveDock::commentChanged(){ } void XYInterpolationCurveDock::xDataColumnChanged(const QModelIndex& index) { - if (m_initializing) - return; - AbstractAspect* aspect = static_cast<AbstractAspect*>(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { @@ -218,9 +220,70 @@ void XYInterpolationCurveDock::xDataColumnChanged(const QModelIndex& index) { foreach(XYCurve* curve, m_curvesList) dynamic_cast<XYInterpolationCurve*>(curve)->setXDataColumn(column); - // update range of cutoff spin boxes (like a unit change) -// unitChanged(uiGeneralTab.cbUnit->currentIndex()); -// unit2Changed(uiGeneralTab.cbUnit2->currentIndex()); + // disable types that need more data points + if(column != 0) { + unsigned int n=0; + for(int row=0;row < column->rowCount();row++) + if (!isnan(column->valueAt(row)) && !column->isMasked(row)) + n++; + + const QStandardItemModel* model = qobject_cast<const QStandardItemModel*>(uiGeneralTab.cbType->model()); + QStandardItem* item = model->item(XYInterpolationCurve::Polynomial); + if(n < gsl_interp_type_min_size(gsl_interp_polynomial)) { + item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); + if(uiGeneralTab.cbType->currentIndex() == XYInterpolationCurve::Polynomial) + uiGeneralTab.cbType->setCurrentIndex(0); + } + else + item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + item = model->item(XYInterpolationCurve::CSpline); + if(n < gsl_interp_type_min_size(gsl_interp_cspline)) { + item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); + if(uiGeneralTab.cbType->currentIndex() == XYInterpolationCurve::CSpline) + uiGeneralTab.cbType->setCurrentIndex(0); + } + else + item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + item = model->item(XYInterpolationCurve::CSplinePeriodic); + if(n < gsl_interp_type_min_size(gsl_interp_cspline_periodic)) { + item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); + if(uiGeneralTab.cbType->currentIndex() == XYInterpolationCurve::CSplinePeriodic) + uiGeneralTab.cbType->setCurrentIndex(0); + } + else + item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + item = model->item(XYInterpolationCurve::Akima); + if(n < gsl_interp_type_min_size(gsl_interp_akima)) { + item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); + if(uiGeneralTab.cbType->currentIndex() == XYInterpolationCurve::Akima) + uiGeneralTab.cbType->setCurrentIndex(0); + } + else + item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + item = model->item(XYInterpolationCurve::AkimaPeriodic); + if(n < gsl_interp_type_min_size(gsl_interp_akima_periodic)) { + item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); + if(uiGeneralTab.cbType->currentIndex() == XYInterpolationCurve::AkimaPeriodic) + uiGeneralTab.cbType->setCurrentIndex(0); + } + else + item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + +#if GSL_MAJOR_VERSION >= 2 + item = model->item(XYInterpolationCurve::Steffen); + if(n < gsl_interp_type_min_size(gsl_interp_steffen)) { + item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); + if(uiGeneralTab.cbType->currentIndex() == XYInterpolationCurve::Steffen) + uiGeneralTab.cbType->setCurrentIndex(0); + } + else + item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); +#endif + } } void XYInterpolationCurveDock::yDataColumnChanged(const QModelIndex& index) { @@ -239,16 +302,14 @@ void XYInterpolationCurveDock::yDataColumnChanged(const QModelIndex& index) { } void XYInterpolationCurveDock::typeChanged(int index) { - XYInterpolationCurve::InterpolationType type = (XYInterpolationCurve::InterpolationType)index; + //XYInterpolationCurve::InterpolationType type = (XYInterpolationCurve::InterpolationType)index; m_interpolationData.type = (XYInterpolationCurve::InterpolationType)uiGeneralTab.cbType->currentIndex(); - //TODO - uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::evaluateChanged(int index) { - XYInterpolationCurve::InterpolationEval eval = (XYInterpolationCurve::InterpolationEval)index; + //XYInterpolationCurve::InterpolationEval eval = (XYInterpolationCurve::InterpolationEval)index; m_interpolationData.evaluate = (XYInterpolationCurve::InterpolationEval)uiGeneralTab.cbEval->currentIndex(); uiGeneralTab.pbRecalculate->setEnabled(true); _______________________________________________ kde-doc-english mailing list [email protected] https://mail.kde.org/mailman/listinfo/kde-doc-english
