File indexing completed on 2024-04-28 07:29:26

0001 /*
0002     KmPlot - a math. function plotter for the KDE-Desktop
0003 
0004     SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de>
0005     SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org>
0006 
0007     This file is part of the KDE Project.
0008     KmPlot is part of the KDE-EDU Project.
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 
0012 */
0013 
0014 #include "function.h"
0015 #include "ksliderwindow.h"
0016 #include "settings.h"
0017 #include "view.h"
0018 #include "xparser.h"
0019 
0020 #include <QImage>
0021 #include <QLinearGradient>
0022 #include <QPainter>
0023 
0024 #include <assert.h>
0025 #include <cmath>
0026 
0027 int MAX_PM = 4;
0028 
0029 // BEGIN class Value
0030 Value::Value(const QString &expression)
0031 {
0032     m_value = 0.0;
0033     if (expression.isEmpty())
0034         m_expression = '0';
0035     else
0036         updateExpression(expression);
0037 }
0038 
0039 Value::Value(double value)
0040 {
0041     updateExpression(value);
0042 }
0043 
0044 bool Value::updateExpression(const QString &expression)
0045 {
0046     Parser::Error error;
0047     double newValue = XParser::self()->eval(expression, &error);
0048     if (error != Parser::ParseSuccess)
0049         return false;
0050 
0051     m_value = newValue;
0052     m_expression = expression;
0053     return true;
0054 }
0055 
0056 void Value::updateExpression(double value)
0057 {
0058     m_value = value;
0059     m_expression = Parser::number(value);
0060 }
0061 
0062 bool Value::operator==(const Value &other) const
0063 {
0064     return m_expression == other.expression();
0065 }
0066 // END class Value
0067 
0068 // BEGIN class PlotAppearance
0069 PlotAppearance::PlotAppearance()
0070 {
0071     lineWidth = 0.3;
0072     color = Qt::black;
0073     useGradient = false;
0074     visible = false;
0075     style = Qt::SolidLine;
0076     showExtrema = false;
0077     showTangentField = false;
0078     showPlotName = false;
0079 }
0080 
0081 bool PlotAppearance::operator!=(const PlotAppearance &other) const
0082 {
0083     return (lineWidth != other.lineWidth) || (color != other.color) || (useGradient != other.useGradient) || (gradient.stops() != other.gradient.stops())
0084         || (visible != other.visible) || (style != other.style) || (showExtrema != other.showExtrema) || (showTangentField != other.showTangentField)
0085         || (showPlotName != other.showPlotName);
0086 }
0087 
0088 QString PlotAppearance::penStyleToString(Qt::PenStyle style)
0089 {
0090     switch (style) {
0091     case Qt::NoPen:
0092         return "NoPen";
0093 
0094     case Qt::SolidLine:
0095         return "SolidLine";
0096 
0097     case Qt::DashLine:
0098         return "DashLine";
0099 
0100     case Qt::DotLine:
0101         return "DotLine";
0102 
0103     case Qt::DashDotLine:
0104         return "DashDotLine";
0105 
0106     case Qt::DashDotDotLine:
0107         return "DashDotDotLine";
0108 
0109     case Qt::MPenStyle:
0110     case Qt::CustomDashLine:
0111         qWarning() << "Unsupported pen style\n";
0112         break;
0113     }
0114 
0115     qWarning() << "Unknown style " << style;
0116     return "SolidLine";
0117 }
0118 
0119 Qt::PenStyle PlotAppearance::stringToPenStyle(const QString &style)
0120 {
0121     if (style == "NoPen")
0122         return Qt::NoPen;
0123 
0124     if (style == "SolidLine")
0125         return Qt::SolidLine;
0126 
0127     if (style == "DashLine")
0128         return Qt::DashLine;
0129 
0130     if (style == "DotLine")
0131         return Qt::DotLine;
0132 
0133     if (style == "DashDotLine")
0134         return Qt::DashDotLine;
0135 
0136     if (style == "DashDotDotLine")
0137         return Qt::DashDotDotLine;
0138 
0139     qWarning() << "Unknown style " << style;
0140     return Qt::SolidLine;
0141 }
0142 // END class PlotAppearance
0143 
0144 // BEGIN class DifferentialState
0145 DifferentialState::DifferentialState()
0146 {
0147     x = 0;
0148 }
0149 
0150 DifferentialState::DifferentialState(int order)
0151 {
0152     x = 0;
0153     setOrder(order);
0154 }
0155 
0156 void DifferentialState::setOrder(int order)
0157 {
0158     bool orderWasZero = (y0.size() == 0);
0159 
0160     y.resize(order);
0161     y0.resize(order);
0162 
0163     if (orderWasZero && order >= 1)
0164         y0[0].updateExpression("1");
0165 
0166     resetToInitial();
0167 }
0168 
0169 bool DifferentialStates::setStep(const Value &step)
0170 {
0171     if (step.value() <= 0)
0172         return false;
0173 
0174     m_step = step;
0175     return true;
0176 }
0177 
0178 void DifferentialState::resetToInitial()
0179 {
0180     x = x0.value();
0181     y = y0;
0182 }
0183 
0184 bool DifferentialState::operator==(const DifferentialState &other) const
0185 {
0186     return (x0 == other.x0) && (x == other.x) && (y0 == other.y0) && (y == other.y);
0187 }
0188 // END class DifferentialState
0189 
0190 // BEGIN class DifferentialStates
0191 DifferentialStates::DifferentialStates()
0192 {
0193     m_uniqueState = false;
0194     m_order = 0;
0195     m_step.updateExpression(0.05);
0196 }
0197 
0198 void DifferentialStates::setOrder(int order)
0199 {
0200     m_order = order;
0201     for (int i = 0; i < m_data.size(); ++i)
0202         m_data[i].setOrder(order);
0203 }
0204 
0205 DifferentialState *DifferentialStates::add()
0206 {
0207     if (!m_uniqueState || m_data.isEmpty())
0208         m_data << DifferentialState(order());
0209     else
0210         qDebug() << "Unable to add another state!\n";
0211 
0212     return &m_data[size() - 1];
0213 }
0214 
0215 void DifferentialStates::setUniqueState(bool unique)
0216 {
0217     m_uniqueState = unique;
0218     if (m_uniqueState && m_data.size() > 1) {
0219         // Remove any states other than the first
0220         m_data.resize(1);
0221     }
0222 }
0223 
0224 void DifferentialStates::resetToInitial()
0225 {
0226     for (int i = 0; i < m_data.size(); ++i)
0227         m_data[i].resetToInitial();
0228 }
0229 // END class DifferentialStates
0230 
0231 // BEGIN class Equation
0232 Equation::Equation(Type type, Function *parent)
0233     : m_type(type)
0234     , m_parent(parent)
0235 {
0236     m_usesParameter = false;
0237     mptr = nullptr;
0238 
0239     if (type == Differential || type == Cartesian) {
0240         differentialStates.setUniqueState(type == Cartesian);
0241         differentialStates.setOrder(order());
0242         differentialStates.add();
0243     }
0244 }
0245 
0246 Equation::~Equation()
0247 {
0248 }
0249 
0250 int Equation::order() const
0251 {
0252     if (type() == Cartesian) {
0253         // For drawing integrals
0254         return 1;
0255     } else
0256         return name(false).count('\'');
0257 }
0258 
0259 int Equation::pmCount() const
0260 {
0261     return m_fstr.count(PmSymbol);
0262 }
0263 
0264 QString Equation::name(bool removePrimes) const
0265 {
0266     if (m_fstr.isEmpty())
0267         return QString();
0268 
0269     int open = m_fstr.indexOf('(');
0270     int equals = m_fstr.indexOf('=');
0271 
0272     if ((equals == -1) && (open == -1))
0273         return QString();
0274 
0275     int pos;
0276     if (((equals > open) && (open != -1)) || (equals == -1))
0277         pos = open;
0278     else
0279         pos = equals;
0280 
0281     QString n = m_fstr.left(pos).trimmed();
0282 
0283     if (removePrimes)
0284         n.remove('\'');
0285 
0286     return n;
0287 }
0288 
0289 bool Equation::looksLikeFunction() const
0290 {
0291     int open = m_fstr.indexOf('(');
0292     int equals = m_fstr.indexOf('=');
0293 
0294     if ((open != -1) && (open < equals))
0295         return true;
0296 
0297     switch (type()) {
0298     case Cartesian:
0299     case Differential:
0300     case ParametricY:
0301         return (name() != "y");
0302 
0303     case Polar:
0304         return (name() != "r");
0305 
0306     case ParametricX:
0307         return (name() != "x");
0308 
0309     case Implicit:
0310         return false;
0311 
0312     case Constant:
0313         return false;
0314     }
0315 
0316     return true;
0317 }
0318 
0319 void Equation::updateVariables()
0320 {
0321     if (type() == Constant) {
0322         return;
0323     }
0324 
0325     m_variables.clear();
0326 
0327     if (looksLikeFunction()) {
0328         int p1 = m_fstr.indexOf('(');
0329         int p2 = m_fstr.indexOf(')');
0330 
0331         const QStringList listSplit = ((p1 != -1) && (p2 != -1)) ? m_fstr.mid(p1 + 1, p2 - p1 - 1).split(',', Qt::SkipEmptyParts) : QStringList();
0332 
0333         // Variables shouldn't contain spaces!
0334         for (QString s : listSplit) {
0335             s = s.remove(' ');
0336             if (!s.isEmpty())
0337                 m_variables << s;
0338         }
0339     } else
0340         switch (type()) {
0341         case Cartesian:
0342         case Differential:
0343             m_variables << "x"
0344                         << "k";
0345             break;
0346 
0347         case Polar:
0348             m_variables << QChar(0x3b8) << "k"; // (theta)
0349             break;
0350 
0351         case ParametricX:
0352         case ParametricY:
0353             m_variables << "t"
0354                         << "k";
0355             break;
0356 
0357         case Implicit:
0358             m_variables << "x"
0359                         << "y"
0360                         << "k";
0361             break;
0362 
0363         case Constant:
0364             break;
0365         }
0366 
0367     // If we are a differential equation, then add on y, y', etc
0368     if (type() == Differential && !name().isEmpty()) {
0369         QString n = name();
0370 
0371         int order = this->order();
0372         for (int i = 0; i < order; ++i) {
0373             m_variables << n;
0374             n += '\'';
0375         }
0376     }
0377 
0378     // BEGIN Update whether we accept a parameter or not
0379     int expectedNumVariables = 0;
0380 
0381     switch (m_type) {
0382     case Cartesian:
0383     case ParametricX:
0384     case ParametricY:
0385     case Polar:
0386         expectedNumVariables = 1;
0387         break;
0388 
0389     case Implicit:
0390         expectedNumVariables = 2;
0391         break;
0392 
0393     case Differential:
0394         expectedNumVariables = order() + 1;
0395         break;
0396 
0397     case Constant:
0398         expectedNumVariables = 0;
0399         break;
0400     }
0401 
0402     m_usesParameter = (variables().size() > expectedNumVariables);
0403     // END Update whether we accept a parameter or not
0404 }
0405 
0406 QString Equation::parameterName() const
0407 {
0408     if (!usesParameter())
0409         return QString();
0410 
0411     int parAt = (type() == Implicit) ? 2 : 1;
0412     return variables()[parAt];
0413 }
0414 
0415 bool Equation::setFstr(const QString &fstr, int *error, int *errorPosition, bool force)
0416 {
0417 #define HANDLE_ERROR                                                                                                                                           \
0418     if (!force) {                                                                                                                                              \
0419         m_fstr = prevFstr;                                                                                                                                     \
0420         updateVariables();                                                                                                                                     \
0421     } else {                                                                                                                                                   \
0422         qDebug() << "fstr " << fstr << " invalid, but forcing anyway: " << Parser::errorString(Parser::Error(*error)) << " at position " << *errorPosition;    \
0423         mem.clear();                                                                                                                                           \
0424     }
0425 
0426     int temp1, temp2;
0427     if (!error)
0428         error = &temp1;
0429     if (!errorPosition)
0430         errorPosition = &temp2;
0431 
0432     *error = Parser::ParseSuccess;
0433     *errorPosition = -1;
0434 
0435     QString prevFstr = m_fstr;
0436     m_fstr = fstr;
0437     updateVariables();
0438 
0439     if (!fstr.contains('=') || QString(fstr).right(fstr.length() - fstr.indexOf('=') - 1).simplified().isEmpty()) {
0440         *error = Parser::SyntaxError;
0441         HANDLE_ERROR;
0442         return false;
0443     }
0444 
0445     // require order to be greater than 0 for differential equations
0446     if ((type() == Differential) && (order() < 1)) {
0447         *error = Parser::ZeroOrder;
0448         HANDLE_ERROR;
0449         /// \todo indicate the position of the error
0450         return false;
0451     }
0452 
0453     int maxArg = order() + ((type() == Implicit) ? 3 : 2);
0454     if (variables().size() > maxArg) {
0455         *error = Parser::TooManyArguments;
0456         HANDLE_ERROR;
0457         /// \todo indicate the position of the invalid argument?
0458         return false;
0459     }
0460 
0461     XParser::self()->initEquation(this, (Parser::Error *)error, errorPosition);
0462     if (*error != Parser::ParseSuccess) {
0463         HANDLE_ERROR;
0464         if (!force)
0465             XParser::self()->initEquation(this);
0466         return false;
0467     }
0468 
0469     differentialStates.setOrder(order());
0470     return true;
0471 }
0472 
0473 void Equation::setPMSignature(QVector<bool> pmSignature)
0474 {
0475     differentialStates.resetToInitial();
0476     m_pmSignature = pmSignature;
0477 }
0478 
0479 bool Equation::operator!=(const Equation &other)
0480 {
0481     return (fstr() != other.fstr()) || (differentialStates != other.differentialStates);
0482 }
0483 
0484 Equation &Equation::operator=(const Equation &other)
0485 {
0486     setFstr(other.fstr());
0487     differentialStates = other.differentialStates;
0488 
0489     return *this;
0490 }
0491 // END class Equation
0492 
0493 // BEGIN class Function
0494 Function::Function(Type type)
0495     : m_type(type)
0496 {
0497     x = y = 0;
0498     m_implicitMode = UnfixedXY;
0499 
0500     usecustomxmin = false;
0501     usecustomxmax = false;
0502 
0503     dmin.updateExpression(QChar('0'));
0504     if (Settings::anglemode() == Parser::Radians)
0505         dmax.updateExpression(QString(QChar('2')) + PiSymbol);
0506     else
0507         dmax.updateExpression("360");
0508 
0509     switch (m_type) {
0510     case Cartesian:
0511         eq << new Equation(Equation::Cartesian, this);
0512         break;
0513 
0514     case Polar:
0515         eq << new Equation(Equation::Polar, this);
0516         usecustomxmin = true;
0517         usecustomxmax = true;
0518         break;
0519 
0520     case Parametric:
0521         eq << new Equation(Equation::ParametricX, this);
0522         eq << new Equation(Equation::ParametricY, this);
0523         usecustomxmin = true;
0524         usecustomxmax = true;
0525         break;
0526 
0527     case Implicit:
0528         eq << new Equation(Equation::Implicit, this);
0529         break;
0530 
0531     case Differential:
0532         eq << new Equation(Equation::Differential, this);
0533         break;
0534     }
0535 
0536     m_id = 0;
0537     f0.visible = true;
0538 
0539     k = 0;
0540 }
0541 
0542 Function::~Function()
0543 {
0544     for (Equation *e : qAsConst(eq))
0545         delete e;
0546 }
0547 
0548 bool Function::copyFrom(const Function &function)
0549 {
0550     bool changed = false;
0551     int i = 0;
0552 #define COPY_AND_CHECK(s)                                                                                                                                      \
0553     {                                                                                                                                                          \
0554         if (s != function.s) {                                                                                                                                 \
0555             s = function.s;                                                                                                                                    \
0556             changed = true;                                                                                                                                    \
0557         }                                                                                                                                                      \
0558     }                                                                                                                                                          \
0559     i++;
0560 
0561     COPY_AND_CHECK(f0); // 0
0562     if (type() == Cartesian) {
0563         COPY_AND_CHECK(f1); // 1
0564         COPY_AND_CHECK(f2); // 2
0565         COPY_AND_CHECK(f3); // 3
0566         COPY_AND_CHECK(integral); // 4
0567     }
0568     COPY_AND_CHECK(dmin); // 5,1
0569     COPY_AND_CHECK(dmax); // 6,2
0570     COPY_AND_CHECK(usecustomxmin); // 7,3
0571     COPY_AND_CHECK(usecustomxmax); // 8,4
0572     COPY_AND_CHECK(m_parameters); // 9,5
0573 
0574     // handle equations separately
0575     for (int i = 0; i < eq.size(); ++i) {
0576         if (*eq[i] != *function.eq[i]) {
0577             changed = true;
0578             *eq[i] = *function.eq[i];
0579         }
0580     }
0581 
0582     return changed;
0583 }
0584 
0585 QString Function::name() const
0586 {
0587     QString n = eq[0]->fstr();
0588     for (int i = 1; i < eq.size(); ++i)
0589         n += '\n' + eq[i]->fstr();
0590 
0591     return n;
0592 }
0593 
0594 PlotAppearance &Function::plotAppearance(PMode plot)
0595 {
0596     // NOTE: This function is identical to the const one, so changes to this should be applied to both
0597 
0598     switch (plot) {
0599     case Function::Derivative0:
0600         return f0;
0601     case Function::Derivative1:
0602         return f1;
0603     case Function::Derivative2:
0604         return f2;
0605     case Function::Derivative3:
0606         return f3;
0607     case Function::Integral:
0608         return integral;
0609     }
0610 
0611     qCritical() << "Unknown plot " << plot;
0612     return f0;
0613 }
0614 PlotAppearance Function::plotAppearance(PMode plot) const
0615 {
0616     // NOTE: This function is identical to the none-const one, so changes to this should be applied to both
0617 
0618     switch (plot) {
0619     case Function::Derivative0:
0620         return f0;
0621     case Function::Derivative1:
0622         return f1;
0623     case Function::Derivative2:
0624         return f2;
0625     case Function::Derivative3:
0626         return f3;
0627     case Function::Integral:
0628         return integral;
0629     }
0630 
0631     qCritical() << "Unknown plot " << plot;
0632     return f0;
0633 }
0634 
0635 bool Function::allPlotsAreHidden() const
0636 {
0637     return !f0.visible && !f1.visible && !f2.visible && !integral.visible;
0638 }
0639 
0640 QString Function::typeToString(Type type)
0641 {
0642     switch (type) {
0643     case Cartesian:
0644         return "cartesian";
0645 
0646     case Parametric:
0647         return "parametric";
0648 
0649     case Polar:
0650         return "polar";
0651 
0652     case Implicit:
0653         return "implicit";
0654 
0655     case Differential:
0656         return "differential";
0657     }
0658 
0659     qWarning() << "Unknown type " << type;
0660     return "unknown";
0661 }
0662 
0663 Function::Type Function::stringToType(const QString &type)
0664 {
0665     if (type == "cartesian")
0666         return Cartesian;
0667 
0668     if (type == "parametric")
0669         return Parametric;
0670 
0671     if (type == "polar")
0672         return Polar;
0673 
0674     if (type == "implicit")
0675         return Implicit;
0676 
0677     if (type == "differential")
0678         return Differential;
0679 
0680     qWarning() << "Unknown type " << type;
0681     return Cartesian;
0682 }
0683 
0684 QList<Plot> Function::plots(PlotCombinations combinations) const
0685 {
0686     QList<Plot> list;
0687 
0688     if (allPlotsAreHidden())
0689         return list;
0690 
0691     Plot plot;
0692     plot.setFunctionID(id());
0693     plot.plotNumberCount = m_parameters.useList ? m_parameters.list.size() + (m_parameters.useSlider ? 1 : 0) : 1;
0694 
0695     bool singlePlot = (!m_parameters.useList && !m_parameters.useSlider) || m_parameters.animating || (~combinations & DifferentParameters)
0696         || (!m_parameters.useSlider && m_parameters.useList && m_parameters.list.isEmpty());
0697 
0698     if (singlePlot) {
0699         if (m_parameters.animating)
0700             plot.parameter = Parameter(Parameter::Animated);
0701 
0702         list << plot;
0703     } else {
0704         int i = 0;
0705 
0706         if (m_parameters.useSlider) {
0707             Parameter param(Parameter::Slider);
0708             param.setSliderID(m_parameters.sliderID);
0709             plot.parameter = param;
0710             plot.plotNumber = i++;
0711             list << plot;
0712         }
0713 
0714         if (m_parameters.useList) {
0715             const int listsize = m_parameters.list.size();
0716             for (int pos = 0; pos < listsize; ++pos) {
0717                 Parameter param(Parameter::List);
0718                 param.setListPos(pos);
0719                 plot.parameter = param;
0720                 plot.plotNumber = i++;
0721                 list << plot;
0722             }
0723         }
0724     }
0725 
0726     // Copy each plot in the list for other variations
0727     if ((type() == Cartesian) && (combinations & DifferentDerivatives)) {
0728         QList<Plot> duplicated;
0729 
0730         for (PMode p = Derivative0; p <= Integral; p = PMode(p + 1)) {
0731             for (Plot plot : qAsConst(list)) {
0732                 if (!plotAppearance(p).visible)
0733                     continue;
0734                 plot.plotMode = p;
0735                 duplicated << plot;
0736             }
0737         }
0738 
0739         list = duplicated;
0740     }
0741 
0742     if ((type() == Differential) && (combinations & DifferentInitialStates)) {
0743         QList<Plot> duplicated;
0744 
0745         for (int i = 0; i < eq[0]->differentialStates.size(); ++i) {
0746             for (Plot plot : qAsConst(list)) {
0747                 plot.stateNumber = i;
0748                 duplicated << plot;
0749             }
0750         }
0751 
0752         list = duplicated;
0753     }
0754 
0755     if (combinations & DifferentPMSignatures) {
0756         int size = 0;
0757         for (Equation *equation : qAsConst(eq))
0758             size += equation->pmCount();
0759 
0760         unsigned max = unsigned(std::pow(2.0, (double)size));
0761         QVector<QVector<bool>> signatures(max);
0762 
0763         for (unsigned i = 0; i < max; ++i) {
0764             QVector<bool> sig(size);
0765 
0766             for (int j = 0; j < size; ++j)
0767                 sig[j] = i & (1 << j);
0768 
0769             signatures[i] = sig;
0770         }
0771 
0772         // Generate a plot for each signature in signatures
0773         QList<Plot> duplicated;
0774         for (const QVector<bool> &signature : qAsConst(signatures)) {
0775             int at = 0;
0776             QList<QVector<bool>> pmSignature;
0777 
0778             for (Equation *equation : qAsConst(eq)) {
0779                 int pmCount = equation->pmCount();
0780                 QVector<bool> sig(pmCount);
0781                 for (int i = 0; i < pmCount; ++i)
0782                     sig[i] = signature[i + at];
0783                 at += pmCount;
0784 
0785                 pmSignature << sig;
0786             }
0787 
0788             for (Plot plot : qAsConst(list)) {
0789                 plot.pmSignature = pmSignature;
0790                 duplicated << plot;
0791             }
0792         }
0793         list = duplicated;
0794     }
0795 
0796     return list;
0797 }
0798 
0799 void Function::addFunctionDependency(Function *function)
0800 {
0801     if (!function || m_dependencies.contains(function->id()))
0802         return;
0803 
0804     Q_ASSERT_X(!function->dependsOn(this), "addFunctionDependency", "circular dependency");
0805 
0806     m_dependencies << function->id();
0807 }
0808 
0809 bool Function::dependsOn(Function *function) const
0810 {
0811     if (!function)
0812         return false;
0813 
0814     if (m_dependencies.contains(function->id()))
0815         return true;
0816 
0817     for (int functionId : qAsConst(m_dependencies)) {
0818         Function *f = XParser::self()->functionWithID(functionId);
0819 
0820         if (f->dependsOn(function))
0821             return true;
0822     }
0823 
0824     return false;
0825 }
0826 // END class Function
0827 
0828 // BEGIN class ParameterSettings
0829 ParameterSettings::ParameterSettings()
0830 {
0831     animating = false;
0832     useSlider = false;
0833     sliderID = 0;
0834     useList = false;
0835 }
0836 
0837 bool ParameterSettings::operator==(const ParameterSettings &other) const
0838 {
0839     return (useSlider == other.useSlider) && (sliderID == other.sliderID) && (useList == other.useList) && (list == other.list);
0840 }
0841 // END class ParameterSettings
0842 
0843 // BEGIN class Parameter
0844 Parameter::Parameter(Type type)
0845     : m_type(type)
0846 {
0847     m_sliderID = -1;
0848     m_listPos = -1;
0849 }
0850 
0851 bool Parameter::operator==(const Parameter &other) const
0852 {
0853     return (type() == other.type()) && (listPos() == other.listPos()) && (sliderID() == other.sliderID());
0854 }
0855 // END class Parameter
0856 
0857 // BEGIN class Plot
0858 Plot::Plot()
0859 {
0860     stateNumber = -1;
0861     plotNumberCount = 1;
0862     plotNumber = 0;
0863     m_function = nullptr;
0864     m_functionID = -1;
0865     plotMode = Function::Derivative0;
0866 }
0867 
0868 bool Plot::operator==(const Plot &other) const
0869 {
0870     return (m_functionID == other.functionID()) && (plotMode == other.plotMode) && (parameter == other.parameter) && (stateNumber == other.stateNumber);
0871 }
0872 
0873 void Plot::setFunctionID(int id)
0874 {
0875     m_functionID = id;
0876     updateCached();
0877 }
0878 
0879 void Plot::updateCached()
0880 {
0881     m_function = XParser::self()->functionWithID(m_functionID);
0882 }
0883 
0884 QString Plot::name() const
0885 {
0886     if (!m_function)
0887         return QString();
0888 
0889     QString n = m_function->name();
0890 
0891     if (m_function->eq[0]->usesParameter())
0892         n += QString("\n%1 = %2").arg(m_function->eq[0]->parameterName()).arg(Parser::number(parameterValue()));
0893 
0894     if (plotMode == Function::Derivative1)
0895         n = n.section('=', 0, 0).replace('(', "\'(");
0896 
0897     if (plotMode == Function::Derivative2)
0898         n = n.section('=', 0, 0).replace('(', "\'\'(");
0899 
0900     if (plotMode == Function::Integral) {
0901         QString functionName = n.section('=', 0, 0);
0902         n = QChar(0x222B) + ' ' + functionName + 'd' + functionName.section('(', 1, 1).remove(')').section(',', 0, 0);
0903     }
0904 
0905     return n;
0906 }
0907 
0908 void Plot::updateFunction() const
0909 {
0910     if (!m_function)
0911         return;
0912 
0913     // Update the plus-minus signature
0914     assert(pmSignature.size() <= m_function->eq.size());
0915     for (int i = 0; i < pmSignature.size(); ++i)
0916         m_function->eq[i]->setPMSignature(pmSignature[i]);
0917 
0918     if (parameter.type() != Parameter::Animated)
0919         m_function->setParameter(parameterValue());
0920 }
0921 
0922 double Plot::parameterValue() const
0923 {
0924     switch (parameter.type()) {
0925     case Parameter::Unknown:
0926         return 0;
0927 
0928     case Parameter::Slider: {
0929         KSliderWindow *sw = View::self()->m_sliderWindow;
0930 
0931         if (!sw) {
0932             // Slider window isn't open. Ask View to open it
0933             View::self()->updateSliders();
0934 
0935             // It should now be open
0936             sw = View::self()->m_sliderWindow;
0937             assert(sw);
0938         }
0939 
0940         return sw->value(parameter.sliderID());
0941     }
0942 
0943     case Parameter::List: {
0944         if ((parameter.listPos() >= 0) && (parameter.listPos() < m_function->m_parameters.list.size()))
0945             return m_function->m_parameters.list[parameter.listPos()].value();
0946         return 0;
0947     }
0948 
0949     case Parameter::Animated: {
0950         qWarning() << "Shouldn't use this function for animated parameter!\n";
0951         return 0;
0952     }
0953     }
0954 
0955     return 0;
0956 }
0957 
0958 void Plot::differentiate()
0959 {
0960     switch (plotMode) {
0961     case Function::Integral:
0962         plotMode = Function::Derivative0;
0963         break;
0964 
0965     case Function::Derivative0:
0966         plotMode = Function::Derivative1;
0967         break;
0968 
0969     case Function::Derivative1:
0970         plotMode = Function::Derivative2;
0971         break;
0972 
0973     case Function::Derivative2:
0974         plotMode = Function::Derivative3;
0975         break;
0976 
0977     case Function::Derivative3:
0978         qWarning() << "Can't handle this yet!\n";
0979         break;
0980     }
0981 }
0982 
0983 void Plot::integrate()
0984 {
0985     switch (plotMode) {
0986     case Function::Integral:
0987         qWarning() << "Can't handle this yet!\n";
0988         break;
0989 
0990     case Function::Derivative0:
0991         plotMode = Function::Integral;
0992         break;
0993 
0994     case Function::Derivative1:
0995         plotMode = Function::Derivative0;
0996         break;
0997 
0998     case Function::Derivative2:
0999         plotMode = Function::Derivative1;
1000         break;
1001 
1002     case Function::Derivative3:
1003         plotMode = Function::Derivative2;
1004         break;
1005     }
1006 }
1007 
1008 QColor Plot::color() const
1009 {
1010     Function *f = function();
1011     assert(f); // Shouldn't call color without a function
1012     PlotAppearance appearance = f->plotAppearance(plotMode);
1013 
1014     if ((plotNumberCount <= 1) || !appearance.useGradient)
1015         return appearance.color;
1016 
1017     // Is a gradient
1018 
1019     int x = plotNumber;
1020     int l = plotNumberCount;
1021 
1022     QLinearGradient lg(0, 0, l - 1, 0);
1023     lg.setStops(appearance.gradient.stops());
1024     QImage im(l, 1, QImage::Format_RGB32);
1025     QPainter p(&im);
1026     p.setPen(QPen(lg, 1));
1027     p.drawLine(0, 0, l, 0);
1028     return im.pixel(x, 0);
1029 }
1030 
1031 int Plot::derivativeNumber() const
1032 {
1033     switch (plotMode) {
1034     case Function::Integral:
1035         return -1;
1036     case Function::Derivative0:
1037         return 0;
1038     case Function::Derivative1:
1039         return 1;
1040     case Function::Derivative2:
1041         return 2;
1042     case Function::Derivative3:
1043         return 3;
1044     }
1045 
1046     qWarning() << "Unknown derivative number.\n";
1047     return 0;
1048 }
1049 
1050 DifferentialState *Plot::state() const
1051 {
1052     if (!function() || (stateNumber < 0))
1053         return nullptr;
1054 
1055     if (function()->eq[0]->differentialStates.size() <= stateNumber)
1056         return nullptr;
1057 
1058     return &function()->eq[0]->differentialStates[stateNumber];
1059 }
1060 // END class Plot