File indexing completed on 2024-05-05 03:41:15

0001 /*************************************************************************************
0002  *  Copyright (C) 2007-2009 by Aleix Pol <aleixpol@kde.org>                          *
0003  *  Copyright (C) 2010-2012 by Percy Camilo T. Aucahuasi <percy.camilo.ta@gmail.com> *
0004  *                                                                                   *
0005  *  This program is free software; you can redistribute it and/or                    *
0006  *  modify it under the terms of the GNU General Public License                      *
0007  *  as published by the Free Software Foundation; either version 2                   *
0008  *  of the License, or (at your option) any later version.                           *
0009  *                                                                                   *
0010  *  This program is distributed in the hope that it will be useful,                  *
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
0013  *  GNU General Public License for more details.                                     *
0014  *                                                                                   *
0015  *  You should have received a copy of the GNU General Public License                *
0016  *  along with this program; if not, write to the Free Software                      *
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
0018  *************************************************************************************/
0019 
0020 #include "private/abstractplanecurve.h"
0021 #include "private/functiongraphfactory.h"
0022 #include <private/utils/mathutils.h>
0023 
0024 #include <QRectF>
0025 #include <QLineF>
0026 #include "analitza/value.h"
0027 #include "analitza/variable.h"
0028 
0029 using namespace Analitza;
0030 
0031 class FunctionCartesian : public AbstractPlaneCurve
0032 {
0033     public:
0034         FunctionCartesian(const Analitza::Expression &functionExpression, const QSharedPointer<Analitza::Variables>& variables)
0035             : AbstractPlaneCurve(functionExpression, variables) { }
0036 
0037         void update(const QRectF& viewport) override;
0038         QPair<QPointF, QString> image(const QPointF &mousepos) override;
0039         QLineF tangent(const QPointF &mousepos) override ;
0040         Analitza::Cn* arg() { return AbstractPlaneCurve::arg(parameters().at(0)); }
0041 
0042     protected:
0043         void initDerivative() {
0044             if(!analyzer->isCorrect())
0045                 return;
0046             
0047             m_deriv = analyzer->derivative(parameters().at(0));
0048             if(!analyzer->isCorrect() || !m_deriv.isCorrect()) {
0049                 m_deriv.clear();
0050                 analyzer->flushErrors();
0051             }
0052         }
0053         void optimizeJump();
0054         void calculateValues(double, double);
0055         Analitza::Expression m_deriv;
0056 };
0057 
0058 ///Functions where the x is bounding. like x->sin(x)
0059 class FunctionY : public FunctionCartesian
0060 {
0061 public:
0062     FunctionY(const Analitza::Expression &functionExpression, const QSharedPointer<Analitza::Variables>& variables)
0063         : FunctionCartesian(functionExpression, variables) { initDerivative(); }
0064     TYPE_NAME(QT_TRANSLATE_NOOP("Function type", "Plane Curve F(y)"))
0065     EXPRESSION_TYPE(Analitza::ExpressionType(Analitza::ExpressionType::Lambda).addParameter(
0066                    Analitza::ExpressionType(Analitza::ExpressionType::Value)).addParameter(
0067                    Analitza::ExpressionType(Analitza::ExpressionType::Value)))
0068     COORDDINATE_SYSTEM(Cartesian)
0069     PARAMETERS(QStringList(QStringLiteral("x")))
0070     ICON_NAME(QStringLiteral("newfunction"))
0071     EXAMPLES(QStringList(QStringLiteral("x")) << QStringLiteral("x*x") << QStringLiteral("x+4"))
0072 };
0073 
0074 ///Functions where the y is bounding. like y->sin(y). FunctionY mirrored
0075 class FunctionX : public FunctionCartesian
0076 {
0077 public:
0078     FunctionX(const Analitza::Expression &functionExpression, const QSharedPointer<Analitza::Variables>& variables)
0079         : FunctionCartesian(functionExpression, variables) { initDerivative(); }
0080     TYPE_NAME(QT_TRANSLATE_NOOP("Function type", "Plane Curve F(x)"))
0081     EXPRESSION_TYPE(Analitza::ExpressionType(Analitza::ExpressionType::Lambda).addParameter(
0082                    Analitza::ExpressionType(Analitza::ExpressionType::Value)).addParameter(
0083                    Analitza::ExpressionType(Analitza::ExpressionType::Value)))
0084     COORDDINATE_SYSTEM(Cartesian)
0085     PARAMETERS(QStringList(QStringLiteral("y")))
0086     ICON_NAME(QStringLiteral("newfunction"))
0087     EXAMPLES(QStringList(QStringLiteral("y")) << QStringLiteral("y*y") << QStringLiteral("y+4"))
0088     
0089     void update(const QRectF& viewport) override;
0090     
0091     QPair<QPointF, QString> image(const QPointF &mousepos) override;
0092     QLineF tangent(const QPointF &mousepos) override;
0093     
0094     qreal resolution() const { return 5000; }
0095 };
0096 
0097 void FunctionCartesian::update(const QRectF& viewport)
0098 {
0099     double l_lim=0, r_lim=0;
0100     
0101     if (!hasIntervals()) {
0102         l_lim=viewport.left();
0103         r_lim=viewport.right();
0104     } else {
0105         QPair< double, double> limits = interval(parameters().at(0));
0106         l_lim = limits.first;
0107         r_lim = limits.second;
0108     }
0109 
0110     if(!points.isEmpty()
0111             && isSimilar(points.first().x(), l_lim)
0112             && isSimilar(points.last().x(), r_lim)) {
0113         return;
0114     }
0115     
0116     calculateValues(l_lim, r_lim);
0117 //  qDebug() << "end." << jumps;
0118 }
0119 
0120 QPair<QPointF, QString> FunctionCartesian::image(const QPointF &p)
0121 {
0122     QPointF dp=p;
0123     QString pos;
0124 
0125     if (hasIntervals())
0126     {
0127         QPair<double, double> intervalx = interval(parameters().at(0));
0128     
0129         if (intervalx.first >=dp.x() || dp.x() >= intervalx.second)
0130             return QPair<QPointF, QString>(QPointF(), QString());
0131     }
0132 
0133     arg()->setValue(dp.x());
0134     Analitza::Expression r=analyzer->calculateLambda();
0135 
0136     if(!r.isReal())
0137         appendError(QCoreApplication::tr("We can only draw Real results."));
0138 
0139     dp.setY(r.toReal().value());
0140     pos = QStringLiteral("x=%1 y=%2").arg(dp.x(),3,'f',2).arg(dp.y(),3,'f',2);
0141     return QPair<QPointF, QString>(dp, pos);
0142 }
0143 
0144 QLineF FunctionCartesian::tangent(const QPointF &mousepos)
0145 {
0146     Analitza::Analyzer a(analyzer->variables());
0147     double ret = 0;
0148     if(m_deriv.isCorrect()) {
0149         arg()->setValue(mousepos.x());
0150         QVector<Analitza::Object*> runStack;
0151         runStack += arg();
0152 
0153         a.setExpression(m_deriv);
0154         a.setStack(runStack);
0155         if(a.isCorrect())
0156             ret = a.calculateLambda().toReal().value();
0157 
0158         if(!a.isCorrect()) {
0159             qDebug() << "Derivative error: " <<  a.errors();
0160             ret = 0;
0161         }
0162     }
0163 
0164     if(ret==0) {
0165         QVector<Analitza::Object*> vars;
0166         vars.append(new Analitza::Cn(mousepos.x()));
0167         a.setExpression(analyzer->expression());
0168         ret=a.derivative(vars);
0169         qDeleteAll(vars);
0170     }
0171     return slopeToLine(ret);
0172 }
0173 
0174 void FunctionCartesian::optimizeJump()
0175 {
0176     QPointF before = points.at(points.count()-2), after=points.last();
0177     qreal x1=before.x(), x2=after.x();
0178     qreal y1=before.y(), y2=after.y();
0179     int iterations=5;
0180 
0181 //  qDebug() << "+++++++++" << before << after;
0182     for(; iterations>0; --iterations) {
0183         qreal dist = x2-x1;
0184         qreal x=x1+dist/2;
0185 
0186         arg()->setValue(x);
0187         qreal y = analyzer->calculateLambda().toReal().value();
0188 
0189         if(fabs(y1-y)<fabs(y2-y)) {
0190             before.setX(x);
0191             before.setY(y);
0192             x1=x;
0193             y1=y;
0194         } else {
0195             after.setX(x);
0196             after.setY(y);
0197             x2=x;
0198             y2=y;
0199         }
0200     }
0201 //  qDebug() << "---------" << before << after;
0202     points[points.count()-2]=before;
0203     points.last()=after;
0204 }
0205 
0206 void FunctionCartesian::calculateValues(double l_lim, double r_lim)
0207 {
0208     jumps.clear();
0209     points.clear();
0210     points.reserve(m_resolution);
0211 
0212     double step = double((-l_lim+r_lim)/m_resolution);
0213     bool jumping=true;
0214     for(double x=l_lim; x<r_lim-step; x+=step) {
0215         arg()->setValue(x);
0216         Analitza::Cn y = analyzer->calculateLambda().toReal();
0217         QPointF p(x, y.value());
0218         bool ch=addPoint(p);
0219 
0220         bool jj=jumping;
0221         jumping=false;
0222         if(ch && !jj) {
0223 //          if(!jumps.isEmpty()) qDebug() << "popopo" << jumps.last() << points.count();
0224             double prevY=points[points.count()-2].y();
0225             if(y.format()!=Analitza::Cn::Real && prevY!=y.value()) {
0226                 jumps.append(points.count()-1);
0227                 jumping=true;
0228             } else if(points.count()>3 && traverse(points[points.count()-3].y(), prevY, y.value())) {
0229                 optimizeJump();
0230                 jumps.append(points.count()-1);
0231                 jumping=true;
0232             }
0233         }
0234     }
0235 //  qDebug() << "juuuumps" << m_jumps << resolution();
0236 }
0237 
0238 QPair<QPointF, QString> FunctionX::image(const QPointF& p)
0239 {
0240     QPointF dp=p;
0241     arg()->setValue(dp.y());
0242     Analitza::Expression r=analyzer->calculateLambda();
0243 
0244     if(!r.isReal())
0245         appendError(QCoreApplication::tr("We can only draw Real results."));
0246     
0247     dp.setX(r.toReal().value());
0248     return QPair<QPointF, QString>(dp, QCoreApplication::tr("x=%1 y=%2").arg(dp.x()).arg(dp.y()));
0249 }
0250 
0251 void FunctionX::update(const QRectF& viewport)
0252 {
0253     double l_lim=0, r_lim=0;
0254     
0255     if (!hasIntervals()) {
0256         l_lim=viewport.left();
0257         r_lim=viewport.right();
0258     } else {
0259         QPair< double, double> limits = interval(parameters().at(0));
0260         l_lim = limits.first;
0261         r_lim = limits.second;
0262     }
0263 
0264     calculateValues(l_lim, r_lim);
0265     
0266     for(int i=0; i<points.size(); i++) {
0267         QPointF p=points[i];
0268         points[i]=QPointF(p.y(), p.x());
0269     }
0270 }
0271 
0272 QLineF FunctionX::tangent(const QPointF &p) 
0273 {
0274     QPointF p1(p.y(), p.x());
0275     QLineF ret=FunctionCartesian::tangent(p1);
0276     return mirrorXY(ret);
0277 }
0278 
0279 REGISTER_PLANECURVE(FunctionY)
0280 REGISTER_PLANECURVE(FunctionX)