/* Copyright (c) 2008-2018 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 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/jkqtpgraphsevaluatedfunction.h" #include "jkqtplotter/jkqtpbaseplotter.h" #include #include #include #include "jkqtplottertools/jkqtptools.h" #include "jkqtplotter/jkqtpgraphsimage.h" #include "jkqtplotter/jkqtpbaseelements.h" #include "jkqtplotter/jkqtplotter.h" double JKQTPxFunctionLineGraphPolynomial(double x, void* param) { double res=0; QVector* d=static_cast*>(param); if (d && d->size()>0) { res=d->value(0,0); double xx=x; for (int i=1; isize(); i++) { res=res+d->value(i,0)*xx; xx=xx*x; } } return res; } double JKQTPxFunctionLineGraphExponential(double x, void* param) { double res=0; QVector* d=static_cast*>(param); if (d) { if (d->size()>=3) { res=d->value(0,0)+d->value(1,0)*exp(x/d->value(2,0)); } else if (d->size()>=2) { res=d->value(0,0)*exp(x/d->value(1,0)); } } return res; } double JKQTPxFunctionLineGraphPowerLaw(double x, void* param) { double res=0; QVector* d=static_cast*>(param); if (d) { if (d->size()>=3) { res=d->value(0,0)+d->value(1,0)*pow(x, d->value(2,1)); } else if (d->size()>=2) { res=d->value(0,0)*pow(x, d->value(1,1)); } else if (d->size()>=1) { res=pow(x, d->value(0,1)); } } return res; } JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtBasePlotter* parent): JKQTPgraph(parent) { color=QColor("red"); fillColor=color.lighter(); style=Qt::SolidLine; lineWidth=2; fillStyle=Qt::SolidPattern; drawLine=true; fillCurve=false; plotFunction=nullptr; params=nullptr; minSamples=10; maxRefinementDegree=7; slopeTolerance=0.005; minPixelPerSample=32; plotRefinement=true; displaySamplePoints=false; data=nullptr; drawErrorPolygons=false; drawErrorLines=false; errorPlotFunction=nullptr; errorParams=nullptr; errorColor=color.lighter(); errorFillColor=color.lighter(); errorStyle=Qt::SolidLine; errorLineWidth=1; errorFillStyle=Qt::SolidPattern; parameterColumn=-1; errorParameterColumn=-1; if (parent) { // get style settings from parent object parentPlotStyle=parent->getNextStyle(); //std::cout<<"got style settings from parent: "<getPlotStyle(parentPlotStyle).color(); fillColor=color.lighter(); style=parent->getPlotStyle(parentPlotStyle).style(); errorColor=color.lighter(); errorFillColor=color.lighter(); errorStyle=style; } fillColor.setAlphaF(0.5); errorFillColor.setAlphaF(0.5); } JKQTPxFunctionLineGraph::JKQTPxFunctionLineGraph(JKQtPlotter* parent): JKQTPgraph(parent) { color=QColor("red"); fillColor=color.lighter(); style=Qt::SolidLine; lineWidth=2; fillStyle=Qt::SolidPattern; drawLine=true; fillCurve=false; plotFunction=nullptr; params=nullptr; minSamples=10; maxRefinementDegree=7; slopeTolerance=0.005; minPixelPerSample=32; plotRefinement=true; displaySamplePoints=false; data=nullptr; drawErrorPolygons=false; drawErrorLines=false; errorPlotFunction=nullptr; errorParams=nullptr; errorColor=color.lighter(); errorFillColor=color.lighter(); errorStyle=Qt::SolidLine; errorLineWidth=1; errorFillStyle=Qt::SolidPattern; parameterColumn=-1; errorParameterColumn=-1; if (parent) { // get style settings from parent object parentPlotStyle=parent->getNextStyle(); //std::cout<<"got style settings from parent: "<getPlotStyle(parentPlotStyle).color(); fillColor=color.lighter(); style=parent->getPlotStyle(parentPlotStyle).style(); errorColor=color.lighter(); errorFillColor=color.lighter(); errorStyle=style; } fillColor.setAlphaF(0.5); errorFillColor.setAlphaF(0.5); } JKQTPxFunctionLineGraph::~JKQTPxFunctionLineGraph() { clearData(); } void JKQTPxFunctionLineGraph::clearData() { while (data!=nullptr) { doublePair* d=data; data=data->next; delete d; } data=nullptr; } void JKQTPxFunctionLineGraph::set_plotFunction(const jkqtpPlotFunctionType &__value) { this->plotFunction = __value; clearData(); } void JKQTPxFunctionLineGraph::set_plotFunction(jkqtpPlotFunctionType &&__value) { this->plotFunction = std::move(__value); clearData(); } jkqtpPlotFunctionType JKQTPxFunctionLineGraph::get_plotFunction() const { return this->plotFunction; } void JKQTPxFunctionLineGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) { painter.save(); QPen p=painter.pen(); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); QPen np(Qt::NoPen); p.setColor(color); p.setStyle(style); QBrush b=painter.brush(); b.setColor(fillColor); b.setStyle(fillStyle); int y=rect.top()+rect.height()/2.0; painter.setPen(np); if (drawLine) painter.setPen(p); painter.setBrush(b); if (fillCurve) painter.drawRect(rect); if (!fillCurve & drawLine) painter.drawLine(QLineF(rect.left(), y, rect.right(), y)); painter.restore(); } QColor JKQTPxFunctionLineGraph::getKeyLabelColor() { return color; } bool JKQTPxFunctionLineGraph::getXMinMax(double &minx, double &maxx, double &smallestGreaterZero) { smallestGreaterZero=minx=maxx=0; return false; } bool JKQTPxFunctionLineGraph::getYMinMax(double &miny, double &maxy, double &smallestGreaterZero) { smallestGreaterZero=miny=maxy=0; return false; } void JKQTPxFunctionLineGraph::createPlotData(bool collectParams) { #ifdef JKQTBP_AUTOTIMER JKQTPAutoOutputTimer jkaat(QString("JKQTPxFunctionLineGraph[%1]::createPlotData()").arg(title)); #endif clearData(); if (collectParams) collectParameters(); if (parent==nullptr) return; if (plotFunction==nullptr) return; double xmin=parent->getXMin(); double xmax=parent->getXMax(); double pxmin=xAxis->x2p(xmin); double pxmax=xAxis->x2p(xmax); double delta0=(pxmax-pxmin)/(double)minSamples; //double logdelta0=(log(xmax)-log(xmin))/(double)minSamples; // initially sample function doublePair* d=new doublePair; d->x=xmin; d->f=plotFunction(xmin, params); d->next=nullptr; data=d; /*if (parent && parent->getXAxis()->isLogAxis()) { for (double x=log(xmin)+logdelta0; xnext = new doublePair; d->next->x=exp(x+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0); d->next->f=plotFunction(d->next->x, params); d->next->next=nullptr; doublePair* dd=d; d=d->next; refine(dd, d); } } else {*/ QVector* dv=static_cast*>(params); for (double x=pxmin+delta0; xnext = new doublePair; d->next->x=parent->p2x(x+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0); d->next->f=plotFunction(d->next->x, params); d->next->next=nullptr; doublePair* dd=d; d=d->next; refine(dd, d); } //} d->next = new doublePair; d->next->x=xmax; d->next->f=plotFunction(xmax, params); d->next->next=nullptr; refine(d, d->next); } void JKQTPxFunctionLineGraph::collectParameters() { if (parent && parameterColumn>=0) { iparams.clear(); JKQTPdatastore* datastore=parent->getDatastore(); int imin=0; int imax=datastore->getColumn(parameterColumn).getRows(); for (int i=imin; iget(parameterColumn,i); iparams<=0 && !JKQTPIsOKFloat(iparams[i])) { iparams.remove(i,1); i--; } //qDebug()<<"iparams:"; //for (i=0; i=0) { ierrorparams.clear(); JKQTPdatastore* datastore=parent->getDatastore(); int imin=0; int imax=datastore->getColumn(errorParameterColumn).getRows(); for (int i=imin; iget(errorParameterColumn,i); ierrorparams<=0 && !JKQTPIsOKFloat(ierrorparams[i])) { ierrorparams.remove(i,1); i--; } errorParams=&ierrorparams; } } void JKQTPxFunctionLineGraph::refine(doublePair* a, doublePair* b, unsigned int degree) { if (degree>=maxRefinementDegree) return; double ax=xAxis->x2p(a->x); double af=xAxis->x2p(a->f); double bx=xAxis->x2p(b->x); double bf=xAxis->x2p(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+((double)rand()/(double)RAND_MAX-0.5)*delta/5.0; xmid=exp(xmid); } else {*/ xmid=xmid+((double)rand()/(double)RAND_MAX-0.5)*delta/5.0; // shake by 10% //} double realxmid=parent->p2x(xmid); double realfmid=plotFunction(realxmid, params); double fmid=yAxis->x2p(realfmid); double a1=(fmid - af)/(xmid - ax); 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::draw(JKQTPEnhancedPainter& painter) { #ifdef JKQTBP_AUTOTIMER JKQTPAutoOutputTimer jkaaot("JKQTPxFunctionLineGraph::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(); QPen p=painter.pen(); p.setColor(color); p.setWidthF(qMax(JKQTPLOTTER_ABS_MIN_LINEWIDTH, parent->pt2px(painter, lineWidth*parent->get_lineWidthMultiplier()))); p.setStyle(style); p.setJoinStyle(Qt::RoundJoin); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); QPen np(Qt::NoPen); QPen ep=painter.pen(); ep.setColor(errorColor); ep.setWidthF(qMax(JKQTPLOTTER_ABS_MIN_LINEWIDTH, parent->pt2px(painter, errorLineWidth*parent->get_lineWidthMultiplier()))); ep.setStyle(errorStyle); ep.setJoinStyle(Qt::RoundJoin); QBrush b=painter.brush(); b.setColor(fillColor); b.setStyle(fillStyle); QBrush eb=painter.brush(); eb.setColor(errorFillColor); eb.setStyle(errorFillStyle); // double xold=-1; // double yold=-1; // double ypeold=-1; // double ymeold=-1; // double x0=xAxis->x2p(0); // if (parent->getXAxis()->isLogAxis()) x0=xAxis->x2p(parent->getXAxis()->getMin()); double y0=yAxis->x2p(0); if (parent->getYAxis()->isLogAxis()) y0=yAxis->x2p(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(yAxis->x2p(parent->getYAxis()->getMin()),yAxis->x2p(parent->getYAxis()->getMax())); double yama=qMax(yAxis->x2p(parent->getYAxis()->getMin()),yAxis->x2p(parent->getYAxis()->getMax())); double dypix=fabs(yama-yami); yami=yami-2*dypix; yama=yama+2*dypix; while (d!=nullptr) { double xv=d->x; double yv=d->f; //std::cout<<"(xv, yv) = ( "<x2p(xv); double y=yAxis->x2p(yv); double ype=0, yme=0; if ((drawErrorLines || drawErrorPolygons) && (static_cast(errorPlotFunction))) { double e=errorPlotFunction(xv, errorParams); ype=yAxis->x2p(yv+e); yme=yAxis->x2p(yv-e); ype=qBound(yami, ype, yama); yme=qBound(yami, yme, yama); } y=qBound(yami, y, yama); if (fillCurve) { if (!first) filledPolygon<next) filledPolygon<(errorPlotFunction))) { epTop<(errorPlotFunction))) { errorLineTop<next; } if (drawErrorPolygons) { painter.save(); 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=0; i--) { poly<(errorPlotFunction))) { painter.save(); painter.setPen(ep); painter.drawPolyline(errorLineTop); painter.drawPolyline(errorLineBottom); painter.restore(); } QColor c=color; c.setHsv(fmod(color.hue()+90, 360), color.saturation(), color.value()); d=data; if (displaySamplePoints) { painter.save(); while (d!=nullptr) { double xv=d->x; double yv=d->f; //std::cout<<"(xv, yv) = ( "<x2p(xv); double y=yAxis->x2p(yv); JKQTPplotSymbol(painter, x, y, JKQTPcross, 6,1*parent->get_lineWidthMultiplier(), c, QColor(Qt::transparent)); } d=d->next; } painter.restore(); } painter.restore(); drawErrorsAfter(painter); //std::cout<<"plot done\n"; } void JKQTPyFunctionLineGraph::draw(JKQTPEnhancedPainter& painter) { #ifdef JKQTBP_AUTOTIMER JKQTPAutoOutputTimer jkaaot("JKQTPyFunctionLineGraph::draw"); #endif if (parent==nullptr) return; JKQTPdatastore* datastore=parent->getDatastore(); if (datastore==nullptr) return; //std::cout<<"start plot\n"; createPlotData(); //std::cout<<"plot data created\n"; drawErrorsBefore(painter); painter.save(); QPen p=painter.pen(); p.setColor(color); p.setWidthF(qMax(JKQTPLOTTER_ABS_MIN_LINEWIDTH, parent->pt2px(painter, lineWidth*parent->get_lineWidthMultiplier()))); p.setStyle(style); p.setJoinStyle(Qt::RoundJoin); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); QPen np(Qt::NoPen); QPen ep=painter.pen(); ep.setColor(errorColor); ep.setWidthF(qMax(JKQTPLOTTER_ABS_MIN_LINEWIDTH, parent->pt2px(painter, errorLineWidth*parent->get_lineWidthMultiplier()))); ep.setStyle(errorStyle); ep.setJoinStyle(Qt::RoundJoin); QBrush b=painter.brush(); b.setColor(fillColor); b.setStyle(fillStyle); QBrush eb=painter.brush(); eb.setColor(errorFillColor); eb.setStyle(errorFillStyle); double xold=-1; double yold=-1; double xpeold=-1; double xmeold=-1; double x0=xAxis->x2p(0); if (parent->getXAxis()->isLogAxis()) x0=xAxis->x2p(parent->getXAxis()->getMin()); // double y0=yAxis->x2p(0); // if (parent->getYAxis()->isLogAxis()) y0=yAxis->x2p(parent->getYAxis()->getMin()); bool first=false; doublePair* d=data; while (d!=nullptr) { double yv=d->x; double xv=d->f; //std::cout<<"(xv, yv) = ( "<x2p(xv); double y=yAxis->x2p(yv); double xpe=0, xme=0; if ((drawErrorLines || drawErrorPolygons) && (static_cast(errorPlotFunction))) { double e=errorPlotFunction(xv, errorParams); xpe=xAxis->x2p(xv+e); xme=xAxis->x2p(xv-e); } if (first) { double xl1=xold; double yl1=yold; double xl2=x; double yl2=y; if (fillCurve) { painter.save(); painter.setBrush(b); painter.setPen(np); QPolygonF poly; poly << QPointF(xl1, yl1) << QPointF(xl2, yl2) << QPointF(x0, yl2) << QPointF(x0, yl1); painter.drawConvexPolygon(poly); painter.restore(); /*pfill.lineTo(x, y); if (d->next==nullptr) { // last datapoint pfill.lineTo(x, y0); }*/ } if (drawErrorPolygons && (static_cast(errorPlotFunction))) { painter.save(); painter.setBrush(eb); painter.setPen(np); QPolygonF poly; poly << QPointF(xpeold, yold) << QPointF(xpe, y)<< QPointF(xme, y) << QPointF(xmeold, yold) ; painter.drawConvexPolygon(poly); painter.restore(); } if (drawLine) { painter.setPen(p); //pa.lineTo(x, y); painter.drawLine(QLineF(xl1, yl1, xl2, yl2)); } if (drawErrorLines && (static_cast(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(); painter.setBrush(b); painter.setPen(np); painter.drawPath(pfill); painter.restore(); } if (drawLine) { painter.setPen(p); painter.drawPath(pa); painter.restore(); }*/ QColor c=color; c.setHsv(fmod(color.hue()+90, 360), color.saturation(), color.value()); d=data; if (displaySamplePoints) while (d!=nullptr) { double yv=d->x; double xv=d->f; //std::cout<<"(xv, yv) = ( "<x2p(xv); double y=yAxis->x2p(yv); JKQTPplotSymbol(painter, x, y, JKQTPcross, 6, 1*parent->get_lineWidthMultiplier(), c, QColor(Qt::transparent)); } d=d->next; } painter.restore(); drawErrorsAfter(painter); //std::cout<<"plot done\n"; } void JKQTPyFunctionLineGraph::createPlotData(bool /*collectParams*/) { clearData(); if (parent==nullptr) return; if (plotFunction==nullptr) return; double ymin=parent->getYMin(); double ymax=parent->getYMax(); double delta0=(ymax-ymin)/(double)minSamples; // initially sample function doublePair* d=new doublePair; d->x=ymin; d->f=plotFunction(ymin, params); d->next=nullptr; data=d; for (double y=ymin+delta0; ynext = new doublePair; d->next->x=y+((double)rand()/(double)RAND_MAX-0.5)*delta0/2.0; d->next->f=plotFunction(d->next->x, params); d->next->next=nullptr; doublePair* dd=d; d=d->next; refine(dd, d); } d->next = new doublePair; d->next->x=ymax; d->next->f=plotFunction(ymax, params); d->next->next=nullptr; refine(d, d->next); } QBrush JKQTPxFunctionLineGraph::getBrush(JKQTPEnhancedPainter& /*painter*/) const { QBrush b; b.setColor(fillColor); b.setStyle(fillStyle); return b; } QPen JKQTPxFunctionLineGraph::getLinePen(JKQTPEnhancedPainter &painter) const { QPen p; p.setColor(color); p.setWidthF(qMax(JKQTPLOTTER_ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->get_lineWidthMultiplier()*lineWidth))); p.setStyle(style); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); return p; } 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(JKQTPLOTTER_ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->get_lineWidthMultiplier()*errorLineWidth))); p.setStyle(errorStyle); p.setJoinStyle(Qt::RoundJoin); p.setCapStyle(Qt::RoundCap); return p; } void JKQTPxFunctionLineGraph::set_params(const QVector ¶ms) { iparams=params; set_params(&iparams); } void JKQTPxFunctionLineGraph::set_copiedParams(const double *params, int N) { QVector v; for (int i=0; i &errorParams) { ierrorparams=errorParams; set_errorParams(&ierrorparams); } void JKQTPxFunctionLineGraph::setSpecialFunction(JKQTPxFunctionLineGraph::SpecialFunction function) { if (function==JKQTPxFunctionLineGraph::Polynomial) set_plotFunction(JKQTPxFunctionLineGraphPolynomial); else if (function==JKQTPxFunctionLineGraph::Exponential) set_plotFunction(JKQTPxFunctionLineGraphExponential); else if (function==JKQTPxFunctionLineGraph::PowerLaw) set_plotFunction(JKQTPxFunctionLineGraphPowerLaw); } QVector JKQTPxFunctionLineGraph::get_internalParams() const { return iparams; } QVector JKQTPxFunctionLineGraph::get_internalErrorParams() const { return ierrorparams; } void JKQTPxFunctionLineGraph::set_errorPlotFunction(const jkqtpPlotFunctionType &__value) { errorPlotFunction=__value; } void JKQTPxFunctionLineGraph::set_errorPlotFunction(jkqtpPlotFunctionType &&__value) { this->errorPlotFunction = std::move(__value); clearData(); } jkqtpPlotFunctionType JKQTPxFunctionLineGraph::get_errorPlotFunction() const { return errorPlotFunction; } bool JKQTPxFunctionLineGraph::usesColumn(int c) { return (c==parameterColumn)||(c==errorParameterColumn); }