[kde-doc-english] [labplot/analysis_interpolation] /: finished interpolation

Stefan Gerlach stefan.gerlach at uni-konstanz.de
Sat Apr 9 19:32:22 UTC 2016


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 (alexander.semke at web.de)
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 (alexander.semke at web.de)
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 (stefan.gerlach at uni.kn)
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 (stefan.gerlach at uni.kn)
@@ -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);


More information about the kde-doc-english mailing list