File indexing completed on 2024-04-14 03:40:51

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, 2007 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 "xparser.h"
0015 
0016 #include <kmplot/config-kmplot.h>
0017 
0018 // local includes
0019 #include "maindlg.h"
0020 #include "parseradaptor.h"
0021 
0022 // KDE includes
0023 #include <KLocalizedString>
0024 #include <KMessageBox>
0025 
0026 #include <QList>
0027 
0028 #include <assert.h>
0029 #include <cmath>
0030 #ifdef HAVE_IEEEFP_H
0031 #include <ieeefp.h>
0032 #endif
0033 
0034 XParser *XParser::m_self = nullptr;
0035 
0036 XParser *XParser::self()
0037 {
0038     if (!m_self)
0039         m_self = new XParser();
0040 
0041     return m_self;
0042 }
0043 
0044 XParser::XParser()
0045 {
0046     differentialFinite = true;
0047     differentialDiverge = 0;
0048 
0049     new ParserAdaptor(this);
0050     QDBusConnection::sessionBus().registerObject(QStringLiteral("/parser"), this);
0051 }
0052 
0053 XParser::~XParser()
0054 {
0055 }
0056 
0057 bool XParser::getext(Function *item, const QString &fstr)
0058 {
0059     bool errflg = false;
0060     int p1, p2, p3, pe;
0061     QString tstr;
0062     pe = fstr.length();
0063     if (fstr.indexOf('N') != -1)
0064         item->plotAppearance(Function::Derivative0).visible = false;
0065     else {
0066         if (fstr.indexOf(QLatin1String("A1")) != -1)
0067             item->plotAppearance(Function::Derivative1).visible = true;
0068         if (fstr.indexOf(QLatin1String("A2")) != -1)
0069             item->plotAppearance(Function::Derivative2).visible = true;
0070     }
0071     switch (fstr[0].unicode()) {
0072     case 'x':
0073     case 'y':
0074     case 'r':
0075         item->plotAppearance(Function::Derivative1).visible = item->plotAppearance(Function::Derivative2).visible = false;
0076     }
0077 
0078     p1 = fstr.indexOf(QLatin1String("D["));
0079     if (p1 != -1) {
0080         p1 += 2;
0081         const QString str = fstr.mid(p1, pe - p1);
0082         p2 = str.indexOf(',');
0083         p3 = str.indexOf(']');
0084         if (p2 > 0 && p2 < p3) {
0085             tstr = str.left(p2);
0086             errflg |= !item->dmin.updateExpression(tstr);
0087             tstr = str.mid(p2 + 1, p3 - p2 - 1);
0088             errflg |= !item->dmax.updateExpression(tstr);
0089             if (item->dmin.value() > item->dmax.value())
0090                 errflg = true;
0091         } else
0092             errflg = true;
0093     }
0094     p1 = fstr.indexOf(QLatin1String("P["));
0095     if (p1 != -1) {
0096         int i = 0;
0097         p1 += 2;
0098         QString str = fstr.mid(p1, 1000);
0099         p3 = str.indexOf(']');
0100         do {
0101             p2 = str.indexOf(',');
0102             if (p2 == -1 || p2 > p3)
0103                 p2 = p3;
0104             tstr = str.left(p2++);
0105             str = str.mid(p2, 1000);
0106             Value value;
0107             if (!value.updateExpression(tstr)) {
0108                 errflg = true;
0109                 break;
0110             }
0111             item->m_parameters.list.append(value);
0112             p3 -= p2;
0113         } while (p3 > 0 && i < 10);
0114     }
0115 
0116     if (errflg) {
0117         KMessageBox::error(nullptr, i18n("Error in extension."));
0118         return false;
0119     } else
0120         return true;
0121 }
0122 
0123 double XParser::derivative(int n, Equation *eq, DifferentialState *state, double x, double h)
0124 {
0125     if (n < -1) {
0126         qCritical() << "Can't handle derivative < -1\n";
0127         return 0.0;
0128     }
0129 
0130     switch (n) {
0131     case -1:
0132         return differential(eq, &eq->differentialStates[0], x, h);
0133 
0134     case 0:
0135         if (state)
0136             return differential(eq, state, x, h);
0137         else
0138             return fkt(eq, x);
0139 
0140     case 1:
0141         if (state)
0142             return (differential(eq, state, x + (h / 2), h) - differential(eq, state, x - (h / 2), h)) / h;
0143         else
0144             return (fkt(eq, x + (h / 2)) - fkt(eq, x - (h / 2))) / h;
0145 
0146     default:
0147         return (derivative(n - 1, eq, state, x + (h / 2), (h / 4)) - derivative(n - 1, eq, state, x - (h / 2), (h / 4))) / h;
0148     }
0149 }
0150 
0151 double XParser::partialDerivative(int n1, int n2, Equation *eq, DifferentialState *state, double x, double y, double h1, double h2)
0152 {
0153     if (n1 < 0 || n2 < 0) {
0154         qCritical() << "Can't handle derivative < 0\n";
0155         return 0.0;
0156     }
0157 
0158     if (n1 > 0)
0159         return (partialDerivative(n1 - 1, n2, eq, state, x + (h1 / 2), y, (h1 / 4), h2)
0160                 - partialDerivative(n1 - 1, n2, eq, state, x - (h1 / 2), y, (h1 / 4), h2))
0161             / h1;
0162 
0163     Function *f = eq->parent();
0164     f->m_implicitMode = Function::FixedX;
0165     f->x = x;
0166 
0167     return derivative(n2, eq, state, y, h2);
0168 }
0169 
0170 QString XParser::findFunctionName(const QString &preferredName, int id, const QStringList &neededPatterns)
0171 {
0172     // The position of the character attempting to replace
0173     int pos = preferredName.length() - 1;
0174 
0175     QString name = preferredName;
0176 
0177     for (;; ++pos) {
0178         for (QChar lastChar = 'f'; lastChar < 'x'; ++lastChar.unicode()) {
0179             bool ok = true;
0180             name[pos] = lastChar;
0181 
0182             for (Function *it : qAsConst(m_ufkt)) {
0183                 if (int(it->id()) == id)
0184                     continue;
0185 
0186                 for (Equation *eq : qAsConst(it->eq)) {
0187                     for (const QString &pattern : neededPatterns) {
0188                         if (eq->name() == pattern.arg(name))
0189                             ok = false;
0190                     }
0191                 }
0192 
0193                 if (!ok)
0194                     break;
0195             }
0196             if (!ok)
0197                 continue;
0198 
0199             // Found a free name :)
0200             return name;
0201         }
0202         name[pos] = 'f';
0203         name.append('f');
0204     }
0205 }
0206 
0207 void XParser::fixFunctionName(QString &str, Equation::Type const type, int const id)
0208 {
0209     int p1 = str.indexOf('(');
0210     int p2 = str.indexOf(')');
0211     int p3 = str.indexOf('=');
0212 
0213     if (p1 < 0)
0214         return;
0215 
0216     for (int i = p2 + 1; i < p3; ++i) {
0217         if (!str.at(i).isSpace())
0218             return;
0219     }
0220 
0221     QString const fname = str.left(p1);
0222     for (Function *it : qAsConst(m_ufkt)) {
0223         if (int(it->id()) == id)
0224             continue;
0225 
0226         for (Equation *eq : qAsConst(it->eq)) {
0227             if (eq->name() != fname)
0228                 continue;
0229 
0230             str = str.mid(p1, str.length() - 1);
0231             QString function_name;
0232             if (type == Equation::ParametricX)
0233                 function_name = 'x';
0234             else if (type == Equation::ParametricY)
0235                 function_name = 'y';
0236             else
0237                 function_name = 'f';
0238             function_name = findFunctionName(function_name, id);
0239             str.prepend(function_name);
0240             return;
0241         }
0242     }
0243 }
0244 
0245 Vector XParser::rk4_f(int order, Equation *eq, double x, const Vector &y)
0246 {
0247     bool useParameter = eq->usesParameter();
0248 
0249     m_result.resize(order);
0250     m_arg.resize(order + 1 + (useParameter ? 1 : 0));
0251 
0252     m_arg[0] = x;
0253 
0254     if (useParameter)
0255         m_arg[1] = eq->parent()->k;
0256 
0257     memcpy(m_arg.data() + 1 + (useParameter ? 1 : 0), y.data(), order * sizeof(double));
0258     memcpy(m_result.data(), y.data() + 1, (order - 1) * sizeof(double));
0259 
0260     m_result[order - 1] = XParser::fkt(eq, m_arg);
0261 
0262     return m_result;
0263 }
0264 
0265 double XParser::differential(Equation *eq, DifferentialState *state, double x_target, double max_dx)
0266 {
0267     differentialFinite = true;
0268 
0269     if (eq->order() < 1) {
0270         qWarning() << "Zero order!\n";
0271         return 0;
0272     }
0273 
0274     max_dx = qAbs(max_dx);
0275     assert(max_dx > 0); // in case anyone tries to pass us a zero h
0276 
0277     // the difference between h and dx is that h is only used as a hint for the
0278     // stepwidth; dx is made similar to h in size, yet tiles the gap between x
0279     // and the previous x perfectly
0280 
0281     // see if the initial integral point in the function is closer to our
0282     // required x value than the last one (or the last point is invalid)
0283     if (qAbs(state->x0.value() - x_target) < qAbs(state->x - x_target))
0284         state->resetToInitial();
0285 
0286     int order = eq->order();
0287 
0288     m_k1.resize(order);
0289     m_k2.resize(order);
0290     m_k3.resize(order);
0291     m_k4.resize(order);
0292     m_y_temp.resize(order);
0293 
0294     double x = state->x;
0295     m_y = state->y;
0296     if (x_target == x)
0297         return m_y[0];
0298 
0299     int intervals = int(qAbs(x_target - x) / max_dx + 1);
0300     double dx = (x_target - x) / double(intervals);
0301 
0302     for (int i = 0; i < intervals; ++i) {
0303         // Update differentialDiverge before y possible becomes infinite
0304         differentialDiverge = x;
0305 
0306         x = state->x + i * dx;
0307 
0308         m_k1 = rk4_f(order, eq, x, m_y);
0309 
0310         m_y_temp.combine(m_y, dx / 2, m_k1);
0311         m_k2 = rk4_f(order, eq, x + dx / 2, m_y_temp);
0312 
0313         m_y_temp.combine(m_y, dx / 2, m_k2);
0314         m_k3 = rk4_f(order, eq, x + dx / 2, m_y_temp);
0315 
0316         m_y_temp.combine(m_y, dx, m_k3);
0317         m_k4 = rk4_f(order, eq, x + dx, m_y_temp);
0318 
0319         m_y.addRK4(dx, m_k1, m_k2, m_k3, m_k4);
0320 
0321         // The condition on the total accumulated error (O(dx^5)) should not be violated for rapidly increasing functions, e.g. e^x^2
0322         if (!std::isfinite(m_y[0]) || qAbs((state->y[0] - m_y[0]) * dx * dx) > 1) {
0323             differentialFinite = false;
0324             state->resetToInitial();
0325             return 1e200 * ((m_y[0] > 0) - (m_y[0] < 0));
0326         }
0327     }
0328 
0329     state->x = x + dx;
0330     state->y = m_y;
0331 
0332     return m_y[0];
0333 }
0334 
0335 QColor XParser::defaultColor(int function)
0336 {
0337     switch (function % 10) {
0338     case 0:
0339         return Settings::color0();
0340     case 1:
0341         return Settings::color1();
0342     case 2:
0343         return Settings::color2();
0344     case 3:
0345         return Settings::color3();
0346     case 4:
0347         return Settings::color4();
0348     case 5:
0349         return Settings::color5();
0350     case 6:
0351         return Settings::color6();
0352     case 7:
0353         return Settings::color7();
0354     case 8:
0355         return Settings::color8();
0356     case 9:
0357         return Settings::color9();
0358     }
0359 
0360     assert(!"Should not happen - XParser::defaultColor");
0361     return QColor();
0362 }
0363 
0364 QStringList XParser::listFunctionNames()
0365 {
0366     return userFunctions();
0367 }
0368 
0369 bool XParser::functionFVisible(uint id)
0370 {
0371     return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance(Function::Derivative0).visible : false;
0372 }
0373 bool XParser::functionF1Visible(uint id)
0374 {
0375     return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance(Function::Derivative1).visible : false;
0376 }
0377 bool XParser::functionF2Visible(uint id)
0378 {
0379     return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance(Function::Derivative2).visible : false;
0380 }
0381 bool XParser::functionIntVisible(uint id)
0382 {
0383     return m_ufkt.contains(id) ? m_ufkt[id]->plotAppearance(Function::Integral).visible : false;
0384 }
0385 
0386 bool XParser::setFunctionFVisible(uint id, bool visible)
0387 {
0388     if (!m_ufkt.contains(id))
0389         return false;
0390     m_ufkt[id]->plotAppearance(Function::Derivative0).visible = visible;
0391     MainDlg::self()->requestSaveCurrentState();
0392     return true;
0393 }
0394 bool XParser::setFunctionF1Visible(uint id, bool visible)
0395 {
0396     if (!m_ufkt.contains(id))
0397         return false;
0398     m_ufkt[id]->plotAppearance(Function::Derivative1).visible = visible;
0399     MainDlg::self()->requestSaveCurrentState();
0400     return true;
0401 }
0402 bool XParser::setFunctionF2Visible(uint id, bool visible)
0403 {
0404     if (!m_ufkt.contains(id))
0405         return false;
0406     m_ufkt[id]->plotAppearance(Function::Derivative2).visible = visible;
0407     MainDlg::self()->requestSaveCurrentState();
0408     return true;
0409 }
0410 bool XParser::setFunctionIntVisible(uint id, bool visible)
0411 {
0412     if (!m_ufkt.contains(id))
0413         return false;
0414     m_ufkt[id]->plotAppearance(Function::Integral).visible = visible;
0415     MainDlg::self()->requestSaveCurrentState();
0416     return true;
0417 }
0418 
0419 QString XParser::functionStr(uint id, uint eq)
0420 {
0421     if (!m_ufkt.contains(id) || (eq >= 2))
0422         return QLatin1String("");
0423     return m_ufkt[id]->eq[eq]->fstr();
0424 }
0425 
0426 QColor XParser::functionFColor(uint id)
0427 {
0428     if (!m_ufkt.contains(id))
0429         return QColor();
0430     return QColor(m_ufkt[id]->plotAppearance(Function::Derivative0).color);
0431 }
0432 QColor XParser::functionF1Color(uint id)
0433 {
0434     if (!m_ufkt.contains(id))
0435         return QColor();
0436     return QColor(m_ufkt[id]->plotAppearance(Function::Derivative1).color);
0437 }
0438 QColor XParser::functionF2Color(uint id)
0439 {
0440     if (!m_ufkt.contains(id))
0441         return QColor();
0442     return QColor(m_ufkt[id]->plotAppearance(Function::Derivative2).color);
0443 }
0444 QColor XParser::functionIntColor(uint id)
0445 {
0446     if (!m_ufkt.contains(id))
0447         return QColor();
0448     return QColor(m_ufkt[id]->plotAppearance(Function::Integral).color);
0449 }
0450 bool XParser::setFunctionFColor(uint id, const QColor &color)
0451 {
0452     if (!m_ufkt.contains(id))
0453         return false;
0454     m_ufkt[id]->plotAppearance(Function::Derivative0).color = color;
0455     MainDlg::self()->requestSaveCurrentState();
0456     return true;
0457 }
0458 bool XParser::setFunctionF1Color(uint id, const QColor &color)
0459 {
0460     if (!m_ufkt.contains(id))
0461         return false;
0462     m_ufkt[id]->plotAppearance(Function::Derivative1).color = color;
0463     MainDlg::self()->requestSaveCurrentState();
0464     return true;
0465 }
0466 bool XParser::setFunctionF2Color(uint id, const QColor &color)
0467 {
0468     if (!m_ufkt.contains(id))
0469         return false;
0470     m_ufkt[id]->plotAppearance(Function::Derivative2).color = color;
0471     MainDlg::self()->requestSaveCurrentState();
0472     return true;
0473 }
0474 bool XParser::setFunctionIntColor(uint id, const QColor &color)
0475 {
0476     if (!m_ufkt.contains(id))
0477         return false;
0478     m_ufkt[id]->plotAppearance(Function::Integral).color = color;
0479     MainDlg::self()->requestSaveCurrentState();
0480     return true;
0481 }
0482 
0483 double XParser::functionFLineWidth(uint id)
0484 {
0485     if (!m_ufkt.contains(id))
0486         return 0;
0487     return m_ufkt[id]->plotAppearance(Function::Derivative0).lineWidth;
0488 }
0489 double XParser::functionF1LineWidth(uint id)
0490 {
0491     if (!m_ufkt.contains(id))
0492         return 0;
0493     return m_ufkt[id]->plotAppearance(Function::Derivative1).lineWidth;
0494 }
0495 double XParser::functionF2LineWidth(uint id)
0496 {
0497     if (!m_ufkt.contains(id))
0498         return 0;
0499     return m_ufkt[id]->plotAppearance(Function::Derivative2).lineWidth;
0500 }
0501 double XParser::functionIntLineWidth(uint id)
0502 {
0503     if (!m_ufkt.contains(id))
0504         return 0;
0505     return m_ufkt[id]->plotAppearance(Function::Integral).lineWidth;
0506 }
0507 bool XParser::setFunctionFLineWidth(uint id, double linewidth)
0508 {
0509     if (!m_ufkt.contains(id))
0510         return false;
0511     m_ufkt[id]->plotAppearance(Function::Derivative0).lineWidth = linewidth;
0512     MainDlg::self()->requestSaveCurrentState();
0513     return true;
0514 }
0515 bool XParser::setFunctionF1LineWidth(uint id, double linewidth)
0516 {
0517     if (!m_ufkt.contains(id))
0518         return false;
0519     m_ufkt[id]->plotAppearance(Function::Derivative1).lineWidth = linewidth;
0520     MainDlg::self()->requestSaveCurrentState();
0521     return true;
0522 }
0523 bool XParser::setFunctionF2LineWidth(uint id, double linewidth)
0524 {
0525     if (!m_ufkt.contains(id))
0526         return false;
0527     m_ufkt[id]->plotAppearance(Function::Derivative2).lineWidth = linewidth;
0528     MainDlg::self()->requestSaveCurrentState();
0529     return true;
0530 }
0531 bool XParser::setFunctionIntLineWidth(uint id, double linewidth)
0532 {
0533     if (!m_ufkt.contains(id))
0534         return false;
0535     m_ufkt[id]->plotAppearance(Function::Integral).lineWidth = linewidth;
0536     MainDlg::self()->requestSaveCurrentState();
0537     return true;
0538 }
0539 
0540 QString XParser::functionMinValue(uint id)
0541 {
0542     if (!m_ufkt.contains(id))
0543         return nullptr;
0544     return m_ufkt[id]->dmin.expression();
0545 }
0546 
0547 bool XParser::setFunctionMinValue(uint id, const QString &min)
0548 {
0549     if (!m_ufkt.contains(id))
0550         return false;
0551     m_ufkt[id]->dmin.expression() = min;
0552     MainDlg::self()->requestSaveCurrentState();
0553     return true;
0554 }
0555 
0556 QString XParser::functionMaxValue(uint id)
0557 {
0558     if (!m_ufkt.contains(id))
0559         return nullptr;
0560     return m_ufkt[id]->dmax.expression();
0561 }
0562 
0563 bool XParser::setFunctionMaxValue(uint id, const QString &max)
0564 {
0565     if (!m_ufkt.contains(id))
0566         return false;
0567     m_ufkt[id]->dmax.expression() = max;
0568     MainDlg::self()->requestSaveCurrentState();
0569     return true;
0570 }
0571 
0572 bool XParser::setFunctionStartValue(uint id, const QString &x, const QString &y)
0573 {
0574     if (!m_ufkt.contains(id))
0575         return false;
0576     DifferentialState *state = &m_ufkt[id]->eq[0]->differentialStates[0];
0577     state->x0.updateExpression(x);
0578     state->y0[0].updateExpression(y);
0579     MainDlg::self()->requestSaveCurrentState();
0580     return true;
0581 }
0582 
0583 QString XParser::functionStartXValue(uint id)
0584 {
0585     if (!m_ufkt.contains(id))
0586         return nullptr;
0587     DifferentialState *state = &m_ufkt[id]->eq[0]->differentialStates[0];
0588     return state->x0.expression();
0589 }
0590 
0591 QString XParser::functionStartYValue(uint id)
0592 {
0593     if (!m_ufkt.contains(id))
0594         return nullptr;
0595     DifferentialState *state = &m_ufkt[id]->eq[0]->differentialStates[0];
0596     return state->y0[0].expression();
0597 }
0598 
0599 QStringList XParser::functionParameterList(uint id)
0600 {
0601     if (!m_ufkt.contains(id))
0602         return QStringList();
0603     Function *item = m_ufkt[id];
0604     QStringList str_parameter;
0605     for (const Value &it : qAsConst(item->m_parameters.list))
0606         str_parameter << it.expression();
0607     return str_parameter;
0608 }
0609 bool XParser::functionAddParameter(uint id, const QString &new_parameter)
0610 {
0611     if (!m_ufkt.contains(id))
0612         return false;
0613     Function *tmp_ufkt = m_ufkt[id];
0614 
0615     // check if the parameter already exists
0616     for (const Value &it : qAsConst(tmp_ufkt->m_parameters.list)) {
0617         if (it.expression() == new_parameter)
0618             return false;
0619     }
0620 
0621     Value value;
0622     if (!value.updateExpression(new_parameter))
0623         return false;
0624     tmp_ufkt->m_parameters.list.append(value);
0625     MainDlg::self()->requestSaveCurrentState();
0626     return true;
0627 }
0628 bool XParser::functionRemoveParameter(uint id, const QString &remove_parameter)
0629 {
0630     if (!m_ufkt.contains(id))
0631         return false;
0632     Function *tmp_ufkt = m_ufkt[id];
0633 
0634     bool found = false;
0635     QList<Value>::iterator it;
0636     for (it = tmp_ufkt->m_parameters.list.begin(); it != tmp_ufkt->m_parameters.list.end(); ++it) {
0637         if ((*it).expression() == remove_parameter) // check if the parameter already exists
0638         {
0639             found = true;
0640             break;
0641         }
0642     }
0643     if (!found)
0644         return false;
0645     tmp_ufkt->m_parameters.list.erase(it);
0646     MainDlg::self()->requestSaveCurrentState();
0647     return true;
0648 }
0649 int XParser::addFunction(const QString &f_str0, const QString &_f_str1)
0650 {
0651     QString added_function(f_str0);
0652     QString f_str1(_f_str1);
0653     int const pos = added_function.indexOf(';');
0654     if (pos != -1)
0655         added_function = added_function.left(pos);
0656 
0657     fixFunctionName(added_function);
0658     if (!f_str1.isEmpty())
0659         fixFunctionName(f_str1);
0660 
0661     Function::Type type;
0662 
0663     if (!f_str1.isEmpty())
0664         type = Function::Parametric;
0665     else if (f_str0.count('=') > 1)
0666         type = Function::Implicit;
0667     else
0668         type = (added_function[0] == 'r') ? Function::Polar : Function::Cartesian;
0669 
0670     int const id = Parser::addFunction(added_function, f_str1, type);
0671     if (id == -1)
0672         return -1;
0673     Function *tmp_ufkt = m_ufkt[id];
0674     if (pos != -1 && !getext(tmp_ufkt, f_str0)) {
0675         Parser::removeFunction(tmp_ufkt);
0676         return -1;
0677     }
0678     MainDlg::self()->requestSaveCurrentState();
0679     return id;
0680 }
0681 
0682 bool XParser::addFunction(const QString &fstr_const0,
0683                           const QString &fstr_const1,
0684                           bool f_mode,
0685                           bool f1_mode,
0686                           bool f2_mode,
0687                           bool integral_mode,
0688                           double linewidth,
0689                           double f1_linewidth,
0690                           double f2_linewidth,
0691                           double integral_linewidth,
0692                           const QString &str_dmin,
0693                           const QString &str_dmax,
0694                           const QString &str_startx,
0695                           const QString &str_starty,
0696                           double integral_precision,
0697                           const QColor &color,
0698                           const QColor &f1_color,
0699                           const QColor &f2_color,
0700                           const QColor &integral_color,
0701                           const QStringList &str_parameter,
0702                           int use_slider)
0703 {
0704     QString fstr[2] = {fstr_const0, fstr_const1};
0705     Function::Type type = Function::Cartesian;
0706     for (unsigned i = 0; i < 2; ++i) {
0707         if (fstr[i].isEmpty())
0708             continue;
0709 
0710         switch (fstr[i][0].unicode()) {
0711         case 'r': {
0712             fixFunctionName(fstr[i], Equation::Polar);
0713             type = Function::Polar;
0714             break;
0715         }
0716         case 'x':
0717             fixFunctionName(fstr[i], Equation::ParametricX);
0718             type = Function::Parametric;
0719             break;
0720         case 'y':
0721             fixFunctionName(fstr[i], Equation::ParametricY);
0722             type = Function::Parametric;
0723             break;
0724         default:
0725             fixFunctionName(fstr[i], Equation::Cartesian);
0726             type = Function::Cartesian;
0727             break;
0728         }
0729     }
0730 
0731     int const id = Parser::addFunction(fstr[0], fstr[1], type);
0732     if (id == -1)
0733         return false;
0734     Function *added_function = m_ufkt[id];
0735 
0736     PlotAppearance appearance;
0737 
0738     // f0
0739     appearance.visible = f_mode;
0740     appearance.color = color;
0741     appearance.lineWidth = linewidth;
0742     added_function->plotAppearance(Function::Derivative0) = appearance;
0743 
0744     // f1
0745     appearance.visible = f1_mode;
0746     appearance.color = f1_color;
0747     appearance.lineWidth = f1_linewidth;
0748     added_function->plotAppearance(Function::Derivative1) = appearance;
0749 
0750     // f2
0751     appearance.visible = f2_mode;
0752     appearance.color = f2_color;
0753     appearance.lineWidth = f2_linewidth;
0754     added_function->plotAppearance(Function::Derivative2) = appearance;
0755 
0756     // integral
0757     appearance.visible = integral_mode;
0758     appearance.color = integral_color;
0759     appearance.lineWidth = integral_linewidth;
0760     added_function->plotAppearance(Function::Integral) = appearance;
0761 
0762     added_function->dmin.updateExpression(str_dmin);
0763     added_function->usecustomxmin = !str_dmin.isEmpty();
0764 
0765     added_function->dmax.updateExpression(str_dmax);
0766     added_function->usecustomxmax = !str_dmax.isEmpty();
0767 
0768     DifferentialState *state = &added_function->eq[0]->differentialStates[0];
0769     state->x0.updateExpression(str_startx);
0770     state->y0[0].updateExpression(str_starty);
0771 
0772     added_function->eq[0]->differentialStates.setStep(Value(integral_precision));
0773 
0774     added_function->m_parameters.sliderID = use_slider;
0775     for (QStringList::ConstIterator it = str_parameter.begin(); it != str_parameter.end(); ++it) {
0776         added_function->m_parameters.list.append(*it);
0777     }
0778     MainDlg::self()->requestSaveCurrentState();
0779     return true;
0780 }
0781 
0782 bool XParser::setFunctionExpression(uint id, uint eq, const QString &f_str)
0783 {
0784     Function *tmp_ufkt = functionWithID(id);
0785     if (!tmp_ufkt)
0786         return false;
0787     QString const old_fstr = tmp_ufkt->eq[eq]->fstr();
0788     QString const fstr_begin = tmp_ufkt->eq[eq]->fstr().left(tmp_ufkt->eq[eq]->fstr().indexOf('=') + 1);
0789 
0790     return tmp_ufkt->eq[eq]->setFstr(fstr_begin + f_str);
0791 }