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 }