tidied up code of parsed function graphs, added f(y) parsed function class (JKQTPyParsedFunctionLineGraph), improved examples

This commit is contained in:
Jan W. Krieger 2018-12-24 15:07:14 +01:00
parent 94acc1b7d5
commit d70c6cf377
11 changed files with 376 additions and 103 deletions

View File

@ -711,8 +711,9 @@ void JKQTPyFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) {
}
void JKQTPyFunctionLineGraph::createPlotData(bool /*collectParams*/) {
void JKQTPyFunctionLineGraph::createPlotData(bool collectParams) {
clearData();
if (collectParams) collectParameters();
if (parent==nullptr) return;
if (!plotFunction && !simplePlotFunction) return;

View File

@ -17,8 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
#include "jkqtplotter/jkqtpgraphs.h"
#include "jkqtplotter/jkqtpbaseplotter.h"
@ -29,32 +27,6 @@
#include <utility>
double JKQTPxParsedFunctionLineGraphFunction(double x, void* data) {
JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunctionData* d=(JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunctionData*)data;
if (d && d->parser && d->node) {
try {
d->parser->addVariableDouble("x", x);
JKQTPMathParser::jkmpResult r=d->node->evaluate();
if (r.isValid) {
if (r.type==JKQTPMathParser::jkmpBool) {
return r.boolean?1.0:0.0;
} else if (r.type==JKQTPMathParser::jkmpDouble) {
return r.num;
}
}
} catch(std::exception& /*E*/) {
//qDebug()<<QString("parser error: %1").arg(E.what());
/*ok= QMessageBox::critical(this, tr("QuickFit-table"),
tr("An error occured while parsing the expression '%1' in cell (row, column)=(%3, %4):\n%2\n\n\"OK\" will still go on evaluating\n\"Cancel\" will cancel evaluation for the rest of the cells.").arg(dlgMathExpression->getExpression()).arg(E.what()).arg(row).arg(column),
QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok)==QMessageBox::Ok;*/
}
}
return NAN;
}
JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraph(JKQtBasePlotter *parent):
JKQTPxFunctionLineGraph(parent)
{
@ -64,7 +36,7 @@ JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraph(JKQtBasePlotter *pa
function="";
parameterColumn=-1;
set_params(&fdata);
set_plotFunction(JKQTPxParsedFunctionLineGraphFunction);
set_plotFunction(&JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunction);
efdata.parser=new JKQTPMathParser();
efdata.node=nullptr;
@ -72,7 +44,7 @@ JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraph(JKQtBasePlotter *pa
errorFunction="";
errorParameterColumn=-1;
set_errorParams(&efdata);
set_errorPlotFunction(JKQTPxParsedFunctionLineGraphFunction);
set_errorPlotFunction(&JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunction);
}
JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraph(JKQtPlotter *parent):
@ -84,7 +56,7 @@ JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraph(JKQtPlotter *parent
function="";
parameterColumn=-1;
set_params(&fdata);
set_plotFunction(JKQTPxParsedFunctionLineGraphFunction);
set_plotFunction(&JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunction);
efdata.parser=new JKQTPMathParser();
efdata.node=nullptr;
@ -92,8 +64,9 @@ JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraph(JKQtPlotter *parent
errorFunction="";
errorParameterColumn=-1;
set_errorParams(&efdata);
set_errorPlotFunction(JKQTPxParsedFunctionLineGraphFunction);
set_errorPlotFunction(&JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunction);
}
JKQTPxParsedFunctionLineGraph::~JKQTPxParsedFunctionLineGraph()
{
if (fdata.node) delete fdata.node;
@ -102,13 +75,6 @@ JKQTPxParsedFunctionLineGraph::~JKQTPxParsedFunctionLineGraph()
delete efdata.parser;
}
void JKQTPxParsedFunctionLineGraph::set_plotFunction(jkqtpPlotFunctionType &&f){
JKQTPxFunctionLineGraph::set_plotFunction(std::move(f));
}
void JKQTPxParsedFunctionLineGraph::set_plotFunction(const jkqtpPlotFunctionType &f) {
JKQTPxFunctionLineGraph::set_plotFunction(f);
}
void JKQTPxParsedFunctionLineGraph::createPlotData(bool /*collectParams*/)
{
@ -121,23 +87,13 @@ void JKQTPxParsedFunctionLineGraph::createPlotData(bool /*collectParams*/)
}
fdata.varcount=0;
try {
if (parent && parameterColumn>=0) {
JKQTPdatastore* datastore=parent->getDatastore();
int imin=0;
int imax=datastore->getColumn(parameterColumn).getRows();
for (int i=imin; i<imax; i++) {
double xv=datastore->get(parameterColumn,i);
fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), xv);
QVector<double>* parameters=static_cast<QVector<double>*>(params);
if (parameters) {
for (int i=0; i<parameters->size(); i++) {
fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), parameters->at(i));
fdata.varcount=fdata.varcount+1;
}
}
for (int i=0; i<parameters.size(); i++) {
fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), parameters[i]);
fdata.varcount=fdata.varcount+1;
}
fdata.parser->addVariableDouble(std::string("x"), 0.0);
if (fdata.node) delete fdata.node;
//qint64 t=timer.elapsed();
@ -156,23 +112,13 @@ void JKQTPxParsedFunctionLineGraph::createPlotData(bool /*collectParams*/)
}
efdata.varcount=0;
try {
if (parent && errorParameterColumn>=0) {
JKQTPdatastore* datastore=parent->getDatastore();
int imin=0;
int imax=datastore->getColumn(errorParameterColumn).getRows();
for (int i=imin; i<imax; i++) {
double xv=datastore->get(errorParameterColumn,i);
efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), xv);
QVector<double>* errorParameters=static_cast<QVector<double>*>(errorParams);
if (errorParameters) {
for (int i=0; i<errorParameters->size(); i++) {
efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), errorParameters->at(i));
efdata.varcount=efdata.varcount+1;
}
}
for (int i=0; i<errorParameters.size(); i++) {
efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), errorParameters[i]);
efdata.varcount=efdata.varcount+1;
}
efdata.parser->addVariableDouble(std::string("x"), 0.0);
if (efdata.node) delete efdata.node;
//qint64 t=timer.elapsed();
@ -200,3 +146,194 @@ void JKQTPxParsedFunctionLineGraph::createPlotData(bool /*collectParams*/)
}
qDebug()<<"refined to "<<count<<" daatapoints";*/
}
double JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunction(double x, void* data) {
JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunctionData* d=(JKQTPxParsedFunctionLineGraph::JKQTPxParsedFunctionLineGraphFunctionData*)data;
if (d && d->parser && d->node) {
try {
d->parser->addVariableDouble("x", x);
JKQTPMathParser::jkmpResult r=d->node->evaluate();
if (r.isValid) {
if (r.type==JKQTPMathParser::jkmpBool) {
return r.boolean?1.0:0.0;
} else if (r.type==JKQTPMathParser::jkmpDouble) {
return r.num;
}
}
} catch(std::exception& /*E*/) {
//qDebug()<<QString("parser error: %1").arg(E.what());
/*ok= QMessageBox::critical(this, tr("QuickFit-table"),
tr("An error occured while parsing the expression '%1' in cell (row, column)=(%3, %4):\n%2\n\n\"OK\" will still go on evaluating\n\"Cancel\" will cancel evaluation for the rest of the cells.").arg(dlgMathExpression->getExpression()).arg(E.what()).arg(row).arg(column),
QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok)==QMessageBox::Ok;*/
}
}
return NAN;
}
JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraph(JKQtBasePlotter *parent):
JKQTPyFunctionLineGraph(parent)
{
fdata.parser=new JKQTPMathParser();
fdata.node=nullptr;
fdata.varcount=0;
function="";
parameterColumn=-1;
set_params(&fdata);
set_plotFunction(&JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunction);
efdata.parser=new JKQTPMathParser();
efdata.node=nullptr;
efdata.varcount=0;
errorFunction="";
errorParameterColumn=-1;
set_errorParams(&efdata);
set_errorPlotFunction(&JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunction);
}
JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraph(JKQtPlotter *parent):
JKQTPyFunctionLineGraph(parent)
{
fdata.parser=new JKQTPMathParser();
fdata.node=nullptr;
fdata.varcount=0;
function="";
parameterColumn=-1;
set_params(&fdata);
set_plotFunction(&JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunction);
efdata.parser=new JKQTPMathParser();
efdata.node=nullptr;
efdata.varcount=0;
errorFunction="";
errorParameterColumn=-1;
set_errorParams(&efdata);
set_errorPlotFunction(&JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunction);
}
JKQTPyParsedFunctionLineGraph::~JKQTPyParsedFunctionLineGraph()
{
if (fdata.node) delete fdata.node;
delete fdata.parser;
if (efdata.node) delete efdata.node;
delete efdata.parser;
}
void JKQTPyParsedFunctionLineGraph::createPlotData(bool /*collectParams*/)
{
collectParameters();
//QElapsedTimer timer;
//timer.start();
for (int i=0; i<fdata.varcount; i++) {
fdata.parser->deleteVariable(std::string("p")+jkqtp_inttostr(i+1));
}
fdata.varcount=0;
try {
QVector<double>* parameters=static_cast<QVector<double>*>(params);
if (parameters) {
for (int i=0; i<parameters->size(); i++) {
fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), parameters->at(i));
fdata.varcount=fdata.varcount+1;
}
}
fdata.parser->addVariableDouble(std::string("x"), 0.0);
fdata.parser->addVariableDouble(std::string("y"), 0.0);
if (fdata.node) delete fdata.node;
//qint64 t=timer.elapsed();
//qDebug()<<"createPlotData(): adding variables: "<<t<<"ms";
fdata.node=fdata.parser->parse(function.toStdString());
//qDebug()<<"createPlotData(): parsing: "<<timer.elapsed()-t<<"ms";
} catch(std::exception& /*E*/) {
//qDebug()<<QString("parser error: %1").arg(E.what());
}
//qint64 t0=timer.elapsed();
for (int i=0; i<efdata.varcount; i++) {
efdata.parser->deleteVariable(std::string("p")+jkqtp_inttostr(i+1));
}
efdata.varcount=0;
try {
QVector<double>* errorParameters=static_cast<QVector<double>*>(errorParams);
if (errorParameters) {
for (int i=0; i<errorParameters->size(); i++) {
efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), errorParameters->at(i));
efdata.varcount=efdata.varcount+1;
}
}
efdata.parser->addVariableDouble(std::string("x"), 0.0);
efdata.parser->addVariableDouble(std::string("y"), 0.0);
if (efdata.node) delete efdata.node;
//qint64 t=timer.elapsed();
//qDebug()<<"createPlotData(): adding variables: "<<t-t0<<"ms";
efdata.node=efdata.parser->parse(errorFunction.toStdString());
//qDebug()<<"createPlotData(): parsing: "<<timer.elapsed()-t<<"ms";
} catch(std::exception& /*E*/) {
//qDebug()<<QString("parser error: %1").arg(E.what());
}
set_params(&fdata);
set_plotFunction(JKQTPyParsedFunctionLineGraphFunction);
set_errorParams(&efdata);
set_errorPlotFunction(JKQTPyParsedFunctionLineGraphFunction);
//qint64 t=timer.elapsed();
JKQTPyFunctionLineGraph::createPlotData(false);
//qDebug()<<"createPlotData(): JKQTPyFunctionLineGraph::createPlotData(): "<<timer.elapsed()-t<<"ms";
/*int count=0;
doublePair* d=data;
while (d!=nullptr) {
count++;
d=d->next;
}
qDebug()<<"refined to "<<count<<" daatapoints";*/
}
double JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunction(double x, void* data) {
JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunctionData* d=(JKQTPyParsedFunctionLineGraph::JKQTPyParsedFunctionLineGraphFunctionData*)data;
if (d && d->parser && d->node) {
try {
d->parser->addVariableDouble("x", x);
d->parser->addVariableDouble("y", x);
JKQTPMathParser::jkmpResult r=d->node->evaluate();
if (r.isValid) {
if (r.type==JKQTPMathParser::jkmpBool) {
return r.boolean?1.0:0.0;
} else if (r.type==JKQTPMathParser::jkmpDouble) {
return r.num;
}
}
} catch(std::exception& /*E*/) {
//qDebug()<<QString("parser error: %1").arg(E.what());
/*ok= QMessageBox::critical(this, tr("QuickFit-table"),
tr("An error occured while parsing the expression '%1' in cell (row, column)=(%3, %4):\n%2\n\n\"OK\" will still go on evaluating\n\"Cancel\" will cancel evaluation for the rest of the cells.").arg(dlgMathExpression->getExpression()).arg(E.what()).arg(row).arg(column),
QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok)==QMessageBox::Ok;*/
}
}
return NAN;
}

