From 8c0c8bf62eb17a690be4aa09ce96bcef68e88532 Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Sat, 5 Sep 2020 12:44:02 +0200 Subject: [PATCH] simplification/unification: JKQTPXFunctionLineGraph and JKQTPYFunctionLineGraph now use JKQTPAdaptiveFunctionGraphEvaluator to generate sample points --- examples/functionplot/functionplot.cpp | 47 ++- lib/jkqtcommon/jkqtpgeometrytools.cpp | 2 +- lib/jkqtcommon/jkqtpgeometrytools.h | 2 +- .../graphs/jkqtpevaluatedfunction.cpp | 376 ++++++------------ .../graphs/jkqtpevaluatedfunction.h | 20 +- .../graphs/jkqtpevaluatedparametriccurve.cpp | 2 +- 6 files changed, 150 insertions(+), 299 deletions(-) diff --git a/examples/functionplot/functionplot.cpp b/examples/functionplot/functionplot.cpp index 95f1160ea5..42c687fd70 100644 --- a/examples/functionplot/functionplot.cpp +++ b/examples/functionplot/functionplot.cpp @@ -24,21 +24,20 @@ private: double a; }; -int main(int argc, char* argv[]) -{ - QApplication app(argc, argv); - +template +void drawExample(QApplication& app, const QString& name) { // 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; - JKQTPlotter* plot=new JKQTPlotter(&mainWin); + QWidget* mainWin=new QWidget(); + mainWin->setWindowTitle(name); + JKQTPlotter* plot=new JKQTPlotter(mainWin); QVBoxLayout* layout=new QVBoxLayout; - mainWin.setLayout(layout); + mainWin->setLayout(layout); layout->addWidget(plot); // 2. now we add a JKQTPXFunctionLineGraph object, which will draw a simple function // the function is defined as C++ inline function - JKQTPXFunctionLineGraph* func1=new JKQTPXFunctionLineGraph(plot); + 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$"); plot->addGraph(func1); @@ -46,7 +45,7 @@ int main(int argc, char* argv[]) // 3. now we add a JKQTPXFunctionLineGraph object, which will draw a simple function // the function is again defined as C++ inline function, but now uses internal // parameters (handed over to the function as a pointer to QVector - JKQTPXFunctionLineGraph* func2=new JKQTPXFunctionLineGraph(plot); + TFUNCGRAPH* func2=new TFUNCGRAPH(plot); func2->setPlotFunctionFunctor([](double x, void* params) { QVector* p=static_cast*>(params); return p->at(0)*sin(2.0*JKQTPSTATISTICS_PI*x*p->at(1)); @@ -59,7 +58,7 @@ int main(int argc, char* argv[]) // 4. now we add a JKQTPXFunctionLineGraph object, which will draw a simple function // the function is again defined as C++ inline function, but now uses external // parameters, which may have any type (here QMapsetPlotFunctionFunctor([](double x, void* params) { QMap* p=static_cast*>(params); return p->value("amplitude")*sin(2.0*JKQTPSTATISTICS_PI*x*p->value("frequency")); @@ -70,32 +69,32 @@ int main(int argc, char* argv[]) params3["frequency"]=0.3; func3->setParams(¶ms3); func3->setTitle("C++-inline function with ext. params $p_0\\cdot\\sin(x*2.0*\\pi\\cdot p_1)$"); - plot->addGraph(func3); + plot->addGraph(func3);*/ // 5. of course the function may also be any C+ funtor object: - JKQTPXFunctionLineGraph* func4=new JKQTPXFunctionLineGraph(plot); + TFUNCGRAPH* func4=new TFUNCGRAPH(plot); func4->setPlotFunctionFunctor(SincSqr(-8)); func4->setTitle("C++ functor $-8*\\sin^2(x)/x^2$"); plot->addGraph(func4); // 6. now we use a JKQTPXFunctionLineGraph to draw a static C function - JKQTPXFunctionLineGraph* func5=new JKQTPXFunctionLineGraph(plot); + TFUNCGRAPH* func5=new TFUNCGRAPH(plot); func5->setPlotFunctionFunctor(&sinc); func5->setTitle("static C function $10*\\sin(x)/x$"); plot->addGraph(func5); // 7. finally JKQTPXFunctionLineGraph defines a small set of common functions - JKQTPXFunctionLineGraph* func6=new JKQTPXFunctionLineGraph(plot); - func6->setSpecialFunction(JKQTPXFunctionLineGraph::Line); + TFUNCGRAPH* func6=new TFUNCGRAPH(plot); + func6->setSpecialFunction(TFUNCGRAPH::Line); // 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"); plot->addGraph(func6); // 7. finally JKQTPXFunctionLineGraph defines a small set of common functions - JKQTPXFunctionLineGraph* func7=new JKQTPXFunctionLineGraph(plot); - func7->setSpecialFunction(JKQTPXFunctionLineGraph::Line); + TFUNCGRAPH* func7=new TFUNCGRAPH(plot); + func7->setSpecialFunction(TFUNCGRAPH::Line); // here we set offset p0=1 and slope p1=-1.5 of the line p0+p1*x by adding these into a column // in the internal datastore and then set that column as parameterColumn for the function graph QVector params; @@ -114,10 +113,20 @@ int main(int argc, char* argv[]) // 4. scale the plot so the graph is contained plot->setXY(-10,10,-10,10); + plot->redrawPlot(); // show window and make it a decent size - mainWin.show(); - mainWin.resize(800,800); + mainWin->show(); + mainWin->resize(800,800); +} + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + drawExample(app, "functionplot: JKQTPXFunctionLineGraph"); + drawExample(app, "functionplot: JKQTPYFunctionLineGraph"); + return app.exec(); } diff --git a/lib/jkqtcommon/jkqtpgeometrytools.cpp b/lib/jkqtcommon/jkqtpgeometrytools.cpp index 4afd2f1332..a0e7c43fdd 100644 --- a/lib/jkqtcommon/jkqtpgeometrytools.cpp +++ b/lib/jkqtcommon/jkqtpgeometrytools.cpp @@ -289,7 +289,7 @@ void JKQTPAdaptiveFunctionGraphEvaluator::refine(JKQTPAdaptiveFunctionGraphEvalu if (degree>=maxRefinementDegree) return; const double ta=a->first; const double tb=b->first; - const double tmid=ta+(tb-ta)/2.0; + const double tmid=ta+(tb-ta)*(0.5 +(static_cast(rand())/static_cast(RAND_MAX)-0.5)/5.0); const QPointF pa=a->second; const QPointF pb=b->second; const QPointF pmid(fxy(tmid)); diff --git a/lib/jkqtcommon/jkqtpgeometrytools.h b/lib/jkqtcommon/jkqtpgeometrytools.h index 9ed8d43bfd..3a629a1bcb 100644 --- a/lib/jkqtcommon/jkqtpgeometrytools.h +++ b/lib/jkqtcommon/jkqtpgeometrytools.h @@ -193,7 +193,7 @@ JKQTCOMMON_LIB_EXPORT QVector JKQTPSplitPolylineIntoPoints(const QVecto \note this implements an incomplete algorithm */ -JKQTCOMMON_LIB_EXPORT QVector JKQTPSimplyfyLineSegemnts(const QVector& points, double maxConsecutiveAngleDegree=1.0); +JKQTCOMMON_LIB_EXPORT QVector JKQTPSimplyfyLineSegemnts(const QVector& points, double maxConsecutiveAngleDegree=0.2); /** \brief cleans a polygon by uniting all consecutive points that were closer than distanceThreshold are united diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp index 3eebb330f0..2b74be32ff 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.cpp @@ -40,13 +40,13 @@ JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTBasePlotter* parent): drawLine=true; fillCurve=false; params=nullptr; - minSamples=10; - maxRefinementDegree=7; + minSamples=50; + maxRefinementDegree=5; slopeTolerance=0.005; minPixelPerSample=32; plotRefinement=true; displaySamplePoints=false; - data=nullptr; + data.clear(); initLineStyle(parent, parentPlotStyle); initFillStyle(parent, parentPlotStyle); @@ -88,7 +88,7 @@ JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionTy plotFunction=jkqtpPlotFunctionType(); simplePlotFunction=f; functionType=SpecialFunction::UserFunction; - clearData(); + data.clear(); } JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(const jkqtpSimplePlotFunctionType &f, const QString &title_, JKQTPlotter *parent): @@ -103,7 +103,7 @@ JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType &&f plotFunction=jkqtpPlotFunctionType(); simplePlotFunction=std::move(f); functionType=SpecialFunction::UserFunction; - clearData(); + data.clear(); } JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(jkqtpSimplePlotFunctionType &&f, const QString &title_, JKQTPlotter *parent): @@ -118,7 +118,7 @@ JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::Specia title=title_; functionType=type; setParams(params); - clearData(); + data.clear(); } JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::SpecialFunction type, const QVector ¶ms, const QString &title, JKQTPlotter *parent): @@ -129,17 +129,9 @@ JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTPXFunctionLineGraph::Specia JKQTPXFunctionLineGraph::~JKQTPXFunctionLineGraph() { - clearData(); + data.clear(); } -void JKQTPXFunctionLineGraph::clearData() { - while (data!=nullptr) { - doublePair* d=data; - data=data->next; - delete d; - } - data=nullptr; -} void JKQTPXFunctionLineGraph::setDrawLine(bool __value) { @@ -157,7 +149,7 @@ void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(const jkqtpPlotFunctionType plotFunction = __value; functionType=SpecialFunction::UserFunction; - clearData(); + data.clear(); } void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(const jkqtpSimplePlotFunctionType &__value) @@ -166,7 +158,7 @@ void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(const jkqtpSimplePlotFuncti simplePlotFunction=__value; functionType=SpecialFunction::UserFunction; - clearData(); + data.clear(); } void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(jkqtpPlotFunctionType &&__value) @@ -174,7 +166,7 @@ void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(jkqtpPlotFunctionType &&__v simplePlotFunction=jkqtpSimplePlotFunctionType(); plotFunction = std::move(__value); functionType=SpecialFunction::UserFunction; - clearData(); + data.clear(); } void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(jkqtpSimplePlotFunctionType &&__value) @@ -183,7 +175,7 @@ void JKQTPXFunctionLineGraph::setPlotFunctionFunctor(jkqtpSimplePlotFunctionType simplePlotFunction=std::move(__value); functionType=SpecialFunction::UserFunction; - clearData(); + data.clear(); } jkqtpPlotFunctionType JKQTPXFunctionLineGraph::getPlotFunctionFunctor() const @@ -200,7 +192,7 @@ void JKQTPXFunctionLineGraph::setParams(void *__value) { if (this->params != __value) { this->params = __value; - clearData(); + data.clear(); } } @@ -244,7 +236,7 @@ void JKQTPXFunctionLineGraph::createPlotData(bool collectParams) { #ifdef JKQTBP_AUTOTIMER JKQTPAutoOutputTimer jkaat(QString("JKQTPXFunctionLineGraph[%1]::createPlotData()").arg(title)); #endif - clearData(); + data.clear(); if (collectParams) collectParameters(); if (parent==nullptr) return; @@ -254,52 +246,14 @@ void JKQTPXFunctionLineGraph::createPlotData(bool collectParams) { if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, params); else if (simplePlotFunction) func=simplePlotFunction; - double xmin=parent->getXMin(); - double xmax=parent->getXMax(); - double pxmin=transformX(xmin); - double pxmax=transformX(xmax); - double delta0=(pxmax-pxmin)/static_cast(minSamples); - //double logdelta0=(log(xmax)-log(xmin))/static_cast(minSamples); + const double xmin=parent->getXMin(); + const double xmax=parent->getXMax(); - // initially sample function - doublePair* d=new doublePair; - d->x=xmin; - d->f=func(xmin); - d->next=nullptr; - data=d; - /*if (parent && parent->getXAxis()->isLogAxis()) { - for (double x=log(xmin)+logdelta0; xnext = new doublePair; - d->next->x=exp(x+(static_cast(rand())/static_cast(RAND_MAX)-0.5)*delta0/2.0); - d->next->f=func(d->next->x,); - d->next->next=nullptr; - doublePair* dd=d; - d=d->next; - refine(dd, d); - } - } else {*/ - QVector* dv=static_cast*>(params); - if (functionType==Polynomial && dv && dv->size()<=2) { - // we only need the first and last datapoint - } else { - for (double x=pxmin+delta0; xnext = new doublePair; - d->next->x=parent->p2x(x+(static_cast(rand())/static_cast(RAND_MAX)-0.5)*delta0/2.0); - d->next->f=func(d->next->x); - d->next->next=nullptr; - doublePair* dd=d; - d=d->next; - refine(dd, d); - } - } - - //} - d->next = new doublePair; - d->next->x=xmax; - d->next->f=func(xmax); - d->next->next=nullptr; - refine(d, d->next); + 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); } void JKQTPXFunctionLineGraph::collectParameters() @@ -347,40 +301,14 @@ void JKQTPXFunctionLineGraph::collectParameters() } } -void JKQTPXFunctionLineGraph::refine(doublePair* a, doublePair* b, unsigned int degree) { - if (degree>=maxRefinementDegree) return; - double ax=transformX(a->x); - double af=transformX(a->f); - double bx=transformX(b->x); - double bf=transformX(b->f); - - double delta=bx - ax; - //double logdelta=log(bx) - log(ax); - double xmid=ax+(delta)/2.0; - /*if (parent && parent->getXAxis()->isLogAxis()) { - xmid=log(a->x)+(logdelta)/2.0; - xmid=xmid+(static_cast(rand())/static_cast(RAND_MAX)-0.5)*delta/5.0; - xmid=exp(xmid); - } else {*/ - xmid=xmid+(static_cast(rand())/static_cast(RAND_MAX)-0.5)*delta/5.0; // shake by 10% - //} - const double realxmid=parent->p2x(xmid); - double realfmid = 0.0; - if (plotFunction) realfmid=plotFunction(realxmid, params); - else if (simplePlotFunction) realfmid=simplePlotFunction(realxmid); - const double fmid=transformY(realfmid); - const double a1=(fmid - af)/(xmid - ax); - const double a2=(bf - fmid)/(bx - xmid); - //std::cout<x<<", "<f<<"], ["<x<<", "<f<<"] ): a1="<slopeTolerance || delta>minPixelPerSample) { - doublePair* dmid = new doublePair; - dmid->x=realxmid; - dmid->f=realfmid; - a->next=dmid; - dmid->next=b; - refine(a, dmid, degree+1); - refine(dmid, b, degree+1); +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)); + } } } @@ -417,34 +345,25 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { eb.setStyle(errorFillStyle); - // double xold=-1; - // double yold=-1; - // double ypeold=-1; - // double ymeold=-1; - - // double x0=transformX(0); - // if (parent->getXAxis()->isLogAxis()) x0=transformX(parent->getXAxis()->getMin()); double y0=transformY(0); if (parent->getYAxis()->isLogAxis()) y0=transformY(parent->getYAxis()->getMin()); bool first=false; - doublePair* d=data; - //QPainterPath pa, pfill; - //QPainterPath pel, pef; QPolygonF filledPolygon, linePolygon, errorLineTop, errorLineBottom; QList epTop, epBottom; double yami=qMin(transformY(parent->getYAxis()->getMin()),transformY(parent->getYAxis()->getMax())); double yama=qMax(transformY(parent->getYAxis()->getMin()),transformY(parent->getYAxis()->getMax())); double dypix=fabs(yama-yami); - yami=yami-2*dypix; - yama=yama+2*dypix; - while (d!=nullptr) { + yami=yami-2.0*dypix; + yama=yama+2.0*dypix; + for (auto it=data.begin(); it!=data.end(); ++it) { + const QPointF& d=*it; + double x=d.x(); + double y=d.y(); - double xv=d->x; - double yv=d->f; //std::cout<<"(xv, yv) = ( "<(errorPlotFunction))) { double e=errorPlotFunction(xv, errorParams); @@ -459,7 +378,7 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { if (fillCurve) { if (!first) filledPolygon<next) filledPolygon<(errorPlotFunction))) { @@ -476,20 +395,14 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { errorLineBottom<next; } if (drawErrorPolygons) { painter.save(); auto __finalpainterrpoly=JKQTPFinally([&painter]() {painter.restore();}); painter.setBrush(eb); painter.setPen(np); QPolygonF poly; - //poly << QPointF(xold, ypeold) << QPointF(x, ype)<< QPointF(x, yme) << QPointF(xold, ymeold) ; for (int i=0; i(errorPlotFunction))) { @@ -518,29 +429,10 @@ void JKQTPXFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { painter.setPen(ep); painter.drawPolyline(errorLineTop); painter.drawPolyline(errorLineBottom); - - } - QColor c=getLineColor(); - c.setHsv(fmod(c.hue()+90, 360), c.saturation(), c.value()); - d=data; - if (displaySamplePoints) { - painter.save(); auto __finalpaintsamplepoints=JKQTPFinally([&painter]() {painter.restore();}); - while (d!=nullptr) { - double xv=d->x; - double yv=d->f; - //std::cout<<"(xv, yv) = ( "<getLineWidthMultiplier(), c, QColor(Qt::transparent)); - } - d=d->next; - } - - } + if (displaySamplePoints) drawSamplePoints(painter); } drawErrorsAfter(painter); //std::cout<<"plot done\n"; @@ -642,121 +534,93 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { eb.setStyle(errorFillStyle); - double xold=-1; - double yold=-1; - double xpeold=-1; - double xmeold=-1; - double x0=transformX(0); if (parent->getXAxis()->isLogAxis()) x0=transformX(parent->getXAxis()->getMin()); - // double y0=transformY(0); - // if (parent->getYAxis()->isLogAxis()) y0=transformY(parent->getYAxis()->getMin()); bool first=false; - doublePair* d=data; + QPolygonF filledPolygon, linePolygon, errorLineTop, errorLineBottom; + QList epTop, epBottom; + double xami=qMin(transformY(parent->getXAxis()->getMin()),transformY(parent->getXAxis()->getMax())); + double xama=qMax(transformY(parent->getXAxis()->getMin()),transformY(parent->getXAxis()->getMax())); + double dxpix=fabs(xama-xami); + xami=xami-2.0*dxpix; + xama=xama+2.0*dxpix; + for (auto it=data.begin(); it!=data.end(); ++it) { + const QPointF& d=*it; + double x=d.x(); + double y=d.y(); - while (d!=nullptr) { - double yv=d->x; - double xv=d->f; //std::cout<<"(xv, yv) = ( "<(errorPlotFunction))) { - double e=errorPlotFunction(xv, errorParams); + double e=errorPlotFunction(yv, errorParams); xpe=transformX(xv+e); xme=transformX(xv-e); + xpe=qBound(xami, xpe, xama); + xme=qBound(xami, xme, xama); } - if (first) { - double xl1=xold; - double yl1=yold; - double xl2=x; - double yl2=y; + x=qBound(xami, x, xama); - if (fillCurve) { - painter.save(); auto __finalpaintfillcurve=JKQTPFinally([&painter]() {painter.restore();}); - painter.setBrush(b); - painter.setPen(np); - QPolygonF poly; - poly << QPointF(xl1, yl1) << QPointF(xl2, yl2) << QPointF(x0, yl2) << QPointF(x0, yl1); - painter.drawConvexPolygon(poly); + if (fillCurve) { + if (!first) filledPolygon<next==nullptr) { // last datapoint - pfill.lineTo(x, y0); - }*/ - } + if (drawErrorPolygons && (static_cast(errorPlotFunction))) { + epTop<(errorPlotFunction))) { - painter.save(); auto __finalpainterrorpoly=JKQTPFinally([&painter]() {painter.restore();}); - painter.setBrush(eb); - painter.setPen(np); - QPolygonF poly; - poly << QPointF(xpeold, yold) << QPointF(xpe, y)<< QPointF(xme, y) << QPointF(xmeold, yold) ; - painter.drawConvexPolygon(poly); + if (drawLine) { + linePolygon<(errorPlotFunction))) { + errorLineTop<(errorPlotFunction))) { - painter.setPen(ep); - painter.drawLine(QLineF(xpeold, yold, xpe, y)); - painter.drawLine(QLineF(xmeold, yold, xme, y)); - } - - //std::cout<<"line ("<next; } - /*if (fillCurve) { - pfill.closeSubpath(); - painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); + if (drawErrorPolygons) { + painter.save(); auto __finalpainterrpoly=JKQTPFinally([&painter]() {painter.restore();}); + painter.setBrush(eb); + painter.setPen(np); + QPolygonF poly; + for (int i=0; i=0; i--) { + poly<x; - double xv=d->f; - //std::cout<<"(xv, yv) = ( "<getLineWidthMultiplier(), c, QColor(Qt::transparent)); - } - d=d->next; + painter.drawPolyline(linePolygon); } + + if (drawErrorLines && (static_cast(errorPlotFunction))) { + painter.save(); auto __finalpainterrline=JKQTPFinally([&painter]() {painter.restore();}); + painter.setPen(ep); + painter.drawPolyline(errorLineTop); + painter.drawPolyline(errorLineBottom); + } + + + if (displaySamplePoints) drawSamplePoints(painter); } drawErrorsAfter(painter); //std::cout<<"plot done\n"; @@ -764,7 +628,7 @@ void JKQTPYFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { void JKQTPYFunctionLineGraph::createPlotData(bool collectParams) { - clearData(); + data.clear(); if (collectParams) collectParameters(); if (parent==nullptr) return; @@ -774,30 +638,14 @@ void JKQTPYFunctionLineGraph::createPlotData(bool collectParams) { if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, params); else if (simplePlotFunction) func=simplePlotFunction; - double ymin=parent->getYMin(); - double ymax=parent->getYMax(); - double delta0=(ymax-ymin)/static_cast(minSamples); + const double ymin=parent->getYMin(); + const double ymax=parent->getYMax(); - // initially sample function - doublePair* d=new doublePair; - d->x=ymin; - d->f=func(ymin); - d->next=nullptr; - data=d; - for (double y=ymin+delta0; ynext = new doublePair; - d->next->x=y+(static_cast(rand())/static_cast(RAND_MAX)-0.5)*delta0/2.0; - d->next->f=func(d->next->x); - d->next->next=nullptr; - doublePair* dd=d; - d=d->next; - refine(dd, d); - } - d->next = new doublePair; - d->next->x=ymax; - d->next->f=func(ymax); - d->next->next=nullptr; - refine(d, d->next); + 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); } @@ -1094,14 +942,14 @@ void JKQTPXFunctionLineGraph::setErrorPlotFunction(const jkqtpPlotFunctionType & { errorSimplePlotFunction=jkqtpSimplePlotFunctionType(); errorPlotFunction=__value; - clearData(); + data.clear(); } void JKQTPXFunctionLineGraph::setErrorPlotFunction(jkqtpPlotFunctionType &&__value) { errorSimplePlotFunction=jkqtpSimplePlotFunctionType(); errorPlotFunction = std::move(__value); - clearData(); + data.clear(); } jkqtpPlotFunctionType JKQTPXFunctionLineGraph::getErrorPlotFunction() const { @@ -1112,14 +960,14 @@ void JKQTPXFunctionLineGraph::setErrorPlotFunction(const jkqtpSimplePlotFunction { errorPlotFunction=jkqtpPlotFunctionType(); errorSimplePlotFunction=__value; - clearData(); + data.clear(); } void JKQTPXFunctionLineGraph::setErrorPlotFunction(jkqtpSimplePlotFunctionType &&__value) { errorPlotFunction=jkqtpPlotFunctionType(); errorSimplePlotFunction = std::move(__value); - clearData(); + data.clear(); } jkqtpSimplePlotFunctionType JKQTPXFunctionLineGraph::getErrorSimplePlotFunction() const { diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h index d120cf212c..94b2e5900c 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedfunction.h @@ -25,6 +25,7 @@ #include "jkqtplotter/graphs/jkqtpscatter.h" #include "jkqtplotter/jkqtpgraphsbasestylingmixins.h" #include "jkqtplotter/jkqtplotter_imexport.h" +#include "jkqtcommon/jkqtpgeometrytools.h" #include #ifndef jkqtpgraphsevaluatedfunction_H @@ -67,7 +68,7 @@ typedef std::function jkqtpSimplePlotFunctionType; \image html plot_functionplots.png - \see \ref JKQTPlotterFunctionPlots, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph, jkqtpstatAddPolyFit(), jkqtpstatAddWeightedRegression(), jkqtpstatAddRobustIRLSRegression(), jkqtpstatAddRegression(), jkqtpstatAddLinearWeightedRegression(), jkqtpstatAddRobustIRLSLinearRegression(), jkqtpstatAddLinearRegression() + \see \ref JKQTPlotterFunctionPlots, JKQTPAdaptiveFunctionGraphEvaluator, JKQTPYFunctionLineGraph, JKQTPXYFunctionLineGraph, jkqtpstatAddPolyFit(), jkqtpstatAddWeightedRegression(), jkqtpstatAddRobustIRLSRegression(), jkqtpstatAddRegression(), jkqtpstatAddLinearWeightedRegression(), jkqtpstatAddRobustIRLSLinearRegression(), jkqtpstatAddLinearRegression() */ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public JKQTPGraphLineStyleMixin, public JKQTPGraphFillStyleMixin { Q_OBJECT @@ -120,9 +121,6 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public */ virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override; - /** \brief clear the data sampled from the function. */ - void clearData(); - /*! \brief set color, fill color and error color at the same time */ void setColor(QColor c); @@ -287,20 +285,14 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXFunctionLineGraph: public JKQTPGraph, public protected: - struct doublePair { - double x; - double f; - doublePair* next; - }; - /** \brief a linked list holding the datapoints \f$ \left(x, y=f(x, \vec{p})\right) \f$ to be plotted */ - doublePair* data; + /** \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); /** \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(); - /** \brief refine datapoints on the function graph between two evaluations \a a and \a b */ - void refine(doublePair* a, doublePair* b, unsigned int degree=0); + /** \brief if set, the values from this datatsore column are used for the parameters \c p1 , \c p2 , \c p3 , ... of the plot function */ int parameterColumn; @@ -366,6 +358,8 @@ 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/jkqtpevaluatedparametriccurve.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp index 9f7e50fd24..07c06935bf 100644 --- a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp +++ b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp @@ -264,7 +264,7 @@ void JKQTPXYFunctionLineGraph::createPlotData(bool collectParams) { if (plotFunction) func=std::bind(plotFunction, std::placeholders::_1, params); else if (simplePlotFunction) func=simplePlotFunction; - jkqtpSimpleParametricCurveFunctionType fTransformedFunc= std::bind([&](const JKQTPXYFunctionLineGraph* plot, double t) -> QPointF { return plot->transform(func(t)); }, this, std::placeholders::_1); + 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);