mirror of
https://github.com/jkriege2/JKQtPlotter.git
synced 2024-11-16 02:25:50 +08:00
0e2237e92f
- fixed JKQTPColumnMathImage with modifiers - improved documentation - make use of link_prl/create_prl in QMake projects (see http://doc.qt.io/qt-5/qmake-advanced-usage.html)
946 lines
28 KiB
C++
946 lines
28 KiB
C++
/*
|
|
Copyright (c) 2008-2019 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 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 "jkqtplotter/jkqtpgraphsevaluatedfunction.h"
|
|
#include "jkqtplotter/jkqtpbaseplotter.h"
|
|
#include <stdlib.h>
|
|
#include <QDebug>
|
|
#include <iostream>
|
|
#include "jkqtplottertools/jkqtptools.h"
|
|
#include "jkqtplotter/jkqtpgraphsimage.h"
|
|
#include "jkqtplotter/jkqtpbaseelements.h"
|
|
#include "jkqtplotter/jkqtplotter.h"
|
|
#include "jkqtpgraphsevaluatedfunction.h"
|
|
|
|
|
|
|
|
|
|
|
|
JKQTPXFunctionLineGraph::JKQTPXFunctionLineGraph(JKQTBasePlotter* parent):
|
|
JKQTPGraph(parent)
|
|
{
|
|
functionType=SpecialFunction::UserFunction;
|
|
color=QColor("red");
|
|
fillColor=color.lighter();
|
|
style=Qt::SolidLine;
|
|
lineWidth=2;
|
|
fillStyle=Qt::SolidPattern;
|
|
drawLine=true;
|
|
fillCurve=false;
|
|
params=nullptr;
|
|
minSamples=10;
|
|
maxRefinementDegree=7;
|
|
slopeTolerance=0.005;
|
|
minPixelPerSample=32;
|
|
plotRefinement=true;
|
|
displaySamplePoints=false;
|
|
data=nullptr;
|
|
|
|
drawErrorPolygons=false;
|
|
drawErrorLines=false;
|
|
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: "<<parentPlotStyle<<std::endl;
|
|
color=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)
|
|
{
|
|
functionType=SpecialFunction::UserFunction;
|
|
color=QColor("red");
|
|
fillColor=color.lighter();
|
|
style=Qt::SolidLine;
|
|
lineWidth=2;
|
|
fillStyle=Qt::SolidPattern;
|
|
drawLine=true;
|
|
fillCurve=false;
|
|
params=nullptr;
|
|
minSamples=10;
|
|
maxRefinementDegree=7;
|
|
slopeTolerance=0.005;
|
|
minPixelPerSample=32;
|
|
plotRefinement=true;
|
|
displaySamplePoints=false;
|
|
data=nullptr;
|
|
|
|
drawErrorPolygons=false;
|
|
drawErrorLines=false;
|
|
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: "<<parentPlotStyle<<std::endl;
|
|
color=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::setPlotFunction(const jkqtpPlotFunctionType &__value)
|
|
{
|
|
simplePlotFunction=jkqtpSimplePlotFunctionType();
|
|
plotFunction = __value;
|
|
functionType=SpecialFunction::UserFunction;
|
|
|
|
clearData();
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setPlotFunction(const jkqtpSimplePlotFunctionType &__value)
|
|
{
|
|
plotFunction=jkqtpPlotFunctionType();
|
|
simplePlotFunction=__value;
|
|
functionType=SpecialFunction::UserFunction;
|
|
|
|
clearData();
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setPlotFunction(jkqtpPlotFunctionType &&__value)
|
|
{
|
|
simplePlotFunction=jkqtpSimplePlotFunctionType();
|
|
plotFunction = std::move(__value);
|
|
functionType=SpecialFunction::UserFunction;
|
|
clearData();
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setPlotFunction(jkqtpSimplePlotFunctionType &&__value)
|
|
{
|
|
plotFunction=jkqtpPlotFunctionType();
|
|
simplePlotFunction=std::move(__value);
|
|
functionType=SpecialFunction::UserFunction;
|
|
|
|
clearData();
|
|
}
|
|
|
|
jkqtpPlotFunctionType JKQTPXFunctionLineGraph::getPlotFunction() const
|
|
{
|
|
return plotFunction;
|
|
}
|
|
|
|
jkqtpSimplePlotFunctionType JKQTPXFunctionLineGraph::getSimplePlotFunction() const
|
|
{
|
|
return simplePlotFunction;
|
|
}
|
|
|
|
|
|
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);
|
|
const double 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 && !simplePlotFunction) return;
|
|
|
|
jkqtpSimplePlotFunctionType func;
|
|
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<double>(minSamples);
|
|
//double logdelta0=(log(xmax)-log(xmin))/static_cast<double>(minSamples);
|
|
|
|
// 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; x<log(xmax); x=x+logdelta0) {
|
|
d->next = new doublePair;
|
|
d->next->x=exp(x+(static_cast<double>(rand())/static_cast<double>(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<double>* dv=static_cast<QVector<double>*>(params);
|
|
if (functionType==Polynomial && dv && dv->size()<=2) {
|
|
// we only need the first and last datapoint
|
|
} else {
|
|
for (double x=pxmin+delta0; x<pxmax; x=x+delta0) {
|
|
d->next = new doublePair;
|
|
d->next->x=parent->p2x(x+(static_cast<double>(rand())/static_cast<double>(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);
|
|
|
|
}
|
|
|
|
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; i<imax; i++) {
|
|
double xv=datastore->get(parameterColumn,i);
|
|
iparams<<xv;
|
|
}
|
|
//qDebug()<<"iparams_beforeclean:";
|
|
//for (int i=0; i<iparams.size(); i++) qDebug()<<iparams[i];
|
|
int i=iparams.size()-1;
|
|
while (i>=0 && !JKQTPIsOKFloat(iparams[i])) {
|
|
iparams.remove(i,1);
|
|
i--;
|
|
}
|
|
|
|
//qDebug()<<"iparams:";
|
|
//for (i=0; i<iparams.size(); i++) qDebug()<<iparams[i];
|
|
|
|
params=&iparams;
|
|
}
|
|
if (parent && errorParameterColumn>=0) {
|
|
ierrorparams.clear();
|
|
JKQTPDatastore* datastore=parent->getDatastore();
|
|
int imin=0;
|
|
int imax=datastore->getColumn(errorParameterColumn).getRows();
|
|
|
|
for (int i=imin; i<imax; i++) {
|
|
double xv=datastore->get(errorParameterColumn,i);
|
|
ierrorparams<<xv;
|
|
}
|
|
int i=ierrorparams.size()-1;
|
|
while (i>=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=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<double>(rand())/static_cast<double>(RAND_MAX)-0.5)*delta/5.0;
|
|
xmid=exp(xmid);
|
|
} else {*/
|
|
xmid=xmid+(static_cast<double>(rand())/static_cast<double>(RAND_MAX)-0.5)*delta/5.0; // shake by 10%
|
|
//}
|
|
double realxmid=parent->p2x(xmid);
|
|
double realfmid;
|
|
if (plotFunction) realfmid=plotFunction(realxmid, params);
|
|
else if (simplePlotFunction) realfmid=simplePlotFunction(realxmid);
|
|
double fmid=transformY(realfmid);
|
|
double a1=(fmid - af)/(xmid - ax);
|
|
double a2=(bf - fmid)/(bx - xmid);
|
|
//std::cout<<std::string(degree*2, ' ')<<"refine( ["<<a->x<<", "<<a->f<<"], ["<<xmid<<", "<<fmid<<"], ["<<b->x<<", "<<b->f<<"] ): a1="<<a1<<", a2="<<a2<<" acrit="<<abs(a2/a1)-1.0<<"\n";
|
|
//std::cout<<std::string(degree*2, ' ')<<"refine(): a1="<<a1<<", a2="<<a2<<" acrit="<<fabs(a2-a1)<<"\n";
|
|
if (fabs(a2-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->getLineWidthMultiplier())));
|
|
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->getLineWidthMultiplier())));
|
|
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=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<QPointF> 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) {
|
|
|
|
double xv=d->x;
|
|
double yv=d->f;
|
|
//std::cout<<"(xv, yv) = ( "<<xv<<", "<<yv<<" )\n";
|
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv)) {
|
|
double x=transformX(xv);
|
|
double y=transformY(yv);
|
|
double ype=0, yme=0;
|
|
if ((drawErrorLines || drawErrorPolygons) && (static_cast<bool>(errorPlotFunction))) {
|
|
double e=errorPlotFunction(xv, errorParams);
|
|
ype=transformY(yv+e);
|
|
yme=transformY(yv-e);
|
|
ype=qBound(yami, ype, yama);
|
|
yme=qBound(yami, yme, yama);
|
|
}
|
|
|
|
y=qBound(yami, y, yama);
|
|
|
|
if (fillCurve) {
|
|
if (!first) filledPolygon<<QPointF(x, y0);
|
|
filledPolygon<<QPointF(x, y);
|
|
if (!d->next) filledPolygon<<QPointF(x, y0);
|
|
}
|
|
|
|
if (drawErrorPolygons && (static_cast<bool>(errorPlotFunction))) {
|
|
epTop<<QPointF(x, ype);
|
|
epBottom<<QPointF(x, yme);
|
|
}
|
|
|
|
if (drawLine) {
|
|
linePolygon<<QPointF(x, y);
|
|
}
|
|
|
|
if (drawErrorLines && (static_cast<bool>(errorPlotFunction))) {
|
|
errorLineTop<<QPointF(x, ype);
|
|
errorLineBottom<<QPointF(x, yme);
|
|
}
|
|
|
|
// xold=x;
|
|
// yold=y;
|
|
// ypeold=ype;
|
|
// ymeold=yme;
|
|
first=true;
|
|
}
|
|
d=d->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<epTop.size(); i++) {
|
|
poly<<epTop[i];
|
|
}
|
|
for (int i=epBottom.size()-1; i>=0; i--) {
|
|
poly<<epBottom[i];
|
|
}
|
|
painter.drawPolygon(poly, Qt::OddEvenFill);
|
|
painter.restore();
|
|
}
|
|
if (fillCurve) {
|
|
painter.save();
|
|
painter.setBrush(b);
|
|
painter.setPen(np);
|
|
painter.drawPolygon(filledPolygon, Qt::OddEvenFill);
|
|
painter.restore();
|
|
}
|
|
if (drawLine) {
|
|
painter.save();
|
|
painter.setPen(p);
|
|
painter.drawPolyline(linePolygon);
|
|
painter.restore();
|
|
}
|
|
|
|
if (drawErrorLines && (static_cast<bool>(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) = ( "<<xv<<", "<<yv<<" )\n";
|
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv)) {
|
|
double x=transformX(xv);
|
|
double y=transformY(yv);
|
|
JKQTPPlotSymbol(painter, x, y, JKQTPCross, 6,1*parent->getLineWidthMultiplier(), c, QColor(Qt::transparent));
|
|
}
|
|
d=d->next;
|
|
}
|
|
painter.restore();
|
|
}
|
|
painter.restore();
|
|
drawErrorsAfter(painter);
|
|
//std::cout<<"plot done\n";
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTBasePlotter *parent):JKQTPXFunctionLineGraph(parent) {}
|
|
|
|
JKQTPYFunctionLineGraph::JKQTPYFunctionLineGraph(JKQTPlotter *parent):JKQTPXFunctionLineGraph(parent) {}
|
|
|
|
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->getLineWidthMultiplier())));
|
|
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->getLineWidthMultiplier())));
|
|
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=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;
|
|
|
|
while (d!=nullptr) {
|
|
double yv=d->x;
|
|
double xv=d->f;
|
|
//std::cout<<"(xv, yv) = ( "<<xv<<", "<<yv<<" )\n";
|
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv)) {
|
|
double x=transformX(xv);
|
|
double y=transformY(yv);
|
|
double xpe=0, xme=0;
|
|
if ((drawErrorLines || drawErrorPolygons) && (static_cast<bool>(errorPlotFunction))) {
|
|
double e=errorPlotFunction(xv, errorParams);
|
|
xpe=transformX(xv+e);
|
|
xme=transformX(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<bool>(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<bool>(errorPlotFunction))) {
|
|
painter.setPen(ep);
|
|
painter.drawLine(QLineF(xpeold, yold, xpe, y));
|
|
painter.drawLine(QLineF(xmeold, yold, xme, y));
|
|
}
|
|
|
|
//std::cout<<"line ("<<xl1<<", "<<yl1<<") -- ("<<xl2<<", "<<yl2<<")"<<std::endl;
|
|
} /*else {
|
|
if (drawLine) {
|
|
pa.moveTo(x, y);
|
|
}
|
|
if (fillCurve) {
|
|
pfill.moveTo(x, y0);
|
|
pfill.lineTo(x, y);
|
|
}
|
|
}*/
|
|
xold=x;
|
|
yold=y;
|
|
xpeold=xpe;
|
|
xmeold=xme;
|
|
first=true;
|
|
}
|
|
d=d->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) = ( "<<xv<<", "<<yv<<" )\n";
|
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv)) {
|
|
double x=transformX(xv);
|
|
double y=transformY(yv);
|
|
JKQTPPlotSymbol(painter, x, y, JKQTPCross, 6, 1*parent->getLineWidthMultiplier(), c, QColor(Qt::transparent));
|
|
}
|
|
d=d->next;
|
|
}
|
|
painter.restore();
|
|
drawErrorsAfter(painter);
|
|
//std::cout<<"plot done\n";
|
|
}
|
|
|
|
|
|
void JKQTPYFunctionLineGraph::createPlotData(bool collectParams) {
|
|
clearData();
|
|
if (collectParams) collectParameters();
|
|
|
|
if (parent==nullptr) return;
|
|
if (!plotFunction && !simplePlotFunction) return;
|
|
|
|
jkqtpSimplePlotFunctionType func;
|
|
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<double>(minSamples);
|
|
|
|
// initially sample function
|
|
doublePair* d=new doublePair;
|
|
d->x=ymin;
|
|
d->f=func(ymin);
|
|
d->next=nullptr;
|
|
data=d;
|
|
for (double y=ymin+delta0; y<ymax; y=y+delta0) {
|
|
d->next = new doublePair;
|
|
d->next->x=y+(static_cast<double>(rand())/static_cast<double>(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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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->getLineWidthMultiplier()*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->getLineWidthMultiplier()*errorLineWidth)));
|
|
p.setStyle(errorStyle);
|
|
p.setJoinStyle(Qt::RoundJoin);
|
|
p.setCapStyle(Qt::RoundCap);
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
void JKQTPXFunctionLineGraph::setParams(const QVector<double> ¶ms)
|
|
{
|
|
iparams=params;
|
|
setParams(&iparams);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setCopiedParams(const double *params, int N)
|
|
{
|
|
QVector<double> v;
|
|
for (int i=0; i<N; i++) { v<<params[i]; }
|
|
setParams(v);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setParamsV(double p1) {
|
|
QVector<double> p;
|
|
p<<p1;
|
|
setParams(p);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setParamsV(double p1, double p2) {
|
|
QVector<double> p;
|
|
p<<p1<<p2;
|
|
setParams(p);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setParamsV(double p1, double p2, double p3) {
|
|
QVector<double> p;
|
|
p<<p1<<p2<<p3;
|
|
setParams(p);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setParamsV(double p1, double p2, double p3, double p4) {
|
|
QVector<double> p;
|
|
p<<p1<<p2<<p3<<p4;
|
|
setParams(p);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setParamsV(double p1, double p2, double p3, double p4, double p5) {
|
|
QVector<double> p;
|
|
p<<p1<<p2<<p3<<p4<<p5;
|
|
setParams(p);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setErrorParams(const QVector<double> &errorParams)
|
|
{
|
|
ierrorparams=errorParams;
|
|
setErrorParams(&ierrorparams);
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setSpecialFunction(JKQTPXFunctionLineGraph::SpecialFunction function)
|
|
{
|
|
if (function==JKQTPXFunctionLineGraph::Polynomial) {
|
|
setPlotFunction([](double x, void* param) {
|
|
double res=0;
|
|
QVector<double>* d=static_cast<QVector<double>*>(param);
|
|
if (d && d->size()>0) {
|
|
res=d->value(0,0);
|
|
double xx=x;
|
|
for (int i=1; i<d->size(); i++) {
|
|
res=res+d->value(i,0)*xx;
|
|
xx=xx*x;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
});
|
|
}
|
|
else if (function==JKQTPXFunctionLineGraph::Exponential) setPlotFunction([](double x, void* param) {
|
|
double res=0;
|
|
QVector<double>* d=static_cast<QVector<double>*>(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;
|
|
});
|
|
else if (function==JKQTPXFunctionLineGraph::PowerLaw) setPlotFunction([](double x, void* param) {
|
|
double res=0;
|
|
QVector<double>* d=static_cast<QVector<double>*>(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;
|
|
});
|
|
else throw std::runtime_error("unknown special function type");
|
|
}
|
|
|
|
JKQTPXFunctionLineGraph::SpecialFunction JKQTPXFunctionLineGraph::getFunctionType() const
|
|
{
|
|
return functionType;
|
|
}
|
|
|
|
QVector<double> JKQTPXFunctionLineGraph::getInternalParams() const {
|
|
return iparams;
|
|
}
|
|
QVector<double> JKQTPXFunctionLineGraph::getInternalErrorParams() const {
|
|
return ierrorparams;
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setErrorPlotFunction(const jkqtpPlotFunctionType &__value)
|
|
{
|
|
errorSimplePlotFunction=jkqtpSimplePlotFunctionType();
|
|
errorPlotFunction=__value;
|
|
clearData();
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setErrorPlotFunction(jkqtpPlotFunctionType &&__value)
|
|
{
|
|
errorSimplePlotFunction=jkqtpSimplePlotFunctionType();
|
|
errorPlotFunction = std::move(__value);
|
|
clearData();
|
|
}
|
|
jkqtpPlotFunctionType JKQTPXFunctionLineGraph::getErrorPlotFunction() const
|
|
{
|
|
return errorPlotFunction;
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setErrorPlotFunction(const jkqtpSimplePlotFunctionType &__value)
|
|
{
|
|
errorPlotFunction=jkqtpPlotFunctionType();
|
|
errorSimplePlotFunction=__value;
|
|
clearData();
|
|
}
|
|
|
|
void JKQTPXFunctionLineGraph::setErrorPlotFunction(jkqtpSimplePlotFunctionType &&__value)
|
|
{
|
|
errorPlotFunction=jkqtpPlotFunctionType();
|
|
errorSimplePlotFunction = std::move(__value);
|
|
clearData();
|
|
}
|
|
jkqtpSimplePlotFunctionType JKQTPXFunctionLineGraph::getErrorSimplePlotFunction() const
|
|
{
|
|
return errorSimplePlotFunction;
|
|
}
|
|
|
|
|
|
bool JKQTPXFunctionLineGraph::usesColumn(int c) const
|
|
{
|
|
return (c==parameterColumn)||(c==errorParameterColumn);
|
|
}
|
|
|