File indexing completed on 2025-01-26 03:34:18

0001 /*
0002     File                 : XYDifferentiationCurve.cpp
0003     Project              : LabPlot
0004     Description          : A xy-curve defined by an differentiation
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016-2022 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 /*!
0011   \class XYDifferentiationCurve
0012   \brief A xy-curve defined by an differentiation
0013 
0014   \ingroup worksheet
0015 */
0016 
0017 #include "XYDifferentiationCurve.h"
0018 #include "CartesianCoordinateSystem.h"
0019 #include "XYDifferentiationCurvePrivate.h"
0020 #include "backend/core/column/Column.h"
0021 #include "backend/lib/XmlStreamReader.h"
0022 #include "backend/lib/commandtemplates.h"
0023 #include "backend/lib/macros.h"
0024 
0025 #include <KLocalizedString>
0026 #include <QElapsedTimer>
0027 #include <QIcon>
0028 #include <QThreadPool>
0029 
0030 XYDifferentiationCurve::XYDifferentiationCurve(const QString& name)
0031     : XYAnalysisCurve(name, new XYDifferentiationCurvePrivate(this), AspectType::XYDifferentiationCurve) {
0032 }
0033 
0034 XYDifferentiationCurve::XYDifferentiationCurve(const QString& name, XYDifferentiationCurvePrivate* dd)
0035     : XYAnalysisCurve(name, dd, AspectType::XYDifferentiationCurve) {
0036 }
0037 
0038 // no need to delete the d-pointer here - it inherits from QGraphicsItem
0039 // and is deleted during the cleanup in QGraphicsScene
0040 XYDifferentiationCurve::~XYDifferentiationCurve() = default;
0041 
0042 void XYDifferentiationCurve::recalculate() {
0043     Q_D(XYDifferentiationCurve);
0044     d->recalculate();
0045 }
0046 
0047 const XYAnalysisCurve::Result& XYDifferentiationCurve::result() const {
0048     return differentiationResult();
0049 }
0050 
0051 /*!
0052     Returns an icon to be used in the project explorer.
0053 */
0054 QIcon XYDifferentiationCurve::icon() const {
0055     return QIcon::fromTheme(QStringLiteral("labplot-xy-curve"));
0056 }
0057 
0058 // ##############################################################################
0059 // ##########################  getter methods  ##################################
0060 // ##############################################################################
0061 BASIC_SHARED_D_READER_IMPL(XYDifferentiationCurve, XYDifferentiationCurve::DifferentiationData, differentiationData, differentiationData)
0062 
0063 const XYDifferentiationCurve::DifferentiationResult& XYDifferentiationCurve::differentiationResult() const {
0064     Q_D(const XYDifferentiationCurve);
0065     return d->differentiationResult;
0066 }
0067 
0068 // ##############################################################################
0069 // #################  setter methods and undo commands ##########################
0070 // ##############################################################################
0071 STD_SETTER_CMD_IMPL_F_S(XYDifferentiationCurve, SetDifferentiationData, XYDifferentiationCurve::DifferentiationData, differentiationData, recalculate)
0072 void XYDifferentiationCurve::setDifferentiationData(const XYDifferentiationCurve::DifferentiationData& differentiationData) {
0073     Q_D(XYDifferentiationCurve);
0074     exec(new XYDifferentiationCurveSetDifferentiationDataCmd(d, differentiationData, ki18n("%1: set options and perform the differentiation")));
0075 }
0076 
0077 // ##############################################################################
0078 // ######################### Private implementation #############################
0079 // ##############################################################################
0080 XYDifferentiationCurvePrivate::XYDifferentiationCurvePrivate(XYDifferentiationCurve* owner)
0081     : XYAnalysisCurvePrivate(owner)
0082     , q(owner) {
0083 }
0084 
0085 // no need to delete xColumn and yColumn, they are deleted
0086 // when the parent aspect is removed
0087 XYDifferentiationCurvePrivate::~XYDifferentiationCurvePrivate() = default;
0088 
0089 void XYDifferentiationCurvePrivate::resetResults() {
0090     differentiationResult = XYDifferentiationCurve::DifferentiationResult();
0091 }
0092 
0093 // ...
0094 // see XYFitCurvePrivate
0095 bool XYDifferentiationCurvePrivate::recalculateSpecific(const AbstractColumn* tmpXDataColumn, const AbstractColumn* tmpYDataColumn) {
0096     QElapsedTimer timer;
0097     timer.start();
0098 
0099     // copy all valid data point for the differentiation to temporary vectors
0100     QVector<double> xdataVector;
0101     QVector<double> ydataVector;
0102 
0103     double xmin;
0104     double xmax;
0105     if (differentiationData.autoRange) {
0106         xmin = tmpXDataColumn->minimum();
0107         xmax = tmpXDataColumn->maximum();
0108     } else {
0109         xmin = differentiationData.xRange.first();
0110         xmax = differentiationData.xRange.last();
0111     }
0112 
0113     XYAnalysisCurve::copyData(xdataVector, ydataVector, tmpXDataColumn, tmpYDataColumn, xmin, xmax, true);
0114 
0115     // number of data points to differentiate
0116     const size_t n = (size_t)xdataVector.size();
0117     if (n < 3) {
0118         differentiationResult.available = true;
0119         differentiationResult.valid = false;
0120         differentiationResult.status = i18n("Not enough data points available.");
0121         return true;
0122     }
0123 
0124     double* xdata = xdataVector.data();
0125     double* ydata = ydataVector.data();
0126 
0127     // differentiation settings
0128     const nsl_diff_deriv_order_type derivOrder = differentiationData.derivOrder;
0129     const int accOrder = differentiationData.accOrder;
0130 
0131     DEBUG(nsl_diff_deriv_order_name[derivOrder] << " derivative");
0132     DEBUG("accuracy order: " << accOrder);
0133     // WARN("DATA:")
0134     // for (int i = 0; i < n; i++)
0135     //  WARN(xdata[i] << "," << ydata[i])
0136 
0137     ///////////////////////////////////////////////////////////
0138     int status = 0;
0139 
0140     switch (derivOrder) {
0141     case nsl_diff_deriv_order_first:
0142         status = nsl_diff_first_deriv(xdata, ydata, n, accOrder);
0143         break;
0144     case nsl_diff_deriv_order_second:
0145         status = nsl_diff_second_deriv(xdata, ydata, n, accOrder);
0146         break;
0147     case nsl_diff_deriv_order_third:
0148         status = nsl_diff_third_deriv(xdata, ydata, n, accOrder);
0149         break;
0150     case nsl_diff_deriv_order_fourth:
0151         status = nsl_diff_fourth_deriv(xdata, ydata, n, accOrder);
0152         break;
0153     case nsl_diff_deriv_order_fifth:
0154         status = nsl_diff_fifth_deriv(xdata, ydata, n, accOrder);
0155         break;
0156     case nsl_diff_deriv_order_sixth:
0157         status = nsl_diff_sixth_deriv(xdata, ydata, n, accOrder);
0158         break;
0159     }
0160 
0161     xVector->resize((int)n);
0162     yVector->resize((int)n);
0163     memcpy(xVector->data(), xdata, n * sizeof(double));
0164     memcpy(yVector->data(), ydata, n * sizeof(double));
0165     ///////////////////////////////////////////////////////////
0166     // WARN("RESULT:")
0167     // for (int i = 0; i < n; i++)
0168     //  WARN(xdata[i] << "," << ydata[i])
0169 
0170     // write the result
0171     differentiationResult.available = true;
0172     differentiationResult.valid = (status == 0);
0173     differentiationResult.status = QString::number(status);
0174     differentiationResult.elapsedTime = timer.elapsed();
0175 
0176     return true;
0177 }
0178 
0179 // ##############################################################################
0180 // ##################  Serialization/Deserialization  ###########################
0181 // ##############################################################################
0182 //! Save as XML
0183 void XYDifferentiationCurve::save(QXmlStreamWriter* writer) const {
0184     Q_D(const XYDifferentiationCurve);
0185 
0186     writer->writeStartElement(QStringLiteral("xyDifferentiationCurve"));
0187 
0188     // write the base class
0189     XYAnalysisCurve::save(writer);
0190 
0191     // write xy-differentiation-curve specific information
0192     //  differentiation data
0193     writer->writeStartElement(QStringLiteral("differentiationData"));
0194     writer->writeAttribute(QStringLiteral("derivOrder"), QString::number(d->differentiationData.derivOrder));
0195     writer->writeAttribute(QStringLiteral("accOrder"), QString::number(d->differentiationData.accOrder));
0196     writer->writeAttribute(QStringLiteral("autoRange"), QString::number(d->differentiationData.autoRange));
0197     writer->writeAttribute(QStringLiteral("xRangeMin"), QString::number(d->differentiationData.xRange.first()));
0198     writer->writeAttribute(QStringLiteral("xRangeMax"), QString::number(d->differentiationData.xRange.last()));
0199     writer->writeEndElement(); // differentiationData
0200 
0201     // differentiation results (generated columns)
0202     writer->writeStartElement(QStringLiteral("differentiationResult"));
0203     writer->writeAttribute(QStringLiteral("available"), QString::number(d->differentiationResult.available));
0204     writer->writeAttribute(QStringLiteral("valid"), QString::number(d->differentiationResult.valid));
0205     writer->writeAttribute(QStringLiteral("status"), d->differentiationResult.status);
0206     writer->writeAttribute(QStringLiteral("time"), QString::number(d->differentiationResult.elapsedTime));
0207 
0208     // save calculated columns if available
0209     if (saveCalculations() && d->xColumn) {
0210         d->xColumn->save(writer);
0211         d->yColumn->save(writer);
0212     }
0213     writer->writeEndElement(); //"differentiationResult"
0214 
0215     writer->writeEndElement(); //"xyDifferentiationCurve"
0216 }
0217 
0218 //! Load from XML
0219 bool XYDifferentiationCurve::load(XmlStreamReader* reader, bool preview) {
0220     Q_D(XYDifferentiationCurve);
0221 
0222     QXmlStreamAttributes attribs;
0223     QString str;
0224 
0225     while (!reader->atEnd()) {
0226         reader->readNext();
0227         if (reader->isEndElement() && reader->name() == QLatin1String("xyDifferentiationCurve"))
0228             break;
0229 
0230         if (!reader->isStartElement())
0231             continue;
0232 
0233         if (reader->name() == QLatin1String("xyAnalysisCurve")) {
0234             if (!XYAnalysisCurve::load(reader, preview))
0235                 return false;
0236         } else if (!preview && reader->name() == QLatin1String("differentiationData")) {
0237             attribs = reader->attributes();
0238             READ_INT_VALUE("autoRange", differentiationData.autoRange, bool);
0239             READ_DOUBLE_VALUE("xRangeMin", differentiationData.xRange.first());
0240             READ_DOUBLE_VALUE("xRangeMax", differentiationData.xRange.last());
0241             READ_INT_VALUE("derivOrder", differentiationData.derivOrder, nsl_diff_deriv_order_type);
0242             READ_INT_VALUE("accOrder", differentiationData.accOrder, int);
0243         } else if (!preview && reader->name() == QLatin1String("differentiationResult")) {
0244             attribs = reader->attributes();
0245             READ_INT_VALUE("available", differentiationResult.available, int);
0246             READ_INT_VALUE("valid", differentiationResult.valid, int);
0247             READ_STRING_VALUE("status", differentiationResult.status);
0248             READ_INT_VALUE("time", differentiationResult.elapsedTime, int);
0249         } else if (reader->name() == QLatin1String("column")) {
0250             Column* column = new Column(QString(), AbstractColumn::ColumnMode::Double);
0251             if (!column->load(reader, preview)) {
0252                 delete column;
0253                 return false;
0254             }
0255             if (column->name() == QLatin1String("x"))
0256                 d->xColumn = column;
0257             else if (column->name() == QLatin1String("y"))
0258                 d->yColumn = column;
0259         } else { // unknown element
0260             reader->raiseUnknownElementWarning();
0261             if (!reader->skipToEndElement())
0262                 return false;
0263         }
0264     }
0265 
0266     if (preview)
0267         return true;
0268 
0269     // wait for data to be read before using the pointers
0270     QThreadPool::globalInstance()->waitForDone();
0271 
0272     if (d->xColumn && d->yColumn) {
0273         d->xColumn->setHidden(true);
0274         addChild(d->xColumn);
0275 
0276         d->yColumn->setHidden(true);
0277         addChild(d->yColumn);
0278 
0279         d->xVector = static_cast<QVector<double>*>(d->xColumn->data());
0280         d->yVector = static_cast<QVector<double>*>(d->yColumn->data());
0281 
0282         static_cast<XYCurvePrivate*>(d_ptr)->xColumn = d->xColumn;
0283         static_cast<XYCurvePrivate*>(d_ptr)->yColumn = d->yColumn;
0284 
0285         recalcLogicalPoints();
0286     }
0287 
0288     return true;
0289 }