2020-09-05 19:14:46 +08:00
/*
Copyright ( c ) 2008 - 2020 Jan W . Krieger ( < jan @ jkrieger . de > )
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 < http : //www.gnu.org/licenses/>.
*/
# include <QString>
# include <QPainter>
# include <QPair>
# include "jkqtplotter/graphs/jkqtpscatter.h"
# include "jkqtplotter/jkqtpgraphsbasestylingmixins.h"
# include "jkqtplotter/jkqtplotter_imexport.h"
# include "jkqtcommon/jkqtpgeometrytools.h"
# include <functional>
# ifndef jkqtpevaluatedfunctionbase_H
# define jkqtpevaluatedfunctionbase_H
2020-09-08 02:57:25 +08:00
/** \brief Base class for graph classes that evaluate a mathematical function (e.g. defined as a C-function),
2020-09-05 19:47:46 +08:00
* using an adaptive plotting algorithm from JKQTPAdaptiveFunctionGraphEvaluator
* \ ingroup jkqtplotter_functiongraphs
*
2020-09-08 02:57:25 +08:00
* This class always plots a general 2 D - 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 .
*
*
2020-09-05 19:47:46 +08:00
* 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 .
*
*
2020-09-08 02:57:25 +08:00
*
*
*
2020-09-05 19:47:46 +08:00
* \ see JKQTPAdaptiveFunctionGraphEvaluator , JKQTPXFunctionLineGraph , JKQTPYFunctionLineGraph , JKQTPXYFunctionLineGraph
2020-09-05 19:14:46 +08:00
*/
2020-09-05 19:16:26 +08:00
class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionGraphBase : public JKQTPGraph {
2020-09-05 19:14:46 +08:00
Q_OBJECT
public :
/** \brief class constructor */
2020-09-05 19:47:46 +08:00
explicit JKQTPEvaluatedFunctionGraphBase ( JKQTBasePlotter * parent = nullptr ) ;
2020-09-05 19:14:46 +08:00
/** \brief class constructor */
2020-09-05 19:47:46 +08:00
explicit JKQTPEvaluatedFunctionGraphBase ( JKQTPlotter * parent ) ;
2020-09-05 19:14:46 +08:00
/** \brief class destructor */
2020-09-05 19:16:26 +08:00
virtual ~ JKQTPEvaluatedFunctionGraphBase ( ) ;
2020-09-05 19:14:46 +08:00
2020-09-05 19:47:46 +08:00
/** \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 ;
2020-09-08 02:57:25 +08:00
/** \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 < double > & 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 < double > & getInternalParams ( ) const ;
/** \brief returns the currently set internal parameter vector */
QVector < double > & getInternalParams ( ) ;
/** \copydoc parameterColumn */
int getParameterColumn ( ) const ;
/** \copydoc JKQTPGraph::usesColumn() */
virtual bool usesColumn ( int c ) const override ;
2020-09-05 19:14:46 +08:00
2020-09-05 19:47:46 +08:00
/** \copydoc minSamples */
2020-09-05 19:14:46 +08:00
unsigned int getMinSamples ( ) const ;
2020-09-05 19:47:46 +08:00
/** \copydoc maxRefinementDegree */
2020-09-05 19:14:46 +08:00
unsigned int getMaxRefinementDegree ( ) const ;
2020-09-05 19:47:46 +08:00
/** \copydoc slopeTolerance */
2020-09-05 19:14:46 +08:00
double getSlopeTolerance ( ) const ;
2020-09-05 19:47:46 +08:00
/** \copydoc minPixelPerSample */
2020-09-05 19:14:46 +08:00
double getMinPixelPerSample ( ) const ;
2020-09-05 19:47:46 +08:00
/** \copydoc dataCleanupMaxAllowedAngleDegree */
2020-09-05 19:14:46 +08:00
double getDataCleanupMaxAllowedAngleDegree ( ) const ;
2020-09-05 19:47:46 +08:00
/** \copydoc displaySamplePoints */
2020-09-05 19:14:46 +08:00
bool getDisplaySamplePoints ( ) const ;
public slots :
2020-09-05 19:47:46 +08:00
/** \copydoc minSamples */
2020-09-05 19:14:46 +08:00
void setMinSamples ( const unsigned int & __value ) ;
2020-09-05 19:47:46 +08:00
/** \copydoc maxRefinementDegree */
2020-09-05 19:14:46 +08:00
void setMaxRefinementDegree ( const unsigned int & __value ) ;
2020-09-05 19:47:46 +08:00
/** \copydoc slopeTolerance */
2020-09-05 19:14:46 +08:00
void setSlopeTolerance ( double __value ) ;
2020-09-05 19:47:46 +08:00
/** \copydoc minPixelPerSample */
2020-09-05 19:14:46 +08:00
void setMinPixelPerSample ( double __value ) ;
2020-09-05 19:47:46 +08:00
/** \copydoc dataCleanupMaxAllowedAngleDegree */
2020-09-05 19:14:46 +08:00
void setDataCleanupMaxAllowedAngleDegree ( double __value ) ;
2020-09-05 19:47:46 +08:00
/** \copydoc displaySamplePoints */
2020-09-05 19:14:46 +08:00
void setDisplaySamplePoints ( bool __value ) ;
2020-09-08 02:57:25 +08:00
/** \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 ) ;
2020-09-05 19:14:46 +08:00
protected :
2020-09-08 02:57:25 +08:00
/** \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 < QPointF ( double ) > 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 ) ;
2020-09-05 19:14:46 +08:00
/** \brief fill the data array with data from the function plotFunction */
2020-09-08 02:57:25 +08:00
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 < double > 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 < QPointF > data ;
2020-09-05 19:14:46 +08:00
/** \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 ;
} ;
2020-09-08 02:57:25 +08:00
/** \brief extends JKQTPEvaluatedFunctionGraphBase with some basic properties (e.g. function parameters)
* for a second function that calculates an error ( for drawing error indicators )
2020-09-05 19:47:46 +08:00
* \ ingroup jkqtplotter_functiongraphs
*
2020-09-08 02:57:25 +08:00
* When implementing this , you will have to implement buildErrorFunctorSpec ( ) in addition to
* JKQTPEvaluatedFunctionGraphBase : : buildPlotFunctorSpec ( ) !
*
2020-09-05 19:47:46 +08:00
* \ see JKQTPEvaluatedFunctionGraphBase
*/
2020-09-08 02:57:25 +08:00
class JKQTPLOTTER_LIB_EXPORT JKQTPEvaluatedFunctionWithErrorsGraphBase : public JKQTPEvaluatedFunctionGraphBase {
2020-09-05 19:47:46 +08:00
Q_OBJECT
public :
/** \brief class constructor */
2020-09-08 02:57:25 +08:00
explicit JKQTPEvaluatedFunctionWithErrorsGraphBase ( JKQTBasePlotter * parent = nullptr ) ;
2020-09-05 19:47:46 +08:00
/** \brief class constructor */
2020-09-08 02:57:25 +08:00
explicit JKQTPEvaluatedFunctionWithErrorsGraphBase ( JKQTPlotter * parent ) ;
2020-09-05 19:47:46 +08:00
/** \brief class destructor */
2020-09-08 02:57:25 +08:00
virtual ~ JKQTPEvaluatedFunctionWithErrorsGraphBase ( ) ;
2020-09-05 19:47:46 +08:00
2020-09-08 02:57:25 +08:00
/** \copydoc errorParameterColumn */
int getErrorParameterColumn ( ) const ;
2020-09-05 19:47:46 +08:00
/** \brief returns the currently set internal parameter vector */
2020-09-08 02:57:25 +08:00
const QVector < double > & getInternalErrorParams ( ) const ;
/** \brief returns the currently set internal parameter vector */
QVector < double > & getInternalErrorParams ( ) ;
2020-09-05 19:47:46 +08:00
/** \copydoc JKQTPGraph::usesColumn() */
virtual bool usesColumn ( int c ) const override ;
public slots :
2020-09-08 02:57:25 +08:00
/** \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 < double > & errorParams ) ;
/** \copydoc errorParameterColumn */
void setErrorParameterColumn ( int __value ) ;
/** \copydoc errorParameterColumn */
void setErrorParameterColumn ( size_t __value ) ;
2020-09-05 19:47:46 +08:00
2020-09-08 02:57:25 +08:00
/** \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 ) ;
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 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 < QPointF ( double ) > buildErrorFunctorSpec ( ) = 0 ;
2020-09-05 19:47:46 +08:00
2020-09-08 02:57:25 +08:00
/** \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 < double > ierrorparams ;
2020-09-05 19:47:46 +08:00
} ;
2020-09-05 19:14:46 +08:00
# endif // jkqtpevaluatedfunctionbase_H