Warning, file /education/kmplot/kmplot/function.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 = 0; 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 = 0; 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 0; 1054 1055 if (function()->eq[0]->differentialStates.size() <= stateNumber) 1056 return 0; 1057 1058 return &function()->eq[0]->differentialStates[stateNumber]; 1059 } 1060 // END class Plot