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

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