From 94acc1b7d57af1f4cf3276a1490bda81e5d4e3d8 Mon Sep 17 00:00:00 2001 From: "Jan W. Krieger" Date: Mon, 24 Dec 2018 12:29:33 +0100 Subject: [PATCH] JKQTPxFunctionLineGraph and JKQTPyFunctionLineGraph now allow for simplified functions without parameters + improved handling of special functions, improved example for parsed functions (with possibility to display sample points --- .../jkqtpgraphsevaluatedfunction.cpp | 240 ++++++++++++------ .../jkqtpgraphsevaluatedfunction.h | 85 +++++-- ...parsedfunctionplot_2overx_samplepoints.png | Bin 0 -> 31512 bytes test/simpletest_functionplot/README.md | 6 +- .../jkqtplotter_simpletest_functionplot.cpp | 6 +- test/simpletest_parsedfunctionplot/README.md | 6 +- ...tplotter_simpletest_parsedfunctionplot.cpp | 5 + 7 files changed, 240 insertions(+), 108 deletions(-) create mode 100644 screenshots/jkqtplotter_simpletest_parsedfunctionplot_2overx_samplepoints.png diff --git a/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp b/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp index 044eecb5e3..6fdccaedb6 100644 --- a/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp +++ b/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.cpp @@ -28,57 +28,16 @@ #include "jkqtplotter/jkqtpgraphsimage.h" #include "jkqtplotter/jkqtpbaseelements.h" #include "jkqtplotter/jkqtplotter.h" +#include "jkqtpgraphsevaluatedfunction.h" -double JKQTPxFunctionLineGraphPolynomial(double x, void* param) { - double res=0; - QVector* d=static_cast*>(param); - if (d && d->size()>0) { - res=d->value(0,0); - double xx=x; - for (int i=1; isize(); i++) { - res=res+d->value(i,0)*xx; - xx=xx*x; - } - } - - return res; -} - -double JKQTPxFunctionLineGraphExponential(double x, void* param) { - double res=0; - QVector* d=static_cast*>(param); - if (d) { - if (d->size()>=3) { - res=d->value(0,0)+d->value(1,0)*exp(x/d->value(2,0)); - } else if (d->size()>=2) { - res=d->value(0,0)*exp(x/d->value(1,0)); - } - } - return res; -} - -double JKQTPxFunctionLineGraphPowerLaw(double x, void* param) { - double res=0; - QVector* d=static_cast*>(param); - if (d) { - if (d->size()>=3) { - res=d->value(0,0)+d->value(1,0)*pow(x, d->value(2,1)); - } else if (d->size()>=2) { - res=d->value(0,0)*pow(x, d->value(1,1)); - } else if (d->size()>=1) { - res=pow(x, d->value(0,1)); - } - - } - return res; -} JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtBasePlotter* parent): JKQTPgraph(parent) { + functionType=SpecialFunction::UserFunction; color=QColor("red"); fillColor=color.lighter(); style=Qt::SolidLine; @@ -86,7 +45,6 @@ JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtBasePlotter* parent): fillStyle=Qt::SolidPattern; drawLine=true; fillCurve=false; - plotFunction=nullptr; params=nullptr; minSamples=10; maxRefinementDegree=7; @@ -98,7 +56,6 @@ JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtBasePlotter* parent): drawErrorPolygons=false; drawErrorLines=false; - errorPlotFunction=nullptr; errorParams=nullptr; errorColor=color.lighter(); errorFillColor=color.lighter(); @@ -127,6 +84,7 @@ JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtBasePlotter* parent): JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtPlotter* parent): JKQTPgraph(parent) { + functionType=SpecialFunction::UserFunction; color=QColor("red"); fillColor=color.lighter(); style=Qt::SolidLine; @@ -134,7 +92,6 @@ JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtPlotter* parent): fillStyle=Qt::SolidPattern; drawLine=true; fillCurve=false; - plotFunction=nullptr; params=nullptr; minSamples=10; maxRefinementDegree=7; @@ -146,7 +103,6 @@ JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtPlotter* parent): drawErrorPolygons=false; drawErrorLines=false; - errorPlotFunction=nullptr; errorParams=nullptr; errorColor=color.lighter(); errorFillColor=color.lighter(); @@ -188,19 +144,47 @@ void JKQTPxFunctionLineGraph::clearData() { void JKQTPxFunctionLineGraph::set_plotFunction(const jkqtpPlotFunctionType &__value) { - this->plotFunction = __value; + simplePlotFunction=jkqtpSimplePlotFunctionType(); + plotFunction = __value; + functionType=SpecialFunction::UserFunction; + + clearData(); +} + +void JKQTPxFunctionLineGraph::set_plotFunction(const jkqtpSimplePlotFunctionType &__value) +{ + plotFunction=jkqtpPlotFunctionType(); + simplePlotFunction=__value; + functionType=SpecialFunction::UserFunction; + clearData(); } void JKQTPxFunctionLineGraph::set_plotFunction(jkqtpPlotFunctionType &&__value) { - this->plotFunction = std::move(__value); + simplePlotFunction=jkqtpSimplePlotFunctionType(); + plotFunction = std::move(__value); + functionType=SpecialFunction::UserFunction; + clearData(); +} + +void JKQTPxFunctionLineGraph::set_plotFunction(jkqtpSimplePlotFunctionType &&__value) +{ + plotFunction=jkqtpPlotFunctionType(); + simplePlotFunction=std::move(__value); + functionType=SpecialFunction::UserFunction; + clearData(); } jkqtpPlotFunctionType JKQTPxFunctionLineGraph::get_plotFunction() const { - return this->plotFunction; + return plotFunction; +} + +jkqtpSimplePlotFunctionType JKQTPxFunctionLineGraph::get_simplePlotFunction() const +{ + return simplePlotFunction; } @@ -215,7 +199,7 @@ void JKQTPxFunctionLineGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, QRect QBrush b=painter.brush(); b.setColor(fillColor); b.setStyle(fillStyle); - int y=rect.top()+rect.height()/2.0; + const double y=rect.top()+rect.height()/2.0; painter.setPen(np); if (drawLine) painter.setPen(p); painter.setBrush(b); @@ -246,7 +230,11 @@ void JKQTPxFunctionLineGraph::createPlotData(bool collectParams) { if (collectParams) collectParameters(); if (parent==nullptr) return; - if (plotFunction==nullptr) return; + if (!plotFunction && !simplePlotFunction) return; + + jkqtpSimplePlotFunctionType func; + if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, params); + else if (simplePlotFunction) func=simplePlotFunction; double xmin=parent->getXMin(); double xmax=parent->getXMax(); @@ -258,14 +246,14 @@ void JKQTPxFunctionLineGraph::createPlotData(bool collectParams) { // initially sample function doublePair* d=new doublePair; d->x=xmin; - d->f=plotFunction(xmin, params); + d->f=func(xmin); d->next=nullptr; data=d; /*if (parent && parent->getXAxis()->isLogAxis()) { for (double x=log(xmin)+logdelta0; xnext = new doublePair; d->next->x=exp(x+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0); - d->next->f=plotFunction(d->next->x, params); + d->next->f=func(d->next->x,); d->next->next=nullptr; doublePair* dd=d; d=d->next; @@ -273,20 +261,24 @@ void JKQTPxFunctionLineGraph::createPlotData(bool collectParams) { } } else {*/ QVector* dv=static_cast*>(params); - for (double x=pxmin+delta0; xnext = new doublePair; - d->next->x=parent->p2x(x+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0); - d->next->f=plotFunction(d->next->x, params); - d->next->next=nullptr; - doublePair* dd=d; - d=d->next; - refine(dd, d); + if (functionType==Polynomial && dv && dv->size()<=2) { + // we only need the first and last datapoint + } else { + for (double x=pxmin+delta0; xnext = new doublePair; + d->next->x=parent->p2x(x+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0); + d->next->f=func(d->next->x); + d->next->next=nullptr; + doublePair* dd=d; + d=d->next; + refine(dd, d); + } } //} d->next = new doublePair; d->next->x=xmax; - d->next->f=plotFunction(xmax, params); + d->next->f=func(xmax); d->next->next=nullptr; refine(d, d->next); @@ -355,7 +347,9 @@ void JKQTPxFunctionLineGraph::refine(doublePair* a, doublePair* b, unsigned int xmid=xmid+((double)rand()/(double)RAND_MAX-0.5)*delta/5.0; // shake by 10% //} double realxmid=parent->p2x(xmid); - double realfmid=plotFunction(realxmid, params); + double realfmid; + if (plotFunction) realfmid=plotFunction(realxmid, params); + else if (simplePlotFunction) realfmid=simplePlotFunction(realxmid); double fmid=yAxis->x2p(realfmid); double a1=(fmid - af)/(xmid - ax); double a2=(bf - fmid)/(bx - xmid); @@ -721,7 +715,11 @@ void JKQTPyFunctionLineGraph::createPlotData(bool /*collectParams*/) { clearData(); if (parent==nullptr) return; - if (plotFunction==nullptr) return; + if (!plotFunction && !simplePlotFunction) return; + + jkqtpSimplePlotFunctionType func; + if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, params); + else if (simplePlotFunction) func=simplePlotFunction; double ymin=parent->getYMin(); double ymax=parent->getYMax(); @@ -730,13 +728,13 @@ void JKQTPyFunctionLineGraph::createPlotData(bool /*collectParams*/) { // initially sample function doublePair* d=new doublePair; d->x=ymin; - d->f=plotFunction(ymin, params); + d->f=func(ymin); d->next=nullptr; data=d; for (double y=ymin+delta0; ynext = new doublePair; d->next->x=y+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0; - d->next->f=plotFunction(d->next->x, params); + d->next->f=func(d->next->x); d->next->next=nullptr; doublePair* dd=d; d=d->next; @@ -744,7 +742,7 @@ void JKQTPyFunctionLineGraph::createPlotData(bool /*collectParams*/) { } d->next = new doublePair; d->next->x=ymax; - d->next->f=plotFunction(ymax, params); + d->next->f=func(ymax); d->next->next=nullptr; refine(d, d->next); @@ -802,18 +800,93 @@ void JKQTPxFunctionLineGraph::set_copiedParams(const double *params, int N) set_params(v); } +void JKQTPxFunctionLineGraph::set_paramsV(double p1) { + QVector p; + p< p; + p< p; + p< p; + p< p; + p< &errorParams) { ierrorparams=errorParams; set_errorParams(&ierrorparams); } - void JKQTPxFunctionLineGraph::setSpecialFunction(JKQTPxFunctionLineGraph::SpecialFunction function) { - if (function==JKQTPxFunctionLineGraph::Polynomial) set_plotFunction(JKQTPxFunctionLineGraphPolynomial); - else if (function==JKQTPxFunctionLineGraph::Exponential) set_plotFunction(JKQTPxFunctionLineGraphExponential); - else if (function==JKQTPxFunctionLineGraph::PowerLaw) set_plotFunction(JKQTPxFunctionLineGraphPowerLaw); + if (function==JKQTPxFunctionLineGraph::Polynomial) { + set_plotFunction([](double x, void* param) { + double res=0; + QVector* d=static_cast*>(param); + if (d && d->size()>0) { + res=d->value(0,0); + double xx=x; + for (int i=1; isize(); i++) { + res=res+d->value(i,0)*xx; + xx=xx*x; + } + } + + return res; + }); + } + else if (function==JKQTPxFunctionLineGraph::Exponential) set_plotFunction([](double x, void* param) { + double res=0; + QVector* d=static_cast*>(param); + if (d) { + if (d->size()>=3) { + res=d->value(0,0)+d->value(1,0)*exp(x/d->value(2,0)); + } else if (d->size()>=2) { + res=d->value(0,0)*exp(x/d->value(1,0)); + } + } + return res; + }); + else if (function==JKQTPxFunctionLineGraph::PowerLaw) set_plotFunction([](double x, void* param) { + double res=0; + QVector* d=static_cast*>(param); + if (d) { + if (d->size()>=3) { + res=d->value(0,0)+d->value(1,0)*pow(x, d->value(2,1)); + } else if (d->size()>=2) { + res=d->value(0,0)*pow(x, d->value(1,1)); + } else if (d->size()>=1) { + res=pow(x, d->value(0,1)); + } + + } + return res; + }); + else throw std::runtime_error("unknown special function type"); +} + +JKQTPxFunctionLineGraph::SpecialFunction JKQTPxFunctionLineGraph::getFunctionType() const +{ + return functionType; } QVector JKQTPxFunctionLineGraph::get_internalParams() const { @@ -825,12 +898,15 @@ QVector JKQTPxFunctionLineGraph::get_internalErrorParams() const { void JKQTPxFunctionLineGraph::set_errorPlotFunction(const jkqtpPlotFunctionType &__value) { + errorSimplePlotFunction=jkqtpSimplePlotFunctionType(); errorPlotFunction=__value; + clearData(); } void JKQTPxFunctionLineGraph::set_errorPlotFunction(jkqtpPlotFunctionType &&__value) { - this->errorPlotFunction = std::move(__value); + errorSimplePlotFunction=jkqtpSimplePlotFunctionType(); + errorPlotFunction = std::move(__value); clearData(); } jkqtpPlotFunctionType JKQTPxFunctionLineGraph::get_errorPlotFunction() const @@ -838,6 +914,24 @@ jkqtpPlotFunctionType JKQTPxFunctionLineGraph::get_errorPlotFunction() const return errorPlotFunction; } +void JKQTPxFunctionLineGraph::set_errorPlotFunction(const jkqtpSimplePlotFunctionType &__value) +{ + errorPlotFunction=jkqtpPlotFunctionType(); + errorSimplePlotFunction=__value; + clearData(); +} + +void JKQTPxFunctionLineGraph::set_errorPlotFunction(jkqtpSimplePlotFunctionType &&__value) +{ + errorPlotFunction=jkqtpPlotFunctionType(); + errorSimplePlotFunction = std::move(__value); + clearData(); +} +jkqtpSimplePlotFunctionType JKQTPxFunctionLineGraph::get_errorSimplePlotFunction() const +{ + return errorSimplePlotFunction; +} + bool JKQTPxFunctionLineGraph::usesColumn(int c) { diff --git a/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.h b/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.h index 0456f3baa1..af8cbb3457 100644 --- a/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.h +++ b/lib/jkqtplotter/jkqtpgraphsevaluatedfunction.h @@ -53,6 +53,14 @@ */ typedef std::function jkqtpPlotFunctionType; +/*! \brief simplified type of functions (without parameters) that may be plottet + \ingroup jkqtplotter_plots + + This is the type of functions \f$ y=f(x) \f$ that may be plottet by JKQTPxFunctionLineGraph + and JKQTPyFunctionLineGraph. +*/ +typedef std::function jkqtpSimplePlotFunctionType; + /*! \brief This implements line plots where the data is taken from a user supplied function \f$ y=f(x) \f$ \ingroup jkqtplotter_plots @@ -76,7 +84,9 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { Polynomial, /*!< \brief a polynomial \f$ f(x)=p_0+p_1x+p_2x^2+p_3x^3+... \f$ The parameters \a params have to be point to a QVector and contain the parameters \f$ p_0, p_1, ... \f$ */ Line=Polynomial, /*!< \brief a polynomial \f$ f(x)=p_0+p_1x \f$ The parameters \a params have to be point to a QVector and contain the parameters \f$ p_0, p_1, ... \f$ */ Exponential, /*!< \brief an exponential function \f$ f(x)=p_0+p_1\cdot\exp(x/p_2) \f$ or \f$ f(x)=p_0\cdot\exp(x/p_1) \f$ (depending on the number of parameters). The parameters \a params have to be point to a QVector and contain the parameters \f$ p_0, p_1, ... \f$ */ - PowerLaw /*!< \brief an exponential function \f$ f(x)=p_0+p_1\cdot x^{p_3} \f$ or \f$ f(x)=p_0\cdot x^{p_1} \f$ or \f$ f(x)= x^{p_0} \f$ (depending on the number of parameters) The parameters \a params have to be point to a QVector and contain the parameters \f$ p_0, p_1, ... \f$ */ + PowerLaw, /*!< \brief an exponential function \f$ f(x)=p_0+p_1\cdot x^{p_3} \f$ or \f$ f(x)=p_0\cdot x^{p_1} \f$ or \f$ f(x)= x^{p_0} \f$ (depending on the number of parameters) The parameters \a params have to be point to a QVector and contain the parameters \f$ p_0, p_1, ... \f$ */ + + UserFunction, /*!< \brief no special function but the function is provided by the user */ }; /** \brief class constructor */ @@ -125,41 +135,40 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { * \details Description of the parameter varname is:
\copybrief plotFunction.
* \see plotFunction for more information */ virtual void set_plotFunction (const jkqtpPlotFunctionType & __value); - /** \brief returns the property varname. \see varname for more information */ \ + /** \brief sets the property plotFunction to the specified \a __value. + * + * \details Description of the parameter plotFunction is:
\copybrief plotFunction.
+ * \see plotFunction for more information */ + virtual void set_plotFunction (jkqtpSimplePlotFunctionType && __value); + /** \brief sets the property plotFunction to the specified \a __value. + * + * \details Description of the parameter plotFunction is:
\copybrief plotFunction.
+ * \see plotFunction for more information */ + virtual void set_plotFunction (const jkqtpSimplePlotFunctionType & __value); + /** \brief returns the property plotFunction. \see plotFunction for more information */ \ virtual jkqtpPlotFunctionType get_plotFunction () const; + /** \brief returns the property simplePlotFunction. \see simplePlotFunction for more information */ \ + virtual jkqtpSimplePlotFunctionType get_simplePlotFunction () const; JKQTPGET_SET_MACRO_I(void*, params, clearData()) /** \brief sets the params as a pointer to an internal COPY of the given vector (not the data of the vector, as then the size would be unknown!!!) */ void set_params(const QVector& params); /** \brief sets the params from a copy of the given array of length \a N */ void set_copiedParams(const double* params, int N); - inline void set_paramsV(double p1) { - QVector p; - p< p; - p< p; - p< p; - p< p; - p< get_internalParams() const; + /** \brief returns the currently set internal parameter vector */ QVector get_internalErrorParams() const; JKQTPGET_SET_MACRO(unsigned int, minSamples) JKQTPGET_SET_MACRO(unsigned int, maxRefinementDegree) @@ -181,6 +190,18 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { virtual void set_errorPlotFunction (const jkqtpPlotFunctionType & __value); /** \brief returns the property varname. \see varname for more information */ \ virtual jkqtpPlotFunctionType get_errorPlotFunction () const; + /** \brief sets the property errorPlotFunction to the specified \a __value. + * + * \details Description of the parameter varname is:
\copybrief errorPlotFunction.
+ * \see errorPlotFunction for more information */ + virtual void set_errorPlotFunction (jkqtpSimplePlotFunctionType && __value); + /** \brief sets the property errorPlotFunction to the specified \a __value. + * + * \details Description of the parameter varname is:
\copybrief errorPlotFunction.
+ * \see errorPlotFunction for more information */ + virtual void set_errorPlotFunction (const jkqtpSimplePlotFunctionType & __value); + /** \brief returns the property varname. \see varname for more information */ \ + virtual jkqtpSimplePlotFunctionType get_errorSimplePlotFunction () const; JKQTPGET_SET_MACRO(void*, errorParams) /** \brief sets the error params as a pointer to an internal COPY of the given vector (not the data of the vector, as then the size would be unknown!!!) */ void set_errorParams(const QVector& errorParams); @@ -202,6 +223,8 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { /** \brief sets function to the given special function */ void setSpecialFunction(SpecialFunction function); + /** \brief returns, which special function is set (or if any is set) */ + SpecialFunction getFunctionType() const; protected: /** \brief which plot style to use from the parent plotter (via JKQtPlotterBase::getPlotStyle() and JKQtPlotterBase::getNextStyle() ) */ int parentPlotStyle; @@ -218,7 +241,7 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { virtual void createPlotData( bool collectParams=true); virtual void collectParameters(); - + /** \brief refine datapoints on the function graph between two evaluations \a a and \a b */ void refine(doublePair* a, doublePair* b, unsigned int degree=0); /** \brief if set, the values from this datatsore column are used for the parameters \c p1 , \c p2 , \c p3 , ... of the plot function */ @@ -242,6 +265,10 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { bool fillCurve; /** \brief the function to be plotted */ jkqtpPlotFunctionType plotFunction; + /** \brief a simple function to be plotted, simplified form without parameters */ + jkqtpSimplePlotFunctionType simplePlotFunction; + /** \brief indicates whether a special function is set (and if so, which one), or a user-supplied function */ + SpecialFunction functionType; /** \brief pointer to the parameters supplied to the plotting funtion */ void* params; /** \brief the minimum number of points to evaluate the function at */ @@ -267,6 +294,8 @@ class LIB_EXPORT JKQTPxFunctionLineGraph: public JKQTPgraph { bool drawErrorLines; /** \brief this function calculates the error at a given position */ jkqtpPlotFunctionType errorPlotFunction; + /** \brief this function calculates the error at a given position, simplified form without parameters */ + jkqtpSimplePlotFunctionType errorSimplePlotFunction; /** \brief parameters for errorFunction */ void* errorParams; /** \brief color of the error graph */ diff --git a/screenshots/jkqtplotter_simpletest_parsedfunctionplot_2overx_samplepoints.png b/screenshots/jkqtplotter_simpletest_parsedfunctionplot_2overx_samplepoints.png new file mode 100644 index 0000000000000000000000000000000000000000..0e309cfef4a5fd28fea144b132ab87ee861a4d91 GIT binary patch literal 31512 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 CUQoOM literal 0 HcmV?d00001 diff --git a/test/simpletest_functionplot/README.md b/test/simpletest_functionplot/README.md index 6073bbd7c0..58d994a72e 100644 --- a/test/simpletest_functionplot/README.md +++ b/test/simpletest_functionplot/README.md @@ -8,7 +8,7 @@ This project (see `./test/simpletest_functionplot/`) demonstrates how to plot ma The first example shows how to plot a C++ inline function: ```c++ JKQTPxFunctionLineGraph* func1=new JKQTPxFunctionLineGraph(plot); - func1->set_plotFunction([](double x, void* /*params*/) { return 0.2*x*x-0.015*x*x*x; }); + func1->set_plotFunction([](double x) { return 0.2*x*x-0.015*x*x*x; }); func1->set_title("C++-inline function $0.2x^2-0.015x^3$"); plot->addGraph(func1); ``` @@ -47,7 +47,7 @@ You can also use C++ functors (or function objects): struct SincSqr { public: inline SincSqr(double amplitude): a(amplitude) {} - inline double operator()(double x, void* /*params*/) { + inline double operator()(double x) { return a*sin(x)*sin(x)/x/x; } private: @@ -64,7 +64,7 @@ You can also use C++ functors (or function objects): ... or simple static C functions: ```c++ - double sinc(double x, void* /*params*/) { + double sinc(double x) { return 10.0*sin(x)/x; } diff --git a/test/simpletest_functionplot/jkqtplotter_simpletest_functionplot.cpp b/test/simpletest_functionplot/jkqtplotter_simpletest_functionplot.cpp index 63b932d4ec..6576144b28 100644 --- a/test/simpletest_functionplot/jkqtplotter_simpletest_functionplot.cpp +++ b/test/simpletest_functionplot/jkqtplotter_simpletest_functionplot.cpp @@ -4,14 +4,14 @@ #include "jkqtplotter/jkqtplotter.h" #include "jkqtplotter/jkqtpgraphsevaluatedfunction.h" -double sinc(double x, void* /*params*/) { +double sinc(double x) { return 10.0*sin(x)/x; } struct SincSqr { public: inline SincSqr(double amplitude): a(amplitude) {} - inline double operator()(double x, void* /*params*/) { + inline double operator()(double x) { return a*sin(x)*sin(x)/x/x; } private: @@ -33,7 +33,7 @@ int main(int argc, char* argv[]) // 2. now we add a JKQTPxFunctionLineGraph object, which will draw a simple function // the function is defined as C++ inline function JKQTPxFunctionLineGraph* func1=new JKQTPxFunctionLineGraph(plot); - func1->set_plotFunction([](double x, void* /*params*/) { return 0.2*x*x-0.015*x*x*x; }); + func1->set_plotFunction([](double x) { return 0.2*x*x-0.015*x*x*x; }); func1->set_title("C++-inline function $0.2x^2-0.015x^3$"); plot->addGraph(func1); diff --git a/test/simpletest_parsedfunctionplot/README.md b/test/simpletest_parsedfunctionplot/README.md index 9065d82af4..99d105d42b 100644 --- a/test/simpletest_parsedfunctionplot/README.md +++ b/test/simpletest_parsedfunctionplot/README.md @@ -45,9 +45,13 @@ This code snippet results in a plot like this: ![jkqtplotter_simpletest_parsedfunctionplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot.png) -the adaptive capabilities of the rendering algorithm can be seen, when plotting e.g. `2/x`, which is drawn smoothely, even around the undefined value at `x=0`: +The adaptive capabilities of the rendering algorithm can be seen, when plotting e.g. `2/x`, which is drawn smoothely, even around the undefined value at `x=0`: ![jkqtplotter_simpletest_parsedfunctionplot_2overx.png](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot_2overx.png) +With an additional checkbox in this example, you can switch drawing the actual sample points of the drawing algorithm on and off, by calling `parsedFunc->set_displaySamplePoints(...)`. This can be used to debug the drawing algorithm and explore its parameters (which you can set with `set_minSamples()`, `set_maxRefinementDegree()`, `set_slopeTolerance()`, `set_minPixelPerSample()`). Here is an example of a 2/x function with shown sample points: + +![jkqtplotter_simpletest_parsedfunctionplot_2overx_samplepoints.png](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot_2overx_samplepoints.png) + [Back to JKQTPlotter main page](https://github.com/jkriege2/JKQtPlotter/) \ No newline at end of file diff --git a/test/simpletest_parsedfunctionplot/jkqtplotter_simpletest_parsedfunctionplot.cpp b/test/simpletest_parsedfunctionplot/jkqtplotter_simpletest_parsedfunctionplot.cpp index 04daf1cbb6..90e63d0e36 100644 --- a/test/simpletest_parsedfunctionplot/jkqtplotter_simpletest_parsedfunctionplot.cpp +++ b/test/simpletest_parsedfunctionplot/jkqtplotter_simpletest_parsedfunctionplot.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "jkqtplotter/jkqtplotter.h" #include "jkqtplotter/jkqtpgraphsparsedfunction.h" @@ -14,10 +15,12 @@ int main(int argc, char* argv[]) QWidget mainWin; QLineEdit* edit=new QLineEdit(&mainWin); edit->setToolTip("enter a function in dependence of the variable x and press ENTER to update the graph"); + QCheckBox* check=new QCheckBox("display sample points"); JKQtPlotter* plot=new JKQtPlotter(&mainWin); QVBoxLayout* layout=new QVBoxLayout; mainWin.setLayout(layout); layout->addWidget(edit); + layout->addWidget(check); layout->addWidget(plot); // 2. now we add a JKQTPxParsedFunctionLineGraph object, which will draw the function from @@ -30,10 +33,12 @@ int main(int argc, char* argv[]) [=]() { parsedFunc->set_title("user function: \\verb{"+edit->text()+"}"); parsedFunc->set_function(edit->text()); + parsedFunc->set_displaySamplePoints(check->isChecked()); plot->update_plot(); }; QObject::connect(edit, &QLineEdit::returnPressed, updateGraphFunctor); QObject::connect(edit, &QLineEdit::editingFinished, updateGraphFunctor); + QObject::connect(check, &QCheckBox::toggled, updateGraphFunctor); edit->setText("sin(x*8)*exp(-x/4)"); updateGraphFunctor();