View File

@ -40,6 +40,8 @@ class JKQtPlotter;
Additional function parameters may be given in the vector parameters. They are accessible in the function as \c p1 , \c p2 , \c p3 , ...
Parameters may also be given from a data column. Then first the params from the column and the the parameters from the vector are numbered.
Use the variable \c x in an equation to refer to the free parameter of the curve.
*/
class LIB_EXPORT JKQTPxParsedFunctionLineGraph: public JKQTPxFunctionLineGraph {
Q_OBJECT
@ -55,10 +57,8 @@ class LIB_EXPORT JKQTPxParsedFunctionLineGraph: public JKQTPxFunctionLineGraph {
/** \brief class destructor */
virtual ~JKQTPxParsedFunctionLineGraph();
JKQTPGET_SET_MACRO(QList<double>, parameters)
JKQTPGET_SET_MACRO(QString, function)
JKQTPGET_SET_MACRO(QList<double>, errorParameters)
JKQTPGET_SET_MACRO(QString, errorFunction)
/** \brief INTERNAL data structure
@ -73,23 +73,81 @@ class LIB_EXPORT JKQTPxParsedFunctionLineGraph: public JKQTPxFunctionLineGraph {
protected:
/** \brief the function to be evaluated for the plot. Use \c x as the free variable, e.g. \c "x^2+2" */
QString function;
/** \brief values of the parameters \c p1 , \c p2 , \c p3 , ... */
QList<double> parameters;
JKQTPxParsedFunctionLineGraphFunctionData fdata;
/** \brief the function to be evaluated to add error indicators to the graph. This function is evaluated to an error for every x. Use \c x as the free variable, e.g. \c "x^2+2". */
QString errorFunction;
/** \brief values of the parameters \c p1 , \c p2 , \c p3 , ... for the error function*/
QList<double> errorParameters;
JKQTPxParsedFunctionLineGraphFunctionData efdata;
virtual void set_plotFunction(jkqtpPlotFunctionType&& f);
virtual void set_plotFunction(const jkqtpPlotFunctionType& f);
JKQTPGET_SET_MACRO_I(void*, params, clearData())
JKQTPGET_SET_MACRO(jkqtpPlotFunctionType, errorPlotFunction)
JKQTPGET_SET_MACRO(void*, errorParams)
// hide functions that should not be used in this class!
using JKQTPxFunctionLineGraph::set_plotFunction;
using JKQTPxFunctionLineGraph::set_params;
using JKQTPxFunctionLineGraph::set_errorPlotFunction;
using JKQTPxFunctionLineGraph::set_errorParams;
/** \brief fill the data array with data from the function plotFunction */
virtual void createPlotData(bool collectParams=true);
/** \brief implements the actual plot function */
static double JKQTPxParsedFunctionLineGraphFunction(double x, void *data);
};
/*! \brief This implements line plots where the data is taken from a user supplied function \f$ x=f(y) \f$ The function is defined as a string and parsed by JKMathParser
\ingroup jkqtplotter_plots
Additional function parameters may be given in the vector parameters. They are accessible in the function as \c p1 , \c p2 , \c p3 , ...
Parameters may also be given from a data column. Then first the params from the column and the the parameters from the vector are numbered.
Use the variable \c y in an equation to refer to the free parameter of the curve (\c is also understood for convenience).
*/
class LIB_EXPORT JKQTPyParsedFunctionLineGraph: public JKQTPyFunctionLineGraph {
Q_OBJECT
public:
/** \brief class constructor */
JKQTPyParsedFunctionLineGraph(JKQtBasePlotter* parent=nullptr);
/** \brief class constructor */
JKQTPyParsedFunctionLineGraph(JKQtPlotter* parent);
/** \brief class destructor */
virtual ~JKQTPyParsedFunctionLineGraph();
JKQTPGET_SET_MACRO(QString, function)
JKQTPGET_SET_MACRO(QString, errorFunction)
/** \brief INTERNAL data structure
* \internal
*/
struct JKQTPyParsedFunctionLineGraphFunctionData {
JKQTPMathParser* parser;
JKQTPMathParser::jkmpNode* node;
int varcount;
};
protected:
/** \brief the function to be evaluated for the plot. Use \c x as the free variable, e.g. \c "x^2+2" */
QString function;
JKQTPyParsedFunctionLineGraphFunctionData fdata;
/** \brief the function to be evaluated to add error indicators to the graph. This function is evaluated to an error for every x. Use \c x as the free variable, e.g. \c "x^2+2". */
QString errorFunction;
JKQTPyParsedFunctionLineGraphFunctionData efdata;
// hide functions that should not be used in this class!
using JKQTPxFunctionLineGraph::set_plotFunction;
using JKQTPxFunctionLineGraph::set_params;
using JKQTPxFunctionLineGraph::set_errorPlotFunction;
using JKQTPxFunctionLineGraph::set_errorParams;
/** \brief fill the data array with data from the function plotFunction */
virtual void createPlotData(bool collectParams=true);
/** \brief implements the actual plot function */
static double JKQTPyParsedFunctionLineGraphFunction(double x, void *data);
};
#endif // jkqtpgraphsparsedfunction_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -3,8 +3,10 @@
# JKQtPlotter
## Plotting Mathematical Functions as Line Graphs
### Basics
This project (see `./test/simpletest_functionplot/`) demonstrates how to plot mathematical functions as line graphs. The functions may be defined as static C functions, C++ functors or c++ inline functions. See [test/simpletest_parsedfunctionplot](https://github.com/jkriege2/JKQtPlotter/tree/master/test/simpletest_parsedfunctionplot) for an example of how to use an internal equation parser provided with JKQtPlotter instead of directly defining functions.
### Simple C++ inline function
The first example shows how to plot a C++ inline function:
```c++
JKQTPxFunctionLineGraph* func1=new JKQTPxFunctionLineGraph(plot);
@ -13,6 +15,7 @@ The first example shows how to plot a C++ inline function:
plot->addGraph(func1);
```
### Simple C++ inline function with parameters
In any such plot function, you can also use parameters, provided via the second parameter. Usually these are "internal parameters", defined by `func2->set_paramsV(p0, p1, ...)`:
```c++
JKQTPxFunctionLineGraph* func2=new JKQTPxFunctionLineGraph(plot);
@ -42,6 +45,7 @@ In any such plot function, you can also use parameters, provided via the second
plot->addGraph(func3);
```
### C++ functors as plot functions
You can also use C++ functors (or function objects):
```c++
struct SincSqr {
@ -62,7 +66,8 @@ You can also use C++ functors (or function objects):
plot->addGraph(func4);
```
... or simple static C functions:
### Static C functions
You can also plot simple static C functions:
```c++
double sinc(double x) {
return 10.0*sin(x)/x;
@ -76,21 +81,44 @@ You can also use C++ functors (or function objects):
plot->addGraph(func5);
```
Finally `JKQTPxFunctionLineGraph` provides a small set of special functions (polynomial, exponential, ...), which draw their parameters from the internal or external parameters:
### Predefined "special" functions
Finally `JKQTPxFunctionLineGraph` provides a small set of special functions (polynomial `p0+p1*x+p2*x^2+...`, exponential `p0+p1*exp(x/p2)`, power-law `p0+p1*x^p2`, ...), which are parametrized from the internal or external parameters:
```c++
JKQTPxFunctionLineGraph* func6=new JKQTPxFunctionLineGraph(plot);
func6->setSpecialFunction(JKQTPxFunctionLineGraph::Line);
// here we set offset and slope of the line
// here we set offset p0=-1 and slope p1=1.5 of the line p0+p1*x
func6->set_paramsV(-1,1.5);
func6->set_title("special function: linear");
plot->addGraph(func6);
```
To demonstrate how to use parameters from a datastore column, have a look at the next example. It is derived from the special-function plot above, but adds a line with a different offset and slope and reads the parameters from a datastore column `paramCol`, which is initialized from the vector `params`:
```c++
JKQTPxFunctionLineGraph* func7=new JKQTPxFunctionLineGraph(plot);
func7->setSpecialFunction(JKQTPxFunctionLineGraph::Line);
// here we set offset p0=1 and slope p1=-1.5 of the line p0+p1*x by adding these into a column
// in the internal datastore and then set that column as parameterColumn for the function graph
QVector<double> params;
params << /*p0=*/1 << /*p1=*/-1.5;
size_t paramCol=plot->getDatastore()->addCopiedColumn(params);
func7->set_parameterColumn(paramCol);
func7->set_title("special function: linear");
plot->addGraph(func7);
```
### Screenshot
This code snippets above result in a plot like this:
![jkqtplotter_simpletest_functionplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_functionplot.png)
### Notes
Note that all the different variants to provide parameters can be used with all types of functions!
Also see the example [Plotting Parsed Mathematical Functions as Line Graphs](https://github.com/jkriege2/JKQtPlotter/tree/master/test/simpletest_parsedfunctionplot) for details on how the actual plotting algorithm works. That example also shows how to define a function as a string, which is then parsed and evaluated by an expression parser library embedded in JKQtPlotter.
All examples above use the graph class `JKQTPxFunctionLineGraph`, which plots a function `y=f(x)`. If you want to plot a function `x=f(y)`, you can use the class `JKQTPyFunctionLineGraph` instead. If in the examples above, we exchange all `JKQTPxFunctionLineGraph` for `JKQTPyFunctionLineGraph`, the graphs will be rotated by 90 degree, as all functions are interpreted as `x=f(y)`:
![jkqtplotter_simpletest_functionplot_fy](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_functionplot_fy.png)
[Back to JKQTPlotter main page](https://github.com/jkriege2/JKQtPlotter/)

View File

@ -79,14 +79,26 @@ int main(int argc, char* argv[])
func5->set_title("static C function $10*\\sin(x)/x$");
plot->addGraph(func5);
// 6. finally JKQTPxFunctionLineGraph defines a small set of common functions
// 7. finally JKQTPxFunctionLineGraph defines a small set of common functions
JKQTPxFunctionLineGraph* func6=new JKQTPxFunctionLineGraph(plot);
func6->setSpecialFunction(JKQTPxFunctionLineGraph::Line);
// here we set offset and slope of the line
// here we set offset p0=-1 and slope p1=1.5 of the line p0+p1*x
func6->set_paramsV(-1,1.5);
func6->set_title("special function: linear");
func6->set_title("special function: linear p_0=-1, p_1=1.5");
plot->addGraph(func6);
// 7. finally JKQTPxFunctionLineGraph defines a small set of common functions
JKQTPxFunctionLineGraph* func7=new JKQTPxFunctionLineGraph(plot);
func7->setSpecialFunction(JKQTPxFunctionLineGraph::Line);
// here we set offset p0=1 and slope p1=-1.5 of the line p0+p1*x by adding these into a column
// in the internal datastore and then set that column as parameterColumn for the function graph
QVector<double> params;
params << /*p0=*/1 << /*p1=*/-1.5;
size_t paramCol=plot->getDatastore()->addCopiedColumn(params);
func7->set_parameterColumn(paramCol);
func7->set_title("special function: linear p_0=1, p_1=-1.5");
plot->addGraph(func7);
// 8. set some axis properties (we use LaTeX for nice equation rendering)
plot->getXAxis()->set_axisLabel(QObject::tr("x-axis"));

View File

@ -3,8 +3,11 @@
# JKQtPlotter
## Plotting Parsed Mathematical Functions as Line Graphs
### Plot Function f(x)
This project (see `./test/simpletest_parsedfunctionplot/`) demonstrates how to plot mathematical functions as line graphs. The functions are defined as strings that will be evaluated with the equation parser, integrated into JKQtPlotter.
Note: See the example [Plotting Mathematical Functions as Line Graphs](https://github.com/jkriege2/JKQtPlotter/tree/master/test/simpletest_functionplot) if you don't want to draw parsed functions, but want to provide a C function, or C++ functor!
Adding an evaluated funtion to a graph is very simple:
```c++
JKQTPxParsedFunctionLineGraph* parsedFunc=new JKQTPxParsedFunctionLineGraph(plot);
@ -45,6 +48,23 @@ This code snippet results in a plot like this:
![jkqtplotter_simpletest_parsedfunctionplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot.png)
### Plotting with parameters
As shown in [Plotting Mathematical Functions as Line Graphs](https://github.com/jkriege2/JKQtPlotter/tree/master/test/simpletest_functionplot) you can also use externally set parameters in a plot function. These parameters can be double numbers and may be set with either as an internal parameter vector, or may be read from a parameter column (as shown in the [linked example](https://github.com/jkriege2/JKQtPlotter/tree/master/test/simpletest_functionplot)). These parameters are available as variables `p1`, `p2`, ... in the function string. Here is a small example:
```c++
JKQTPxParsedFunctionLineGraph* parsedFunc=new JKQTPxParsedFunctionLineGraph(plot);
parsedFunc->set_function("sin(x*p1)*exp(-x/p2)");
parsedFunc->set_paramV(/*p1=*/8, /*p2=*/4);
parsedFunc->set_title("user function");
```
### Plot Function f(y)
If you use the graph class `JKQTPyParsedFunctionLineGraph` instead of `JKQTPxParsedFunctionLineGraph`, you can plot functions `x=f(y)` (instead of `y=f(x)`). The function from the example above will then ahve to be changed to `sin(y*8)*exp(-y/4)` and the result will look like this:
![jkqtplotter_simpletest_parsedfunctionplot_fy](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot_fy.png)
### Properties of the Adaptive Plotting Algorithm
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)

View File

@ -1,6 +1,8 @@
#include <QApplication>
#include <QLineEdit>
#include <QCheckBox>
#include <QFormLayout>
#include <QDoubleSpinBox>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsparsedfunction.h"
@ -16,11 +18,23 @@ int main(int argc, char* argv[])
QLineEdit* edit=new QLineEdit(&mainWin);
edit->setToolTip("enter a function in dependence of the variable <tt>x</tt> and press ENTER to update the graph");
QCheckBox* check=new QCheckBox("display sample points");
QDoubleSpinBox* spinP1=new QDoubleSpinBox(&mainWin);
spinP1->setValue(8);
spinP1->setRange(-10000,10000);
spinP1->setToolTip("enter a Value for parameter <tt>p1</tt> and press ENTER to update the graph");
QDoubleSpinBox* spinP2=new QDoubleSpinBox(&mainWin);
spinP2->setValue(4);
spinP2->setRange(-10000,10000);
spinP2->setToolTip("enter a Value for parameter <tt>p1</tt> and press ENTER to update the graph");
JKQtPlotter* plot=new JKQtPlotter(&mainWin);
QFormLayout* flayout=new QFormLayout;
QVBoxLayout* layout=new QVBoxLayout;
mainWin.setLayout(layout);
layout->addWidget(edit);
layout->addWidget(check);
flayout->addRow("equation:", edit);
flayout->addRow("p1 =", spinP1);
flayout->addRow("p2 =", spinP2);
flayout->addRow("", check);
layout->addLayout(flayout);
layout->addWidget(plot);
// 2. now we add a JKQTPxParsedFunctionLineGraph object, which will draw the function from
@ -31,15 +45,18 @@ int main(int argc, char* argv[])
// the graph is updated:
auto updateGraphFunctor=
[=]() {
parsedFunc->set_title("user function: \\verb{"+edit->text()+"}");
parsedFunc->set_title(QString("user function: \\verb{"+edit->text()+"}, p_1=%1, p_2=%2").arg(spinP1->value()).arg(spinP2->value()));
parsedFunc->set_function(edit->text());
parsedFunc->set_paramsV(spinP1->value(), spinP2->value());
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)");
QObject::connect(spinP1, &QDoubleSpinBox::editingFinished, updateGraphFunctor);
QObject::connect(spinP2, &QDoubleSpinBox::editingFinished, updateGraphFunctor);
edit->setText("sin(x*p1)*exp(-x/p2)");
updateGraphFunctor();