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 }