File indexing completed on 2024-05-12 15:28:09

0001 /***************************************************************************
0002     File             : XYInterpolationCurveDock.cpp
0003     Project          : LabPlot
0004     --------------------------------------------------------------------
0005     Copyright        : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn)
0006     Copyright        : (C) 20016-2017 Alexander Semke (alexander.semke@web.de)
0007     Description      : widget for editing properties of interpolation curves
0008 
0009  ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  This program is free software; you can redistribute it and/or modify   *
0014  *  it under the terms of the GNU General Public License as published by   *
0015  *  the Free Software Foundation; either version 2 of the License, or      *
0016  *  (at your option) any later version.                                    *
0017  *                                                                         *
0018  *  This program is distributed in the hope that it will be useful,        *
0019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0021  *  GNU General Public License for more details.                           *
0022  *                                                                         *
0023  *   You should have received a copy of the GNU General Public License     *
0024  *   along with this program; if not, write to the Free Software           *
0025  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0026  *   Boston, MA  02110-1301  USA                                           *
0027  *                                                                         *
0028  ***************************************************************************/
0029 
0030 #include "XYInterpolationCurveDock.h"
0031 #include "backend/core/AspectTreeModel.h"
0032 #include "backend/core/Project.h"
0033 #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h"
0034 #include "commonfrontend/widgets/TreeViewComboBox.h"
0035 
0036 #include <QMenu>
0037 #include <QWidgetAction>
0038 #include <QStandardItemModel>
0039 
0040 extern "C" {
0041 #include <gsl/gsl_interp.h> // gsl_interp types
0042 }
0043 #include <cmath>        // isnan
0044 
0045 /*!
0046   \class XYInterpolationCurveDock
0047  \brief  Provides a widget for editing the properties of the XYInterpolationCurves
0048         (2D-curves defined by an interpolation) currently selected in
0049         the project explorer.
0050 
0051   If more then one curves are set, the properties of the first column are shown.
0052   The changes of the properties are applied to all curves.
0053   The exclusions are the name, the comment and the datasets (columns) of
0054   the curves  - these properties can only be changed if there is only one single curve.
0055 
0056   \ingroup kdefrontend
0057 */
0058 
0059 XYInterpolationCurveDock::XYInterpolationCurveDock(QWidget* parent): XYCurveDock(parent) {
0060 }
0061 
0062 /*!
0063  *  // Tab "General"
0064  */
0065 void XYInterpolationCurveDock::setupGeneral() {
0066     QWidget* generalTab = new QWidget(ui.tabGeneral);
0067     uiGeneralTab.setupUi(generalTab);
0068     m_leName = uiGeneralTab.leName;
0069     m_leComment = uiGeneralTab.leComment;
0070 
0071     auto* gridLayout = static_cast<QGridLayout*>(generalTab->layout());
0072     gridLayout->setContentsMargins(2,2,2,2);
0073     gridLayout->setHorizontalSpacing(2);
0074     gridLayout->setVerticalSpacing(2);
0075 
0076     uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet"));
0077     uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve"));
0078 
0079     cbDataSourceCurve = new TreeViewComboBox(generalTab);
0080     gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 2);
0081     cbXDataColumn = new TreeViewComboBox(generalTab);
0082     gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2);
0083     cbYDataColumn = new TreeViewComboBox(generalTab);
0084     gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2);
0085 
0086     for (int i = 0; i < NSL_INTERP_TYPE_COUNT; i++)
0087         uiGeneralTab.cbType->addItem(i18n(nsl_interp_type_name[i]));
0088 #if GSL_MAJOR_VERSION < 2
0089     // disable Steffen spline item
0090     const QStandardItemModel* model = qobject_cast<const QStandardItemModel*>(uiGeneralTab.cbType->model());
0091     QStandardItem* item = model->item(nsl_interp_type_steffen);
0092     item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0093 #endif
0094     for (int i = 0; i < NSL_INTERP_PCH_VARIANT_COUNT; i++)
0095         uiGeneralTab.cbVariant->addItem(i18n(nsl_interp_pch_variant_name[i]));
0096     for (int i = 0; i < NSL_INTERP_EVALUATE_COUNT; i++)
0097         uiGeneralTab.cbEval->addItem(i18n(nsl_interp_evaluate_name[i]));
0098 
0099     uiGeneralTab.cbPointsMode->addItem(i18n("Auto (5x data points)"));
0100     uiGeneralTab.cbPointsMode->addItem(i18n("Multiple of data points"));
0101     uiGeneralTab.cbPointsMode->addItem(i18n("Custom"));
0102 
0103     //TODO: use line edits
0104     uiGeneralTab.sbMin->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
0105     uiGeneralTab.sbMax->setRange(-std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
0106 
0107     uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build"));
0108 
0109     auto* layout = new QHBoxLayout(ui.tabGeneral);
0110     layout->setMargin(0);
0111     layout->addWidget(generalTab);
0112 
0113     //Slots
0114     connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYInterpolationCurveDock::nameChanged );
0115     connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYInterpolationCurveDock::commentChanged );
0116     connect(uiGeneralTab.chkVisible, &QCheckBox::clicked, this, &XYInterpolationCurveDock::visibilityChanged);
0117     connect(uiGeneralTab.cbDataSourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYInterpolationCurveDock::dataSourceTypeChanged);
0118     connect(uiGeneralTab.cbAutoRange, &QCheckBox::clicked, this, &XYInterpolationCurveDock::autoRangeChanged);
0119     connect(uiGeneralTab.sbMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYInterpolationCurveDock::xRangeMinChanged);
0120     connect(uiGeneralTab.sbMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYInterpolationCurveDock::xRangeMaxChanged);
0121     connect(uiGeneralTab.dateTimeEditMin, &QDateTimeEdit::dateTimeChanged, this, &XYInterpolationCurveDock::xRangeMinDateTimeChanged);
0122     connect(uiGeneralTab.dateTimeEditMax, &QDateTimeEdit::dateTimeChanged, this, &XYInterpolationCurveDock::xRangeMaxDateTimeChanged);
0123     connect(uiGeneralTab.cbType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYInterpolationCurveDock::typeChanged);
0124     connect(uiGeneralTab.cbVariant, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYInterpolationCurveDock::variantChanged);
0125     //TODO: use line edits?
0126     connect(uiGeneralTab.sbTension, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYInterpolationCurveDock::tensionChanged);
0127     connect(uiGeneralTab.sbContinuity, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYInterpolationCurveDock::continuityChanged);
0128     connect(uiGeneralTab.sbBias, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYInterpolationCurveDock::biasChanged);
0129     connect(uiGeneralTab.cbEval, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYInterpolationCurveDock::evaluateChanged);
0130     // double?
0131     connect(uiGeneralTab.sbPoints, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &XYInterpolationCurveDock::numberOfPointsChanged);
0132     connect(uiGeneralTab.cbPointsMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &XYInterpolationCurveDock::pointsModeChanged);
0133     connect(uiGeneralTab.pbRecalculate, &QPushButton::clicked, this, &XYInterpolationCurveDock::recalculateClicked);
0134 
0135     connect(cbDataSourceCurve, &TreeViewComboBox::currentModelIndexChanged, this, &XYInterpolationCurveDock::dataSourceCurveChanged);
0136     connect(cbXDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYInterpolationCurveDock::xDataColumnChanged);
0137     connect(cbYDataColumn, &TreeViewComboBox::currentModelIndexChanged, this, &XYInterpolationCurveDock::yDataColumnChanged);
0138 }
0139 
0140 void XYInterpolationCurveDock::initGeneralTab() {
0141     //if there are more then one curve in the list, disable the tab "general"
0142     if (m_curvesList.size() == 1) {
0143         uiGeneralTab.lName->setEnabled(true);
0144         uiGeneralTab.leName->setEnabled(true);
0145         uiGeneralTab.lComment->setEnabled(true);
0146         uiGeneralTab.leComment->setEnabled(true);
0147 
0148         uiGeneralTab.leName->setText(m_curve->name());
0149         uiGeneralTab.leComment->setText(m_curve->comment());
0150     } else {
0151         uiGeneralTab.lName->setEnabled(false);
0152         uiGeneralTab.leName->setEnabled(false);
0153         uiGeneralTab.lComment->setEnabled(false);
0154         uiGeneralTab.leComment->setEnabled(false);
0155 
0156         uiGeneralTab.leName->setText(QString());
0157         uiGeneralTab.leComment->setText(QString());
0158     }
0159 
0160     //show the properties of the first curve
0161     m_interpolationCurve = dynamic_cast<XYInterpolationCurve*>(m_curve);
0162     checkColumnAvailability(cbXDataColumn, m_interpolationCurve->xDataColumn(), m_interpolationCurve->xDataColumnPath());
0163     checkColumnAvailability(cbYDataColumn, m_interpolationCurve->yDataColumn(), m_interpolationCurve->yDataColumnPath());
0164 
0165     //data source
0166     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(m_interpolationCurve->dataSourceType()));
0167     this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex());
0168     XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_interpolationCurve->dataSourceCurve());
0169     XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_interpolationCurve->xDataColumn());
0170     XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_interpolationCurve->yDataColumn());
0171 
0172     //range widgets
0173     const auto* plot = static_cast<const CartesianPlot*>(m_interpolationCurve->parentAspect());
0174     m_dateTimeRange = (plot->xRangeFormat() != CartesianPlot::RangeFormat::Numeric);
0175     if (!m_dateTimeRange) {
0176         uiGeneralTab.sbMin->setValue(m_interpolationData.xRange.first());
0177         uiGeneralTab.sbMax->setValue(m_interpolationData.xRange.last());
0178     } else {
0179         uiGeneralTab.dateTimeEditMin->setDateTime( QDateTime::fromMSecsSinceEpoch(m_interpolationData.xRange.first()) );
0180         uiGeneralTab.dateTimeEditMax->setDateTime( QDateTime::fromMSecsSinceEpoch(m_interpolationData.xRange.last()) );
0181     }
0182 
0183     uiGeneralTab.lMin->setVisible(!m_dateTimeRange);
0184     uiGeneralTab.sbMin->setVisible(!m_dateTimeRange);
0185     uiGeneralTab.lMax->setVisible(!m_dateTimeRange);
0186     uiGeneralTab.sbMax->setVisible(!m_dateTimeRange);
0187     uiGeneralTab.lMinDateTime->setVisible(m_dateTimeRange);
0188     uiGeneralTab.dateTimeEditMin->setVisible(m_dateTimeRange);
0189     uiGeneralTab.lMaxDateTime->setVisible(m_dateTimeRange);
0190     uiGeneralTab.dateTimeEditMax->setVisible(m_dateTimeRange);
0191 
0192     //auto range
0193     uiGeneralTab.cbAutoRange->setChecked(m_interpolationData.autoRange);
0194     this->autoRangeChanged();
0195 
0196     // update list of selectable types
0197     xDataColumnChanged(cbXDataColumn->currentModelIndex());
0198 
0199     uiGeneralTab.cbType->setCurrentIndex(m_interpolationData.type);
0200     this->typeChanged(m_interpolationData.type);
0201     uiGeneralTab.cbVariant->setCurrentIndex(m_interpolationData.variant);
0202     this->variantChanged(m_interpolationData.variant);
0203     uiGeneralTab.sbTension->setValue(m_interpolationData.tension);
0204     uiGeneralTab.sbContinuity->setValue(m_interpolationData.continuity);
0205     uiGeneralTab.sbBias->setValue(m_interpolationData.bias);
0206     uiGeneralTab.cbEval->setCurrentIndex(m_interpolationData.evaluate);
0207 
0208     if (m_interpolationData.pointsMode == XYInterpolationCurve::PointsMode::Multiple)
0209         uiGeneralTab.sbPoints->setValue(m_interpolationData.npoints/5.);
0210     else
0211         uiGeneralTab.sbPoints->setValue(m_interpolationData.npoints);
0212     uiGeneralTab.cbPointsMode->setCurrentIndex(static_cast<int>(m_interpolationData.pointsMode));
0213 
0214     this->showInterpolationResult();
0215 
0216     uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() );
0217 
0218     //Slots
0219     connect(m_interpolationCurve, &XYInterpolationCurve::aspectDescriptionChanged, this, &XYInterpolationCurveDock::curveDescriptionChanged);
0220     connect(m_interpolationCurve, &XYInterpolationCurve::dataSourceTypeChanged, this, &XYInterpolationCurveDock::curveDataSourceTypeChanged);
0221     connect(m_interpolationCurve, &XYInterpolationCurve::dataSourceCurveChanged, this, &XYInterpolationCurveDock::curveDataSourceCurveChanged);
0222     connect(m_interpolationCurve, &XYInterpolationCurve::xDataColumnChanged, this, &XYInterpolationCurveDock::curveXDataColumnChanged);
0223     connect(m_interpolationCurve, &XYInterpolationCurve::yDataColumnChanged, this, &XYInterpolationCurveDock::curveYDataColumnChanged);
0224     connect(m_interpolationCurve, &XYInterpolationCurve::interpolationDataChanged, this, &XYInterpolationCurveDock::curveInterpolationDataChanged);
0225     connect(m_interpolationCurve, &XYInterpolationCurve::sourceDataChanged, this, &XYInterpolationCurveDock::enableRecalculate);
0226 }
0227 
0228 void XYInterpolationCurveDock::setModel() {
0229     QList<AspectType> list{AspectType::Folder, AspectType::Datapicker, AspectType::Worksheet,
0230                             AspectType::CartesianPlot, AspectType::XYCurve, AspectType::XYAnalysisCurve};
0231     cbDataSourceCurve->setTopLevelClasses(list);
0232 
0233     QList<const AbstractAspect*> hiddenAspects;
0234     for (auto* curve : m_curvesList)
0235         hiddenAspects << curve;
0236     cbDataSourceCurve->setHiddenAspects(hiddenAspects);
0237 
0238     list = {AspectType::Folder, AspectType::Workbook, AspectType::Datapicker,
0239             AspectType::DatapickerCurve, AspectType::Spreadsheet, AspectType::LiveDataSource,
0240             AspectType::Column, AspectType::Worksheet, AspectType::CartesianPlot,
0241             AspectType::XYFitCurve, AspectType::CantorWorksheet
0242            };
0243     cbXDataColumn->setTopLevelClasses(list);
0244     cbYDataColumn->setTopLevelClasses(list);
0245 
0246     cbDataSourceCurve->setModel(m_aspectTreeModel);
0247     cbXDataColumn->setModel(m_aspectTreeModel);
0248     cbYDataColumn->setModel(m_aspectTreeModel);
0249 
0250     XYCurveDock::setModel();
0251 }
0252 
0253 /*!
0254   sets the curves. The properties of the curves in the list \c list can be edited in this widget.
0255 */
0256 void XYInterpolationCurveDock::setCurves(QList<XYCurve*> list) {
0257     m_initializing = true;
0258     m_curvesList = list;
0259     m_curve = list.first();
0260     m_aspect = m_curve;
0261     m_interpolationCurve = dynamic_cast<XYInterpolationCurve*>(m_curve);
0262     Q_ASSERT(m_interpolationCurve);
0263     m_aspectTreeModel = new AspectTreeModel(m_curve->project());
0264     this->setModel();
0265     m_interpolationData = m_interpolationCurve->interpolationData();
0266 
0267     SET_NUMBER_LOCALE
0268     uiGeneralTab.sbMin->setLocale(numberLocale);
0269     uiGeneralTab.sbMax->setLocale(numberLocale);
0270     uiGeneralTab.sbTension->setLocale(numberLocale);
0271     uiGeneralTab.sbContinuity->setLocale(numberLocale);
0272     uiGeneralTab.sbBias->setLocale(numberLocale);
0273     uiGeneralTab.sbPoints->setLocale(numberLocale);
0274 
0275     initGeneralTab();
0276     initTabs();
0277     m_initializing = false;
0278 
0279     //hide the "skip gaps" option after the curves were set
0280     ui.lLineSkipGaps->hide();
0281     ui.chkLineSkipGaps->hide();
0282 }
0283 
0284 //*************************************************************
0285 //**** SLOTs for changes triggered in XYFitCurveDock *****
0286 //*************************************************************
0287 void XYInterpolationCurveDock::dataSourceTypeChanged(int index) {
0288     const auto type = (XYAnalysisCurve::DataSourceType)index;
0289     if (type == XYAnalysisCurve::DataSourceType::Spreadsheet) {
0290         uiGeneralTab.lDataSourceCurve->hide();
0291         cbDataSourceCurve->hide();
0292         uiGeneralTab.lXColumn->show();
0293         cbXDataColumn->show();
0294         uiGeneralTab.lYColumn->show();
0295         cbYDataColumn->show();
0296     } else {
0297         uiGeneralTab.lDataSourceCurve->show();
0298         cbDataSourceCurve->show();
0299         uiGeneralTab.lXColumn->hide();
0300         cbXDataColumn->hide();
0301         uiGeneralTab.lYColumn->hide();
0302         cbYDataColumn->hide();
0303     }
0304 
0305     if (m_initializing)
0306         return;
0307 
0308     for (XYCurve* curve: m_curvesList)
0309         dynamic_cast<XYInterpolationCurve*>(curve)->setDataSourceType(type);
0310 }
0311 
0312 void XYInterpolationCurveDock::dataSourceCurveChanged(const QModelIndex& index) {
0313     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0314     auto* dataSourceCurve = dynamic_cast<XYCurve*>(aspect);
0315 
0316     // disable types that need more data points
0317     if (dataSourceCurve)
0318         this->updateSettings(dataSourceCurve->xColumn());
0319 
0320     if (m_initializing)
0321         return;
0322 
0323     for (XYCurve* curve: m_curvesList)
0324         dynamic_cast<XYInterpolationCurve*>(curve)->setDataSourceCurve(dataSourceCurve);
0325 }
0326 
0327 void XYInterpolationCurveDock::xDataColumnChanged(const QModelIndex& index) {
0328     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0329     AbstractColumn* column = nullptr;
0330     if (aspect) {
0331         column = dynamic_cast<AbstractColumn*>(aspect);
0332         Q_ASSERT(column);
0333     }
0334 
0335     this->updateSettings(column);
0336 
0337     if (m_initializing)
0338         return;
0339 
0340     for (XYCurve* curve: m_curvesList)
0341         dynamic_cast<XYInterpolationCurve*>(curve)->setXDataColumn(column);
0342 
0343     cbXDataColumn->useCurrentIndexText(true);
0344     cbXDataColumn->setInvalid(false);
0345 }
0346 
0347 void XYInterpolationCurveDock::updateSettings(const AbstractColumn* column) {
0348     if (!column)
0349         return;
0350 
0351     // disable types that need more data points
0352     if (uiGeneralTab.cbAutoRange->isChecked()) {
0353         uiGeneralTab.sbMin->setValue(column->minimum());
0354         uiGeneralTab.sbMax->setValue(column->maximum());
0355     }
0356 
0357     unsigned int n = 0;
0358     for (int row = 0; row < column->rowCount(); row++)
0359         if (!std::isnan(column->valueAt(row)) && !column->isMasked(row))
0360             n++;
0361     dataPoints = n;
0362     if (m_interpolationData.pointsMode == XYInterpolationCurve::PointsMode::Auto)
0363         pointsModeChanged(uiGeneralTab.cbPointsMode->currentIndex());
0364 
0365     const auto* model = qobject_cast<const QStandardItemModel*>(uiGeneralTab.cbType->model());
0366     QStandardItem* item = model->item(nsl_interp_type_polynomial);
0367     if (dataPoints < gsl_interp_type_min_size(gsl_interp_polynomial) || dataPoints > 100) { // not good for many points
0368         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0369         if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_polynomial)
0370             uiGeneralTab.cbType->setCurrentIndex(0);
0371     }
0372     else
0373         item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
0374 
0375     item = model->item(nsl_interp_type_cspline);
0376     if (dataPoints < gsl_interp_type_min_size(gsl_interp_cspline)) {
0377         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0378         if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_cspline)
0379             uiGeneralTab.cbType->setCurrentIndex(0);
0380     }
0381     else
0382         item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
0383 
0384     item = model->item(nsl_interp_type_cspline_periodic);
0385     if (dataPoints < gsl_interp_type_min_size(gsl_interp_cspline_periodic)) {
0386         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0387         if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_cspline_periodic)
0388             uiGeneralTab.cbType->setCurrentIndex(0);
0389     }
0390     else
0391         item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
0392 
0393     item = model->item(nsl_interp_type_akima);
0394     if (dataPoints < gsl_interp_type_min_size(gsl_interp_akima)) {
0395         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0396         if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_akima)
0397             uiGeneralTab.cbType->setCurrentIndex(0);
0398     }
0399     else
0400         item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
0401 
0402     item = model->item(nsl_interp_type_akima_periodic);
0403     if (dataPoints < gsl_interp_type_min_size(gsl_interp_akima_periodic)) {
0404         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0405         if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_akima_periodic)
0406             uiGeneralTab.cbType->setCurrentIndex(0);
0407     }
0408     else
0409         item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
0410 
0411 #if GSL_MAJOR_VERSION >= 2
0412     item = model->item(nsl_interp_type_steffen);
0413     if (dataPoints < gsl_interp_type_min_size(gsl_interp_steffen)) {
0414         item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
0415         if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_steffen)
0416             uiGeneralTab.cbType->setCurrentIndex(0);
0417     }
0418     else
0419         item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
0420 #endif
0421     // own types work with 2 or more data points
0422 }
0423 
0424 void XYInterpolationCurveDock::yDataColumnChanged(const QModelIndex& index) {
0425     if (m_initializing)
0426         return;
0427 
0428     auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
0429     AbstractColumn* column = nullptr;
0430     if (aspect) {
0431         column = dynamic_cast<AbstractColumn*>(aspect);
0432         Q_ASSERT(column);
0433     }
0434 
0435     for (XYCurve* curve: m_curvesList)
0436         dynamic_cast<XYInterpolationCurve*>(curve)->setYDataColumn(column);
0437 
0438     cbYDataColumn->useCurrentIndexText(true);
0439     cbYDataColumn->setInvalid(false);
0440 }
0441 
0442 void XYInterpolationCurveDock::autoRangeChanged() {
0443     bool autoRange = uiGeneralTab.cbAutoRange->isChecked();
0444     m_interpolationData.autoRange = autoRange;
0445 
0446     uiGeneralTab.lMin->setEnabled(!autoRange);
0447     uiGeneralTab.sbMin->setEnabled(!autoRange);
0448     uiGeneralTab.lMax->setEnabled(!autoRange);
0449     uiGeneralTab.sbMax->setEnabled(!autoRange);
0450     uiGeneralTab.lMinDateTime->setEnabled(!autoRange);
0451     uiGeneralTab.dateTimeEditMin->setEnabled(!autoRange);
0452     uiGeneralTab.lMaxDateTime->setEnabled(!autoRange);
0453     uiGeneralTab.dateTimeEditMax->setEnabled(!autoRange);
0454 
0455     if (autoRange) {
0456         const AbstractColumn* xDataColumn = nullptr;
0457         if (m_interpolationCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet)
0458             xDataColumn = m_interpolationCurve->xDataColumn();
0459         else {
0460             if (m_interpolationCurve->dataSourceCurve())
0461                 xDataColumn = m_interpolationCurve->dataSourceCurve()->xColumn();
0462         }
0463 
0464         if (xDataColumn) {
0465             if (!m_dateTimeRange) {
0466                 uiGeneralTab.sbMin->setValue(xDataColumn->minimum());
0467                 uiGeneralTab.sbMax->setValue(xDataColumn->maximum());
0468             } else {
0469                 uiGeneralTab.dateTimeEditMin->setDateTime(QDateTime::fromMSecsSinceEpoch(xDataColumn->minimum()));
0470                 uiGeneralTab.dateTimeEditMax->setDateTime(QDateTime::fromMSecsSinceEpoch(xDataColumn->maximum()));
0471             }
0472         }
0473     }
0474 }
0475 
0476 void XYInterpolationCurveDock::xRangeMinChanged(double value) {
0477     m_interpolationData.xRange.first() = value;
0478     uiGeneralTab.pbRecalculate->setEnabled(true);
0479 }
0480 
0481 void XYInterpolationCurveDock::xRangeMaxChanged(double value) {
0482     m_interpolationData.xRange.last() = value;
0483     uiGeneralTab.pbRecalculate->setEnabled(true);
0484 }
0485 
0486 void XYInterpolationCurveDock::xRangeMinDateTimeChanged(const QDateTime& dateTime) {
0487     if (m_initializing)
0488         return;
0489 
0490     m_interpolationData.xRange.first() = dateTime.toMSecsSinceEpoch();
0491     uiGeneralTab.pbRecalculate->setEnabled(true);
0492 }
0493 
0494 void XYInterpolationCurveDock::xRangeMaxDateTimeChanged(const QDateTime& dateTime) {
0495     if (m_initializing)
0496         return;
0497 
0498     m_interpolationData.xRange.last() = dateTime.toMSecsSinceEpoch();
0499     uiGeneralTab.pbRecalculate->setEnabled(true);
0500 }
0501 
0502 void XYInterpolationCurveDock::typeChanged(int index) {
0503     const auto type = (nsl_interp_type)index;
0504     m_interpolationData.type = type;
0505 
0506     switch (type) {
0507     case nsl_interp_type_pch:
0508         uiGeneralTab.lVariant->show();
0509         uiGeneralTab.cbVariant->show();
0510         break;
0511     case nsl_interp_type_linear:
0512     case nsl_interp_type_polynomial:
0513     case nsl_interp_type_cspline:
0514     case nsl_interp_type_cspline_periodic:
0515     case nsl_interp_type_akima:
0516     case nsl_interp_type_akima_periodic:
0517     case nsl_interp_type_steffen:
0518     case nsl_interp_type_cosine:
0519     case nsl_interp_type_exponential:
0520     case nsl_interp_type_rational:
0521         uiGeneralTab.lVariant->hide();
0522         uiGeneralTab.cbVariant->hide();
0523         uiGeneralTab.cbVariant->setCurrentIndex(nsl_interp_pch_variant_finite_difference);
0524         uiGeneralTab.lParameter->hide();
0525         uiGeneralTab.lTension->hide();
0526         uiGeneralTab.sbTension->hide();
0527         uiGeneralTab.lContinuity->hide();
0528         uiGeneralTab.sbContinuity->hide();
0529         uiGeneralTab.lBias->hide();
0530         uiGeneralTab.sbBias->hide();
0531     }
0532 
0533     uiGeneralTab.pbRecalculate->setEnabled(true);
0534 }
0535 
0536 void XYInterpolationCurveDock::variantChanged(int index) {
0537     const auto variant = (nsl_interp_pch_variant)index;
0538     m_interpolationData.variant = variant;
0539 
0540     switch (variant) {
0541     case nsl_interp_pch_variant_finite_difference:
0542         uiGeneralTab.lParameter->hide();
0543         uiGeneralTab.lTension->hide();
0544         uiGeneralTab.sbTension->hide();
0545         uiGeneralTab.lContinuity->hide();
0546         uiGeneralTab.sbContinuity->hide();
0547         uiGeneralTab.lBias->hide();
0548         uiGeneralTab.sbBias->hide();
0549         break;
0550     case nsl_interp_pch_variant_catmull_rom:
0551         uiGeneralTab.lParameter->show();
0552         uiGeneralTab.lTension->show();
0553         uiGeneralTab.sbTension->show();
0554         uiGeneralTab.sbTension->setEnabled(false);
0555         uiGeneralTab.sbTension->setValue(0.0);
0556         uiGeneralTab.lContinuity->hide();
0557         uiGeneralTab.sbContinuity->hide();
0558         uiGeneralTab.lBias->hide();
0559         uiGeneralTab.sbBias->hide();
0560         break;
0561     case nsl_interp_pch_variant_cardinal:
0562         uiGeneralTab.lParameter->show();
0563         uiGeneralTab.lTension->show();
0564         uiGeneralTab.sbTension->show();
0565         uiGeneralTab.sbTension->setEnabled(true);
0566         uiGeneralTab.lContinuity->hide();
0567         uiGeneralTab.sbContinuity->hide();
0568         uiGeneralTab.lBias->hide();
0569         uiGeneralTab.sbBias->hide();
0570         break;
0571     case nsl_interp_pch_variant_kochanek_bartels:
0572         uiGeneralTab.lParameter->show();
0573         uiGeneralTab.lTension->show();
0574         uiGeneralTab.sbTension->show();
0575         uiGeneralTab.sbTension->setEnabled(true);
0576         uiGeneralTab.lContinuity->show();
0577         uiGeneralTab.sbContinuity->show();
0578         uiGeneralTab.lBias->show();
0579         uiGeneralTab.sbBias->show();
0580         break;
0581     }
0582 
0583     uiGeneralTab.pbRecalculate->setEnabled(true);
0584 }
0585 
0586 void XYInterpolationCurveDock::tensionChanged(double value) {
0587     m_interpolationData.tension = value;
0588     uiGeneralTab.pbRecalculate->setEnabled(true);
0589 }
0590 
0591 void XYInterpolationCurveDock::continuityChanged(double value) {
0592     m_interpolationData.continuity = value;
0593     uiGeneralTab.pbRecalculate->setEnabled(true);
0594 }
0595 
0596 void XYInterpolationCurveDock::biasChanged(double value) {
0597     m_interpolationData.bias = value;
0598     uiGeneralTab.pbRecalculate->setEnabled(true);
0599 }
0600 
0601 void XYInterpolationCurveDock::evaluateChanged(int index) {
0602     m_interpolationData.evaluate = (nsl_interp_evaluate)index;
0603     uiGeneralTab.pbRecalculate->setEnabled(true);
0604 }
0605 
0606 void XYInterpolationCurveDock::pointsModeChanged(int index) {
0607     const auto mode = (XYInterpolationCurve::PointsMode)index;
0608 
0609     switch (mode) {
0610     case XYInterpolationCurve::PointsMode::Auto:
0611         uiGeneralTab.sbPoints->setEnabled(false);
0612         uiGeneralTab.sbPoints->setDecimals(0);
0613         uiGeneralTab.sbPoints->setSingleStep(1.0);
0614         uiGeneralTab.sbPoints->setValue(5*dataPoints);
0615         break;
0616     case XYInterpolationCurve::PointsMode::Multiple:
0617         uiGeneralTab.sbPoints->setEnabled(true);
0618         if (m_interpolationData.pointsMode != XYInterpolationCurve::PointsMode::Multiple && dataPoints > 0) {
0619             uiGeneralTab.sbPoints->setDecimals(2);
0620             uiGeneralTab.sbPoints->setValue(uiGeneralTab.sbPoints->value()/(double)dataPoints);
0621             uiGeneralTab.sbPoints->setSingleStep(0.01);
0622         }
0623         break;
0624     case XYInterpolationCurve::PointsMode::Custom:
0625         uiGeneralTab.sbPoints->setEnabled(true);
0626         if (m_interpolationData.pointsMode == XYInterpolationCurve::PointsMode::Multiple) {
0627             uiGeneralTab.sbPoints->setDecimals(0);
0628             uiGeneralTab.sbPoints->setSingleStep(1.0);
0629             uiGeneralTab.sbPoints->setValue(uiGeneralTab.sbPoints->value()*dataPoints);
0630         }
0631         break;
0632     }
0633 
0634     m_interpolationData.pointsMode = mode;
0635 }
0636 
0637 void XYInterpolationCurveDock::numberOfPointsChanged() {
0638     m_interpolationData.npoints = uiGeneralTab.sbPoints->value();
0639     if (uiGeneralTab.cbPointsMode->currentIndex() == static_cast<int>(XYInterpolationCurve::PointsMode::Multiple))
0640         m_interpolationData.npoints *= dataPoints;
0641 
0642     // warn if points is smaller than data points
0643     QPalette palette = uiGeneralTab.sbPoints->palette();
0644     if (m_interpolationData.npoints < dataPoints)
0645         palette.setColor(QPalette::Text, Qt::red);
0646     else
0647         palette.setColor(QPalette::Text, Qt::black);
0648     uiGeneralTab.sbPoints->setPalette(palette);
0649 
0650     enableRecalculate();
0651 }
0652 
0653 void XYInterpolationCurveDock::recalculateClicked() {
0654     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0655 
0656     for (XYCurve* curve: m_curvesList)
0657         dynamic_cast<XYInterpolationCurve*>(curve)->setInterpolationData(m_interpolationData);
0658 
0659     uiGeneralTab.pbRecalculate->setEnabled(false);
0660     emit info(i18n("Interpolation status: %1", m_interpolationCurve->interpolationResult().status));
0661     QApplication::restoreOverrideCursor();
0662 }
0663 
0664 void XYInterpolationCurveDock::enableRecalculate() const {
0665     if (m_initializing)
0666         return;
0667 
0668     //no interpolation possible without the x- and y-data
0669     bool hasSourceData = false;
0670     if (m_interpolationCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet) {
0671         AbstractAspect* aspectX = static_cast<AbstractAspect*>(cbXDataColumn->currentModelIndex().internalPointer());
0672         AbstractAspect* aspectY = static_cast<AbstractAspect*>(cbYDataColumn->currentModelIndex().internalPointer());
0673         hasSourceData = (aspectX != nullptr && aspectY != nullptr);
0674         if (aspectX) {
0675             cbXDataColumn->useCurrentIndexText(true);
0676             cbXDataColumn->setInvalid(false);
0677         }
0678         if (aspectY) {
0679             cbYDataColumn->useCurrentIndexText(true);
0680             cbYDataColumn->setInvalid(false);
0681         }
0682     } else {
0683          hasSourceData = (m_interpolationCurve->dataSourceCurve() != nullptr);
0684     }
0685 
0686     uiGeneralTab.pbRecalculate->setEnabled(hasSourceData);
0687 }
0688 
0689 /*!
0690  * show the result and details of the interpolation
0691  */
0692 void XYInterpolationCurveDock::showInterpolationResult() {
0693     const auto& interpolationResult = m_interpolationCurve->interpolationResult();
0694     if (!interpolationResult.available) {
0695         uiGeneralTab.teResult->clear();
0696         return;
0697     }
0698 
0699     QString str = i18n("status: %1", interpolationResult.status) + "<br>";
0700 
0701     if (!interpolationResult.valid) {
0702         uiGeneralTab.teResult->setText(str);
0703         return; //result is not valid, there was an error which is shown in the status-string, nothing to show more.
0704     }
0705 
0706     SET_NUMBER_LOCALE
0707     if (interpolationResult.elapsedTime > 1000)
0708         str += i18n("calculation time: %1 s", numberLocale.toString(interpolationResult.elapsedTime/1000)) + "<br>";
0709     else
0710         str += i18n("calculation time: %1 ms", numberLocale.toString(interpolationResult.elapsedTime)) + "<br>";
0711 
0712     str += "<br><br>";
0713 
0714     uiGeneralTab.teResult->setText(str);
0715 
0716     //enable the "recalculate"-button if the source data was changed since the last interpolation
0717     uiGeneralTab.pbRecalculate->setEnabled(m_interpolationCurve->isSourceDataChangedSinceLastRecalc());
0718 }
0719 
0720 //*************************************************************
0721 //*********** SLOTs for changes triggered in XYCurve **********
0722 //*************************************************************
0723 //General-Tab
0724 void XYInterpolationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) {
0725     if (m_curve != aspect)
0726         return;
0727 
0728     m_initializing = true;
0729     if (aspect->name() != uiGeneralTab.leName->text())
0730         uiGeneralTab.leName->setText(aspect->name());
0731     else if (aspect->comment() != uiGeneralTab.leComment->text())
0732         uiGeneralTab.leComment->setText(aspect->comment());
0733     m_initializing = false;
0734 }
0735 
0736 void XYInterpolationCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) {
0737     m_initializing = true;
0738     uiGeneralTab.cbDataSourceType->setCurrentIndex(static_cast<int>(type));
0739     m_initializing = false;
0740 }
0741 
0742 void XYInterpolationCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) {
0743     m_initializing = true;
0744     XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve);
0745     m_initializing = false;
0746 }
0747 
0748 void XYInterpolationCurveDock::curveXDataColumnChanged(const AbstractColumn* column) {
0749     m_initializing = true;
0750     XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column);
0751     m_initializing = false;
0752 }
0753 
0754 void XYInterpolationCurveDock::curveYDataColumnChanged(const AbstractColumn* column) {
0755     m_initializing = true;
0756     XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column);
0757     m_initializing = false;
0758 }
0759 
0760 void XYInterpolationCurveDock::curveInterpolationDataChanged(const XYInterpolationCurve::InterpolationData& data) {
0761     m_initializing = true;
0762     m_interpolationData = data;
0763     uiGeneralTab.cbType->setCurrentIndex(m_interpolationData.type);
0764     this->typeChanged(m_interpolationData.type);
0765 
0766     this->showInterpolationResult();
0767     m_initializing = false;
0768 }
0769 
0770 void XYInterpolationCurveDock::dataChanged() {
0771     this->enableRecalculate();
0772 }