diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index 5b55d92c04..eeb0b137b5 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -23,6 +23,7 @@ Changes, compared to \ref page_whatsnew_V2019_11 "v2019.11" include:
  • removed the usage of some deprecated functions and objects (e.g. QMatrix)
  • improved: geometric objects now use an adaptive drawing algorithm to represent curves (before e.g. ellipses were always separated into a fixed number of line-segments)
  • improved: constructors and access functions for several geometric objects (e.g. more constructors, additional functions to retrieve parameters in diferent forms, iterators for polygons, ...)
  • +
  • improved: reworked class hirarchy of parsed function plots and declared several setters as slots.
  • new: added geometric plot objects JKQTPGeoArrow to draw arrows (aka lines with added line-end decorators, also extended JKQTPGeoLine, JKQTPGeoInfiniteLine, JKQTPGeoPolyLines to draw line-end decorator (aka arrows)
  • new: all geometric objects can either be drawn as graphic element (i.e. lines are straight line, even on non-linear axes), or as mathematical curve (i.e. on non-linear axes, lines become the appropriate curve representing the linear function, connecting the given start/end-points). The only exceptions are ellipses (and the derived arcs,pies,chords), which are always drawn as mathematical curves
  • new: a new graph class JKQTPXYFunctionLineGraph draws parametric 2D curves ( \f$ [x,y] = f(t) \f$ ), see \ref JKQTPlotterEvalCurves for an example
  • diff --git a/examples/evalcurve/README.md b/examples/evalcurve/README.md index a0ac644885..5106946a14 100644 --- a/examples/evalcurve/README.md +++ b/examples/evalcurve/README.md @@ -1,6 +1,8 @@ # Example (JKQTPlotter): Plotting Parametric Mathematical Curves as Line Graphs {#JKQTPlotterEvalCurves} ## Basics -This project (see `./examples/evalcurve/`) demonstrates how to plot mathematical functions as line graphs. The functions may be defined as static C functions, C++ functors or c++ inline functions. +This project (see `./examples/evalcurve/`) demonstrates how to plot mathematical functions as line graphs. The functions may be defined as static C functions, C++ functors or c++ inline functions. The functions may simply depend on the parameter `t`, or on `t` and a vector of parameters. + +The class uses an adaptive algorithm, which determines by the local slope, at how many points (or how close points) the functor has to be evaluated. [TOC] @@ -9,24 +11,55 @@ The example shows how to plot a simple C++ inline function: ```.cpp JKQTPXYFunctionLineGraph* func1=new JKQTPXYFunctionLineGraph(plot); - func1->setPlotFunctionFunctor([](double t) -> QPointF { - const double a=5; - const double b=4; - const double delta=JKQTPSTATISTICS_PI/4.0; - return QPointF(sin(a*t+delta), sin(b*t)); + func1->setPlotFunctionFunctor([](double t) ->QPointF { + return QPointF( + sin(t)*(exp(cos(t))-2.0*cos(4.0*t)-jkqtp_pow5(sin(t/12.0))), + cos(t)*(exp(cos(t))-2.0*cos(4.0*t)-jkqtp_pow5(sin(t/12.0))) + ); }); - func1->setTRange(0, 2.0*JKQTPSTATISTICS_PI); - func1->setTitle("C++-inline function $[ sin(5{\\cdot}t+\\pi/4), sin(4{\\cdot}t) ]$"); + func1->setTRange(0, 12.0*JKQTPSTATISTICS_PI); + func1->setTitle("C++-inline function: \"Butterfly Curve\""); plot->addGraph(func1); ``` -Note that here a functor is defined, which calculates the points on a [Lissajous Curve](https://en.wikipedia.org/wiki/Lissajous_curve), i.e. a function mapping a parameter `t` to a two-dimensional point `QPointF` with `x=sin(a*t+delta)`and `y=sin(b*t)`. This function is evaluated on a range of values for `t`, set by +Note that here a functor is defined, which calculates the points on a [Butterfly Curve](https://en.wikipedia.org/wiki/Butterfly_curve_(transcendental)), i.e. a function mapping a parameter `t` to a two-dimensional point `QPointF` with complex functions for x and y. This function is evaluated on a range of values for `t`, set by ```.cpp func1->setTRange(0, 2.0*JKQTPSTATISTICS_PI); ``` -The class uses an adaptive algorithm, which determines by the local slope, at how many points (or how close points) the functor has to be evaluated. +# Simple C++ inline function with Parameters +`JKQTPXYFunctionLineGraph` allows to use more complex functors alternatively: These depend on the variable `t`and a vector of parameters. Here is an example: + +```.cpp + func2->setPlotFunctionFunctor([](double t, const QVector& params) ->QPointF { + return QPointF( + 3.0*sin(params[0]*t+params[2])+8.0, + 3.0*sin(params[1]*t) + ); + }); + // now we define the 3 parameters of the function + func2->setParamsV(5, 4, JKQTPSTATISTICS_PI/4.0); + // and define the range over which to evaluate + func2->setTRange(0, 2.0*JKQTPSTATISTICS_PI); + func1->setTitle("C++-inline function $[ sin(5{\\cdot}t+\\pi/4), sin(4{\\cdot}t) ]$"); + plot->addGraph(func1); +``` + +In the functor we can use the entries in the vector `param` as function parameters. The values in this vector are defined by + +```.cpp + // now we define the 3 parameters of the function + func2->setParamsV(5, 4, JKQTPSTATISTICS_PI/4.0); +``` + +Alternatively they can also be taken from a column in the internal datastore. Then you have to call: + +```.cpp + func2->setParameterColumn(ColumnID); +``` + +instead, where `ColumnID` is the ID of the column with the parameter values. # Screenshot @@ -36,7 +69,6 @@ This code snippets above result in a plot like this: # Notes -Just as shown in [examples/functionplot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/functionplot) for JKQTPXFunctionLineGraph and JKQTPYFunctionLineGraph, different types of functions can be used to plot. Either simple C++ inline functions, that take a `double t` and return a `QPointF`, or functions that additionally take a parameter vector `void* params`. In that case, the parameters may be provided from different sources, as described in [examples/functionplot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/functionplot) . - +This example describes how to draw 2D parametric curves. For (simpler) 1D-functions f(x) or f(y), see [examples/functionplot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/functionplot) . diff --git a/examples/evalcurve/evalcurve.cpp b/examples/evalcurve/evalcurve.cpp index 11c82eed45..c22db16202 100644 --- a/examples/evalcurve/evalcurve.cpp +++ b/examples/evalcurve/evalcurve.cpp @@ -27,14 +27,34 @@ int main(int argc, char* argv[]) // the function is defined as C++ inline function JKQTPXYFunctionLineGraph* func1=new JKQTPXYFunctionLineGraph(plot); func1->setPlotFunctionFunctor([](double t) ->QPointF { - const double a=5; - const double b=4; - const double delta=JKQTPSTATISTICS_PI/4.0; - return QPointF(sin(a*t+delta), sin(b*t)); + return QPointF( + sin(t)*(exp(cos(t))-2.0*cos(4.0*t)-jkqtp_pow5(sin(t/12.0))), + cos(t)*(exp(cos(t))-2.0*cos(4.0*t)-jkqtp_pow5(sin(t/12.0))) + ); }); - func1->setTRange(0, 2.0*JKQTPSTATISTICS_PI); - func1->setTitle("C++-inline function $[ sin(5{\\cdot}t+\\pi/4), sin(4{\\cdot}t) ]$"); + func1->setTRange(0, 12.0*JKQTPSTATISTICS_PI); + func1->setTitle("C++-inline function: \"Butterfly Curve\""); plot->addGraph(func1); + + + + // 3. a second JKQTPXYFunctionLineGraph object shows how to use functions that + // also take a parameter vector, in addition to the dependent variable t + JKQTPXYFunctionLineGraph* func2=new JKQTPXYFunctionLineGraph(plot); + func2->setPlotFunctionFunctor([](double t, const QVector& params) ->QPointF { + return QPointF( + 3.0*sin(params[0]*t+params[2])+8.0, + 3.0*sin(params[1]*t) + ); + }); + // now we define the 3 parameters of the function + func2->setParamsV(5, 4, JKQTPSTATISTICS_PI/4.0); + // and define the range over which to evaluate + func2->setTRange(0, 2.0*JKQTPSTATISTICS_PI); + func2->setTitle("C++-inline function $[ sin(5{\\cdot}t+\\pi/4), sin(4{\\cdot}t) ]$"); + plot->addGraph(func2); + + // 8. set some axis properties (we use LaTeX for nice equation rendering) plot->getXAxis()->setAxisLabel(QObject::tr("x-axis")); plot->getYAxis()->setAxisLabel(QObject::tr("y-axis")); @@ -42,7 +62,7 @@ int main(int argc, char* argv[]) // 4. scale the plot so the graph is contained - plot->setXY(-1.1,1.1,-1.1,1.1); + plot->setXY(-3,12,-3.2,3.2); // show window and make it a decent size mainWin.show(); diff --git a/examples/functionplot/README.md b/examples/functionplot/README.md index e854a10b58..bd4367ce52 100644 --- a/examples/functionplot/README.md +++ b/examples/functionplot/README.md @@ -108,3 +108,6 @@ All examples above use the graph class `JKQTPXFunctionLineGraph`, which plots a ![functionplot_fy](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/functionplot_fy.png) +This example describes how to draw 1D functions. For an example of how to draw 2D parametric curves `[x,y]=f(t)`, see [examples/evalcurve](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/evalcurve) . + + diff --git a/lib/jkqtcommon/jkqtpmathtools.h b/lib/jkqtcommon/jkqtpmathtools.h index b8cd839d32..36d544165b 100644 --- a/lib/jkqtcommon/jkqtpmathtools.h +++ b/lib/jkqtcommon/jkqtpmathtools.h @@ -290,12 +290,22 @@ inline T jkqtp_sqr(const T& v) { \ingroup jkqtptools_math_basic */ -template -inline T jkqtp_pow4(T x) { + template + inline T jkqtp_pow4(T x) { const T xx=x*x; return xx*xx; } +/*! \brief 5-th power of a number + \ingroup jkqtptools_math_basic + +*/ +template +inline T jkqtp_pow5(T x) { + const T xx=x*x; + return xx*xx*x; +} + /*! \brief cube of a number \ingroup jkqtptools_math_basic diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp index 4f25373fee..e676f6f8e0 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp @@ -31,29 +31,22 @@ - - -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTBasePlotter* parent): - JKQTPEvaluatedFunctionWithParamsGraphBase(parent) +JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(JKQTBasePlotter* parent): + JKQTPEvaluatedFunctionWithErrorsGraphBase(parent), + drawLine(true), + fillCurve(false), + drawErrorPolygons(false), + drawErrorLines(false) { - functionType=SpecialFunction::UserFunction; - drawLine=true; - fillCurve=false; - initLineStyle(parent, parentPlotStyle); initFillStyle(parent, parentPlotStyle); - drawErrorPolygons=false; - drawErrorLines=false; - errorParams=nullptr; errorColor=getLineColor().lighter(); errorFillColor=getLineColor().lighter(); errorStyle=Qt::SolidLine; errorLineWidth=1; errorFillStyle=Qt::SolidPattern; - errorParameterColumn=-1; - if (parent && parentPlotStyle>=0) { // get style settings from parent object //std::cout<<"got style settings from parent: "<getPlotter()) +JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(JKQTPlotter* parent): + JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(parent->getPlotter()) { } -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(parent) -{ - title=title_; - plotFunction=jkqtpPlotFunctionType(); - simplePlotFunction=f; - functionType=SpecialFunction::UserFunction; - data.clear(); -} - -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTPlotter *parent): - JKQTPXFunctionLineGraph(f, title_, parent->getPlotter()) -{ -} - -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(parent) -{ - title=title_; - plotFunction=jkqtpPlotFunctionType(); - simplePlotFunction=std::move(f); - functionType=SpecialFunction::UserFunction; - data.clear(); -} - -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTPlotter *parent): - JKQTPXFunctionLineGraph(std::move(f), title_, parent->getPlotter()) -{ - -} - -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(parent) -{ - title=title_; - functionType=type; - setParams(params); - data.clear(); -} - -JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title, JKQTPlotter *parent): - JKQTPXFunctionLineGraph(type, params, title, parent->getPlotter()) +JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::~JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase() { } -JKQTPXFunctionLineGraph::~JKQTPXFunctionLineGraph() { - data.clear(); -} - - -void JKQTPXFunctionLineGraph::setDrawLine(bool __value) +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setDrawLine(bool __value) { this->drawLine = __value; } -bool JKQTPXFunctionLineGraph::getDrawLine() const +bool JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getDrawLine() const { return this->drawLine; } -void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(const jkqtpPlotFunctionType &__value) -{ - simplePlotFunction=jkqtpSimplePlotFunctionType(); - plotFunction = __value; - functionType=SpecialFunction::UserFunction; - - data.clear(); -} - -void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(const jkqtpSimplePlotFunctionType &__value) -{ - plotFunction=jkqtpPlotFunctionType(); - simplePlotFunction=__value; - functionType=SpecialFunction::UserFunction; - - data.clear(); -} - -void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(jkqtpPlotFunctionType &&__value) -{ - simplePlotFunction=jkqtpSimplePlotFunctionType(); - plotFunction = std::move(__value); - functionType=SpecialFunction::UserFunction; - data.clear(); -} - -void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(jkqtpSimplePlotFunctionType &&__value) -{ - plotFunction=jkqtpPlotFunctionType(); - simplePlotFunction=std::move(__value); - functionType=SpecialFunction::UserFunction; - - data.clear(); -} - -jkqtpPlotFunctionType JKQTPXFunctionLineGraph::getPlotFunctionFunctor() const -{ - return plotFunction; -} - -jkqtpSimplePlotFunctionType JKQTPXFunctionLineGraph::getSimplePlotFunction() const -{ - return simplePlotFunction; -} - -void JKQTPXFunctionLineGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) { +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen p=getLinePen(painter, parent); p.setJoinStyle(Qt::RoundJoin); @@ -195,73 +97,110 @@ void JKQTPXFunctionLineGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, QRect } -QColor JKQTPXFunctionLineGraph::getKeyLabelColor() const { +QColor JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getKeyLabelColor() const { return getLineColor(); } -bool JKQTPXFunctionLineGraph::getXMinMax(double &minx, double &maxx, double &smallestGreaterZero) -{ - smallestGreaterZero=minx=maxx=0; return false; + +QBrush JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorBrush(JKQTPEnhancedPainter& /*painter*/) const { + QBrush b; + b.setColor(errorFillColor); + b.setStyle(errorFillStyle); + return b; } -bool JKQTPXFunctionLineGraph::getYMinMax(double &miny, double &maxy, double &smallestGreaterZero) -{ - smallestGreaterZero=miny=maxy=0; return false; +QPen JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorLinePen(JKQTPEnhancedPainter& painter) const { + QPen p; + p.setColor(errorColor); + p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*errorLineWidth))); + p.setStyle(errorStyle); + p.setJoinStyle(Qt::RoundJoin); + p.setCapStyle(Qt::RoundCap); + + return p; } -void JKQTPXFunctionLineGraph::createPlotData(bool collectParams) { + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setErrorLineColor(const QColor &__value) +{ + this->errorColor = __value; +} + +QColor JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorLineColor() const +{ + return this->errorColor; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setErrorFillColor(const QColor &__value) +{ + this->errorFillColor = __value; +} + +QColor JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorFillColor() const +{ + return this->errorFillColor; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setErrorFillStyle(Qt::BrushStyle __value) +{ + this->errorFillStyle = __value; +} + +Qt::BrushStyle JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorFillStyle() const +{ + return this->errorFillStyle; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setErrorLineStyle(Qt::PenStyle __value) +{ + this->errorStyle = __value; +} + +Qt::PenStyle JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorLineStyle() const +{ + return this->errorStyle; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setErrorLineWidth(double __value) +{ + this->errorLineWidth = __value; +} + +double JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getErrorLineWidth() const +{ + return this->errorLineWidth; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setColor(QColor c) +{ + setLineColor(c); + c.setAlphaF(0.5); + setHighlightingLineColor(c); +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setDrawErrorPolygons(bool __value) +{ + this->drawErrorPolygons = __value; +} + +bool JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getDrawErrorPolygons() const +{ + return this->drawErrorPolygons; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::setDrawErrorLines(bool __value) +{ + this->drawErrorLines = __value; +} + +bool JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::getDrawErrorLines() const +{ + return this->drawErrorLines; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::drawXGraph(JKQTPEnhancedPainter& painter) { #ifdef JKQTBP_AUTOTIMER - JKQTPAutoOutputTimer jkaat(QString("JKQTPXFunctionLineGraph[%1]::createPlotData()").arg(title)); -#endif - data.clear(); - if (collectParams) collectParameters(); - - if (parent==nullptr) return; - if (!plotFunction && !simplePlotFunction) return; - - jkqtpSimplePlotFunctionType func; - if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, getInternalParams()); - else if (simplePlotFunction) func=simplePlotFunction; - - const double xmin=parent->getXMin(); - const double xmax=parent->getXMax(); - - std::function fTransformedFunc= std::bind([&](const JKQTPPlotElement* plot, double x) -> QPointF { return plot->transform(x, func(x)); }, this, std::placeholders::_1); - - JKQTPAdaptiveFunctionGraphEvaluator evaluator(fTransformedFunc, minSamples, maxRefinementDegree, slopeTolerance, minPixelPerSample); - data=evaluator.evaluate(xmin, xmax); - data=JKQTPSimplyfyLineSegemnts(data, dataCleanupMaxAllowedAngleDegree); -} - -void JKQTPXFunctionLineGraph::collectParameters() -{ - JKQTPEvaluatedFunctionWithParamsGraphBase::collectParameters(); - - if (parent && errorParameterColumn>=0) { - ierrorparams.clear(); - JKQTPDatastore* datastore=parent->getDatastore(); - int imin=0; - int imax= static_cast(datastore->getRows(errorParameterColumn)); - - for (int i=imin; iget(errorParameterColumn,i); - ierrorparams<=0 && !JKQTPIsOKFloat(ierrorparams[i])) { - ierrorparams.remove(i,1); - i--; - } - - errorParams=&ierrorparams; - } -} - - - -void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { -#ifdef JKQTBP_AUTOTIMER - JKQTPAutoOutputTimer jkaaot("JKQTPXFunctionLineGraph::draw"); + JKQTPAutoOutputTimer jkaaot("JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::drawXGraph"); #endif if (parent==nullptr) return; JKQTPDatastore* datastore=parent->getDatastore(); @@ -271,6 +210,8 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { createPlotData(); //qDebug()<<"plot data created\n"; + auto errorPlotFunction=buildErrorFunctorSpec(); + drawErrorsBefore(painter); { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); @@ -308,11 +249,11 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { //std::cout<<"(xv, yv) = ( "<(errorPlotFunction))) { - double e=errorPlotFunction(xv, getInternalErrorParams()); + const double e=errorPlotFunction(xv).y(); ype=transformY(yv+e); yme=transformY(yv-e); ype=qBound(yami, ype, yama); @@ -384,73 +325,9 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { //std::cout<<"plot done\n"; } - - - - - - - - - - - - - - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(parent) -{ - -} - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPlotter *parent): - JKQTPYFunctionLineGraph(parent->getPlotter()) -{ - -} - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title, JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(f, title, parent) -{ - -} - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title, JKQTPlotter *parent): - JKQTPXFunctionLineGraph(f, title, parent) -{ - -} - - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title, JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(std::move(f), title, parent) -{ - -} - - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title, JKQTPlotter *parent): - JKQTPXFunctionLineGraph(std::move(f), title, parent) -{ - -} - - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPYFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(type, params, title_, parent) -{ -} - -JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPYFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTPlotter *parent): - JKQTPXFunctionLineGraph(type, params, title_, parent->getPlotter()) -{ - -} - -void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { +void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::drawYGraph(JKQTPEnhancedPainter& painter) { #ifdef JKQTBP_AUTOTIMER - JKQTPAutoOutputTimer jkaaot("JKQTPYFunctionLineGraph::draw"); + JKQTPAutoOutputTimer jkaaot("JKQTPYFunctionLineGraph::drawYGraph"); #endif if (parent==nullptr) return; JKQTPDatastore* datastore=parent->getDatastore(); @@ -460,6 +337,8 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { createPlotData(); //std::cout<<"plot data created\n"; + auto errorPlotFunction=buildErrorFunctorSpec(); + drawErrorsBefore(painter); { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); @@ -497,11 +376,11 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { //std::cout<<"(xv, yv) = ( "<(errorPlotFunction))) { - double e=errorPlotFunction(yv, getInternalErrorParams()); + const double e=errorPlotFunction(yv).x(); xpe=transformX(xv+e); xme=transformX(xv-e); xpe=qBound(xami, xpe, xama); @@ -573,124 +452,80 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { } -void JKQTPYFunctionLineGraph::createPlotData(bool collectParams) { + + + + + + + + + + +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(JKQTBasePlotter* parent): + JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(parent) +{ + +} + +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(JKQTPlotter* parent): + JKQTPFunctorLineGraphBase(parent->getPlotter()) +{ + +} + +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(parent) +{ + title=title_; + plotFunction=jkqtpPlotFunctionType(); + simplePlotFunction=f; data.clear(); - if (collectParams) collectParameters(); - - if (parent==nullptr) return; - if (!plotFunction && !simplePlotFunction) return; - - jkqtpSimplePlotFunctionType func; - if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, getInternalParams()); - else if (simplePlotFunction) func=simplePlotFunction; - - const double ymin=parent->getYMin(); - const double ymax=parent->getYMax(); - - std::function fTransformedFunc= std::bind([&](const JKQTPPlotElement* plot, double y) -> QPointF { return plot->transform(func(y), y); }, this, std::placeholders::_1); - - JKQTPAdaptiveFunctionGraphEvaluator evaluator(fTransformedFunc, minSamples, maxRefinementDegree, slopeTolerance, minPixelPerSample); - data=evaluator.evaluate(ymin, ymax); - data=JKQTPSimplyfyLineSegemnts(data, dataCleanupMaxAllowedAngleDegree); - } - - - -QBrush JKQTPXFunctionLineGraph::getErrorBrush(JKQTPEnhancedPainter& /*painter*/) const { - QBrush b; - b.setColor(errorFillColor); - b.setStyle(errorFillStyle); - return b; -} - -QPen JKQTPXFunctionLineGraph::getErrorLinePen(JKQTPEnhancedPainter& painter) const { - QPen p; - p.setColor(errorColor); - p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*errorLineWidth))); - p.setStyle(errorStyle); - p.setJoinStyle(Qt::RoundJoin); - p.setCapStyle(Qt::RoundCap); - - return p; -} - - - -void JKQTPXFunctionLineGraph::setErrorParams(const QVector &errorParams) +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(f, title_, parent->getPlotter()) { - ierrorparams=errorParams; - setErrorParams(&ierrorparams); } -void JKQTPXFunctionLineGraph::setErrorParameterColumn(int __value) +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(parent) { - this->errorParameterColumn = __value; + title=title_; + plotFunction=jkqtpPlotFunctionType(); + simplePlotFunction=std::move(f); + data.clear(); } -int JKQTPXFunctionLineGraph::getErrorParameterColumn() const +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(std::move(f), title_, parent->getPlotter()) { - return this->errorParameterColumn; + } -void JKQTPXFunctionLineGraph::setErrorParameterColumn(size_t __value) { - this->errorParameterColumn = static_cast(__value); -} - -void JKQTPXFunctionLineGraph::setErrorLineColor(const QColor &__value) +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(JKQTPFunctorLineGraphBase::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(parent) { - this->errorColor = __value; + title=title_; + setSpecialFunction(type); + setParams(params); + data.clear(); } -QColor JKQTPXFunctionLineGraph::getErrorLineColor() const +JKQTPFunctorLineGraphBase::JKQTPFunctorLineGraphBase(JKQTPFunctorLineGraphBase::SpecialFunction type, const QVector ¶ms, const QString &title, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(type, params, title, parent->getPlotter()) { - return this->errorColor; + } -void JKQTPXFunctionLineGraph::setErrorFillColor(const QColor &__value) -{ - this->errorFillColor = __value; + +JKQTPFunctorLineGraphBase::~JKQTPFunctorLineGraphBase() { + } -QColor JKQTPXFunctionLineGraph::getErrorFillColor() const +void JKQTPFunctorLineGraphBase::setSpecialFunction(JKQTPFunctorLineGraphBase::SpecialFunction function) { - return this->errorFillColor; -} - -void JKQTPXFunctionLineGraph::setErrorFillStyle(Qt::BrushStyle __value) -{ - this->errorFillStyle = __value; -} - -Qt::BrushStyle JKQTPXFunctionLineGraph::getErrorFillStyle() const -{ - return this->errorFillStyle; -} - -void JKQTPXFunctionLineGraph::setErrorLineStyle(Qt::PenStyle __value) -{ - this->errorStyle = __value; -} - -Qt::PenStyle JKQTPXFunctionLineGraph::getErrorLineStyle() const -{ - return this->errorStyle; -} - -void JKQTPXFunctionLineGraph::setErrorLineWidth(double __value) -{ - this->errorLineWidth = __value; -} - -double JKQTPXFunctionLineGraph::getErrorLineWidth() const -{ - return this->errorLineWidth; -} - -void JKQTPXFunctionLineGraph::setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction function) -{ - if (function==JKQTPXFunctionLineGraph::Polynomial) { + if (function==JKQTPFunctorLineGraphBase::Polynomial) { setPlotFunctionFunctor([](double x, const QVector& param) { double res=0; if (param.size()>0) { @@ -705,7 +540,7 @@ void JKQTPXFunctionLineGraph::setSpecialFunction(JKQTPXFunctionLineGraph::Specia return res; }); } - else if (function==JKQTPXFunctionLineGraph::Exponential) setPlotFunctionFunctor([](double x, const QVector& param) { + else if (function==JKQTPFunctorLineGraphBase::Exponential) setPlotFunctionFunctor([](double x, const QVector& param) { double res=0; if (param.size()>=3) { res=param.value(0,0)+param.value(1,0)*exp(x/param.value(2,0)); @@ -714,7 +549,7 @@ void JKQTPXFunctionLineGraph::setSpecialFunction(JKQTPXFunctionLineGraph::Specia } return res; }); - else if (function==JKQTPXFunctionLineGraph::PowerLaw) setPlotFunctionFunctor([](double x, const QVector& param) { + else if (function==JKQTPFunctorLineGraphBase::PowerLaw) setPlotFunctionFunctor([](double x, const QVector& param) { double res=0; if (param.size()>=3) { res=param.value(0,0)+param.value(1,0)*pow(x, param.value(2,1)); @@ -728,84 +563,279 @@ void JKQTPXFunctionLineGraph::setSpecialFunction(JKQTPXFunctionLineGraph::Specia else throw std::runtime_error("unknown special function type"); } -JKQTPXFunctionLineGraph::SpecialFunction JKQTPXFunctionLineGraph::getFunctionType() const -{ - return functionType; -} -QVector JKQTPXFunctionLineGraph::getInternalErrorParams() const { - return ierrorparams; -} - -void JKQTPXFunctionLineGraph::setDrawErrorPolygons(bool __value) -{ - this->drawErrorPolygons = __value; -} - -bool JKQTPXFunctionLineGraph::getDrawErrorPolygons() const -{ - return this->drawErrorPolygons; -} - -void JKQTPXFunctionLineGraph::setDrawErrorLines(bool __value) -{ - this->drawErrorLines = __value; -} - -bool JKQTPXFunctionLineGraph::getDrawErrorLines() const -{ - return this->drawErrorLines; -} - -void JKQTPXFunctionLineGraph::setErrorPlotFunction(const jkqtpPlotFunctionType &__value) +void JKQTPFunctorLineGraphBase::setErrorPlotFunction(const jkqtpPlotFunctionType &__value) { errorSimplePlotFunction=jkqtpSimplePlotFunctionType(); errorPlotFunction=__value; data.clear(); } -void JKQTPXFunctionLineGraph::setErrorPlotFunction(jkqtpPlotFunctionType &&__value) +void JKQTPFunctorLineGraphBase::setErrorPlotFunction(jkqtpPlotFunctionType &&__value) { errorSimplePlotFunction=jkqtpSimplePlotFunctionType(); errorPlotFunction = std::move(__value); data.clear(); } -jkqtpPlotFunctionType JKQTPXFunctionLineGraph::getErrorPlotFunction() const +jkqtpPlotFunctionType JKQTPFunctorLineGraphBase::getErrorPlotFunction() const { return errorPlotFunction; } -void JKQTPXFunctionLineGraph::setErrorPlotFunction(const jkqtpSimplePlotFunctionType &__value) +void JKQTPFunctorLineGraphBase::setErrorPlotFunction(const jkqtpSimplePlotFunctionType &__value) { errorPlotFunction=jkqtpPlotFunctionType(); errorSimplePlotFunction=__value; data.clear(); } -void JKQTPXFunctionLineGraph::setErrorPlotFunction(jkqtpSimplePlotFunctionType &&__value) +void JKQTPFunctorLineGraphBase::setErrorPlotFunction(jkqtpSimplePlotFunctionType &&__value) { errorPlotFunction=jkqtpPlotFunctionType(); errorSimplePlotFunction = std::move(__value); data.clear(); } -jkqtpSimplePlotFunctionType JKQTPXFunctionLineGraph::getErrorSimplePlotFunction() const + +jkqtpSimplePlotFunctionType JKQTPFunctorLineGraphBase::getErrorSimplePlotFunction() const { return errorSimplePlotFunction; } -void JKQTPXFunctionLineGraph::setErrorParams(void *__value) +void JKQTPFunctorLineGraphBase::setPlotFunctionFunctor(const jkqtpPlotFunctionType &__value) { - this->errorParams = __value; + simplePlotFunction=jkqtpSimplePlotFunctionType(); + plotFunction = __value; + data.clear(); } -void *JKQTPXFunctionLineGraph::getErrorParams() const +void JKQTPFunctorLineGraphBase::setPlotFunctionFunctor(const jkqtpSimplePlotFunctionType &__value) { - return this->errorParams; + plotFunction=jkqtpPlotFunctionType(); + simplePlotFunction=__value; + data.clear(); +} + +void JKQTPFunctorLineGraphBase::setPlotFunctionFunctor(jkqtpPlotFunctionType &&__value) +{ + simplePlotFunction=jkqtpSimplePlotFunctionType(); + plotFunction = std::move(__value); + data.clear(); +} + +void JKQTPFunctorLineGraphBase::setPlotFunctionFunctor(jkqtpSimplePlotFunctionType &&__value) +{ + plotFunction=jkqtpPlotFunctionType(); + simplePlotFunction=std::move(__value); + data.clear(); +} + +jkqtpPlotFunctionType JKQTPFunctorLineGraphBase::getPlotFunctionFunctor() const +{ + return plotFunction; +} + +jkqtpSimplePlotFunctionType JKQTPFunctorLineGraphBase::getSimplePlotFunction() const +{ + return simplePlotFunction; +} + +bool JKQTPFunctorLineGraphBase::isSimplePlotFunction() const +{ + return !static_cast(plotFunction) && static_cast(simplePlotFunction); } -bool JKQTPXFunctionLineGraph::usesColumn(int c) const + + + + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTBasePlotter* parent): + JKQTPFunctorLineGraphBase(parent) { - return JKQTPEvaluatedFunctionWithParamsGraphBase::usesColumn(c)||(c==errorParameterColumn); + } +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPlotter* parent): + JKQTPFunctorLineGraphBase(parent) +{ + +} + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(f, title_, parent) +{ +} + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(f, title_, parent) +{ +} + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(std::move(f), title_, parent) +{ +} + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(std::move(f), title_, parent) +{ + +} + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(type, params, title_, parent) +{ +} + +JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(type, params, title, parent) +{ + +} + + +JKQTPXFunctionLineGraph::~JKQTPXFunctionLineGraph() { + +} + +void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter &painter) +{ + drawXGraph(painter); +} + + +JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec JKQTPXFunctionLineGraph::buildPlotFunctorSpec() +{ + JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec spec; + + if (parent==nullptr) return spec; // return an invalid PlotFunctorSpec + if (!plotFunction && !simplePlotFunction) return spec; // return an invalid PlotFunctorSpec + + // range over which to evaluate func + spec.range_start=parent->getXMin(); + spec.range_end=parent->getXMax(); + + // the actual function to use + if (plotFunction) spec.func=std::bind([&](double x, const QVector& p) -> QPointF { return QPointF(x, plotFunction(x, p)); }, std::placeholders::_1, getInternalParams()); + else if (simplePlotFunction) spec.func=[&](double x) -> QPointF { return QPointF(x, simplePlotFunction(x)); }; + + return spec; +} + +std::function JKQTPXFunctionLineGraph::buildErrorFunctorSpec() +{ + std::function spec; + if (parent==nullptr) return spec; // return an invalid PlotFunctorSpec + if (!plotFunction && !simplePlotFunction) return spec; // return an invalid PlotFunctorSpec + + // the actual function to use + if (errorPlotFunction) spec=std::bind([&](double x, const QVector& p) -> QPointF { return QPointF(0, errorPlotFunction(x, p)); }, std::placeholders::_1, getInternalParams()); + else if (errorSimplePlotFunction) spec=[&](double x) -> QPointF { return QPointF(0, errorSimplePlotFunction(x)); }; + + return spec; +} + + + + + + + + + + + + + + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(parent) +{ + +} + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPlotter *parent): + JKQTPYFunctionLineGraph(parent->getPlotter()) +{ + +} + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(f, title, parent) +{ + +} + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(f, title, parent) +{ + +} + + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(std::move(f), title, parent) +{ + +} + + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(std::move(f), title, parent) +{ + +} + + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPYFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTBasePlotter *parent): + JKQTPFunctorLineGraphBase(type, params, title_, parent) +{ +} + +JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPYFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title_, JKQTPlotter *parent): + JKQTPFunctorLineGraphBase(type, params, title_, parent) +{ + +} + +void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter &painter) +{ + drawYGraph(painter); +} + +JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec JKQTPYFunctionLineGraph::buildPlotFunctorSpec() +{ + JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec spec; + + if (parent==nullptr) return spec; // return an invalid PlotFunctorSpec + if (!plotFunction && !simplePlotFunction) return spec; // return an invalid PlotFunctorSpec + + // range over which to evaluate func + spec.range_start=parent->getYMin(); + spec.range_end=parent->getYMax(); + + // the actual function to use + if (plotFunction) spec.func=std::bind([&](double y, const QVector& p) -> QPointF { return QPointF(plotFunction(y, p), y); }, std::placeholders::_1, getInternalParams()); + else if (simplePlotFunction) spec.func=[&](double y) -> QPointF { return QPointF(simplePlotFunction(y), y); }; + + return spec; +} + +std::function JKQTPYFunctionLineGraph::buildErrorFunctorSpec() +{ + std::function spec; + if (parent==nullptr) return spec; // return an invalid PlotFunctorSpec + if (!plotFunction && !simplePlotFunction) return spec; // return an invalid PlotFunctorSpec + + // the actual function to use + if (errorPlotFunction) spec=std::bind([&](double y, const QVector& p) -> QPointF { return QPointF(errorPlotFunction(y, p), 0); }, std::placeholders::_1, getInternalParams()); + else if (errorSimplePlotFunction) spec=[&](double y) -> QPointF { return QPointF(errorSimplePlotFunction(y), 0); }; + + return spec; +} + + + + + + diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h index 246227409c..970a04f544 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h @@ -33,6 +33,118 @@ #define jkqtpgraphsevaluatedfunction_H +/** \brief This class extends JKQTPEvaluatedFunctionWithErrorsGraphBase with functions to draw the graphs and + * set the drawing style + * \ingroup jkqtplotter_functiongraphs + * + * \note Since this class is meant as a base for both f(x)- and f(y)-functions, it cannot + * implememt JKQTPGraph::draw(). Therefore it provides two implementations drawXGraph() + * and drawYGraph() and the user has to decide in the concrete graph class, which one to call + * (or whether to do the drawing completely different). + * + * \see e.g. JKQTPXFunctionLineGraph for a concrete implementation + */ +class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase: public JKQTPEvaluatedFunctionWithErrorsGraphBase, public JKQTPGraphLineStyleMixin, public JKQTPGraphFillStyleMixin { + Q_OBJECT + public: + + /** \brief class constructor */ + JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(JKQTBasePlotter* parent=nullptr); + + /** \brief class constructor */ + JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(JKQTPlotter* parent); + + /** \brief class destructor */ + virtual ~JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase() override; + + /** \brief plots a key marker inside the specified rectangle \a rect */ + virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) override; + /** \brief returns the color to be used for the key label */ + virtual QColor getKeyLabelColor() const override; + + /*! \copydoc drawLine */ + bool getDrawLine() const; + + + /*! \copydoc drawErrorPolygons */ + bool getDrawErrorPolygons() const; + /*! \copydoc drawErrorLines */ + bool getDrawErrorLines() const; + /*! \copydoc errorColor */ + virtual QColor getErrorLineColor() const; + /*! \copydoc errorFillColor */ + virtual QColor getErrorFillColor() const; + /*! \copydoc errorFillStyle */ + virtual Qt::BrushStyle getErrorFillStyle() const; + /*! \copydoc errorStyle */ + virtual Qt::PenStyle getErrorLineStyle() const; + /*! \copydoc errorLineWidth */ + virtual double getErrorLineWidth() const; + + + + + + public slots: + /*! \brief set color, fill color and error color at the same time */ + void setColor(QColor c); + + /*! \copydoc drawLine */ + void setDrawLine(bool __value); + /*! \copydoc drawErrorPolygons */ + void setDrawErrorPolygons(bool __value); + /*! \copydoc drawErrorLines */ + void setDrawErrorLines(bool __value); + /*! \copydoc errorColor */ + virtual void setErrorLineColor(const QColor & __value); + /*! \copydoc errorFillColor */ + virtual void setErrorFillColor(const QColor & __value); + /*! \copydoc errorFillStyle */ + virtual void setErrorFillStyle(Qt::BrushStyle __value); + /*! \copydoc errorStyle */ + virtual void setErrorLineStyle(Qt::PenStyle __value); + /*! \copydoc errorLineWidth */ + virtual void setErrorLineWidth(double __value); + + protected: + /** \brief plots the graph to the plotter object specified as parent */ + void drawXGraph(JKQTPEnhancedPainter& painter); + /** \brief plots the graph to the plotter object specified as parent */ + void drawYGraph(JKQTPEnhancedPainter& painter); + + + + + /** \brief indicates whether to draw a line or not */ + bool drawLine; + /** \brief indicates whether to fill the space between the curve and the x-axis */ + bool fillCurve; + + + /** \brief indicates whether an error polygon should be drawn */ + bool drawErrorPolygons; + /** \brief indicates whether error lines should be drawn */ + bool drawErrorLines; + + + + /** \brief color of the error graph */ + QColor errorColor; + /** \brief color of the error graph fill */ + QColor errorFillColor; + /** \brief linestyle of the error graph lines */ + Qt::PenStyle errorStyle; + /** \brief width (pixels) of the error graph */ + double errorLineWidth; + /** \brief fill style, if the error curve should be filled */ + Qt::BrushStyle errorFillStyle; + + + QBrush getErrorBrush(JKQTPEnhancedPainter& painter) const; + QPen getErrorLinePen(JKQTPEnhancedPainter &painter) const; + +}; + /*! \brief type of functions that may be plotted by JKQTPXFunctionLineGraph and JKQTPYFunctionLineGraph @@ -54,84 +166,43 @@ typedef std::function&)> jkqtpPlotFunctionT 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_functiongraphs - This class uses the intelligent plotting algorithm for functions, implemented in JKQTPAdaptiveFunctionGraphEvaluator. - It starts by sampling the function at minSamples positions. Then each function interval is bisected recursively if - necessary. To do so the function is evaluated at the mid point and the slopes \f$ \alpha_{\mbox{left}} \f$ - and \f$ \alpha_{\mbox{right}} \f$ of the two linear segments are compared. the midpoint is added - to the graph if \f[ \left|\alpha_{\mbox{right}}-\alpha_{\mbox{left}}\right|>\mbox{slopeTolerance} \f] - In addition all sampling points except minimum and maximum are beeing shifted by a random fraction their - distance to the other points. This helps to prevent beats when sampling periodic functions. - - Finally the obtained data is cleaned up to reduce the amount of points, by deleting a point, when it leads to an - angle between consecutive line-segments of less than dataCleanupMaxAllowedAngleDegree. - - The following image shows some example graphs: - - \image html plot_functionplots.png - - \see \ref JKQTPlotterFunctionPlots, JKQTPAdaptiveFunctionGraphEvaluator, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph, jkqtpstatAddPolyFit(), jkqtpstatAddWeightedRegression(), jkqtpstatAddRobustIRLSRegression(), jkqtpstatAddRegression(), jkqtpstatAddLinearWeightedRegression(), jkqtpstatAddRobustIRLSLinearRegression(), jkqtpstatAddLinearRegression() +/** \brief extends JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase with the capabilities to define functions from C++-functors + * of type jkqtpSimplePlotFunctionType or jkqtpPlotFunctionType + * \ingroup jkqtplotter_functiongraphs + * + * \see JKQTPXFunctionLineGraph and JKQTPYFunctionLineGraph for a concrete implementation */ -class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPEvaluatedFunctionWithParamsGraphBase, public JKQTPGraphLineStyleMixin, public JKQTPGraphFillStyleMixin { +class JKQTPLOTTER_LIB_EXPORT JKQTPFunctorLineGraphBase: public JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase { Q_OBJECT public: - enum SpecialFunction { 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$ */ - - UserFunction, /*!< \brief no special function but the function is provided by the user */ }; /** \brief class constructor */ - JKQTPXFunctionLineGraph(JKQTBasePlotter* parent=nullptr); + JKQTPFunctorLineGraphBase(JKQTBasePlotter* parent=nullptr); /** \brief class constructor */ - JKQTPXFunctionLineGraph(JKQTPlotter* parent); + JKQTPFunctorLineGraphBase(JKQTPlotter* parent); /** \brief class constructor */ - JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType & f, const QString& title, JKQTBasePlotter* parent=nullptr); + JKQTPFunctorLineGraphBase(const jkqtpSimplePlotFunctionType & f, const QString& title, JKQTBasePlotter* parent=nullptr); /** \brief class constructor */ - JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType & f, const QString& title, JKQTPlotter* parent); + JKQTPFunctorLineGraphBase(const jkqtpSimplePlotFunctionType & f, const QString& title, JKQTPlotter* parent); /** \brief class constructor */ - JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType && f, const QString& title, JKQTBasePlotter* parent=nullptr); + JKQTPFunctorLineGraphBase(jkqtpSimplePlotFunctionType && f, const QString& title, JKQTBasePlotter* parent=nullptr); /** \brief class constructor */ - JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType && f, const QString& title, JKQTPlotter* parent); + JKQTPFunctorLineGraphBase(jkqtpSimplePlotFunctionType && f, const QString& title, JKQTPlotter* parent); /** \brief class constructor */ - JKQTPXFunctionLineGraph(SpecialFunction type, const QVector& params, const QString& title, JKQTBasePlotter* parent); + JKQTPFunctorLineGraphBase(SpecialFunction type, const QVector& params, const QString& title, JKQTBasePlotter* parent); /** \brief class constructor */ - JKQTPXFunctionLineGraph(SpecialFunction type, const QVector& params, const QString& title, JKQTPlotter* parent); + JKQTPFunctorLineGraphBase(SpecialFunction type, const QVector& params, const QString& title, JKQTPlotter* parent); /** \brief class destructor */ - virtual ~JKQTPXFunctionLineGraph() override; - - /** \brief plots the graph to the plotter object specified as parent */ - virtual void draw(JKQTPEnhancedPainter& painter) override; - /** \brief plots a key marker inside the specified rectangle \a rect */ - virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) override; - /** \brief returns the color to be used for the key label */ - virtual QColor getKeyLabelColor() const override; - - /** \brief get the maximum and minimum x-value of the graph - * - * This functions returns 0 for both parameters, so that the plotter uses the predefined - * min and max values. - */ - virtual bool getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) override; - /** \brief get the maximum and minimum y-value of the graph - */ - virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override; - - /*! \brief set color, fill color and error color at the same time */ - void setColor(QColor c); - - /*! \copydoc drawLine */ - void setDrawLine(bool __value); - /*! \copydoc drawLine */ - bool getDrawLine() const; + virtual ~JKQTPFunctorLineGraphBase() override; /** \brief sets a functor to be plotted * @@ -153,23 +224,20 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPEvaluatedFunct * \see simplePlotFunction */ virtual void setPlotFunctionFunctor (const jkqtpSimplePlotFunctionType & __value); - /*! \copydoc plotFunction */ \ + /*! \copydoc plotFunction + * + * \see isSimplePlotFunction() */ \ virtual jkqtpPlotFunctionType getPlotFunctionFunctor () const; - /*! \copydoc simplePlotFunction */ \ + /*! \copydoc simplePlotFunction + * + * \see isSimplePlotFunction() */ \ virtual jkqtpSimplePlotFunctionType getSimplePlotFunction () const; + /** \brief returns whether the plot function was defined as a jkqtpSimpleParametricCurveFunctionType (\c true ) or + * a jkqtpParametricCurveFunctionType (\c false ) */ + bool isSimplePlotFunction() const; - /** \brief returns the currently set internal parameter vector */ - QVector getInternalErrorParams() const; - /*! \copydoc drawErrorPolygons */ - void setDrawErrorPolygons(bool __value); - /*! \copydoc drawErrorPolygons */ - bool getDrawErrorPolygons() const; - /*! \copydoc drawErrorLines */ - void setDrawErrorLines(bool __value); - /*! \copydoc drawErrorLines */ - bool getDrawErrorLines() const; /** \brief sets a functor to be used for calculating errors * * \see errorPlotFunction @@ -194,100 +262,73 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPEvaluatedFunct virtual void setErrorPlotFunction (const jkqtpSimplePlotFunctionType & __value); /*! \copydoc errorSimplePlotFunction */ \ virtual jkqtpSimplePlotFunctionType getErrorSimplePlotFunction () const; - /*! \copydoc errorParams */ - virtual void setErrorParams(void* __value); - /*! \copydoc errorParams */ - void *getErrorParams() const; - /** \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 setErrorParams(const QVector& errorParams); - /*! \copydoc errorParameterColumn */ - void setErrorParameterColumn(int __value); - /*! \copydoc errorParameterColumn */ - int getErrorParameterColumn() const; - /*! \copydoc errorParameterColumn */ - void setErrorParameterColumn (size_t __value); - /*! \copydoc errorColor */ - virtual void setErrorLineColor(const QColor & __value); - /*! \copydoc errorColor */ - virtual QColor getErrorLineColor() const; - /*! \copydoc errorFillColor */ - virtual void setErrorFillColor(const QColor & __value); - /*! \copydoc errorFillColor */ - virtual QColor getErrorFillColor() const; - /*! \copydoc errorFillStyle */ - virtual void setErrorFillStyle(Qt::BrushStyle __value); - /*! \copydoc errorFillStyle */ - virtual Qt::BrushStyle getErrorFillStyle() const; - /*! \copydoc errorStyle */ - virtual void setErrorLineStyle(Qt::PenStyle __value); - /*! \copydoc errorStyle */ - virtual Qt::PenStyle getErrorLineStyle() const; - /*! \copydoc errorLineWidth */ - virtual void setErrorLineWidth(double __value); - /*! \copydoc errorLineWidth */ - virtual double getErrorLineWidth() const; - - /** \copydoc JKQTPGraph::usesColumn() */ - virtual bool usesColumn(int c) const override; /** \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 ensure that current function parameters for plotFunction (which may stem from different sources, as direct data, a datastore column ...) are stored in iparams and ierrorparams */ - virtual void collectParameters() override; - - - /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData( bool collectParams=true) override; - - - /** \brief if set, the values from this datatsore column are used for the parameters \c p1 , \c p2 , \c p3 , ... of the error plot function */ - int errorParameterColumn; - - /** \brief indicates whether to draw a line or not */ - bool drawLine; - /** \brief indicates whether to fill the space between the curve and the x-axis */ - 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 indicates whether an error polygon should be drawn */ - bool drawErrorPolygons; - /** \brief indicates whether error lines should be drawn */ - 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 */ - QColor errorColor; - /** \brief color of the error graph fill */ - QColor errorFillColor; - /** \brief linestyle of the error graph lines */ - Qt::PenStyle errorStyle; - /** \brief width (pixels) of the error graph */ - double errorLineWidth; - /** \brief fill style, if the error curve should be filled */ - Qt::BrushStyle errorFillStyle; +/*! \brief This implements line plots where the data is taken from a user supplied function \f$ y=f(x) \f$ + \ingroup jkqtplotter_functiongraphs + + This class uses the intelligent plotting algorithm for functions, implemented in JKQTPAdaptiveFunctionGraphEvaluator. + + The following image shows some example graphs: + + \image html plot_functionplots.png + + \see \ref JKQTPlotterFunctionPlots, JKQTPAdaptiveFunctionGraphEvaluator, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph, jkqtpstatAddPolyFit(), jkqtpstatAddWeightedRegression(), jkqtpstatAddRobustIRLSRegression(), jkqtpstatAddRegression(), jkqtpstatAddLinearWeightedRegression(), jkqtpstatAddRobustIRLSLinearRegression(), jkqtpstatAddLinearRegression() + */ +class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPFunctorLineGraphBase { + Q_OBJECT + public: - QBrush getErrorBrush(JKQTPEnhancedPainter& painter) const; - QPen getErrorLinePen(JKQTPEnhancedPainter &painter) const; - /** \brief internal storage for the current error function parameters for errorPlotFunction (which may stem from different sources, as direct data, a datastore column ...) */ - QVector ierrorparams; + /** \brief class constructor */ + JKQTPXFunctionLineGraph(JKQTBasePlotter* parent=nullptr); + + /** \brief class constructor */ + JKQTPXFunctionLineGraph(JKQTPlotter* parent); + /** \brief class constructor */ + JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType & f, const QString& title, JKQTBasePlotter* parent=nullptr); + /** \brief class constructor */ + JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType & f, const QString& title, JKQTPlotter* parent); + /** \brief class constructor */ + JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType && f, const QString& title, JKQTBasePlotter* parent=nullptr); + /** \brief class constructor */ + JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType && f, const QString& title, JKQTPlotter* parent); + /** \brief class constructor */ + JKQTPXFunctionLineGraph(SpecialFunction type, const QVector& params, const QString& title, JKQTBasePlotter* parent); + /** \brief class constructor */ + JKQTPXFunctionLineGraph(SpecialFunction type, const QVector& params, const QString& title, JKQTPlotter* parent); + + /** \brief class destructor */ + virtual ~JKQTPXFunctionLineGraph() override; + + /** \brief plots the graph to the plotter object specified as parent */ + virtual void draw(JKQTPEnhancedPainter& painter) override; + + protected: + + /** \copydoc JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() */ + virtual PlotFunctorSpec buildPlotFunctorSpec() override; + + /** \copydoc JKQTPEvaluatedFunctionWithErrorsGraphBase::buildPlotFunctorSpec() */ + virtual std::function buildErrorFunctorSpec() override; + }; /*! \brief This implements line plots where the data is taken from a user supplied function \f$ x=f(y) \f$ @@ -299,7 +340,7 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPEvaluatedFunct \see \ref JKQTPlotterFunctionPlots , JKQTPXFunctionLineGraph, JKQTPXYFunctionLineGraph */ -class JKQTPLOTTER_LIB_EXPORT JKQTPYFunctionLineGraph: public JKQTPXFunctionLineGraph { +class JKQTPLOTTER_LIB_EXPORT JKQTPYFunctionLineGraph: public JKQTPFunctorLineGraphBase { Q_OBJECT public: /** \brief class constructor */ @@ -323,8 +364,11 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPYFunctionLineGraph: public JKQTPXFunctionLineG virtual void draw(JKQTPEnhancedPainter& painter) override; protected: - /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData( bool collectParams=true) override; + /** \copydoc JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() */ + virtual PlotFunctorSpec buildPlotFunctorSpec() override; + + /** \copydoc JKQTPEvaluatedFunctionWithErrorsGraphBase::buildPlotFunctorSpec() */ + virtual std::function buildErrorFunctorSpec() override; }; diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp index 103362b109..d46a06a1fb 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp @@ -35,16 +35,17 @@ JKQTPEvaluatedFunctionGraphBase::JKQTPEvaluatedFunctionGraphBase(JKQTBasePlotter* parent): - JKQTPGraph(parent) + JKQTPGraph(parent), + parameterColumn(-1), + minSamples(50), + maxRefinementDegree(5), + slopeTolerance(0.005), + minPixelPerSample(32), + dataCleanupMaxAllowedAngleDegree(0.2), + displaySamplePoints(false) { - minSamples=50; - maxRefinementDegree=5; - slopeTolerance=0.005; - minPixelPerSample=32; - dataCleanupMaxAllowedAngleDegree=0.2; - displaySamplePoints=false; data.clear(); - + iparams.clear(); } JKQTPEvaluatedFunctionGraphBase::JKQTPEvaluatedFunctionGraphBase(JKQTPlotter* parent): @@ -127,6 +128,24 @@ void JKQTPEvaluatedFunctionGraphBase::setDisplaySamplePoints(bool __value) this->displaySamplePoints = __value; } +void JKQTPEvaluatedFunctionGraphBase::createPlotData(bool collectParams) +{ +#ifdef JKQTBP_AUTOTIMER + JKQTPAutoOutputTimer jkaat(QString("JKQTPEvaluatedFunctionWithErrorsGraphBase[%1]::createPlotData()").arg(title)); +#endif + data.clear(); + if (collectParams) collectParameters(); + PlotFunctorSpec plotfunc= buildPlotFunctorSpec(); + + if (plotfunc.isValid()) { + std::function fTransformedFunc= std::bind([plotfunc](const JKQTPPlotElement* plot, double t) -> QPointF { return plot->transform(plotfunc.func(t)); }, this, std::placeholders::_1); + + JKQTPAdaptiveFunctionGraphEvaluator evaluator(fTransformedFunc, minSamples, maxRefinementDegree, slopeTolerance, minPixelPerSample); + data=evaluator.evaluate(plotfunc.range_start, plotfunc.range_end); + data=JKQTPSimplyfyLineSegemnts(data, dataCleanupMaxAllowedAngleDegree); + } +} + bool JKQTPEvaluatedFunctionGraphBase::getDisplaySamplePoints() const { return this->displaySamplePoints; @@ -181,25 +200,7 @@ bool JKQTPEvaluatedFunctionGraphBase::getYMinMax(double &miny, double &maxy, dou } - -JKQTPEvaluatedFunctionWithParamsGraphBase::JKQTPEvaluatedFunctionWithParamsGraphBase(JKQTBasePlotter *parent): - parameterColumn(-1) -{ - -} - -JKQTPEvaluatedFunctionWithParamsGraphBase::JKQTPEvaluatedFunctionWithParamsGraphBase(JKQTPlotter *parent): - JKQTPEvaluatedFunctionWithParamsGraphBase(parent->getPlotter()) -{ - -} - -JKQTPEvaluatedFunctionWithParamsGraphBase::~JKQTPEvaluatedFunctionWithParamsGraphBase() -{ - -} - -void JKQTPEvaluatedFunctionWithParamsGraphBase::collectParameters() +void JKQTPEvaluatedFunctionGraphBase::collectParameters() { if (parent && parameterColumn>=0) { iparams.clear(); @@ -221,67 +222,195 @@ void JKQTPEvaluatedFunctionWithParamsGraphBase::collectParameters() } -void JKQTPEvaluatedFunctionWithParamsGraphBase::setParams(const QVector ¶ms) +void JKQTPEvaluatedFunctionGraphBase::setParams(const QVector ¶ms) { iparams=params; } -void JKQTPEvaluatedFunctionWithParamsGraphBase::setCopiedParams(const double *params, int N) +void JKQTPEvaluatedFunctionGraphBase::setCopiedParams(const double *params, int N) { QVector v; for (int i=0; i p; p< p; p< p; p< p; p< p; p<parameterColumn = __value; } -int JKQTPEvaluatedFunctionWithParamsGraphBase::getParameterColumn() const +int JKQTPEvaluatedFunctionGraphBase::getParameterColumn() const { return this->parameterColumn; } -void JKQTPEvaluatedFunctionWithParamsGraphBase::setParameterColumn(size_t __value) { +void JKQTPEvaluatedFunctionGraphBase::setParameterColumn(size_t __value) { this->parameterColumn = static_cast(__value); } -QVector JKQTPEvaluatedFunctionWithParamsGraphBase::getInternalParams() const { +const QVector& JKQTPEvaluatedFunctionGraphBase::getInternalParams() const { return iparams; } -bool JKQTPEvaluatedFunctionWithParamsGraphBase::usesColumn(int c) const +QVector &JKQTPEvaluatedFunctionGraphBase::getInternalParams() +{ + return iparams; +} + +bool JKQTPEvaluatedFunctionGraphBase::usesColumn(int c) const { return (c==parameterColumn); } + +JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec::PlotFunctorSpec(): + func(), + range_start(0), + range_end(0) +{ + +} + +bool JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec::isValid() const +{ + return static_cast(func) && (fabs(range_end-range_start)>0); +} + + + + +JKQTPEvaluatedFunctionWithErrorsGraphBase::JKQTPEvaluatedFunctionWithErrorsGraphBase(JKQTBasePlotter *parent): + JKQTPEvaluatedFunctionGraphBase(parent), + errorParameterColumn(-1) +{ + +} + +JKQTPEvaluatedFunctionWithErrorsGraphBase::JKQTPEvaluatedFunctionWithErrorsGraphBase(JKQTPlotter *parent): + JKQTPEvaluatedFunctionWithErrorsGraphBase(parent->getPlotter()) +{ + +} + +JKQTPEvaluatedFunctionWithErrorsGraphBase::~JKQTPEvaluatedFunctionWithErrorsGraphBase() +{ + +} + +const QVector& JKQTPEvaluatedFunctionWithErrorsGraphBase::getInternalErrorParams() const { + return ierrorparams; +} + +QVector& JKQTPEvaluatedFunctionWithErrorsGraphBase::getInternalErrorParams() { + return ierrorparams; +} + +bool JKQTPEvaluatedFunctionWithErrorsGraphBase::usesColumn(int c) const +{ + return JKQTPEvaluatedFunctionGraphBase::usesColumn(c)||(c==errorParameterColumn); +} + +void JKQTPEvaluatedFunctionWithErrorsGraphBase::setErrorParams(const QVector &errorParams) +{ + ierrorparams=errorParams; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphBase::setErrorParameterColumn(int __value) +{ + this->errorParameterColumn = __value; +} + +int JKQTPEvaluatedFunctionWithErrorsGraphBase::getErrorParameterColumn() const +{ + return this->errorParameterColumn; +} + +void JKQTPEvaluatedFunctionWithErrorsGraphBase::setErrorParameterColumn(size_t __value) { + this->errorParameterColumn = static_cast(__value); +} + +void JKQTPEvaluatedFunctionWithErrorsGraphBase::setErrorParamsV(double p1) +{ + QVector p; + p< p; + p< p; + p< p; + p< p; + p<=0) { + ierrorparams.clear(); + JKQTPDatastore* datastore=parent->getDatastore(); + int imin=0; + int imax= static_cast(datastore->getRows(errorParameterColumn)); + + for (int i=imin; iget(errorParameterColumn,i); + ierrorparams<=0 && !JKQTPIsOKFloat(ierrorparams[i])) { + ierrorparams.remove(i,1); + i--; + } + } +} diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h index 728220b28f..4399cb215c 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h @@ -34,10 +34,26 @@ -/** \brief Base class for graph classes that evaluate a mathematical function (e.g. defined as a C-fucntion), +/** \brief Base class for graph classes that evaluate a mathematical function (e.g. defined as a C-function), * using an adaptive plotting algorithm from JKQTPAdaptiveFunctionGraphEvaluator * \ingroup jkqtplotter_functiongraphs * + * This class always plots a general 2D-graph \f$ [x,y]=\vec{f}(t) \f$ , which is calculated in dependence of + * a parameter \f$ t \f$ . This parametrization is general enough to cover the cases of parametric function, as well as + * x- and y-dependent function graphs: + * - plot a function \f$ f(x) \f$ i.e. the plot points will be \f$ [x, f(x)] \f$ + * and the value rage will be the x-axis range. This is implemented by e.g. JKQTPXFunctionLineGraph. + * - plot a function \f$ f(y) \f$ i.e. the plot points will be \f$ [f(y), y] \f$ + * and the value rage will be the y-axis range. This is implemented by e.g. JKQTPYFunctionLineGraph. + * - plot a function \f$ [x,y]=\vec{f}(t) \f$ i.e. the plot points will be \f$ \vec{f}(t) \f$ + * and the value rage will be a user-defined range for \f$ gt \f$. + * This is implemented by e.g. JKQTPXYFunctionLineGraph. + * . + * + * In order to implement a special cas, one has to override/implement buildPlotFunctorSpec(), which + * returns a functor and a value-range that can represent the cases above. + * + * * This class uses the intelligent plotting algorithm for functions, implemented in JKQTPAdaptiveFunctionGraphEvaluator. * It starts by sampling the function at minSamples positions. Then each function interval is bisected recursively if * necessary. To do so the function is evaluated at the mid point and the slopes \f$ \alpha_{\mbox{left}} \f$ @@ -50,6 +66,9 @@ * angle between consecutive line-segments of less than dataCleanupMaxAllowedAngleDegree. * * + * + * + * * \see JKQTPAdaptiveFunctionGraphEvaluator, JKQTPXFunctionLineGraph, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph */ class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionGraphBase: public JKQTPGraph { @@ -75,6 +94,21 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionGraphBase: public JKQTPGraph */ virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override; + /** \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!!!) */ + virtual void setParams(const QVector& params); + /** \brief sets the params from a copy of the given array of length \a N */ + void setCopiedParams(const double* params, int N); + + /** \brief returns the currently set internal parameter vector */ + const QVector& getInternalParams() const; + /** \brief returns the currently set internal parameter vector */ + QVector& getInternalParams(); + /** \copydoc parameterColumn */ + int getParameterColumn() const; + + /** \copydoc JKQTPGraph::usesColumn() */ + virtual bool usesColumn(int c) const override; + /** \copydoc minSamples */ unsigned int getMinSamples() const; @@ -102,12 +136,73 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionGraphBase: public JKQTPGraph /** \copydoc displaySamplePoints */ void setDisplaySamplePoints(bool __value); + /** \brief set an internal parameter vector as function parameters, initialized with {p1} */ + void setParamsV(double p1); + /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2} */ + void setParamsV(double p1, double p2); + /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2,p3} */ + void setParamsV(double p1, double p2, double p3); + /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2,p3,p4} */ + void setParamsV(double p1, double p2, double p3, double p4); + /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2,p3,p4,p5} */ + void setParamsV(double p1, double p2, double p3, double p4, double p5); + + /** \copydoc parameterColumn */ + void setParameterColumn(int __value); + /** \copydoc parameterColumn */ + void setParameterColumn (size_t __value); + protected: - /** \brief plot data calculated by createPlotData(), i.e. the datapoints \f$ \mbox{transform}\left(x, y=f(x, \vec{p})\right) \f$ to be plotted */ - QVector data; + /** \brief specifies an internal plot functor \see buildPlotFunctor() */ + struct PlotFunctorSpec { + /** brief construct an in-valid PlotFunctorSpec ... will become valid, by assigning a non-zero range and a plot-function */ + PlotFunctorSpec(); + /** \brief calculates the points \f$ [x,y] \f$ on the function graph, in dependence on + * a dependent parameter variable, could be e.g. \f$ [x, f(x)] \f$ for plotting + * a function \f$ f(x) \f$ over the x-axis. */ + std::function func; + /** \brief lower bound for the dependent parameter variable of func */ + double range_start; + /** \brief upper bound for the dependent parameter variable of func */ + double range_end; + + bool isValid() const; + }; + /** \brief this function returns a functor that is used to generate the plot data + * in coordinate space, based on a range of the dependent variable in coordinate space. + * In addition it also contains the value range over which to evaluate the functor PlotFunctorSpec::func + * + * This function has to be overridden by each class. Depending on the way that class defines + * the actual plot function, this function has to compose its return type in different ways. + * The three most common ways are: + * - plot a function \f$ f(x) \f$ i.e. the plot points will be \f$ [x, f(x)] \f$ + * and the value rage will be the x-axis range. This is implemented by e.g. JKQTPXFunctionLineGraph. + * - plot a function \f$ f(y) \f$ i.e. the plot points will be \f$ [f(y), y] \f$ + * and the value rage will be the y-axis range. This is implemented by e.g. JKQTPYFunctionLineGraph. + * - plot a function \f$ [x,y]=\vec{f}(t) \f$ i.e. the plot points will be \f$ \vec{f}(t) \f$ + * and the value rage will be a user-defined range for \f$ gt \f$. + * This is implemented by e.g. JKQTPXYFunctionLineGraph. + * . + */ + virtual PlotFunctorSpec buildPlotFunctorSpec() =0; + + /** \brief ensure that current function parameters for a plot function (which may stem from different sources, as direct data, a datastore column ...) are stored in iparams */ + virtual void collectParameters(); + + /** \brief draw all the sample points in data as small symbols */ + void drawSamplePoints(JKQTPEnhancedPainter &painter, QColor graphColor); /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData( bool collectParams=true) =0; + virtual void createPlotData( bool collectParams=true) ; + + /** \brief if set, the values from this datatsore column are used for the parameters \c p1 , \c p2 , \c p3 , ... of the plot function */ + int parameterColumn; + + /** \brief internal storage for the current function parameters for plotFunction (which may stem from different sources, as direct data, a datastore column ...) */ + QVector iparams; + + /** \brief plot data calculated by createPlotData(), i.e. the datapoints \f$ \mbox{transform}\left(x, y=f(x, \vec{p})\right) \f$ to be plotted */ + QVector data; /** \brief the minimum number of points to evaluate the function at */ unsigned int minSamples; @@ -127,66 +222,75 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionGraphBase: public JKQTPGraph double dataCleanupMaxAllowedAngleDegree; /** \brief if true [default: off] display the points where the function has been sampled */ bool displaySamplePoints; - - /** \brief draw all the sample points in data as small symbols */ - void drawSamplePoints(JKQTPEnhancedPainter &painter, QColor graphColor); }; -/** \brief extends JKQTPEvaluatedFunctionGraphBase with a set of functions that support function parameters +/** \brief extends JKQTPEvaluatedFunctionGraphBase with some basic properties (e.g. function parameters) + * for a second function that calculates an error (for drawing error indicators) * \ingroup jkqtplotter_functiongraphs * + * When implementing this, you will have to implement buildErrorFunctorSpec() in addition to + * JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec()! + * * \see JKQTPEvaluatedFunctionGraphBase */ -class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionWithParamsGraphBase: public JKQTPEvaluatedFunctionGraphBase { +class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionWithErrorsGraphBase: public JKQTPEvaluatedFunctionGraphBase { Q_OBJECT public: /** \brief class constructor */ - explicit JKQTPEvaluatedFunctionWithParamsGraphBase(JKQTBasePlotter* parent=nullptr); + explicit JKQTPEvaluatedFunctionWithErrorsGraphBase(JKQTBasePlotter* parent=nullptr); /** \brief class constructor */ - explicit JKQTPEvaluatedFunctionWithParamsGraphBase(JKQTPlotter* parent); + explicit JKQTPEvaluatedFunctionWithErrorsGraphBase(JKQTPlotter* parent); /** \brief class destructor */ - virtual ~JKQTPEvaluatedFunctionWithParamsGraphBase(); + virtual ~JKQTPEvaluatedFunctionWithErrorsGraphBase(); - - /** \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!!!) */ - virtual void setParams(const QVector& params); - /** \brief sets the params from a copy of the given array of length \a N */ - void setCopiedParams(const double* params, int N); + /** \copydoc errorParameterColumn */ + int getErrorParameterColumn() const; /** \brief returns the currently set internal parameter vector */ - QVector getInternalParams() const; - /** \copydoc parameterColumn */ - int getParameterColumn() const; + const QVector& getInternalErrorParams() const; + /** \brief returns the currently set internal parameter vector */ + QVector& getInternalErrorParams(); /** \copydoc JKQTPGraph::usesColumn() */ virtual bool usesColumn(int c) const override; - public slots: - /** \brief set an internal parameter vector as function parameters, initialized with {p1} */ - void setParamsV(double p1); - /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2} */ - void setParamsV(double p1, double p2); - /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2,p3} */ - void setParamsV(double p1, double p2, double p3); - /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2,p3,p4} */ - void setParamsV(double p1, double p2, double p3, double p4); - /** \brief set an internal parameter vector as function parameters, initialized with {p1,p2,p3,p4,p5} */ - void setParamsV(double p1, double p2, double p3, double p4, double p5); + /** \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 setErrorParams(const QVector& errorParams); + /** \copydoc errorParameterColumn */ + void setErrorParameterColumn(int __value); + /** \copydoc errorParameterColumn */ + void setErrorParameterColumn (size_t __value); + + /** \brief set the internal error function parameters to {p1} */ + void setErrorParamsV(double p1); + /** \brief set the internal error function parameters to {p1,p2} */ + void setErrorParamsV(double p1, double p2); + /** \brief set the internal error function parameters to {p1,p2,p3} */ + void setErrorParamsV(double p1, double p2, double p3); + /** \brief set the internal error function parameters to {p1,p2,p3,p4} */ + void setErrorParamsV(double p1, double p2, double p3, double p4); + /** \brief set the internal error function parameters to {p1,p2,p3,p4,p5} */ + void setErrorParamsV(double p1, double p2, double p3, double p4, double p5); - /** \copydoc parameterColumn */ - void setParameterColumn(int __value); - /** \copydoc parameterColumn */ - void setParameterColumn (size_t __value); protected: - /** \brief ensure that current function parameters for a plot function (which may stem from different sources, as direct data, a datastore column ...) are stored in iparams */ - virtual void collectParameters(); + /** \brief ensure that current function parameters for plotFunction (which may stem from different sources, as direct data, a datastore column ...) are stored in iparams and ierrorparams */ + virtual void collectParameters() override; + /** \brief same as JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec(), but for error functions. + * + * The functor, returned by this function should calculate the error of the function (in x- and y-direction) + * for every value \f$ t \f$ of the actual function. + * + * The parameter range is the same as for JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() + * + * \see JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() + */ + virtual std::function buildErrorFunctorSpec() =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 */ - int parameterColumn; - - /** \brief internal storage for the current function parameters for plotFunction (which may stem from different sources, as direct data, a datastore column ...) */ - QVector iparams; + /** \brief if set, the values from this datatsore column are used for the parameters \c p1 , \c p2 , \c p3 , ... of the error plot function */ + int errorParameterColumn; + /** \brief internal storage for the current error function parameters for errorPlotFunction (which may stem from different sources, as direct data, a datastore column ...) */ + QVector ierrorparams; }; diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp index df0d57f314..536902eb0b 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp @@ -33,14 +33,125 @@ -JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(JKQTBasePlotter* parent): - JKQTPEvaluatedFunctionWithParamsGraphBase(parent) +JKQTPXYFunctionLineGraphBase::JKQTPXYFunctionLineGraphBase(JKQTBasePlotter* parent): + JKQTPEvaluatedFunctionGraphBase(parent), + tmin(0.0), + tmax(1.0) { - tmin=0.0; - tmax=1.0; - initLineStyle(parent, parentPlotStyle); + setMaxRefinementDegree(8); +} +JKQTPXYFunctionLineGraphBase::JKQTPXYFunctionLineGraphBase(JKQTPlotter* parent): + JKQTPXYFunctionLineGraphBase(parent->getPlotter()) +{ + +} + +JKQTPXYFunctionLineGraphBase::~JKQTPXYFunctionLineGraphBase() +{ + +} + + + +void JKQTPXYFunctionLineGraphBase::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) { + painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); + QPen p=getLinePen(painter, parent); + p.setJoinStyle(Qt::RoundJoin); + p.setCapStyle(Qt::RoundCap); + QPen np(Qt::NoPen); + const double y=rect.top()+rect.height()/2.0; + painter.setPen(np); + painter.setPen(p); + painter.drawLine(QLineF(rect.left(), y, rect.right(), y)); +} + +QColor JKQTPXYFunctionLineGraphBase::getKeyLabelColor() const { + return getLineColor(); +} + + +void JKQTPXYFunctionLineGraphBase::draw(JKQTPEnhancedPainter& painter) { +#ifdef JKQTBP_AUTOTIMER + JKQTPAutoOutputTimer jkaaot("JKQTPXYFunctionLineGraph::draw"); +#endif + if (parent==nullptr) return; + JKQTPDatastore* datastore=parent->getDatastore(); + if (datastore==nullptr) return; + + //qDebug()<<"start plot\n"; + createPlotData(); + //qDebug()<<"plot data created\n"; + + drawErrorsBefore(painter); + { + painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); + + QPen p=getLinePen(painter, parent); + QPen np(Qt::NoPen); + + + { + painter.save(); auto __finalpaintline=JKQTPFinally([&painter]() {painter.restore();}); + painter.setPen(p); + painter.drawPolyline(data); + } + + + if (displaySamplePoints) drawSamplePoints(painter, getLineColor()); + } + drawErrorsAfter(painter); + //std::cout<<"plot done\n"; +} + + + +double JKQTPXYFunctionLineGraphBase::getTMin() const +{ + return tmin; +} + +double JKQTPXYFunctionLineGraphBase::getTMax() const +{ + return tmax; +} + +void JKQTPXYFunctionLineGraphBase::setTMin(double val) +{ + tmin=val; +} + +void JKQTPXYFunctionLineGraphBase::setTMax(double val) +{ + tmax=val; +} + +QPair JKQTPXYFunctionLineGraphBase::getTRange() const +{ + return QPair(tmin,tmax); +} + +void JKQTPXYFunctionLineGraphBase::setTRange(double tmin_, double tmax_) +{ + tmin=tmin_; + tmax=tmax_; +} + +void JKQTPXYFunctionLineGraphBase::setTRange(const QPair &range) +{ + tmin=range.first; + tmax=range.second; +} + + + + + + +JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(JKQTBasePlotter* parent): + JKQTPXYFunctionLineGraphBase(parent) +{ } JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(JKQTPlotter* parent): @@ -54,10 +165,9 @@ JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(const jkqtpSimpleParametricCu { tmin=tmin_; tmax=tmax_; - title=title_; + setTitle(title_); plotFunction=jkqtpParametricCurveFunctionType(); simplePlotFunction=f; - data.clear(); } JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(const jkqtpSimpleParametricCurveFunctionType &f, const QString &title_, double tmin_, double tmax_, JKQTPlotter *parent): @@ -72,7 +182,7 @@ JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(jkqtpSimpleParametricCurveFun { tmin=tmin_; tmax=tmax_; - title=title_; + setTitle(title_); plotFunction=jkqtpParametricCurveFunctionType(); simplePlotFunction=std::move(f); data.clear(); @@ -89,7 +199,7 @@ JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(jkqtpParametricCurveFunctionT { tmin=tmin_; tmax=tmax_; - title=title_; + setTitle(title_); simplePlotFunction=jkqtpSimpleParametricCurveFunctionType(); plotFunction=std::move(f); data.clear(); @@ -106,7 +216,7 @@ JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(const jkqtpParametricCurveFun { tmin=tmin_; tmax=tmax_; - title=title_; + setTitle(title_); simplePlotFunction=jkqtpSimpleParametricCurveFunctionType(); plotFunction=std::move(f); data.clear(); @@ -119,7 +229,7 @@ JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(const jkqtpParametricCurveFun } JKQTPXYFunctionLineGraph::~JKQTPXYFunctionLineGraph() { - data.clear(); + } @@ -162,115 +272,26 @@ jkqtpSimpleParametricCurveFunctionType JKQTPXYFunctionLineGraph::getSimplePlotFu return simplePlotFunction; } - -void JKQTPXYFunctionLineGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) { - painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); - QPen p=getLinePen(painter, parent); - p.setJoinStyle(Qt::RoundJoin); - p.setCapStyle(Qt::RoundCap); - QPen np(Qt::NoPen); - const double y=rect.top()+rect.height()/2.0; - painter.setPen(np); - painter.setPen(p); - painter.drawLine(QLineF(rect.left(), y, rect.right(), y)); -} - -QColor JKQTPXYFunctionLineGraph::getKeyLabelColor() const { - return getLineColor(); -} - - -void JKQTPXYFunctionLineGraph::createPlotData(bool collectParams) { -#ifdef JKQTBP_AUTOTIMER - JKQTPAutoOutputTimer jkaat(QString("JKQTPXYFunctionLineGraph[%1]::createPlotData()").arg(title)); -#endif - data.clear(); - if (collectParams) collectParameters(); - - if (parent==nullptr) return; - if (!plotFunction && !simplePlotFunction) return; - - jkqtpSimpleParametricCurveFunctionType func; - if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, getInternalParams()); - else if (simplePlotFunction) func=simplePlotFunction; - - jkqtpSimpleParametricCurveFunctionType fTransformedFunc= std::bind([&](const JKQTPPlotElement* plot, double t) -> QPointF { return plot->transform(func(t)); }, this, std::placeholders::_1); - - JKQTPAdaptiveFunctionGraphEvaluator evaluator(fTransformedFunc, minSamples, maxRefinementDegree, slopeTolerance, minPixelPerSample); - data=evaluator.evaluate(tmin, tmax); - data=JKQTPSimplyfyLineSegemnts(data, dataCleanupMaxAllowedAngleDegree); -} - - -void JKQTPXYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { -#ifdef JKQTBP_AUTOTIMER - JKQTPAutoOutputTimer jkaaot("JKQTPXYFunctionLineGraph::draw"); -#endif - if (parent==nullptr) return; - JKQTPDatastore* datastore=parent->getDatastore(); - if (datastore==nullptr) return; - - //qDebug()<<"start plot\n"; - createPlotData(); - //qDebug()<<"plot data created\n"; - - drawErrorsBefore(painter); - { - painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); - - QPen p=getLinePen(painter, parent); - QPen np(Qt::NoPen); - - - { - painter.save(); auto __finalpaintline=JKQTPFinally([&painter]() {painter.restore();}); - painter.setPen(p); - painter.drawPolyline(data); - } - - - if (displaySamplePoints) drawSamplePoints(painter, getLineColor()); - } - drawErrorsAfter(painter); - //std::cout<<"plot done\n"; -} - - - -double JKQTPXYFunctionLineGraph::getTMin() const +bool JKQTPXYFunctionLineGraph::isSimplePlotFunction() const { - return tmin; + return !static_cast(plotFunction) && static_cast(simplePlotFunction); } -double JKQTPXYFunctionLineGraph::getTMax() const + +JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec JKQTPXYFunctionLineGraph::buildPlotFunctorSpec() { - return tmax; -} - -void JKQTPXYFunctionLineGraph::setTMin(double val) -{ - tmin=val; -} - -void JKQTPXYFunctionLineGraph::setTMax(double val) -{ - tmax=val; -} - -QPair JKQTPXYFunctionLineGraph::getTRange() const -{ - return QPair(tmin,tmax); -} - -void JKQTPXYFunctionLineGraph::setTRange(double tmin_, double tmax_) -{ - tmin=tmin_; - tmax=tmax_; -} - -void JKQTPXYFunctionLineGraph::setTRange(const QPair &range) -{ - tmin=range.first; - tmax=range.second; + JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec spec; + + if (!plotFunction && !simplePlotFunction) return spec; // return invalid spec! + + // range over which to evaluate func + spec.range_start=tmin; + spec.range_end=tmax; + + // the actual function to use + if (plotFunction) spec.func=std::bind(plotFunction, std::placeholders::_1, getInternalParams()); + else if (simplePlotFunction) spec.func=simplePlotFunction; + + return spec; } diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h index 6835ead8de..3d25926907 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h @@ -33,62 +33,136 @@ #define jkqtpevaluatedparametriccurve_H +/** \brief Base class for line plots where the data is taken from a user supplied function \f$ [x,y]=f(t) \f$ + * The function is evaluated on a user-specified range \f$ t \in \left[t_\text{min}, t_\text{max}\right] \f$ + * \ingroup jkqtplotter_functiongraphs + * + * This class uses the intelligent plotting algorithm for functions, implemented in JKQTPAdaptiveFunctionGraphEvaluator. + * It starts by sampling the function at minSamples positions. Then each function interval is bisected recursively if + * necessary. To do so the function is evaluated at the mid point and the slopes \f$ \alpha_{\mbox{left}} \f$ + * and \f$ \alpha_{\mbox{right}} \f$ of the two linear segments are compared. the midpoint is added + * to the graph if \f[ \left|\alpha_{\mbox{right}}-\alpha_{\mbox{left}}\right|>\mbox{slopeTolerance} \f] + * In addition all sampling points except minimum and maximum are beeing shifted by a random fraction their + * distance to the other points. This helps to prevent beats when sampling periodic functions. + * + * Finally the obtained data is cleaned up to reduce the amount of points, by deleting a point, when it leads to an + * angle between consecutive line-segments of less than dataCleanupMaxAllowedAngleDegree. + * + * + * \see JKQTPXYFunctionLineGraphBase for a concrete implementation with C++-functors as functions + */ +class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraphBase: public JKQTPEvaluatedFunctionGraphBase, public JKQTPGraphLineStyleMixin { + Q_OBJECT +public: + + /** \brief class constructor */ + JKQTPXYFunctionLineGraphBase(JKQTBasePlotter* parent=nullptr); + + /** \brief class constructor */ + JKQTPXYFunctionLineGraphBase(JKQTPlotter* parent); + + /** \brief class destructor */ + virtual ~JKQTPXYFunctionLineGraphBase() override; + + /** \brief plots the graph to the plotter object specified as parent */ + virtual void draw(JKQTPEnhancedPainter& painter) override; + /** \brief plots a key marker inside the specified rectangle \a rect */ + virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) override; + /** \brief returns the color to be used for the key label */ + virtual QColor getKeyLabelColor() const override; -/*! \brief type of functions that may be plotted by JKQTPXYFunctionLineGraph - \ingroup jkqtplotter_functiongraphs + /** \brief returns the t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ */ + QPair getTRange() const; + /** \copydoc tmin */ + double getTMin() const; + /** \copydoc tmax */ + double getTMax() const; - This is the type of functions \f$ [x,y]=f(t, \vec{p}) \f$ that may be plottet by JKQTPXYFunctionLineGraph. - It is possible to supply parameters \f$ \vec{p} \f$ to the function that - influence its result. Parameters are given as a pointer to some memory location. The function has to - know on its own how to interpret these. +public slots: + /** \copydoc tmin */ + void setTMin(double val); + /** \copydoc tmax */ + void setTMax(double val); + /** \brief set the t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ */ + void setTRange(double tmin_, double tmax_); + /** \brief set the t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ */ + void setTRange(const QPair& range); +protected: + + /** \brief lower bound of t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ , i.e. \f$ t_\text{min} \f$ , default is 0 + * + * \see getTMin(), getTMax(), setTMin(), setTMax(), setTRange(), getTRange() */ + double tmin; + /** \brief upper bound of t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ , i.e. \f$ t_\text{min} \f$ , default is 1 + * + * \see getTMin(), getTMax(), setTMin(), setTMax(), setTRange(), getTRange() */ + double tmax; + +}; + + + +/** \brief type of functions that may be plotted by JKQTPXYFunctionLineGraph + * \ingroup jkqtplotter_functiongraphs + * + * This is the type of functions \f$ [x,y]=f(t, \vec{p}) \f$ that may be plottet by JKQTPXYFunctionLineGraph. + * It is possible to supply parameters \f$ \vec{p} \f$ to the function that + * influence its result. Parameters are given as a pointer to some memory location. The function has to + * know on its own how to interpret these. */ typedef std::function)> jkqtpParametricCurveFunctionType; -/*! \brief simplified type of functions (without parameters) that may be plotted by JKQTPXYFunctionLineGraph - \ingroup jkqtplotter_functiongraphs - - This is the type of functions \f$ [x,y]=f(t) \f$ that may be plottet by JKQTPXYFunctionLineGraph - and JKQTPYFunctionLineGraph. +/** \brief simplified type of functions (without parameters) that may be plotted by JKQTPXYFunctionLineGraph + * \ingroup jkqtplotter_functiongraphs + * + * This is the type of functions \f$ [x,y]=f(t) \f$ that may be plottet by JKQTPXYFunctionLineGraph + * and JKQTPYFunctionLineGraph. */ typedef std::function jkqtpSimpleParametricCurveFunctionType; -/*! \brief This implements line plots where the data is taken from a user supplied function \f$ [x,y]=f(t) \f$ - The function is evaluated on a user-specified range \f$ t \in \left[t_\text{min}, t_\text{max}\right] \f$ - \ingroup jkqtplotter_functiongraphs - - This class uses the intelligent plotting algorithm for functions, implemented in JKQTPAdaptiveFunctionGraphEvaluator. - It starts by sampling the function at minSamples positions. Then each function interval is bisected recursively if - necessary. To do so the function is evaluated at the mid point and the slopes \f$ \alpha_{\mbox{left}} \f$ - and \f$ \alpha_{\mbox{right}} \f$ of the two linear segments are compared. the midpoint is added - to the graph if \f[ \left|\alpha_{\mbox{right}}-\alpha_{\mbox{left}}\right|>\mbox{slopeTolerance} \f] - In addition all sampling points except minimum and maximum are beeing shifted by a random fraction their - distance to the other points. This helps to prevent beats when sampling periodic functions. - - Finally the obtained data is cleaned up to reduce the amount of points, by deleting a point, when it leads to an - angle between consecutive line-segments of less than dataCleanupMaxAllowedAngleDegree. - - the following image shows a Lissajou's fugure drawn with this function - - \image html plot_evalcurve.png - - The source code for this example is: - \code - JKQTPXYFunctionLineGraph* func1=new JKQTPXYFunctionLineGraph(plot); - func1->setPlotFunctionFunctor([](double t) -> QPointF { - const double a=5; - const double b=4; - const double delta=JKQTPSTATISTICS_PI/4.0; - return QPointF(sin(a*t+delta), sin(b*t)); - }); - func1->setTRange(0, 2.0*JKQTPSTATISTICS_PI); - func1->setTitle("C++-inline function $[ sin(5{\\cdot}t+\\pi/4), sin(4{\\cdot}t) ]$"); - \endcode - - \see \ref JKQTPlotterEvalCurves , JKQTPAdaptiveFunctionGraphEvaluator, JKQTPXFunctionLineGraph, JKQTPYFunctionLineGraph +/** \brief This implements line plots where the data is taken from a user supplied function \f$ [x,y]=f(t) \f$ + * The function is evaluated on a user-specified range \f$ t \in \left[t_\text{min}, t_\text{max}\right] \f$ + * \ingroup jkqtplotter_functiongraphs + * + * \see JKQTPXYFunctionLineGraphBase for details on the used plotting algorithm + * + * The following image shows a Lissajou's fugure drawn with this function + * + * \image html plot_evalcurve.png + * + * The source code for this example is: + * \code + * JKQTPXYFunctionLineGraph* func1=new JKQTPXYFunctionLineGraph(plot); + * // here we define the C++-functor for [x,y]=f(t) + * func1->setPlotFunctionFunctor([](double t) -> QPointF { + * const double a=5; + * const double b=4; + * const double delta=JKQTPSTATISTICS_PI/4.0; + * return QPointF(sin(a*t+delta), sin(b*t)); + * }); + * // and define the range over which to evaluate + * func1->setTRange(0, 2.0*JKQTPSTATISTICS_PI); + * \endcode + * + * The source code for the same example, but using functions that also get a parameter vector: + * \code + * JKQTPXYFunctionLineGraph* func1=new JKQTPXYFunctionLineGraph(plot); + * // here we define the C++-functor for [x,y]=f(t) + * func1->setPlotFunctionFunctor([](double t, const QVector& params) -> QPointF { + * return QPointF(3.0*sin(params[0]*t+params[2])+8.0, 3.0*sin(params[1]*t)); + * }); + * // now we define the 3 parameters of the function + * // parameters are a, b, delta, as in the example above (in that order) + * func1->setParamsV(5, 4, JKQTPSTATISTICS_PI/4.0); + * // and define the range over which to evaluate + * func1->setTRange(0, 2.0*JKQTPSTATISTICS_PI); + * \endcode + * + * \see \ref JKQTPlotterEvalCurves , JKQTPAdaptiveFunctionGraphEvaluator, JKQTPXFunctionLineGraph, JKQTPYFunctionLineGraph */ -class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPEvaluatedFunctionWithParamsGraphBase, public JKQTPGraphLineStyleMixin { +class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPXYFunctionLineGraphBase { Q_OBJECT public: @@ -117,60 +191,44 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPEvaluatedFunc /** \brief class destructor */ virtual ~JKQTPXYFunctionLineGraph() override; - /** \brief plots the graph to the plotter object specified as parent */ - virtual void draw(JKQTPEnhancedPainter& painter) override; - /** \brief plots a key marker inside the specified rectangle \a rect */ - virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) override; - /** \brief returns the color to be used for the key label */ - virtual QColor getKeyLabelColor() const override; /** \brief sets a functor to be plotted * * \see plotFunction */ - virtual void setPlotFunctionFunctor (jkqtpParametricCurveFunctionType && __value); + void setPlotFunctionFunctor (jkqtpParametricCurveFunctionType && __value); /** \brief sets a functor to be plotted * * \see plotFunction */ - virtual void setPlotFunctionFunctor (const jkqtpParametricCurveFunctionType & __value); + void setPlotFunctionFunctor (const jkqtpParametricCurveFunctionType & __value); /** \brief sets a functor to be plotted * * \see simplePlotFunction */ - virtual void setPlotFunctionFunctor (jkqtpSimpleParametricCurveFunctionType && __value); + void setPlotFunctionFunctor (jkqtpSimpleParametricCurveFunctionType && __value); /** \brief sets a functor to be plotted * * \see simplePlotFunction */ - virtual void setPlotFunctionFunctor (const jkqtpSimpleParametricCurveFunctionType & __value); - /*! \copydoc plotFunction */ \ - virtual jkqtpParametricCurveFunctionType getPlotFunctionFunctor () const; - /*! \copydoc simplePlotFunction */ \ - virtual jkqtpSimpleParametricCurveFunctionType getSimplePlotFunction () const; + void setPlotFunctionFunctor (const jkqtpSimpleParametricCurveFunctionType & __value); + /** \copydoc plotFunction + * + * \see isSimplePlotFunction() */ + jkqtpParametricCurveFunctionType getPlotFunctionFunctor () const; + /** \copydoc simplePlotFunction + * + * \see isSimplePlotFunction() */ + jkqtpSimpleParametricCurveFunctionType getSimplePlotFunction () const; + + /** \brief returns whether the plot function was defined as a jkqtpSimpleParametricCurveFunctionType (\c true ) or + * a jkqtpParametricCurveFunctionType (\c false ) */ + bool isSimplePlotFunction() const; - /*! \copydoc tmin */ - double getTMin() const; - /*! \copydoc tmax */ - double getTMax() const; - /*! \copydoc tmin */ - void setTMin(double val); - /*! \copydoc tmax */ - void setTMax(double val); - /*! \brief returns the t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ */ - QPair getTRange() const; - /*! \brief set the t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ */ - void setTRange(double tmin_, double tmax_); - /*! \brief set the t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ */ - void setTRange(const QPair& range); protected: - /** \brief lower bound of t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ , i.e. \f$ t_\text{min} \f$ , default is 0 */ - double tmin; - /** \brief upper bound of t-value range for \f$ [x,y]=f(t), t \in \left[t_\text{min}, t_\text{max}\right] \f$ , i.e. \f$ t_\text{min} \f$ , default is 1 */ - double tmax; - /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData( bool collectParams=true) override; + /** \copydoc JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() */ + virtual PlotFunctorSpec buildPlotFunctorSpec() override; /** \brief the function to be plotted */ diff --git a/lib/jkqtplotter/graphs/jkqtpparsedfunction.cpp b/lib/jkqtplotter/graphs/jkqtpparsedfunction.cpp index 86dd7e8870..df12d830b5 100644 --- a/lib/jkqtplotter/graphs/jkqtpparsedfunction.cpp +++ b/lib/jkqtplotter/graphs/jkqtpparsedfunction.cpp @@ -27,131 +27,73 @@ #include #include - -JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraph(JKQTBasePlotter *parent): - JKQTPXFunctionLineGraph(parent) +JKQTPParsedFunctionLineGraphBase::JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName_, const QString& function_, JKQTBasePlotter *parent): + JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase(parent), + dependentVariableName(dependentVariableName_), + function(function_) { - fdata.parser=new JKQTPMathParser(); + fdata.parser=std::make_shared(); fdata.node=nullptr; fdata.varcount=0; - function=""; - parameterColumn=-1; - setPlotFunctionFunctor(jkqtpPlotFunctionType(std::bind(&JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraphFunction, std::placeholders::_1, std::placeholders::_2, &fdata))); - efdata.parser=new JKQTPMathParser(); + + efdata.parser=std::make_shared(); efdata.node=nullptr; efdata.varcount=0; - errorFunction=""; - errorParameterColumn=-1; - setErrorPlotFunction(jkqtpPlotFunctionType(std::bind(&JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraphFunction, std::placeholders::_1, std::placeholders::_2, &efdata))); } -JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraph(JKQTPlotter *parent): - JKQTPXParsedFunctionLineGraph(parent->getPlotter()) +JKQTPParsedFunctionLineGraphBase::JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, const QString& function, JKQTPlotter *parent): + JKQTPParsedFunctionLineGraphBase(dependentVariableName, function, parent->getPlotter()) { } -JKQTPXParsedFunctionLineGraph::~JKQTPXParsedFunctionLineGraph() +JKQTPParsedFunctionLineGraphBase::JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, JKQTBasePlotter *parent): + JKQTPParsedFunctionLineGraphBase(dependentVariableName, QString(), parent) { - if (fdata.node) delete fdata.node; - delete fdata.parser; - if (efdata.node) delete efdata.node; - delete efdata.parser; } -void JKQTPXParsedFunctionLineGraph::setFunction(const QString &__value) +JKQTPParsedFunctionLineGraphBase::JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, JKQTPlotter *parent): + JKQTPParsedFunctionLineGraphBase(dependentVariableName, QString(), parent) +{ +} + + +JKQTPParsedFunctionLineGraphBase::~JKQTPParsedFunctionLineGraphBase() +{ +} + +void JKQTPParsedFunctionLineGraphBase::setFunction(const QString &__value) { this->function = __value; } -QString JKQTPXParsedFunctionLineGraph::getFunction() const +QString JKQTPParsedFunctionLineGraphBase::getFunction() const { return this->function; } -void JKQTPXParsedFunctionLineGraph::setErrorFunction(const QString &__value) +void JKQTPParsedFunctionLineGraphBase::setErrorFunction(const QString &__value) { this->errorFunction = __value; } -QString JKQTPXParsedFunctionLineGraph::getErrorFunction() const +QString JKQTPParsedFunctionLineGraphBase::getErrorFunction() const { return this->errorFunction; } -void JKQTPXParsedFunctionLineGraph::createPlotData(bool /*collectParams*/) +QString JKQTPParsedFunctionLineGraphBase::getDependentVariableName() const { - collectParameters(); - - //QElapsedTimer timer; - //timer.start(); - for (int i=0; ideleteVariable(std::string("p")+jkqtp_inttostr(i+1)); - } - fdata.varcount=0; - try { - for (const auto& p: getInternalParams()) { - fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), p); - fdata.varcount=fdata.varcount+1; - } - fdata.parser->addVariableDouble(std::string("x"), 0.0); - if (fdata.node) delete fdata.node; - //qint64 t=timer.elapsed(); - - - //qDebug()<<"createPlotData(): adding variables: "<parse(function.toStdString()); - //qDebug()<<"createPlotData(): parsing: "<deleteVariable(std::string("p")+jkqtp_inttostr(i+1)); - } - efdata.varcount=0; - try { - QVector* errorParameters=static_cast*>(errorParams); - if (errorParameters) { - for (int i=0; isize(); 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); - if (efdata.node) delete efdata.node; - //qint64 t=timer.elapsed(); - //qDebug()<<"createPlotData(): adding variables: "<parse(errorFunction.toStdString()); - //qDebug()<<"createPlotData(): parsing: "<next; - } - qDebug()<<"refined to "<& /*data*/, JKQTPXParsedFunctionLineGraphFunctionData *fdata) { - JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraphFunctionData* d=fdata;//static_cast(data); +double JKQTPParsedFunctionLineGraphBase::evaluateParsedFunction(double t, ParsedFunctionLineGraphFunctionData *fdata) { + JKQTPParsedFunctionLineGraphBase::ParsedFunctionLineGraphFunctionData* d=fdata;//static_cast(data); if (d && d->parser && d->node) { try { - d->parser->addVariableDouble("x", x); + d->parser->addVariableDouble(d->dependentVariableName.toStdString(), t); JKQTPMathParser::jkmpResult r=d->node->evaluate(); if (r.isValid) { @@ -162,8 +104,8 @@ double JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraphFunction(doub } } } catch(std::exception& E) { - qDebug()<getExpression()).arg(E.what()).arg(row).arg(column), QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok)==QMessageBox::Ok;*/ @@ -186,79 +128,152 @@ double JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraphFunction(doub + + +JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraph(JKQTBasePlotter *parent): + JKQTPParsedFunctionLineGraphBase(QString("x"), parent) +{ +} + +JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraph(JKQTPlotter *parent): + JKQTPParsedFunctionLineGraphBase(QString("x"), parent) +{ +} + +JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraph(const QString& function, JKQTBasePlotter *parent): + JKQTPParsedFunctionLineGraphBase(QString("x"), function, parent) +{ +} + +JKQTPXParsedFunctionLineGraph::JKQTPXParsedFunctionLineGraph(const QString& function, JKQTPlotter *parent): + JKQTPParsedFunctionLineGraphBase(QString("x"), function, parent) +{ +} + +JKQTPXParsedFunctionLineGraph::~JKQTPXParsedFunctionLineGraph() +{ +} + +void JKQTPXParsedFunctionLineGraph::draw(JKQTPEnhancedPainter &painter) +{ + drawXGraph(painter); +} + + +JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec JKQTPXParsedFunctionLineGraph::buildPlotFunctorSpec() +{ + JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec spec; + + if (parent==nullptr) return spec; // return an invalid PlotFunctorSpec + + for (int i=0; ideleteVariable(std::string("p")+jkqtp_inttostr(i+1)); + } + fdata.varcount=0; + try { + for (const auto& p: getInternalParams()) { + fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), p); + fdata.varcount=fdata.varcount+1; + } + fdata.dependentVariableName=getDependentVariableName(); + fdata.parser->addVariableDouble(getDependentVariableName().toStdString(), 0.0); + fdata.node=std::shared_ptr(fdata.parser->parse(function.toStdString())); + } catch(std::exception& E) { + qDebug()< QPointF { return QPointF(0, plotFunction(x)); }, std::placeholders::_1); + + return spec; +} + +std::function JKQTPXParsedFunctionLineGraph::buildErrorFunctorSpec() +{ + std::function spec; + + for (int i=0; ideleteVariable(std::string("p")+jkqtp_inttostr(i+1)); + } + efdata.varcount=0; + try { + for (const auto& p: getInternalErrorParams()) { + efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), p); + efdata.varcount=efdata.varcount+1; + } + efdata.dependentVariableName=getDependentVariableName(); + efdata.parser->addVariableDouble(getDependentVariableName().toStdString(), 0.0); + efdata.node=std::shared_ptr(efdata.parser->parse(errorFunction.toStdString())); + } catch(std::exception& /*E*/) { + //qDebug()< QPointF { return QPointF(0, errorPlotFunction(x)); }, std::placeholders::_1); + + return spec; +} + + + + + + + + + + + + + + + + + + + + JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraph(JKQTBasePlotter *parent): - JKQTPYFunctionLineGraph(parent) + JKQTPParsedFunctionLineGraphBase(QString("y"), parent) { - fdata.parser=new JKQTPMathParser(); - fdata.node=nullptr; - fdata.varcount=0; - function=""; - parameterColumn=-1; - setPlotFunctionFunctor(jkqtpPlotFunctionType(std::bind(&JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraphFunction, std::placeholders::_1, std::placeholders::_2, &fdata))); - - efdata.parser=new JKQTPMathParser(); - efdata.node=nullptr; - efdata.varcount=0; - errorFunction=""; - errorParameterColumn=-1; - setErrorPlotFunction(jkqtpPlotFunctionType(std::bind(&JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraphFunction, std::placeholders::_1, std::placeholders::_2, &efdata))); } JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraph(JKQTPlotter *parent): - JKQTPYFunctionLineGraph(parent) + JKQTPParsedFunctionLineGraphBase(QString("y"), parent) { - fdata.parser=new JKQTPMathParser(); - fdata.node=nullptr; - fdata.varcount=0; - function=""; - parameterColumn=-1; - setPlotFunctionFunctor(jkqtpPlotFunctionType(std::bind(&JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraphFunction, std::placeholders::_1, std::placeholders::_2, &fdata))); +} - efdata.parser=new JKQTPMathParser(); - efdata.node=nullptr; - efdata.varcount=0; - errorFunction=""; - errorParameterColumn=-1; - setErrorPlotFunction(jkqtpPlotFunctionType(std::bind(&JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraphFunction, std::placeholders::_1, std::placeholders::_2, &efdata))); +JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraph(const QString& function, JKQTBasePlotter *parent): + JKQTPParsedFunctionLineGraphBase(QString("y"), function, parent) +{ +} + +JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraph(const QString& function, JKQTPlotter *parent): + JKQTPParsedFunctionLineGraphBase(QString("y"), function, parent) +{ } JKQTPYParsedFunctionLineGraph::~JKQTPYParsedFunctionLineGraph() { - if (fdata.node) delete fdata.node; - delete fdata.parser; - if (efdata.node) delete efdata.node; - delete efdata.parser; } -void JKQTPYParsedFunctionLineGraph::setFunction(const QString &__value) +void JKQTPYParsedFunctionLineGraph::draw(JKQTPEnhancedPainter &painter) { - this->function = __value; + drawYGraph(painter); } -QString JKQTPYParsedFunctionLineGraph::getFunction() const -{ - return this->function; -} -void JKQTPYParsedFunctionLineGraph::setErrorFunction(const QString &__value) +JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec JKQTPYParsedFunctionLineGraph::buildPlotFunctorSpec() { - this->errorFunction = __value; -} + JKQTPEvaluatedFunctionGraphBase::PlotFunctorSpec spec; -QString JKQTPYParsedFunctionLineGraph::getErrorFunction() const -{ - return this->errorFunction; -} + if (parent==nullptr) return spec; // return an invalid PlotFunctorSpec -void JKQTPYParsedFunctionLineGraph::createPlotData(bool /*collectParams*/) -{ - collectParameters(); - - //QElapsedTimer timer; - //timer.start(); for (int i=0; ideleteVariable(std::string("p")+jkqtp_inttostr(i+1)); } @@ -268,82 +283,47 @@ void JKQTPYParsedFunctionLineGraph::createPlotData(bool /*collectParams*/) fdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(fdata.varcount+1), p); 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: "<parse(function.toStdString()); - //qDebug()<<"createPlotData(): parsing: "<addVariableDouble(getDependentVariableName().toStdString(), 0.0); + fdata.node=std::shared_ptr(fdata.parser->parse(function.toStdString())); } catch(std::exception& E) { qDebug()< QPointF { return QPointF(plotFunction(y), 0); }, std::placeholders::_1); + + // range over which to evaluate func + spec.range_start=parent->getXMin(); + spec.range_end=parent->getXMax(); + + return spec; +} + +std::function JKQTPYParsedFunctionLineGraph::buildErrorFunctorSpec() +{ + std::function spec; + for (int i=0; ideleteVariable(std::string("p")+jkqtp_inttostr(i+1)); } efdata.varcount=0; try { - QVector* errorParameters=static_cast*>(errorParams); - if (errorParameters) { - for (int i=0; isize(); i++) { - efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), errorParameters->at(i)); - efdata.varcount=efdata.varcount+1; - } + for (const auto& p: getInternalErrorParams()) { + efdata.parser->addVariableDouble(std::string("p")+jkqtp_inttostr(efdata.varcount+1), p); + 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: "<parse(errorFunction.toStdString()); - //qDebug()<<"createPlotData(): parsing: "<addVariableDouble(getDependentVariableName().toStdString(), 0.0); + efdata.node=std::shared_ptr(efdata.parser->parse(errorFunction.toStdString())); + } catch(std::exception& /*E*/) { + //qDebug()<next; - } - qDebug()<<"refined to "< & /*data*/, JKQTPYParsedFunctionLineGraphFunctionData *fdata) { - JKQTPYParsedFunctionLineGraph::JKQTPYParsedFunctionLineGraphFunctionData* d=fdata;//static_cast(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()<getExpression()).arg(E.what()).arg(row).arg(column), - QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok)==QMessageBox::Ok;*/ - - } - } - return NAN; + jkqtpSimplePlotFunctionType errorPlotFunction=std::bind(&JKQTPXParsedFunctionLineGraph::evaluateParsedFunction, std::placeholders::_1, &efdata); + // the actual function to use + spec=std::bind([=](double y) -> QPointF { return QPointF(errorPlotFunction(y), 0); }, std::placeholders::_1); + return spec; } diff --git a/lib/jkqtplotter/graphs/jkqtpparsedfunction.h b/lib/jkqtplotter/graphs/jkqtpparsedfunction.h index 9f0b6608a2..86b5a93ec0 100644 --- a/lib/jkqtplotter/graphs/jkqtpparsedfunction.h +++ b/lib/jkqtplotter/graphs/jkqtpparsedfunction.h @@ -34,6 +34,74 @@ class JKQTBasePlotter; class JKQTPlotter; +/** \brief extends JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase with the capabilities to define functions from strings + * that are parsed by JKQTPMathParser + * \ingroup jkqtplotter_functiongraphs + * + * \see JKQTPXParsedFunctionLineGraph and JKQTPYParsedFunctionLineGraph for a concrete implementation, see also JKQTPMathParser + */ +class JKQTPLOTTER_LIB_EXPORT JKQTPParsedFunctionLineGraphBase: public JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase { + Q_OBJECT +public: + + + /** \brief class constructor */ + JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, JKQTBasePlotter* parent=nullptr); + /** \brief class constructor */ + JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, JKQTPlotter* parent); + + /** \brief class constructor */ + JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, const QString& function, JKQTBasePlotter* parent=nullptr); + /** \brief class constructor */ + JKQTPParsedFunctionLineGraphBase(const QString& dependentVariableName, const QString& function, JKQTPlotter* parent); + + + /** \brief class destructor */ + virtual ~JKQTPParsedFunctionLineGraphBase() override; + + /** \copydoc function */ + QString getFunction() const; + + /** \copydoc errorFunction */ + QString getErrorFunction() const; + /** \copydoc dependentVariableName */ + QString getDependentVariableName() const; + +public slots: + /** \copydoc errorFunction */ + void setErrorFunction(const QString & __value); + /** \copydoc function */ + void setFunction(const QString & __value); + +protected: + + /** \brief INTERNAL data structure combining a JKQTPMathParser and a JKQTPMathParser::jkmpNode + */ + struct ParsedFunctionLineGraphFunctionData { + std::shared_ptr parser; + std::shared_ptr node; + int varcount; + QString dependentVariableName; + }; + + /** \brief nache of the dependent variable (e.g. x for a function f(x) ) */ + QString dependentVariableName; + + /** \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 parser data structure for function */ + ParsedFunctionLineGraphFunctionData 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 parser data structure for errorFunction */ + ParsedFunctionLineGraphFunctionData efdata; + + /** \brief implements the actual plot function */ + static double evaluateParsedFunction(double x, ParsedFunctionLineGraphFunctionData* fdata) ; +}; + + /*! \brief This implements line plots where the data is taken from a user supplied function \f$ y=f(x) \f$ The function is defined as a string and parsed by JKMathParser \ingroup jkqtplotter_parsedFgraphs @@ -45,7 +113,7 @@ class JKQTPlotter; \see \ref JKQTPlotterParsedFunctionPlot, JKQTPMathParser */ -class JKQTPLOTTER_LIB_EXPORT JKQTPXParsedFunctionLineGraph: public JKQTPXFunctionLineGraph { +class JKQTPLOTTER_LIB_EXPORT JKQTPXParsedFunctionLineGraph: public JKQTPParsedFunctionLineGraphBase { Q_OBJECT public: @@ -55,49 +123,26 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXParsedFunctionLineGraph: public JKQTPXFunctio /** \brief class constructor */ JKQTPXParsedFunctionLineGraph(JKQTPlotter* parent); + /** \brief class constructor */ + JKQTPXParsedFunctionLineGraph(const QString& function, JKQTBasePlotter* parent=nullptr); + /** \brief class constructor */ + JKQTPXParsedFunctionLineGraph(const QString& function, JKQTPlotter* parent); + /** \brief class destructor */ virtual ~JKQTPXParsedFunctionLineGraph() override; - /*! \copydoc function */ - void setFunction(const QString & __value); - /*! \copydoc function */ - QString getFunction() const; - - /*! \copydoc errorFunction */ - void setErrorFunction(const QString & __value); - /*! \copydoc errorFunction */ - QString getErrorFunction() const; - - /** \brief INTERNAL data structure - * \internal - */ - struct JKQTPXParsedFunctionLineGraphFunctionData { - JKQTPMathParser* parser; - JKQTPMathParser::jkmpNode* node; - int varcount; - }; + /** \brief plots the graph to the plotter object specified as parent */ + virtual void draw(JKQTPEnhancedPainter& painter) override; 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; - 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; - JKQTPXParsedFunctionLineGraphFunctionData efdata; - // hide functions that should not be used in this class! - using JKQTPXFunctionLineGraph::setPlotFunctionFunctor; - using JKQTPXFunctionLineGraph::setParams; - using JKQTPXFunctionLineGraph::setErrorPlotFunction; - using JKQTPXFunctionLineGraph::setErrorParams; + /** \copydoc JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() */ + virtual PlotFunctorSpec buildPlotFunctorSpec() override; - /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData(bool collectParams=true) override; - - /** \brief implements the actual plot function */ - static double JKQTPXParsedFunctionLineGraphFunction(double x, const QVector &data, JKQTPXParsedFunctionLineGraphFunctionData* fdata) ; + /** \copydoc JKQTPEvaluatedFunctionWithErrorsGraphBase::buildPlotFunctorSpec() */ + virtual std::function buildErrorFunctorSpec() override; }; @@ -114,7 +159,7 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXParsedFunctionLineGraph: public JKQTPXFunctio \see \ref JKQTPlotterParsedFunctionPlot, JKQTPMathParser */ -class JKQTPLOTTER_LIB_EXPORT JKQTPYParsedFunctionLineGraph: public JKQTPYFunctionLineGraph { +class JKQTPLOTTER_LIB_EXPORT JKQTPYParsedFunctionLineGraph: public JKQTPParsedFunctionLineGraphBase { Q_OBJECT public: @@ -124,47 +169,25 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPYParsedFunctionLineGraph: public JKQTPYFunctio /** \brief class constructor */ JKQTPYParsedFunctionLineGraph(JKQTPlotter* parent); + /** \brief class constructor */ + JKQTPYParsedFunctionLineGraph(const QString& function, JKQTBasePlotter* parent=nullptr); + /** \brief class constructor */ + JKQTPYParsedFunctionLineGraph(const QString& function, JKQTPlotter* parent); + /** \brief class destructor */ virtual ~JKQTPYParsedFunctionLineGraph() override; - /*! \copydoc function */ - void setFunction(const QString & __value); - /*! \copydoc function */ - QString getFunction() const; + /** \brief plots the graph to the plotter object specified as parent */ + virtual void draw(JKQTPEnhancedPainter& painter) override; - /*! \copydoc errorFunction */ - void setErrorFunction(const QString & __value); - /*! \copydoc errorFunction */ - QString getErrorFunction() const; - - /** \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; + /** \copydoc JKQTPEvaluatedFunctionGraphBase::buildPlotFunctorSpec() */ + virtual PlotFunctorSpec buildPlotFunctorSpec() override; - // hide functions that should not be used in this class! - using JKQTPXFunctionLineGraph::setPlotFunctionFunctor; - using JKQTPXFunctionLineGraph::setParams; - using JKQTPXFunctionLineGraph::setErrorPlotFunction; - using JKQTPXFunctionLineGraph::setErrorParams; - - /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData(bool collectParams=true) override; - /** \brief implements the actual plot function */ - static double JKQTPYParsedFunctionLineGraphFunction(double x, const QVector& data, JKQTPYParsedFunctionLineGraphFunctionData* fdata); + /** \copydoc JKQTPEvaluatedFunctionWithErrorsGraphBase::buildPlotFunctorSpec() */ + virtual std::function buildErrorFunctorSpec() override; }; #endif // jkqtpgraphsparsedfunction_H diff --git a/screenshots/evalcurve.png b/screenshots/evalcurve.png index f44cd00696..08ceef17a0 100644 Binary files a/screenshots/evalcurve.png and b/screenshots/evalcurve.png differ diff --git a/screenshots/evalcurve_small.png b/screenshots/evalcurve_small.png index 3638362acf..d78fcfb01e 100644 Binary files a/screenshots/evalcurve_small.png and b/screenshots/evalcurve_small.png differ