File indexing completed on 2025-01-26 03:34:18
0001 /* 0002 File : XYEquationCurve.cpp 0003 Project : LabPlot 0004 Description : A xy-curve defined by a mathematical equation 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2014-2017 Alexander Semke <alexander.semke@web.de> 0007 SPDX-FileCopyrightText: 2023 Stefan Gerlach <stefan.gerlach@uni.kn> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 /*! 0012 \class XYEquationCurve 0013 \brief A xy-curve defined by a mathematical equation 0014 0015 \ingroup worksheet 0016 */ 0017 0018 #include "XYEquationCurve.h" 0019 #include "XYEquationCurvePrivate.h" 0020 #include "backend/core/AbstractColumn.h" 0021 #include "backend/core/Folder.h" 0022 #include "backend/core/column/Column.h" 0023 #include "backend/gsl/ExpressionParser.h" 0024 #include "backend/lib/XmlStreamReader.h" 0025 #include "backend/lib/commandtemplates.h" 0026 #include "backend/spreadsheet/Spreadsheet.h" 0027 #include "backend/worksheet/plots/cartesian/Symbol.h" 0028 0029 #include <KLocalizedString> 0030 #include <QIcon> 0031 0032 XYEquationCurve::XYEquationCurve(const QString& name) 0033 : XYCurve(name, new XYEquationCurvePrivate(this), AspectType::XYEquationCurve) { 0034 init(); 0035 } 0036 0037 XYEquationCurve::XYEquationCurve(const QString& name, XYEquationCurvePrivate* dd) 0038 : XYCurve(name, dd, AspectType::XYEquationCurve) { 0039 init(); 0040 } 0041 0042 // no need to delete the d-pointer here - it inherits from QGraphicsItem 0043 // and is deleted during the cleanup in QGraphicsScene 0044 XYEquationCurve::~XYEquationCurve() = default; 0045 0046 void XYEquationCurve::init() { 0047 Q_D(XYEquationCurve); 0048 0049 d->xColumn->setHidden(true); 0050 addChildFast(d->xColumn); 0051 0052 d->yColumn->setHidden(true); 0053 addChildFast(d->yColumn); 0054 0055 // TODO: read from the saved settings for XYEquationCurve? 0056 d->lineType = XYCurve::LineType::Line; 0057 d->symbol->setStyle(Symbol::Style::NoSymbols); 0058 0059 setUndoAware(false); 0060 setSuppressRetransform(true); 0061 setXColumn(d->xColumn); 0062 setYColumn(d->yColumn); 0063 setSuppressRetransform(false); 0064 setUndoAware(true); 0065 } 0066 0067 void XYEquationCurve::recalculate() { 0068 Q_D(XYEquationCurve); 0069 d->recalculate(); 0070 } 0071 0072 bool XYEquationCurve::dataAvailable() const { 0073 Q_D(const XYEquationCurve); 0074 return (d->equationData.count > 0); 0075 } 0076 0077 /*! 0078 Returns an icon to be used in the project explorer. 0079 */ 0080 QIcon XYEquationCurve::icon() const { 0081 return QIcon::fromTheme(QStringLiteral("labplot-xy-equation-curve")); 0082 } 0083 0084 // ############################################################################## 0085 // ########################## getter methods ################################## 0086 // ############################################################################## 0087 BASIC_SHARED_D_READER_IMPL(XYEquationCurve, XYEquationCurve::EquationData, equationData, equationData) 0088 0089 // ############################################################################## 0090 // ################# setter methods and undo commands ########################## 0091 // ############################################################################## 0092 STD_SETTER_CMD_IMPL_F_S(XYEquationCurve, SetEquationData, XYEquationCurve::EquationData, equationData, recalculate) 0093 void XYEquationCurve::setEquationData(const XYEquationCurve::EquationData& equationData) { 0094 Q_D(XYEquationCurve); 0095 if ((equationData.expression1 != d->equationData.expression1) || (equationData.expression2 != d->equationData.expression2) 0096 || (equationData.min != d->equationData.min) || (equationData.max != d->equationData.max) || (equationData.count != d->equationData.count)) 0097 exec(new XYEquationCurveSetEquationDataCmd(d, equationData, ki18n("%1: set equation"))); 0098 } 0099 0100 // ############################################################################## 0101 // ################################# SLOTS #################################### 0102 // ############################################################################## 0103 0104 /*! 0105 * creates a new spreadsheet having the data with the results of the calculation. 0106 * the new spreadsheet is added to the current folder. 0107 */ 0108 void XYEquationCurve::createDataSpreadsheet() { 0109 if (!xColumn() || !yColumn()) 0110 return; 0111 0112 auto* spreadsheet = new Spreadsheet(i18n("%1 - Data", name())); 0113 spreadsheet->removeColumns(0, spreadsheet->columnCount()); // remove default columns 0114 spreadsheet->setRowCount(xColumn()->rowCount()); 0115 0116 // x values 0117 auto* data = static_cast<const Column*>(xColumn())->data(); 0118 auto* xColumn = new Column(QLatin1String("x"), *static_cast<QVector<double>*>(data)); 0119 xColumn->setPlotDesignation(AbstractColumn::PlotDesignation::X); 0120 spreadsheet->addChild(xColumn); 0121 0122 // y values 0123 data = static_cast<const Column*>(yColumn())->data(); 0124 auto* yColumn = new Column(QLatin1String("y"), *static_cast<QVector<double>*>(data)); 0125 yColumn->setPlotDesignation(AbstractColumn::PlotDesignation::Y); 0126 spreadsheet->addChild(yColumn); 0127 0128 // add the new spreadsheet to the current folder 0129 folder()->addChild(spreadsheet); 0130 } 0131 0132 // ############################################################################## 0133 // ######################### Private implementation ############################# 0134 // ############################################################################## 0135 XYEquationCurvePrivate::XYEquationCurvePrivate(XYEquationCurve* owner) 0136 : XYCurvePrivate(owner) 0137 , xColumn(new Column(QStringLiteral("x"), AbstractColumn::ColumnMode::Double)) 0138 , yColumn(new Column(QStringLiteral("y"), AbstractColumn::ColumnMode::Double)) 0139 , xVector(static_cast<QVector<double>*>(xColumn->data())) 0140 , yVector(static_cast<QVector<double>*>(yColumn->data())) 0141 , q(owner) { 0142 } 0143 0144 // no need to delete xColumn and yColumn, they are deleted 0145 // when the parent aspect is removed 0146 XYEquationCurvePrivate::~XYEquationCurvePrivate() = default; 0147 0148 void XYEquationCurvePrivate::recalculate() { 0149 // resize the vector if a new number of point to calculate was provided 0150 if (equationData.count != xVector->size()) { 0151 if (equationData.count >= 1) { 0152 xVector->resize(equationData.count); 0153 yVector->resize(equationData.count); 0154 } else { 0155 // invalid number of points provided 0156 xVector->clear(); 0157 yVector->clear(); 0158 recalcLogicalPoints(); 0159 Q_EMIT q->dataChanged(); 0160 return; 0161 } 0162 xColumn->invalidateProperties(); 0163 yColumn->invalidateProperties(); 0164 } else { 0165 if (equationData.count < 1) 0166 return; 0167 } 0168 0169 ExpressionParser* parser = ExpressionParser::getInstance(); 0170 bool rc = false; 0171 if (equationData.type == XYEquationCurve::EquationType::Cartesian) { 0172 rc = parser->evaluateCartesian(equationData.expression1, equationData.min, equationData.max, equationData.count, xVector, yVector); 0173 } else if (equationData.type == XYEquationCurve::EquationType::Polar) { 0174 rc = parser->evaluatePolar(equationData.expression1, equationData.min, equationData.max, equationData.count, xVector, yVector); 0175 } else if (equationData.type == XYEquationCurve::EquationType::Parametric) { 0176 rc = parser->evaluateParametric(equationData.expression1, 0177 equationData.expression2, 0178 equationData.min, 0179 equationData.max, 0180 equationData.count, 0181 xVector, 0182 yVector); 0183 } 0184 0185 if (!rc) { 0186 xVector->clear(); 0187 yVector->clear(); 0188 } 0189 xColumn->invalidateProperties(); 0190 yColumn->invalidateProperties(); 0191 0192 recalcLogicalPoints(); 0193 Q_EMIT q->dataChanged(); 0194 } 0195 0196 // ############################################################################## 0197 // ################## Serialization/Deserialization ########################### 0198 // ############################################################################## 0199 //! Save as XML 0200 void XYEquationCurve::save(QXmlStreamWriter* writer) const { 0201 Q_D(const XYEquationCurve); 0202 0203 writer->writeStartElement(QStringLiteral("xyEquationCurve")); 0204 0205 // write xy-curve information 0206 XYCurve::save(writer); 0207 0208 // write xy-equationCurve specific information 0209 writer->writeStartElement(QStringLiteral("equationData")); 0210 writer->writeAttribute(QStringLiteral("type"), QString::number(static_cast<int>(d->equationData.type))); 0211 writer->writeAttribute(QStringLiteral("expression1"), d->equationData.expression1); 0212 writer->writeAttribute(QStringLiteral("expression2"), d->equationData.expression2); 0213 writer->writeAttribute(QStringLiteral("min"), d->equationData.min); 0214 writer->writeAttribute(QStringLiteral("max"), d->equationData.max); 0215 writer->writeAttribute(QStringLiteral("count"), QString::number(d->equationData.count)); 0216 writer->writeEndElement(); 0217 0218 writer->writeEndElement(); 0219 } 0220 0221 //! Load from XML 0222 bool XYEquationCurve::load(XmlStreamReader* reader, bool preview) { 0223 Q_D(XYEquationCurve); 0224 0225 QXmlStreamAttributes attribs; 0226 QString str; 0227 0228 while (!reader->atEnd()) { 0229 reader->readNext(); 0230 if (reader->isEndElement() && reader->name() == QLatin1String("xyEquationCurve")) 0231 break; 0232 0233 if (!reader->isStartElement()) 0234 continue; 0235 0236 if (reader->name() == QLatin1String("xyCurve")) { 0237 if (!XYCurve::load(reader, preview)) 0238 return false; 0239 } else if (!preview && reader->name() == QLatin1String("equationData")) { 0240 attribs = reader->attributes(); 0241 0242 READ_INT_VALUE("type", equationData.type, XYEquationCurve::EquationType); 0243 READ_STRING_VALUE("expression1", equationData.expression1); 0244 READ_STRING_VALUE("expression2", equationData.expression2); 0245 READ_STRING_VALUE("min", equationData.min); 0246 READ_STRING_VALUE("max", equationData.max); 0247 READ_INT_VALUE("count", equationData.count, int); 0248 } else { // unknown element 0249 reader->raiseUnknownElementWarning(); 0250 if (!reader->skipToEndElement()) 0251 return false; 0252 } 0253 } 0254 0255 // Recalculate, otherwise xColumn and yColumn are not updated 0256 // and so autoscale is wrong 0257 recalculate(); 0258 0259 return true; 0260 }