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)