diff --git a/examples/functionplot/functionplot.cpp b/examples/functionplot/functionplot.cpp index 42c687fd70..93d10d22a2 100644 --- a/examples/functionplot/functionplot.cpp +++ b/examples/functionplot/functionplot.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include "jkqtplotter/jkqtplotter.h" @@ -26,7 +27,7 @@ private: template void drawExample(QApplication& app, const QString& name) { - // 1. create a window that contains a line-edit to edit a function + // 1.1 create a window that contains a line-edit to edit a function // and a JKQTPlotter to display the function, combine everything in a layout QWidget* mainWin=new QWidget(); mainWin->setWindowTitle(name); @@ -34,12 +35,20 @@ void drawExample(QApplication& app, const QString& name) { QVBoxLayout* layout=new QVBoxLayout; mainWin->setLayout(layout); layout->addWidget(plot); + // 1.2 add checkbox that allows to switch the display of sample points + QCheckBox* chkShowSamples=new QCheckBox(app.tr("display sample points")); + chkShowSamples->setChecked(false); + layout->addWidget(chkShowSamples); // 2. now we add a JKQTPXFunctionLineGraph object, which will draw a simple function // the function is defined as C++ inline function TFUNCGRAPH* func1=new TFUNCGRAPH(plot); func1->setPlotFunctionFunctor([](double x) { return 0.2*x*x-0.015*x*x*x; }); func1->setTitle("C++-inline function $0.2x^2-0.015x^3$"); + QObject::connect(chkShowSamples, &QCheckBox::toggled, [=](bool en) { + func1->setDisplaySamplePoints(en); + plot->redrawPlot(); + }); plot->addGraph(func1); // 3. now we add a JKQTPXFunctionLineGraph object, which will draw a simple function @@ -53,6 +62,10 @@ void drawExample(QApplication& app, const QString& name) { // here we set the parameters p0, p1 func2->setParamsV(5, 0.2); func2->setTitle("C++-inline function with int. params $p_0\\cdot\\sin(x*2.0*\\pi\\cdot p_1)$"); + QObject::connect(chkShowSamples, &QCheckBox::toggled, [=](bool en) { + func2->setDisplaySamplePoints(en); + plot->redrawPlot(); + }); plot->addGraph(func2); // 4. now we add a JKQTPXFunctionLineGraph object, which will draw a simple function @@ -75,6 +88,10 @@ void drawExample(QApplication& app, const QString& name) { TFUNCGRAPH* func4=new TFUNCGRAPH(plot); func4->setPlotFunctionFunctor(SincSqr(-8)); func4->setTitle("C++ functor $-8*\\sin^2(x)/x^2$"); + QObject::connect(chkShowSamples, &QCheckBox::toggled, [=](bool en) { + func4->setDisplaySamplePoints(en); + plot->redrawPlot(); + }); plot->addGraph(func4); @@ -82,6 +99,10 @@ void drawExample(QApplication& app, const QString& name) { TFUNCGRAPH* func5=new TFUNCGRAPH(plot); func5->setPlotFunctionFunctor(&sinc); func5->setTitle("static C function $10*\\sin(x)/x$"); + QObject::connect(chkShowSamples, &QCheckBox::toggled, [=](bool en) { + func5->setDisplaySamplePoints(en); + plot->redrawPlot(); + }); plot->addGraph(func5); // 7. finally JKQTPXFunctionLineGraph defines a small set of common functions @@ -90,6 +111,10 @@ void drawExample(QApplication& app, const QString& name) { // here we set offset p0=-1 and slope p1=1.5 of the line p0+p1*x func6->setParamsV(-1,1.5); func6->setTitle("special function: linear p_0=-1, p_1=1.5"); + QObject::connect(chkShowSamples, &QCheckBox::toggled, [=](bool en) { + func6->setDisplaySamplePoints(en); + plot->redrawPlot(); + }); plot->addGraph(func6); // 7. finally JKQTPXFunctionLineGraph defines a small set of common functions @@ -102,6 +127,10 @@ void drawExample(QApplication& app, const QString& name) { size_t paramCol=plot->getDatastore()->addCopiedColumn(params); func7->setParameterColumn(paramCol); func7->setTitle("special function: linear p_0=1, p_1=-1.5"); + QObject::connect(chkShowSamples, &QCheckBox::toggled, [=](bool en) { + func7->setDisplaySamplePoints(en); + plot->redrawPlot(); + }); plot->addGraph(func7); diff --git a/lib/jkqtplotter.pri b/lib/jkqtplotter.pri index 2e9fcea2c0..40eee0af72 100644 --- a/lib/jkqtplotter.pri +++ b/lib/jkqtplotter.pri @@ -27,6 +27,7 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) { $$PWD/jkqtplotter/jkqtpoverlaysbase.h \ $$PWD/jkqtplotter/graphs/jkqtpboxplot.h \ $$PWD/jkqtplotter/graphs/jkqtpboxplotstylingmixins.h \ + $$PWD/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h \ $$PWD/jkqtplotter/graphs/jkqtpevaluatedfunction.h \ $$PWD/jkqtplotter/graphs/jkqtpfilledcurve.h \ $$PWD/jkqtplotter/graphs/jkqtpgeometric.h \ @@ -77,6 +78,7 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) { $$PWD/jkqtplotter/jkqtpoverlaysbase.cpp \ $$PWD/jkqtplotter/graphs/jkqtpboxplot.cpp \ $$PWD/jkqtplotter/graphs/jkqtpboxplotstylingmixins.cpp \ + $$PWD/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp \ $$PWD/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp \ $$PWD/jkqtplotter/graphs/jkqtpfilledcurve.cpp \ $$PWD/jkqtplotter/graphs/jkqtpgeometric.cpp \ diff --git a/lib/jkqtplotter/CMakeLists.txt b/lib/jkqtplotter/CMakeLists.txt index c83991e89a..e6d452e761 100644 --- a/lib/jkqtplotter/CMakeLists.txt +++ b/lib/jkqtplotter/CMakeLists.txt @@ -44,6 +44,7 @@ set(SOURCES_GRAPHS graphs/jkqtpbarchart.cpp graphs/jkqtpboxplot.cpp graphs/jkqtpboxplotstylingmixins.cpp + graphs/jkqtpevaluatedfunctionbase.cpp graphs/jkqtpevaluatedfunction.cpp graphs/jkqtpfilledcurve.cpp graphs/jkqtpgeometric.cpp @@ -97,6 +98,7 @@ set(HEADERS set(HEADERS_GRAPHS graphs/jkqtpboxplot.h graphs/jkqtpboxplotstylingmixins.h + graphs/jkqtpevaluatedfunctionbase.h graphs/jkqtpevaluatedfunction.h graphs/jkqtpfilledcurve.h graphs/jkqtpgeometric.h diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp index 58dcf3ce64..b9a9ee4169 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp @@ -34,19 +34,12 @@ JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTBasePlotter* parent): - JKQTPGraph(parent) + JKQTPFunctionLineGraphBase(parent) { functionType=SpecialFunction::UserFunction; drawLine=true; fillCurve=false; params=nullptr; - minSamples=50; - maxRefinementDegree=5; - slopeTolerance=0.005; - minPixelPerSample=32; - dataCleanupMaxAllowedAngleDegree=0.2; - displaySamplePoints=false; - data.clear(); initLineStyle(parent, parentPlotStyle); initFillStyle(parent, parentPlotStyle); @@ -301,16 +294,6 @@ void JKQTPXFunctionLineGraph::collectParameters() } } -void JKQTPXFunctionLineGraph::drawSamplePoints(JKQTPEnhancedPainter& painter) { - QColor c=getLineColor(); - c.setHsv(fmod(c.hue()+90, 360), c.saturation(), c.value()); - painter.save(); auto __finalpaintsamplepoints=JKQTPFinally([&painter]() {painter.restore();}); - for (const auto& d: data) { - if (JKQTPIsOKFloat(d.x()) && JKQTPIsOKFloat(d.y())) { - JKQTPPlotSymbol(painter, d.x(), d.y(), JKQTPCross, 6,1*parent->getLineWidthMultiplier(), c, QColor(Qt::transparent)); - } - } -} void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { @@ -432,7 +415,7 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { } - if (displaySamplePoints) drawSamplePoints(painter); + if (displaySamplePoints) drawSamplePoints(painter, getLineColor()); } drawErrorsAfter(painter); //std::cout<<"plot done\n"; @@ -620,7 +603,7 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { } - if (displaySamplePoints) drawSamplePoints(painter); + if (displaySamplePoints) drawSamplePoints(painter, getLineColor()); } drawErrorsAfter(painter); //std::cout<<"plot done\n"; @@ -858,66 +841,6 @@ QVector JKQTPXFunctionLineGraph::getInternalErrorParams() const { return ierrorparams; } -void JKQTPXFunctionLineGraph::setMinSamples(const unsigned int &__value) -{ - this->minSamples = __value; -} - -unsigned int JKQTPXFunctionLineGraph::getMinSamples() const -{ - return this->minSamples; -} - -void JKQTPXFunctionLineGraph::setMaxRefinementDegree(const unsigned int &__value) -{ - this->maxRefinementDegree = __value; -} - -unsigned int JKQTPXFunctionLineGraph::getMaxRefinementDegree() const -{ - return this->maxRefinementDegree; -} - -void JKQTPXFunctionLineGraph::setSlopeTolerance(double __value) -{ - this->slopeTolerance = __value; -} - -double JKQTPXFunctionLineGraph::getSlopeTolerance() const -{ - return this->slopeTolerance; -} - -void JKQTPXFunctionLineGraph::setMinPixelPerSample(double __value) -{ - this->minPixelPerSample = __value; -} - -double JKQTPXFunctionLineGraph::getMinPixelPerSample() const -{ - return this->minPixelPerSample; -} - -void JKQTPXFunctionLineGraph::setDataCleanupMaxAllowedAngleDegree(double __value) -{ - dataCleanupMaxAllowedAngleDegree=__value; -} - -double JKQTPXFunctionLineGraph::getDataCleanupMaxAllowedAngleDegree() const -{ - return dataCleanupMaxAllowedAngleDegree; -} - -void JKQTPXFunctionLineGraph::setDisplaySamplePoints(bool __value) -{ - this->displaySamplePoints = __value; -} - -bool JKQTPXFunctionLineGraph::getDisplaySamplePoints() const -{ - return this->displaySamplePoints; -} - void JKQTPXFunctionLineGraph::setDrawErrorPolygons(bool __value) { this->drawErrorPolygons = __value; diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h index 8e90a29045..6719cae5fa 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h @@ -26,6 +26,7 @@ #include "jkqtplotter/jkqtpgraphsbasestylingmixins.h" #include "jkqtplotter/jkqtplotter_imexport.h" #include "jkqtcommon/jkqtpgeometrytools.h" +#include "jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h" #include #ifndef jkqtpgraphsevaluatedfunction_H @@ -73,7 +74,7 @@ typedef std::function jkqtpSimplePlotFunctionType; \see \ref JKQTPlotterFunctionPlots, JKQTPAdaptiveFunctionGraphEvaluator, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph, jkqtpstatAddPolyFit(), jkqtpstatAddWeightedRegression(), jkqtpstatAddRobustIRLSRegression(), jkqtpstatAddRegression(), jkqtpstatAddLinearWeightedRegression(), jkqtpstatAddRobustIRLSLinearRegression(), jkqtpstatAddLinearRegression() */ -class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public JKQTPGraphLineStyleMixin, public JKQTPGraphFillStyleMixin { +class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPFunctionLineGraphBase, public JKQTPGraphLineStyleMixin, public JKQTPGraphFillStyleMixin { Q_OBJECT public: @@ -180,30 +181,7 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public QVector getInternalParams() const; /** \brief returns the currently set internal parameter vector */ QVector getInternalErrorParams() const; - /*! \copydoc minSamples */ - void setMinSamples(const unsigned int & __value); - /*! \copydoc minSamples */ - unsigned int getMinSamples() const; - /*! \copydoc maxRefinementDegree */ - void setMaxRefinementDegree(const unsigned int & __value); - /*! \copydoc maxRefinementDegree */ - unsigned int getMaxRefinementDegree() const; - /*! \copydoc slopeTolerance */ - void setSlopeTolerance(double __value); - /*! \copydoc slopeTolerance */ - double getSlopeTolerance() const; - /*! \copydoc minPixelPerSample */ - void setMinPixelPerSample(double __value); - /*! \copydoc minPixelPerSample */ - double getMinPixelPerSample() const; - /*! \copydoc dataCleanupMaxAllowedAngleDegree */ - void setDataCleanupMaxAllowedAngleDegree(double __value); - /*! \copydoc dataCleanupMaxAllowedAngleDegree */ - double getDataCleanupMaxAllowedAngleDegree() const; - /*! \copydoc displaySamplePoints */ - void setDisplaySamplePoints(bool __value); - /*! \copydoc displaySamplePoints */ - bool getDisplaySamplePoints() const; + /*! \copydoc drawErrorPolygons */ void setDrawErrorPolygons(bool __value); /*! \copydoc drawErrorPolygons */ @@ -288,11 +266,8 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public 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 fill the data array with data from the function plotFunction */ - virtual void createPlotData( bool collectParams=true); + virtual void createPlotData( bool collectParams=true) override; /** \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(); @@ -314,24 +289,8 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public SpecialFunction functionType; /** \brief pointer to the parameters supplied to the plotting funtion */ void* params; - /** \brief the minimum number of points to evaluate the function at */ - unsigned int minSamples; - /** \brief the maximum number of recursive refinement steps - * - * each step bisects the interval \f$ [a, b] \f$ into two halfes. So the maximum number - * of points plotted at all are thus: - * \f[ \mbox{minSamples} \cdot 2^{\mbox{maxRefinementDegree}} \f] - */ - unsigned int maxRefinementDegree; - /** \brief the tolerance for the difference of two subsequent slopes */ - double slopeTolerance; - /** \brief create one sample at least every \a minPixelPerSample pixels */ - double minPixelPerSample; - /** \brief in the clean-up step of plot-data creation, a point is removed from the data, if - * it caused its neighboring line-segments to form an angle less than this value, given in degrees. */ - double dataCleanupMaxAllowedAngleDegree; - /** \brief if true [default: off] display the points where the function has been sampled */ - bool displaySamplePoints; + + /** \brief indicates whether an error polygon should be drawn */ bool drawErrorPolygons; /** \brief indicates whether error lines should be drawn */ @@ -362,8 +321,6 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public QVector iparams; /** \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 draw all the sample points in data as small symbols */ - void drawSamplePoints(JKQTPEnhancedPainter &painter); }; /*! \brief This implements line plots where the data is taken from a user supplied function \f$ x=f(y) \f$ diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp new file mode 100644 index 0000000000..4588d26cb8 --- /dev/null +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.cpp @@ -0,0 +1,134 @@ +/* + Copyright (c) 2008-2020 Jan W. Krieger () + + + + This software is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License (LGPL) as published by + the Free Software Foundation, either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License (LGPL) for more details. + + You should have received a copy of the GNU Lesser General Public License (LGPL) + along with this program. If not, see . +*/ + + + +#include "jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h" +#include "jkqtplotter/jkqtpbaseplotter.h" +#include +#include +#include +#include "jkqtplotter/jkqtptools.h" +#include "jkqtplotter/graphs/jkqtpimage.h" +#include "jkqtplotter/jkqtpbaseelements.h" +#include "jkqtplotter/jkqtplotter.h" + + + + + +JKQTPFunctionLineGraphBase::JKQTPFunctionLineGraphBase(JKQTBasePlotter* parent): + JKQTPGraph(parent) +{ + minSamples=50; + maxRefinementDegree=5; + slopeTolerance=0.005; + minPixelPerSample=32; + dataCleanupMaxAllowedAngleDegree=0.2; + displaySamplePoints=false; + data.clear(); + +} + +JKQTPFunctionLineGraphBase::JKQTPFunctionLineGraphBase(JKQTPlotter* parent): + JKQTPFunctionLineGraphBase(parent->getPlotter()) +{ + +} + +JKQTPFunctionLineGraphBase::~JKQTPFunctionLineGraphBase() +{ + data.clear(); +} + + +void JKQTPFunctionLineGraphBase::drawSamplePoints(JKQTPEnhancedPainter& painter, QColor graphColor) { + QColor c=graphColor; + c.setHsv(fmod(c.hue()+90, 360), c.saturation(), c.value()); + painter.save(); auto __finalpaintsamplepoints=JKQTPFinally([&painter]() {painter.restore();}); + for (const auto& d: data) { + if (JKQTPIsOKFloat(d.x()) && JKQTPIsOKFloat(d.y())) { + JKQTPPlotSymbol(painter, d.x(), d.y(), JKQTPCross, 6,1*parent->getLineWidthMultiplier(), c, QColor(Qt::transparent)); + } + } +} + + + +void JKQTPFunctionLineGraphBase::setMinSamples(const unsigned int &__value) +{ + this->minSamples = __value; +} + +unsigned int JKQTPFunctionLineGraphBase::getMinSamples() const +{ + return this->minSamples; +} + +void JKQTPFunctionLineGraphBase::setMaxRefinementDegree(const unsigned int &__value) +{ + this->maxRefinementDegree = __value; +} + +unsigned int JKQTPFunctionLineGraphBase::getMaxRefinementDegree() const +{ + return this->maxRefinementDegree; +} + +void JKQTPFunctionLineGraphBase::setSlopeTolerance(double __value) +{ + this->slopeTolerance = __value; +} + +double JKQTPFunctionLineGraphBase::getSlopeTolerance() const +{ + return this->slopeTolerance; +} + +void JKQTPFunctionLineGraphBase::setMinPixelPerSample(double __value) +{ + this->minPixelPerSample = __value; +} + +double JKQTPFunctionLineGraphBase::getMinPixelPerSample() const +{ + return this->minPixelPerSample; +} + +void JKQTPFunctionLineGraphBase::setDataCleanupMaxAllowedAngleDegree(double __value) +{ + dataCleanupMaxAllowedAngleDegree=__value; +} + +double JKQTPFunctionLineGraphBase::getDataCleanupMaxAllowedAngleDegree() const +{ + return dataCleanupMaxAllowedAngleDegree; +} + +void JKQTPFunctionLineGraphBase::setDisplaySamplePoints(bool __value) +{ + this->displaySamplePoints = __value; +} + +bool JKQTPFunctionLineGraphBase::getDisplaySamplePoints() const +{ + return this->displaySamplePoints; +} + + diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h new file mode 100644 index 0000000000..6ab4e88689 --- /dev/null +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h @@ -0,0 +1,128 @@ +/* + Copyright (c) 2008-2020 Jan W. Krieger () + + + + This software is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License (LGPL) as published by + the Free Software Foundation, either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License (LGPL) for more details. + + You should have received a copy of the GNU Lesser General Public License (LGPL) + along with this program. If not, see . +*/ + + + +#include +#include +#include +#include "jkqtplotter/graphs/jkqtpscatter.h" +#include "jkqtplotter/jkqtpgraphsbasestylingmixins.h" +#include "jkqtplotter/jkqtplotter_imexport.h" +#include "jkqtcommon/jkqtpgeometrytools.h" +#include + +#ifndef jkqtpevaluatedfunctionbase_H +#define jkqtpevaluatedfunctionbase_H + + + + +/*! \brief Base class for graph classes that evaluate a mathematical function (e.g. defined as a C-fucntion), + using an adaptive plotting algorithm from JKQTPAdaptiveFunctionGraphEvaluator + \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 JKQTPAdaptiveFunctionGraphEvaluator, JKQTPXFunctionLineGraph, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph + */ +class JKQTPLOTTER_LIB_EXPORT JKQTPFunctionLineGraphBase: public JKQTPGraph { + Q_OBJECT + public: + + /** \brief class constructor */ + JKQTPFunctionLineGraphBase(JKQTBasePlotter* parent=nullptr); + + /** \brief class constructor */ + JKQTPFunctionLineGraphBase(JKQTPlotter* parent); + + /** \brief class destructor */ + virtual ~JKQTPFunctionLineGraphBase() ; + + + /*! \copydoc minSamples */ + unsigned int getMinSamples() const; + /*! \copydoc maxRefinementDegree */ + unsigned int getMaxRefinementDegree() const; + /*! \copydoc slopeTolerance */ + double getSlopeTolerance() const; + /*! \copydoc minPixelPerSample */ + double getMinPixelPerSample() const; + /*! \copydoc dataCleanupMaxAllowedAngleDegree */ + double getDataCleanupMaxAllowedAngleDegree() const; + /*! \copydoc displaySamplePoints */ + bool getDisplaySamplePoints() const; + public slots: + /*! \copydoc minSamples */ + void setMinSamples(const unsigned int & __value); + /*! \copydoc maxRefinementDegree */ + void setMaxRefinementDegree(const unsigned int & __value); + /*! \copydoc slopeTolerance */ + void setSlopeTolerance(double __value); + /*! \copydoc minPixelPerSample */ + void setMinPixelPerSample(double __value); + /*! \copydoc dataCleanupMaxAllowedAngleDegree */ + void setDataCleanupMaxAllowedAngleDegree(double __value); + /*! \copydoc displaySamplePoints */ + void setDisplaySamplePoints(bool __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 fill the data array with data from the function plotFunction */ + virtual void createPlotData( bool collectParams=true) =0; + + /** \brief the minimum number of points to evaluate the function at */ + unsigned int minSamples; + /** \brief the maximum number of recursive refinement steps + * + * each step bisects the interval \f$ [a, b] \f$ into two halfes. So the maximum number + * of points plotted at all are thus: + * \f[ \mbox{minSamples} \cdot 2^{\mbox{maxRefinementDegree}} \f] + */ + unsigned int maxRefinementDegree; + /** \brief the tolerance for the difference of two subsequent slopes */ + double slopeTolerance; + /** \brief create one sample at least every \a minPixelPerSample pixels */ + double minPixelPerSample; + /** \brief in the clean-up step of plot-data creation, a point is removed from the data, if + * it caused its neighboring line-segments to form an angle less than this value, given in degrees. */ + 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); +}; + + +#endif // jkqtpevaluatedfunctionbase_H diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp index 07c06935bf..8601cc5c3e 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp @@ -35,17 +35,11 @@ JKQTPXYFunctionLineGraph::JKQTPXYFunctionLineGraph(JKQTBasePlotter* parent): - JKQTPGraph(parent) + JKQTPFunctionLineGraphBase(parent) { tmin=0.0; tmax=1.0; params=nullptr; - minSamples=100; - maxRefinementDegree=7; - slopeTolerance=0.005; - minPixelPerSample=32; - plotRefinement=true; - displaySamplePoints=false; initLineStyle(parent, parentPlotStyle); @@ -268,7 +262,7 @@ void JKQTPXYFunctionLineGraph::createPlotData(bool collectParams) { JKQTPAdaptiveFunctionGraphEvaluator evaluator(fTransformedFunc, minSamples, maxRefinementDegree, slopeTolerance, minPixelPerSample); data=evaluator.evaluate(tmin, tmax); - + data=JKQTPSimplyfyLineSegemnts(data, dataCleanupMaxAllowedAngleDegree); } void JKQTPXYFunctionLineGraph::collectParameters() @@ -326,17 +320,7 @@ void JKQTPXYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { } - QColor c=getLineColor(); - c.setHsv(fmod(c.hue()+90, 360), c.saturation(), c.value()); - if (displaySamplePoints) { - painter.save(); auto __finalpaintsamplepoints=JKQTPFinally([&painter]() {painter.restore();}); - for (const auto& d: data) { - if (JKQTPIsOKFloat(d.x()) && JKQTPIsOKFloat(d.x())) { - JKQTPPlotSymbol(painter, d.x(), d.y(), JKQTPCross, 6,1*parent->getLineWidthMultiplier(), c, QColor(Qt::transparent)); - } - } - - } + if (displaySamplePoints) drawSamplePoints(painter, getLineColor()); } drawErrorsAfter(painter); //std::cout<<"plot done\n"; @@ -409,66 +393,6 @@ QVector JKQTPXYFunctionLineGraph::getInternalParams() const { return iparams; } -void JKQTPXYFunctionLineGraph::setMinSamples(const unsigned int &__value) -{ - this->minSamples = __value; -} - -unsigned int JKQTPXYFunctionLineGraph::getMinSamples() const -{ - return this->minSamples; -} - -void JKQTPXYFunctionLineGraph::setMaxRefinementDegree(const unsigned int &__value) -{ - this->maxRefinementDegree = __value; -} - -unsigned int JKQTPXYFunctionLineGraph::getMaxRefinementDegree() const -{ - return this->maxRefinementDegree; -} - -void JKQTPXYFunctionLineGraph::setSlopeTolerance(double __value) -{ - this->slopeTolerance = __value; -} - -double JKQTPXYFunctionLineGraph::getSlopeTolerance() const -{ - return this->slopeTolerance; -} - -void JKQTPXYFunctionLineGraph::setMinPixelPerSample(double __value) -{ - this->minPixelPerSample = __value; -} - -double JKQTPXYFunctionLineGraph::getMinPixelPerSample() const -{ - return this->minPixelPerSample; -} - -void JKQTPXYFunctionLineGraph::setPlotRefinement(bool __value) -{ - this->plotRefinement = __value; -} - -bool JKQTPXYFunctionLineGraph::getPlotRefinement() const -{ - return this->plotRefinement; -} - -void JKQTPXYFunctionLineGraph::setDisplaySamplePoints(bool __value) -{ - this->displaySamplePoints = __value; -} - -bool JKQTPXYFunctionLineGraph::getDisplaySamplePoints() const -{ - return this->displaySamplePoints; -} - bool JKQTPXYFunctionLineGraph::usesColumn(int c) const { diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h index 7d3edad10c..6a4df3b76c 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.h @@ -26,6 +26,7 @@ #include "jkqtplotter/jkqtpgraphsbasestylingmixins.h" #include "jkqtplotter/jkqtplotter_imexport.h" #include "jkqtcommon/jkqtpgeometrytools.h" +#include "jkqtplotter/graphs/jkqtpevaluatedfunctionbase.h" #include #ifndef jkqtpevaluatedparametriccurve_H @@ -57,14 +58,17 @@ typedef std::function jkqtpSimpleParametricCurveFunctionType; 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 implements an intelligent plotting algorithm for functions. It starts by sampling - the function at minSamples positions. Then each function interval is bisected recursively if + 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 + 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 @@ -84,7 +88,7 @@ typedef std::function jkqtpSimpleParametricCurveFunctionType; \see \ref JKQTPlotterEvalCurves , JKQTPAdaptiveFunctionGraphEvaluator, JKQTPXFunctionLineGraph, JKQTPYFunctionLineGraph */ -class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPGraph, public JKQTPGraphLineStyleMixin { +class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPFunctionLineGraphBase, public JKQTPGraphLineStyleMixin { Q_OBJECT public: @@ -177,30 +181,7 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPGraph, public /** \brief returns the currently set internal parameter vector */ QVector getInternalParams() const; - /*! \copydoc minSamples */ - void setMinSamples(const unsigned int & __value); - /*! \copydoc minSamples */ - unsigned int getMinSamples() const; - /*! \copydoc maxRefinementDegree */ - void setMaxRefinementDegree(const unsigned int & __value); - /*! \copydoc maxRefinementDegree */ - unsigned int getMaxRefinementDegree() const; - /*! \copydoc slopeTolerance */ - void setSlopeTolerance(double __value); - /*! \copydoc slopeTolerance */ - double getSlopeTolerance() const; - /*! \copydoc minPixelPerSample */ - void setMinPixelPerSample(double __value); - /*! \copydoc minPixelPerSample */ - double getMinPixelPerSample() const; - /*! \copydoc plotRefinement */ - void setPlotRefinement(bool __value); - /*! \copydoc plotRefinement */ - bool getPlotRefinement() const; - /*! \copydoc displaySamplePoints */ - void setDisplaySamplePoints(bool __value); - /*! \copydoc displaySamplePoints */ - bool getDisplaySamplePoints() const; + /*! \copydoc parameterColumn */ void setParameterColumn(int __value); @@ -233,14 +214,10 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPGraph, public 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 plot data calculated by createPlotData() */ - QVector data; - /** \brief fill the data array with data from the function plotFunction */ - virtual void createPlotData( bool collectParams=true); + virtual void createPlotData( bool collectParams=true) override; /** \brief ensure that current function parameters for plotFunction (which may stem from different sources, as direct data, a datastore column ...) are stored in iparams */ - virtual void collectParameters(); + virtual void collectParameters() ; /** \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; @@ -251,23 +228,7 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYFunctionLineGraph: public JKQTPGraph, public jkqtpSimpleParametricCurveFunctionType simplePlotFunction; /** \brief pointer to the parameters supplied to the plotting funtion */ void* params; - /** \brief the minimum number of points to evaluate the function at */ - unsigned int minSamples; - /** \brief the maximum number of recursive refinement steps - * - * each step bisects the interval \f$ [a, b] \f$ into two halfes. So the maximum number - * of points plotted at all are thus: - * \f[ \mbox{minSamples} \cdot 2^{\mbox{maxRefinementDegree}} \f] - */ - unsigned int maxRefinementDegree; - /** \brief the tolerance for the difference of two subsequent slopes */ - double slopeTolerance; - /** \brief create one sample at least every \a minPixelPerSample pixels */ - double minPixelPerSample; - /** \brief switch on or off [default: on] the plot refinement algorithm */ - bool plotRefinement; - /** \brief if true [default: off] display the points where the function has been sampled */ - bool displaySamplePoints; + /** \brief internal storage for the current function parameters for plotFunction (which may stem from different sources, as direct data, a datastore column ...) */ QVector iparams; };