File indexing completed on 2025-01-26 03:34:18
0001 /* 0002 File : XYDataReductionCurve.cpp 0003 Project : LabPlot 0004 Description : A xy-curve defined by a data reduction 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 XYDataReductionCurve 0013 \brief A xy-curve defined by a data reduction 0014 0015 \ingroup worksheet 0016 */ 0017 0018 #include "XYDataReductionCurve.h" 0019 #include "CartesianCoordinateSystem.h" 0020 #include "XYDataReductionCurvePrivate.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 <KLocalizedString> 0027 #include <QElapsedTimer> 0028 #include <QIcon> 0029 #include <QThreadPool> 0030 0031 XYDataReductionCurve::XYDataReductionCurve(const QString& name) 0032 : XYAnalysisCurve(name, new XYDataReductionCurvePrivate(this), AspectType::XYDataReductionCurve) { 0033 } 0034 0035 XYDataReductionCurve::XYDataReductionCurve(const QString& name, XYDataReductionCurvePrivate* dd) 0036 : XYAnalysisCurve(name, dd, AspectType::XYDataReductionCurve) { 0037 } 0038 0039 // no need to delete the d-pointer here - it inherits from QGraphicsItem 0040 // and is deleted during the cleanup in QGraphicsScene 0041 XYDataReductionCurve::~XYDataReductionCurve() = default; 0042 0043 void XYDataReductionCurve::recalculate() { 0044 Q_D(XYDataReductionCurve); 0045 d->recalculate(); 0046 } 0047 0048 const XYAnalysisCurve::Result& XYDataReductionCurve::result() const { 0049 Q_D(const XYDataReductionCurve); 0050 return d->dataReductionResult; 0051 } 0052 /*! 0053 Returns an icon to be used in the project explorer. 0054 */ 0055 QIcon XYDataReductionCurve::icon() const { 0056 return QIcon::fromTheme(QStringLiteral("labplot-xy-data-reduction-curve")); 0057 } 0058 0059 // ############################################################################## 0060 // ########################## getter methods ################################## 0061 // ############################################################################## 0062 BASIC_SHARED_D_READER_IMPL(XYDataReductionCurve, XYDataReductionCurve::DataReductionData, dataReductionData, dataReductionData) 0063 0064 const XYDataReductionCurve::DataReductionResult& XYDataReductionCurve::dataReductionResult() const { 0065 Q_D(const XYDataReductionCurve); 0066 return d->dataReductionResult; 0067 } 0068 0069 // ############################################################################## 0070 // ################# setter methods and undo commands ########################## 0071 // ############################################################################## 0072 STD_SETTER_CMD_IMPL_F_S(XYDataReductionCurve, SetDataReductionData, XYDataReductionCurve::DataReductionData, dataReductionData, recalculate) 0073 void XYDataReductionCurve::setDataReductionData(const XYDataReductionCurve::DataReductionData& reductionData) { 0074 Q_D(XYDataReductionCurve); 0075 exec(new XYDataReductionCurveSetDataReductionDataCmd(d, reductionData, ki18n("%1: set options and perform the data reduction"))); 0076 } 0077 0078 // ############################################################################## 0079 // ######################### Private implementation ############################# 0080 // ############################################################################## 0081 XYDataReductionCurvePrivate::XYDataReductionCurvePrivate(XYDataReductionCurve* owner) 0082 : XYAnalysisCurvePrivate(owner) 0083 , q(owner) { 0084 } 0085 0086 // no need to delete xColumn and yColumn, they are deleted 0087 // when the parent aspect is removed 0088 XYDataReductionCurvePrivate::~XYDataReductionCurvePrivate() = default; 0089 0090 void XYDataReductionCurvePrivate::resetResults() { 0091 dataReductionResult = XYDataReductionCurve::DataReductionResult(); 0092 } 0093 0094 const XYAnalysisCurve::Result& XYDataReductionCurvePrivate::result() const { 0095 return dataReductionResult; 0096 } 0097 0098 bool XYDataReductionCurvePrivate::recalculateSpecific(const AbstractColumn* tmpXDataColumn, const AbstractColumn* tmpYDataColumn) { 0099 QElapsedTimer timer; 0100 timer.start(); 0101 0102 // copy all valid data point for the data reduction to temporary vectors 0103 QVector<double> xdataVector; 0104 QVector<double> ydataVector; 0105 0106 double xmin; 0107 double xmax; 0108 if (dataReductionData.autoRange) { 0109 xmin = tmpXDataColumn->minimum(); 0110 xmax = tmpXDataColumn->maximum(); 0111 } else { 0112 xmin = dataReductionData.xRange.first(); 0113 xmax = dataReductionData.xRange.last(); 0114 } 0115 0116 XYAnalysisCurve::copyData(xdataVector, ydataVector, tmpXDataColumn, tmpYDataColumn, xmin, xmax); 0117 0118 // number of data points to use 0119 const size_t n = (size_t)xdataVector.size(); 0120 if (n < 2) { 0121 dataReductionResult.available = true; 0122 dataReductionResult.valid = false; 0123 dataReductionResult.status = i18n("Not enough data points available."); 0124 return true; 0125 } 0126 0127 double* xdata = xdataVector.data(); 0128 double* ydata = ydataVector.data(); 0129 0130 // dataReduction settings 0131 const nsl_geom_linesim_type type = dataReductionData.type; 0132 const double tol = dataReductionData.tolerance; 0133 const double tol2 = dataReductionData.tolerance2; 0134 0135 DEBUG("n =" << n); 0136 DEBUG("type:" << nsl_geom_linesim_type_name[type]); 0137 DEBUG("tolerance/step:" << tol); 0138 DEBUG("tolerance2/repeat/maxtol/region:" << tol2); 0139 0140 /////////////////////////////////////////////////////////// 0141 Q_EMIT q->completed(10); 0142 0143 size_t npoints = 0; 0144 double calcTolerance = 0; // calculated tolerance from Douglas-Peucker variant 0145 size_t* index = (size_t*)malloc(n * sizeof(size_t)); 0146 switch (type) { 0147 case nsl_geom_linesim_type_douglas_peucker_variant: // tol used as number of points 0148 npoints = tol; 0149 calcTolerance = nsl_geom_linesim_douglas_peucker_variant(xdata, ydata, n, npoints, index); 0150 break; 0151 case nsl_geom_linesim_type_douglas_peucker: 0152 npoints = nsl_geom_linesim_douglas_peucker(xdata, ydata, n, tol, index); 0153 break; 0154 case nsl_geom_linesim_type_nthpoint: // tol used as step 0155 npoints = nsl_geom_linesim_nthpoint(n, (int)tol, index); 0156 break; 0157 case nsl_geom_linesim_type_raddist: 0158 npoints = nsl_geom_linesim_raddist(xdata, ydata, n, tol, index); 0159 break; 0160 case nsl_geom_linesim_type_perpdist: // tol2 used as repeat 0161 npoints = nsl_geom_linesim_perpdist_repeat(xdata, ydata, n, tol, tol2, index); 0162 break; 0163 case nsl_geom_linesim_type_interp: 0164 npoints = nsl_geom_linesim_interp(xdata, ydata, n, tol, index); 0165 break; 0166 case nsl_geom_linesim_type_visvalingam_whyatt: 0167 npoints = nsl_geom_linesim_visvalingam_whyatt(xdata, ydata, n, tol, index); 0168 break; 0169 case nsl_geom_linesim_type_reumann_witkam: 0170 npoints = nsl_geom_linesim_reumann_witkam(xdata, ydata, n, tol, index); 0171 break; 0172 case nsl_geom_linesim_type_opheim: 0173 npoints = nsl_geom_linesim_opheim(xdata, ydata, n, tol, tol2, index); 0174 break; 0175 case nsl_geom_linesim_type_lang: // tol2 used as region 0176 npoints = nsl_geom_linesim_opheim(xdata, ydata, n, tol, tol2, index); 0177 break; 0178 } 0179 0180 DEBUG("npoints =" << npoints); 0181 if (type == nsl_geom_linesim_type_douglas_peucker_variant) { 0182 DEBUG("calculated tolerance =" << calcTolerance) 0183 } else 0184 Q_UNUSED(calcTolerance); 0185 0186 Q_EMIT q->completed(80); 0187 0188 xVector->resize((int)npoints); 0189 yVector->resize((int)npoints); 0190 for (int i = 0; i < (int)npoints; i++) { 0191 (*xVector)[i] = xdata[index[i]]; 0192 (*yVector)[i] = ydata[index[i]]; 0193 } 0194 0195 Q_EMIT q->completed(90); 0196 0197 const double posError = nsl_geom_linesim_positional_squared_error(xdata, ydata, n, index); 0198 const double areaError = nsl_geom_linesim_area_error(xdata, ydata, n, index); 0199 0200 free(index); 0201 0202 /////////////////////////////////////////////////////////// 0203 0204 // write the result 0205 dataReductionResult.available = true; 0206 dataReductionResult.valid = npoints > 0; 0207 if (npoints > 0) 0208 dataReductionResult.status = QStringLiteral("OK"); 0209 else 0210 dataReductionResult.status = QStringLiteral("FAILURE"); 0211 dataReductionResult.elapsedTime = timer.elapsed(); 0212 dataReductionResult.npoints = npoints; 0213 dataReductionResult.posError = posError; 0214 dataReductionResult.areaError = areaError; 0215 0216 Q_EMIT q->completed(100); 0217 return true; 0218 } 0219 0220 // ############################################################################## 0221 // ################## Serialization/Deserialization ########################### 0222 // ############################################################################## 0223 //! Save as XML 0224 void XYDataReductionCurve::save(QXmlStreamWriter* writer) const { 0225 Q_D(const XYDataReductionCurve); 0226 0227 writer->writeStartElement(QStringLiteral("xyDataReductionCurve")); 0228 0229 // write the base class 0230 XYAnalysisCurve::save(writer); 0231 0232 // write xy-dataReduction-curve specific information 0233 // dataReduction data 0234 writer->writeStartElement(QStringLiteral("dataReductionData")); 0235 writer->writeAttribute(QStringLiteral("autoRange"), QString::number(d->dataReductionData.autoRange)); 0236 writer->writeAttribute(QStringLiteral("xRangeMin"), QString::number(d->dataReductionData.xRange.first())); 0237 writer->writeAttribute(QStringLiteral("xRangeMax"), QString::number(d->dataReductionData.xRange.last())); 0238 writer->writeAttribute(QStringLiteral("type"), QString::number(d->dataReductionData.type)); 0239 writer->writeAttribute(QStringLiteral("autoTolerance"), QString::number(d->dataReductionData.autoTolerance)); 0240 writer->writeAttribute(QStringLiteral("tolerance"), QString::number(d->dataReductionData.tolerance)); 0241 writer->writeAttribute(QStringLiteral("autoTolerance2"), QString::number(d->dataReductionData.autoTolerance2)); 0242 writer->writeAttribute(QStringLiteral("tolerance2"), QString::number(d->dataReductionData.tolerance2)); 0243 writer->writeEndElement(); // dataReductionData 0244 0245 // dataReduction results (generated columns) 0246 writer->writeStartElement(QStringLiteral("dataReductionResult")); 0247 writer->writeAttribute(QStringLiteral("available"), QString::number(d->dataReductionResult.available)); 0248 writer->writeAttribute(QStringLiteral("valid"), QString::number(d->dataReductionResult.valid)); 0249 writer->writeAttribute(QStringLiteral("status"), d->dataReductionResult.status); 0250 writer->writeAttribute(QStringLiteral("time"), QString::number(d->dataReductionResult.elapsedTime)); 0251 writer->writeAttribute(QStringLiteral("npoints"), QString::number(d->dataReductionResult.npoints)); 0252 writer->writeAttribute(QStringLiteral("posError"), QString::number(d->dataReductionResult.posError)); 0253 writer->writeAttribute(QStringLiteral("areaError"), QString::number(d->dataReductionResult.areaError)); 0254 0255 // save calculated columns if available 0256 if (saveCalculations() && d->xColumn) { 0257 d->xColumn->save(writer); 0258 d->yColumn->save(writer); 0259 } 0260 writer->writeEndElement(); //"dataReductionResult" 0261 0262 writer->writeEndElement(); //"xyDataReductionCurve" 0263 } 0264 0265 //! Load from XML 0266 bool XYDataReductionCurve::load(XmlStreamReader* reader, bool preview) { 0267 Q_D(XYDataReductionCurve); 0268 0269 QXmlStreamAttributes attribs; 0270 QString str; 0271 0272 while (!reader->atEnd()) { 0273 reader->readNext(); 0274 if (reader->isEndElement() && reader->name() == QLatin1String("xyDataReductionCurve")) 0275 break; 0276 0277 if (!reader->isStartElement()) 0278 continue; 0279 0280 if (reader->name() == QLatin1String("xyAnalysisCurve")) { 0281 if (!XYAnalysisCurve::load(reader, preview)) 0282 return false; 0283 } else if (!preview && reader->name() == QLatin1String("dataReductionData")) { 0284 attribs = reader->attributes(); 0285 READ_INT_VALUE("autoRange", dataReductionData.autoRange, bool); 0286 READ_DOUBLE_VALUE("xRangeMin", dataReductionData.xRange.first()); 0287 READ_DOUBLE_VALUE("xRangeMax", dataReductionData.xRange.last()); 0288 READ_INT_VALUE("type", dataReductionData.type, nsl_geom_linesim_type); 0289 READ_INT_VALUE("autoTolerance", dataReductionData.autoTolerance, int); 0290 READ_DOUBLE_VALUE("tolerance", dataReductionData.tolerance); 0291 READ_INT_VALUE("autoTolerance2", dataReductionData.autoTolerance2, int); 0292 READ_DOUBLE_VALUE("tolerance2", dataReductionData.tolerance2); 0293 } else if (!preview && reader->name() == QLatin1String("dataReductionResult")) { 0294 attribs = reader->attributes(); 0295 READ_INT_VALUE("available", dataReductionResult.available, int); 0296 READ_INT_VALUE("valid", dataReductionResult.valid, int); 0297 READ_STRING_VALUE("status", dataReductionResult.status); 0298 READ_INT_VALUE("time", dataReductionResult.elapsedTime, int); 0299 READ_INT_VALUE("npoints", dataReductionResult.npoints, size_t); 0300 READ_DOUBLE_VALUE("posError", dataReductionResult.posError); 0301 READ_DOUBLE_VALUE("areaError", dataReductionResult.areaError); 0302 } else if (reader->name() == QLatin1String("column")) { 0303 Column* column = new Column(QString(), AbstractColumn::ColumnMode::Double); 0304 if (!column->load(reader, preview)) { 0305 delete column; 0306 return false; 0307 } 0308 if (column->name() == QLatin1String("x")) 0309 d->xColumn = column; 0310 else if (column->name() == QLatin1String("y")) 0311 d->yColumn = column; 0312 } else { // unknown element 0313 reader->raiseUnknownElementWarning(); 0314 if (!reader->skipToEndElement()) 0315 return false; 0316 } 0317 } 0318 0319 if (preview) 0320 return true; 0321 0322 // wait for data to be read before using the pointers 0323 QThreadPool::globalInstance()->waitForDone(); 0324 0325 if (d->xColumn && d->yColumn) { 0326 d->xColumn->setHidden(true); 0327 addChild(d->xColumn); 0328 0329 d->yColumn->setHidden(true); 0330 addChild(d->yColumn); 0331 0332 d->xVector = static_cast<QVector<double>*>(d->xColumn->data()); 0333 d->yVector = static_cast<QVector<double>*>(d->yColumn->data()); 0334 0335 static_cast<XYCurvePrivate*>(d_ptr)->xColumn = d->xColumn; 0336 static_cast<XYCurvePrivate*>(d_ptr)->yColumn = d->yColumn; 0337 0338 recalcLogicalPoints(); 0339 } 0340 0341 return true; 0342 }