mirror of
https://github.com/jkriege2/JKQtPlotter.git
synced 2024-11-15 18:15:52 +08:00
JKQTPlotter:
- NEW: improved plotting speed for line-graphs by a compression algorithm (see JKQTPGraphLinesCompressionMixin) that removes overlaying lines (e.g. in JKQTPXYLineGraph) - NEW: improved plotting speed for line-graphs by a clipping algorithm (applies to JKQTPXYLineGraph, JKQTPGraphErrorStyleMixin, JKQTPSpecialLineHorizontalGraph, JKQTPSpecialLineVerticalGraph and others) - NEW: improved plotting speed for scatter-graphs by not calling draw functions for symbols outside the plot window (e.g. in JKQTPXYLineGraph)
This commit is contained in:
parent
17b93ab580
commit
03031e3762
2
Doxyfile
2
Doxyfile
@ -463,7 +463,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||
# DOT_NUM_THREADS setting.
|
||||
# Minimum value: 0, maximum value: 32, default value: 1.
|
||||
|
||||
NUM_PROC_THREADS = 1
|
||||
NUM_PROC_THREADS = 2
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
|
@ -33,7 +33,6 @@ This page lists several todos and wishes for future version of JKQTPlotter
|
||||
<li>plot: axes with symlog (see http://dx.doi.org/10.1088/0957-0233/24/2/027001) and logit (https://de.m.wikipedia.org/wiki/Logit) scaling? </li>
|
||||
<li>graphs: add candlestick charts (financial, see https://en.m.wikipedia.org/wiki/Candlestick_chart)</li>
|
||||
<li>graphs: add OHLC charts (financial, see https://en.m.wikipedia.org/wiki/Open-high-low-close_chart)</li>
|
||||
<li>graphs: explore/demonstrate/improve plotting speed for large dataset (e.g. only draw visible elements, use algorithm to draw fewer lines if they overlay ...)</li>
|
||||
<li>plot: refactor print preview/export preview code </li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
|
@ -27,10 +27,13 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
|
||||
<li>IMPROVED: QT6-compatibility by removing deprecated warnings</li>
|
||||
<li>NEW: JKQTPFilledCurveXGraph and JKQTPFilledCurveYGraph can now plot wiggle plots with different fill styles above and below the baseline (feature request <a href="https://github.com/jkriege2/JKQtPlotter/issues/68">#68 Wiggle Plots</a> from <a href="https://github.com/xichaoqiang">user:xichaoqiang</a> </li>
|
||||
<li>NEW/BREAKING CHANGE: data tooltip can now also be shown when "just" moving the mouse (so far this was only possible when dragging the mouse with a button pressed). This also removes JKQtPlotter::getActMouseLeftAsToolTip() and adds JKQtPlotter::getActMouseMoveToolTip() instead! Also the default toolbars and context menus changed!</li>
|
||||
<li>NEW: new "seaborn" style for plots</li>
|
||||
<li>NEW: new "seaborn" style for plots, see \ref jkqtpplotter_styling </li>
|
||||
<li>NEW/BREAKING CHANGE: changed JKQTPColorDerivationMode into a struct, which extends its capabilities above the previously available few enum-items</li>
|
||||
<li>NEW: added debug-feature to show boxes around text in the plot</li>
|
||||
<li>BREAKING: Print-Support can now be switched off with a CMAKE-option JKQtPlotter_BUILD_FORCE_NO_PRINTER_SUPPORT=ON. This also switches off PDF and SVG export, partly solves issue <a href="https://github.com/jkriege2/JKQtPlotter/issues/81">#81</a>, many thanks to <a href="https://github.com/sufuk">user:sufuk</a> for contributing part of the code and supplying the idea! </li>
|
||||
<li>NEW: improved plotting speed for line-graphs by a compression algorithm (see JKQTPGraphLinesCompressionMixin) that removes overlaying lines (e.g. in JKQTPXYLineGraph)</li>
|
||||
<li>NEW: improved plotting speed for line-graphs by a clipping algorithm (applies to JKQTPXYLineGraph, JKQTPGraphErrorStyleMixin, JKQTPSpecialLineHorizontalGraph, JKQTPSpecialLineVerticalGraph and others)</li>
|
||||
<li>NEW: improved plotting speed for scatter-graphs by not calling draw functions for symbols outside the plot window (e.g. in JKQTPXYLineGraph)</li>
|
||||
</ul></li>
|
||||
|
||||
<li>JKQTMathText:<ul>
|
||||
|
BIN
doc/images/JKQTPSimplifyPolyLines.cpt
Normal file
BIN
doc/images/JKQTPSimplifyPolyLines.cpt
Normal file
Binary file not shown.
BIN
doc/images/JKQTPSimplifyPolyLines.png
Normal file
BIN
doc/images/JKQTPSimplifyPolyLines.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
doc/images/JKQTPSimplifyPolyLines_agressive.cpt
Normal file
BIN
doc/images/JKQTPSimplifyPolyLines_agressive.cpt
Normal file
Binary file not shown.
BIN
doc/images/JKQTPSimplifyPolyLines_agressive.png
Normal file
BIN
doc/images/JKQTPSimplifyPolyLines_agressive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
@ -98,7 +98,7 @@ int main(int argc, char* argv[])
|
||||
// graph title is made from symbol+errorStylestyle, we use the LaTeX instruction \verb around the
|
||||
// result of JKQTPErrorPlotstyle2String(), because it contains underscores that would otherwise
|
||||
// lead to lower-case letter, which we don't want
|
||||
graph->setTitle("\\verb{"+JKQTPErrorPlotstyle2String(errorStyles[errorID])+"}");
|
||||
graph->setTitle("\\verb!"+JKQTPErrorPlotstyle2String(errorStyles[errorID])+"!");
|
||||
|
||||
// add the graph to the plot, so it is actually displayed
|
||||
plot.addGraph(graph);
|
||||
|
@ -53,7 +53,7 @@ int main(int argc, char* argv[])
|
||||
plot.addGraph(a=new JKQTPGeoArrow(&plot, 0.7, arr_y, 0.9, arr_y+0.05, decor, JKQTPNoDecorator)); a->setStyle(QColor("red"), 1);
|
||||
plot.addGraph(a=new JKQTPGeoArrow(&plot, 1.0, arr_y, 1.3, arr_y+0.05, decor, JKQTPNoDecorator)); a->setStyle(QColor("red"), 2);
|
||||
plot.addGraph(a=new JKQTPGeoArrow(&plot, 1.4, arr_y, 1.7, arr_y+0.05, decor, JKQTPNoDecorator)); a->setStyle(QColor("red"), 3);
|
||||
plot.addGraph(new JKQTPGeoText(&plot, a->getX2()+0.05, a->getY2(), "\\verb{"+JKQTPLineDecoratorStyle2String(decor)+"}", 12, a->getLineColor()));
|
||||
plot.addGraph(new JKQTPGeoText(&plot, a->getX2()+0.05, a->getY2(), "\\verb!"+JKQTPLineDecoratorStyle2String(decor)+"!", 12, a->getLineColor()));
|
||||
arr_y+=arr_deltay;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ int main(int argc, char* argv[])
|
||||
// the graph is updated:
|
||||
auto updateGraphFunctor=
|
||||
[=]() {
|
||||
parsedFunc->setTitle(QString("user function: \\verb{"+edit->text()+"}, p_1=%1, p_2=%2").arg(spinP1->value()).arg(spinP2->value()));
|
||||
parsedFunc->setTitle(QString("user function: \\verb!"+edit->text()+"!, p_1=%1, p_2=%2").arg(spinP1->value()).arg(spinP2->value()));
|
||||
parsedFunc->setFunction(edit->text());
|
||||
parsedFunc->setParamsV(spinP1->value(), spinP2->value());
|
||||
parsedFunc->setDisplaySamplePoints(check->isChecked());
|
||||
|
@ -6,11 +6,16 @@
|
||||
|
||||
#include "speedtestplot.h"
|
||||
#include "jkqtplotter/graphs/jkqtpscatter.h"
|
||||
#include "jkqtcommon_statistics_and_math/jkqtpstatisticstools.h"
|
||||
|
||||
SpeedTestPlot::SpeedTestPlot():
|
||||
JKQTPlotter(), NDATA(500), dx(1.0/500.0*4.0*JKQTPSTATISTICS_PI), x0(0)
|
||||
|
||||
{
|
||||
X.fill(0);
|
||||
Y.fill(0);
|
||||
Y2.fill(0);
|
||||
|
||||
// 1. optimize JKQTPlotter for speed (by switching off anti-aliasing)
|
||||
getPlotter()->setUseAntiAliasingForGraphs(false);
|
||||
getPlotter()->setUseAntiAliasingForSystem(false);
|
||||
@ -18,12 +23,7 @@ SpeedTestPlot::SpeedTestPlot():
|
||||
|
||||
|
||||
// 2. now we create data for a simple plot (a sine curve + random[-0.5,0.5])
|
||||
for (size_t i=0; i<X.size(); i++) {
|
||||
const double x=static_cast<double>(i)*dx;
|
||||
X[i]=x0+x;
|
||||
Y[i]=sin(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y2[i]=cos(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
}
|
||||
updateDataSize(NDATA, false);
|
||||
|
||||
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
|
||||
// Here the data from the std::array's is not copied, but only the pointer to
|
||||
@ -60,6 +60,7 @@ SpeedTestPlot::SpeedTestPlot():
|
||||
p->getPlotter()->setUseAntiAliasingForGraphs(p->actAntiAliase->isChecked());
|
||||
p->getPlotter()->setUseAntiAliasingForSystem(p->actAntiAliase->isChecked());
|
||||
p->getPlotter()->setUseAntiAliasingForText(p->actAntiAliase->isChecked());
|
||||
if (!p->actAnimation->isChecked()) p->redrawPlot();
|
||||
}, this));
|
||||
|
||||
actTwoGraphs=new QAction(QObject::tr("2 Graphs"));
|
||||
@ -67,6 +68,7 @@ SpeedTestPlot::SpeedTestPlot():
|
||||
actTwoGraphs->setChecked(true);
|
||||
connect(actTwoGraphs, &QAction::triggered, std::bind([](SpeedTestPlot* p, JKQTPXYLineGraph* g){
|
||||
g->setVisible(p->actTwoGraphs->isChecked());
|
||||
if (!p->actAnimation->isChecked()) p->redrawPlot();
|
||||
}, this, graph2));
|
||||
|
||||
actFixedXAxis=new QAction(QObject::tr("Fixed X-Axis"));
|
||||
@ -76,18 +78,20 @@ SpeedTestPlot::SpeedTestPlot():
|
||||
actLines=new QAction(QObject::tr("Show Graph Lines"));
|
||||
actLines->setCheckable(true);
|
||||
actLines->setChecked(true);
|
||||
connect(actLines, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2){
|
||||
connect(actLines, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2,SpeedTestPlot* p){
|
||||
g->setDrawLine(enabled);
|
||||
g2->setDrawLine(enabled);
|
||||
}, std::placeholders::_1, graph, graph2));
|
||||
if (!p->actAnimation->isChecked()) p->redrawPlot();
|
||||
}, std::placeholders::_1, graph, graph2,this));
|
||||
|
||||
actSymbols=new QAction(QObject::tr("Show Graph Symbols"));
|
||||
actSymbols->setCheckable(true);
|
||||
actSymbols->setChecked(true);
|
||||
connect(actSymbols, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2){
|
||||
connect(actSymbols, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2,SpeedTestPlot* p){
|
||||
g->setSymbolType(enabled?JKQTPCross:JKQTPNoSymbol);
|
||||
g2->setSymbolType(enabled?JKQTPCircle:JKQTPNoSymbol);
|
||||
}, std::placeholders::_1, graph, graph2));
|
||||
if (!p->actAnimation->isChecked()) p->redrawPlot();
|
||||
}, std::placeholders::_1, graph, graph2,this));
|
||||
|
||||
menuSizes=new QMenu(QObject::tr("number of datapoints"), this);
|
||||
QActionGroup* actGroup=new QActionGroup(menuSizes);
|
||||
@ -103,12 +107,58 @@ SpeedTestPlot::SpeedTestPlot():
|
||||
menuSizes->addAction(act);
|
||||
}
|
||||
|
||||
actUseNonvisibleLineCompression=new QAction(QObject::tr("use NonvisibleLineCompression"));
|
||||
actUseNonvisibleLineCompression->setCheckable(true);
|
||||
actUseNonvisibleLineCompression->setChecked(true);
|
||||
connect(actUseNonvisibleLineCompression, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2,SpeedTestPlot* p){
|
||||
g->setUseNonvisibleLineCompression(enabled);
|
||||
g2->setUseNonvisibleLineCompression(enabled);
|
||||
if (!p->actAnimation->isChecked()) p->redrawPlot();
|
||||
}, std::placeholders::_1, graph, graph2,this));
|
||||
|
||||
|
||||
menuUseNonvisibleLineCompressionAgressiveness=new QMenu(QObject::tr("NonvisibleLineCompression level"), this);
|
||||
actGroup=new QActionGroup(menuUseNonvisibleLineCompressionAgressiveness);
|
||||
for (double a: {0.5, 0.8, 1.0, 1.5, 2.0, 5.0}) {
|
||||
QAction* act=actGroup->addAction(QString::number(a));
|
||||
act->setCheckable(true);
|
||||
act->setChecked(a==1.0);
|
||||
connect(act, &QAction::toggled, std::bind([](bool enabled,JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2,SpeedTestPlot* p, double a){
|
||||
g->setNonvisibleLineCompressionAgressiveness(a);
|
||||
g2->setNonvisibleLineCompressionAgressiveness(a);
|
||||
if (!p->actAnimation->isChecked()) p->redrawPlot();
|
||||
}, std::placeholders::_1, graph, graph2,this, a));
|
||||
menuUseNonvisibleLineCompressionAgressiveness->addAction(act);
|
||||
}
|
||||
|
||||
actStepAnimation=new QAction(QObject::tr("Next Animation Step"));
|
||||
actStepAnimation->setCheckable(false);
|
||||
actStepAnimation->setEnabled(false);
|
||||
connect(actStepAnimation, &QAction::triggered, std::bind([](SpeedTestPlot* p){
|
||||
p->plotNewData();
|
||||
}, this));
|
||||
|
||||
actAnimation=new QAction(QObject::tr("Animation Active"));
|
||||
actAnimation->setCheckable(true);
|
||||
actAnimation->setChecked(true);
|
||||
connect(actAnimation, &QAction::toggled, std::bind([](bool enabled, SpeedTestPlot* p, QAction* actStepAnimation){
|
||||
if (enabled) {
|
||||
p->plotNewData();
|
||||
}
|
||||
actStepAnimation->setEnabled(!enabled);
|
||||
}, std::placeholders::_1, this, actStepAnimation));
|
||||
|
||||
|
||||
addAction(actAntiAliase);
|
||||
addAction(actFixedXAxis);
|
||||
addAction(menuSizes->menuAction());
|
||||
addAction(actTwoGraphs);
|
||||
addAction(actLines);
|
||||
addAction(actSymbols);
|
||||
addAction(actUseNonvisibleLineCompression);
|
||||
addAction(menuUseNonvisibleLineCompressionAgressiveness->menuAction());
|
||||
addAction(actAnimation);
|
||||
addAction(actStepAnimation);
|
||||
|
||||
// show plotter and make it a decent size
|
||||
show();
|
||||
@ -130,8 +180,8 @@ void SpeedTestPlot::plotNewData()
|
||||
Y2[i]=Y2[i+1];
|
||||
}
|
||||
// add one new data point
|
||||
Y[NDATA-1]=sin(X[NDATA-1]+x0)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y2[NDATA-1]=cos(X[NDATA-1]+x0)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y[NDATA-1]=sin(X[NDATA-1]+x0)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5+addOutlier(1.0/static_cast<double>(NDATA/5), 2.0);
|
||||
Y2[NDATA-1]=cos(X[NDATA-1]+x0)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5+addOutlier(1.0/static_cast<double>(NDATA/7), 2.0);
|
||||
} else {
|
||||
// move old data to the left
|
||||
for (size_t i=0; i<NDATA-1; i++) {
|
||||
@ -141,8 +191,8 @@ void SpeedTestPlot::plotNewData()
|
||||
}
|
||||
// add one new data point
|
||||
X[NDATA-1]=X[NDATA-2]+dx;
|
||||
Y[NDATA-1]=sin(X[NDATA-1])+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y2[NDATA-1]=cos(X[NDATA-1])+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y[NDATA-1]=sin(X[NDATA-1])+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5+addOutlier(1.0/static_cast<double>(NDATA/5), 2.0);
|
||||
Y2[NDATA-1]=cos(X[NDATA-1])+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5+addOutlier(1.0/static_cast<double>(NDATA/7), 2.0);
|
||||
|
||||
}
|
||||
|
||||
@ -155,12 +205,17 @@ void SpeedTestPlot::plotNewData()
|
||||
const auto tlastalst=t_lastplot;
|
||||
t_lastplot=std::chrono::system_clock::now();
|
||||
const double delta_secs=static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(t_lastplot-tlastalst).count())/1000.0;
|
||||
setWindowTitle(QString("Live Data Speed Test: %2 datapoint, %1 fps").arg(1.0/delta_secs,0,'f',2).arg(NDATA));
|
||||
calctimes.push_back(delta_secs);
|
||||
if (delta_secs<0.05) { while (calctimes.size()>60) calctimes.pop_front(); }
|
||||
else if (delta_secs<0.1) { while (calctimes.size()>30) calctimes.pop_front(); }
|
||||
else if (delta_secs<1) { while (calctimes.size()>10) calctimes.pop_front(); }
|
||||
else { while (calctimes.size()>5) calctimes.pop_front(); }
|
||||
setWindowTitle(QString("Live Data Speed Test: %2 datapoint, %3 [this: %1] fps").arg(1.0/delta_secs,0,'f',2).arg(NDATA).arg(1.0/jkqtpstatAverage(calctimes.begin(), calctimes.end()),0,'f',2));
|
||||
// enqueue call for next data value
|
||||
QTimer::singleShot(1, this, SLOT(plotNewData()));
|
||||
if (actAnimation->isChecked()) QTimer::singleShot(1, this, SLOT(plotNewData()));
|
||||
}
|
||||
|
||||
void SpeedTestPlot::updateDataSize(size_t newSize)
|
||||
void SpeedTestPlot::updateDataSize(size_t newSize, bool updatePlots)
|
||||
{
|
||||
NDATA=newSize;
|
||||
dx=1.0/double(NDATA)*4.0*JKQTPSTATISTICS_PI;
|
||||
@ -168,10 +223,11 @@ void SpeedTestPlot::updateDataSize(size_t newSize)
|
||||
for (size_t i=0; i<X.size(); i++) {
|
||||
const double x=static_cast<double>(i)*dx;
|
||||
X[i]=x0+x;
|
||||
Y[i]=sin(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y2[i]=cos(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||
Y[i]=sin(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5+addOutlier(1.0/static_cast<double>(NDATA/5), 2.0);
|
||||
Y2[i]=cos(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5+addOutlier(1.0/static_cast<double>(NDATA/7), 2.0);
|
||||
}
|
||||
|
||||
if (updatePlots) {
|
||||
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
|
||||
// Here the data from the std::array's is not copied, but only the pointer to
|
||||
// the array is added to the datastore. therefore the datastore does not manage
|
||||
@ -193,3 +249,10 @@ void SpeedTestPlot::updateDataSize(size_t newSize)
|
||||
setX(X[0], X[NDATA]);
|
||||
setY(-2,2);
|
||||
}
|
||||
}
|
||||
|
||||
double SpeedTestPlot::addOutlier(double prob, double height)
|
||||
{
|
||||
if (static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)<prob) return height*(2.0*static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-1.0);
|
||||
else return 0;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QActionGroup>
|
||||
#include "jkqtplotter/jkqtplotter.h"
|
||||
@ -22,15 +23,22 @@ class SpeedTestPlot: public JKQTPlotter {
|
||||
QAction* actFixedXAxis;
|
||||
QAction* actLines;
|
||||
QAction* actSymbols;
|
||||
QAction* actUseNonvisibleLineCompression;
|
||||
QMenu* menuUseNonvisibleLineCompressionAgressiveness;
|
||||
QAction* actUseClipping;
|
||||
QAction* actAnimation;
|
||||
QAction* actStepAnimation;
|
||||
QMenu* menuSizes;
|
||||
JKQTPXYLineGraph* graph;
|
||||
JKQTPXYLineGraph* graph2;
|
||||
QList<double> calctimes;
|
||||
public:
|
||||
SpeedTestPlot();
|
||||
|
||||
virtual ~SpeedTestPlot();
|
||||
protected slots:
|
||||
void plotNewData();
|
||||
void updateDataSize(size_t newSize);
|
||||
void updateDataSize(size_t newSize, bool updatePlots=true);
|
||||
static double addOutlier(double prob, double height);
|
||||
|
||||
};
|
||||
|
@ -347,3 +347,137 @@ double JKQTPLineDecoratorStyleCalcDecoratorSize(double line_width, double decora
|
||||
return decoratorSizeFactor*pow(line_width, 0.7);
|
||||
}
|
||||
|
||||
|
||||
QList<QPointF> JKQTPSimplifyPolyLines(const QList<QPointF> &lines_in, double maxDeltaXY)
|
||||
{
|
||||
if (lines_in.size()<=1) return QList<QPointF>();
|
||||
if (lines_in.size()<=10) return lines_in;
|
||||
QList<QPointF> l;
|
||||
l.reserve(lines_in.size());
|
||||
|
||||
int groupStart=0;
|
||||
int groupCount=1;
|
||||
QRectF groupSize(lines_in[0], QSizeF(0,0));
|
||||
|
||||
auto writeGroup=[](QList<QPointF>& l, const int& groupStart, const int& groupCount, const QRectF& groupSize, const QList<QPointF>& lines_in, double maxDeltaXY) {
|
||||
// group ends
|
||||
if (groupCount>4) {
|
||||
// we can optimize the group away
|
||||
// we take the first and the last point and in between one at the top and one at the bottom of the group
|
||||
// unless we have a group that is smaller than maxDeltaXY in both directions, the the first and last point suffice
|
||||
const QPointF& x0=lines_in[groupStart];
|
||||
l<<x0; // first point
|
||||
if (groupSize.width()<=maxDeltaXY && groupSize.height()>maxDeltaXY) {
|
||||
// x-group, i.e. small on x-axis
|
||||
l<<QPointF(x0.x()+groupSize.width()/3.0, groupSize.bottom());
|
||||
l<<QPointF(x0.x()+groupSize.width()*2.0/3.0, groupSize.top());
|
||||
} else if (groupSize.width()>maxDeltaXY && groupSize.height()<=maxDeltaXY) {
|
||||
// y-group, i.e. small on y-axis
|
||||
l<<QPointF(groupSize.left(), x0.y()+groupSize.height()/3.0);
|
||||
l<<QPointF(groupSize.right(), x0.y()+groupSize.height()*2.0/3.0);
|
||||
}
|
||||
l<<lines_in[groupStart+groupCount-1]; // last point
|
||||
} else {
|
||||
// small groups cannot be optimized away
|
||||
// so we simply copy it to the output
|
||||
for (int j=groupStart; j<groupStart+groupCount; j++) l<<lines_in[j];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
for (int i=0; i<lines_in.size(); i++) {
|
||||
const QPointF& x=lines_in[i];
|
||||
QRectF newGroupSize=groupSize;
|
||||
newGroupSize.setLeft(qMin(newGroupSize.left(), x.x()));
|
||||
newGroupSize.setRight(qMax(newGroupSize.right(), x.x()));
|
||||
newGroupSize.setTop(qMin(newGroupSize.top(), x.y()));
|
||||
newGroupSize.setBottom(qMax(newGroupSize.bottom(), x.y()));
|
||||
|
||||
if (newGroupSize.width()<=maxDeltaXY || newGroupSize.height()<=maxDeltaXY) {
|
||||
// points still in group, so extend the group
|
||||
groupCount++;
|
||||
groupSize=newGroupSize;
|
||||
} else {
|
||||
// group ends, because adding a new point would increase it's size too much
|
||||
writeGroup(l, groupStart, groupCount, groupSize, lines_in, maxDeltaXY);
|
||||
// start new group with current point
|
||||
groupStart=i;
|
||||
groupCount=1;
|
||||
groupSize=QRectF(x, QSizeF(0,0));
|
||||
}
|
||||
}
|
||||
writeGroup(l, groupStart, groupCount, groupSize, lines_in, maxDeltaXY);
|
||||
//qDebug()<<"JKQTPSimplifyPolyLines("<<lines_in.size()<<", maxDeltaXY="<<maxDeltaXY<<") -> "<<l.size();
|
||||
return l;
|
||||
}
|
||||
|
||||
QList<QList<QPointF> > JKQTPClipPolyLine(const QList<QPointF> &polyline_in, const QRectF &clipRect)
|
||||
{
|
||||
QList<QList<QPointF>> l;
|
||||
l<<polyline_in;
|
||||
return JKQTPClipPolyLines(l, clipRect);
|
||||
}
|
||||
|
||||
QList<QList<QPointF> > JKQTPClipPolyLines(const QList<QList<QPointF> > &polylines_in, const QRectF &clipRect)
|
||||
{
|
||||
const double xmin=qMin(clipRect.left(), clipRect.right());
|
||||
const double xmax=qMax(clipRect.left(), clipRect.right());
|
||||
const double ymin=qMin(clipRect.top(), clipRect.bottom());
|
||||
const double ymax=qMax(clipRect.top(), clipRect.bottom());
|
||||
QList<QList<QPointF>> out;
|
||||
for (const QList<QPointF>& pl: polylines_in) {
|
||||
if (pl.size()>1) {
|
||||
if (out.size()==0 || out.last().size()>0) {
|
||||
out<<QList<QPointF>();
|
||||
}
|
||||
for (int i=1; i<pl.size(); i++) {
|
||||
const QLineF l(pl[i-1], pl[i]);
|
||||
const QLineF lclipped=JKQTPClipLine(l, xmin, xmax, ymin, ymax);
|
||||
//qDebug()<<" "<<l<<" --> "<<lclipped<<" clip: x="<<xmin<<".."<<xmax<<", y="<<ymin<<".."<<ymax;
|
||||
// 0-length: line remove ==> dont's add anything, start new segment if necessary
|
||||
// l=lclipped: no point was clipped: add second point, or if list empty both points (first line segment)
|
||||
// only p1 clipped, i.e. second point clipped: add second point, end segmnt (i.e. start a new one)
|
||||
// only p2 clipped, i.e. first point clipped: start new segment, add first point to it
|
||||
// p1 and p2 clipped, i.e. segment clipped on both ends: add clipped line as separate segment
|
||||
if (lclipped.length()==0) {
|
||||
if (out.last().size()>0) {
|
||||
out<<QList<QPointF>();
|
||||
}
|
||||
} else if (l==lclipped) {
|
||||
if (out.last().size()==0) {
|
||||
out.last()<<lclipped.p1();
|
||||
} if (out.last().last()!=lclipped.p1()) {
|
||||
out<<QList<QPointF>();
|
||||
out.last()<<lclipped.p1();
|
||||
}
|
||||
out.last()<<lclipped.p2();
|
||||
} else if (l.p1()==lclipped.p1() && l.p2()!=lclipped.p2()) {
|
||||
if (out.last().size()==0) {
|
||||
out.last()<<lclipped.p1();
|
||||
} if (out.last().last()!=lclipped.p1()) {
|
||||
out<<QList<QPointF>();
|
||||
out.last()<<lclipped.p1();
|
||||
}
|
||||
out.last()<<lclipped.p2();
|
||||
out<<QList<QPointF>();
|
||||
} else if (l.p1()!=lclipped.p1() && l.p2()==lclipped.p2()) {
|
||||
if (out.last().size()==0) {
|
||||
out.last()<<lclipped.p1();
|
||||
} if (out.last().last()!=lclipped.p1()) {
|
||||
out<<QList<QPointF>();
|
||||
out.last()<<lclipped.p1();
|
||||
}
|
||||
out.last()<<lclipped.p2();
|
||||
} else if (l.p1()!=lclipped.p1() && l.p2()!=lclipped.p2()) {
|
||||
if (out.last().size()>0) {
|
||||
out<<QList<QPointF>();
|
||||
}
|
||||
out.last()<<lclipped.p1();
|
||||
out.last()<<lclipped.p2();
|
||||
out<<QList<QPointF>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -261,6 +261,154 @@ inline void JKQTPPlotSymbol(TPainter& painter, double x, double y, JKQTPGraphSym
|
||||
JKQTCOMMON_LIB_EXPORT void JKQTPPlotSymbol(QPaintDevice& paintDevice, double x, double y, JKQTPGraphSymbols symbol, double size, double symbolLineWidth, QColor color, QColor fillColor);
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief clips the given line (\a x1 , \a y1 ) -- (\a x2 , \a y2 ) to the given rectangle \a xmin .. \a xmax and \a ymin ... \a ymax
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
\return the clipped line in \a x1 , \a y1 , \a x2 , \a y2 and \c true if the line is still to be drawn or \c false else
|
||||
|
||||
This function implements the algorithm descripbed in https://www.researchgate.net/publication/335018076_Another_Simple_but_Faster_Method_for_2D_Line_Clipping
|
||||
i.e. in Pseudocode
|
||||
\verbatim
|
||||
// x1 , y1 , x2 , y2 , xmin , ymax , xmax , ymin //
|
||||
if not ( x1<xmin and x2<xmin ) and not ( x1>xmax and x2>xmax ) then
|
||||
if not ( y1<ymin and y2<ymin ) and not ( y1>ymax and y2>ymax ) then
|
||||
x[1]= x1
|
||||
y[1]= y1
|
||||
x[2]= x2
|
||||
y[2]= y2
|
||||
i =1
|
||||
repeat
|
||||
if x[i] < xmin then
|
||||
x[i] = xmin
|
||||
y[i] = ( ( y2-y1 ) / ( x2-x1 ) ) * ( xmin-x1)+y1
|
||||
elseif x[i] > xmax then
|
||||
x[i] = xmax
|
||||
y[i] = ( ( y2-y1 ) / ( x2-x1 ) ) * ( xmax-x1)+y1
|
||||
endif
|
||||
if y[i] < ymin then
|
||||
y[i] = ymin
|
||||
x[i] = ( ( x2-x1 ) / ( y2-y1 ) ) * ( ymin-y1)+x1
|
||||
elseif y[i] > ymax then
|
||||
y[i] = ymax
|
||||
x[i] = ( ( x2-x1 ) / ( y2-y1 ) ) * ( ymax-y1)+x1
|
||||
endif
|
||||
i = i + 1
|
||||
until i >2
|
||||
if not ( x [1 ] < xmin and x [2 ] < xmin ) and not ( x [1 ] >xmax and x [2 ] >xmax ) then
|
||||
drawLine ( x[1] , y[1] , x[2] , y[2] )
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
\endverbatim
|
||||
*/
|
||||
inline bool JKQTPClipLine(double& x1 , double& y1 , double& x2 , double& y2 , double xmin , double xmax , double ymin, double ymax) {
|
||||
if (! ( x1<xmin && x2<xmin ) && !( x1>xmax && x2>xmax )) {
|
||||
if ( !( y1<ymin && y2<ymin ) && !( y1>ymax && y2>ymax ) ) {
|
||||
double x[2]= {x1,x2};
|
||||
double y[2]= {y1,y2};
|
||||
for (int i=0; i<2; i++) {
|
||||
if (x[i] < xmin) {
|
||||
x[i] = xmin;
|
||||
y[i] = ( ( y2-y1 ) / ( x2-x1 ) ) * ( xmin-x1)+y1;
|
||||
} else if (x[i] > xmax) {
|
||||
x[i] = xmax;
|
||||
y[i] = ( ( y2-y1 ) / ( x2-x1 ) ) * ( xmax-x1)+y1;
|
||||
}
|
||||
if (y[i] < ymin) {
|
||||
y[i] = ymin;
|
||||
x[i] = ( ( x2-x1 ) / ( y2-y1 ) ) * ( ymin-y1)+x1;
|
||||
} else if (y[i] > ymax) {
|
||||
y[i] = ymax;
|
||||
x[i] = ( ( x2-x1 ) / ( y2-y1 ) ) * ( ymax-y1)+x1;
|
||||
}
|
||||
}
|
||||
if (! ( x[0] < xmin && x[1]< xmin ) && !( x[0] >xmax && x[1] >xmax )) {
|
||||
x1=x[0];
|
||||
y1=y[0];
|
||||
x2=x[1];
|
||||
y2=y[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! \brief clips the given line \a line to the given rectangle rectangle \a xmin .. \a xmax and \a ymin ... \a ymax
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
\return the clipped line or a line with 0-length, i.e. QLineF()
|
||||
|
||||
This function implements the algorithm descripbed in https://www.researchgate.net/publication/335018076_Another_Simple_but_Faster_Method_for_2D_Line_Clipping
|
||||
|
||||
*/
|
||||
inline QLineF JKQTPClipLine(const QLineF& line, double xmin , double xmax , double ymin, double ymax) {
|
||||
double x1=line.x1();
|
||||
double y1=line.y1();
|
||||
double x2=line.x2();
|
||||
double y2=line.y2();
|
||||
if (JKQTPClipLine(x1,y1,x2,y2,xmin,xmax,ymin,ymax)) {
|
||||
return QLineF(x1,y1,x2,y2);
|
||||
} else {
|
||||
return QLineF();
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief clips the given line \a line to the given rectangle \a clipRect
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
\param line line to be clipped
|
||||
\param clipRect rectangle to clip to
|
||||
\return the clipped line or a line with 0-length, i.e. QLineF()
|
||||
|
||||
This function implements the algorithm descripbed in https://www.researchgate.net/publication/335018076_Another_Simple_but_Faster_Method_for_2D_Line_Clipping
|
||||
|
||||
*/
|
||||
inline QLineF JKQTPClipLine(const QLineF& line, const QRectF& clipRect) {
|
||||
const double xmin=qMin(clipRect.left(), clipRect.right());
|
||||
const double xmax=qMax(clipRect.left(), clipRect.right());
|
||||
const double ymin=qMin(clipRect.top(), clipRect.bottom());
|
||||
const double ymax=qMax(clipRect.top(), clipRect.bottom());
|
||||
|
||||
double x1=line.x1();
|
||||
double y1=line.y1();
|
||||
double x2=line.x2();
|
||||
double y2=line.y2();
|
||||
if (JKQTPClipLine(x1,y1,x2,y2,xmin,xmax,ymin,ymax)) {
|
||||
return QLineF(x1,y1,x2,y2);
|
||||
} else {
|
||||
return QLineF();
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief clips the given list of poly-lines \a polylines_in to the given rectangle \a clipRect
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
\param polylines_in list of poly-lines to be clipped
|
||||
\param clipRect rectangle to clip to
|
||||
\return a list of poly-lines representing the clipped lines. Note that some lines may be split further so the number of poly-lines in the output may actually be larger than the number of polylines in the input!
|
||||
*/
|
||||
JKQTCOMMON_LIB_EXPORT QList<QList<QPointF>> JKQTPClipPolyLines(const QList<QList<QPointF>> & polylines_in, const QRectF& clipRect);
|
||||
/*! \brief clips the given poly-line \a polyline_in to the given rectangle \a clipRect
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
\param polyline_in poly-line to be clipped
|
||||
\param clipRect rectangle to clip to
|
||||
\return a list of poly-lines representing the clipped line.
|
||||
*/
|
||||
JKQTCOMMON_LIB_EXPORT QList<QList<QPointF>> JKQTPClipPolyLine(const QList<QPointF> & polyline_in, const QRectF& clipRect);
|
||||
|
||||
/*! \brief tries to reduce the complexity of the given poly-line \a lines_in, but keeping the appearance as if all lines were drawn
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
\param lines_in poly-line to be simplified
|
||||
\param maxDeltaXY a group has to be either less wide or less high than this, typically equals the linewidth of the poly-line
|
||||
\return a simplified version of lines_in
|
||||
*/
|
||||
JKQTCOMMON_LIB_EXPORT QList<QPointF> JKQTPSimplifyPolyLines(const QList<QPointF>& lines_in, double maxDeltaXY=1.0);
|
||||
|
||||
/*! \brief draw a tooltip, using the current brush and pen of the provided painter
|
||||
\ingroup jkqtptools_drawing
|
||||
|
||||
@ -286,6 +434,7 @@ inline void JKQTPDrawTooltip(TPainter& painter, double x, double y, const QRectF
|
||||
|
||||
template <class TPainter>
|
||||
inline void JKQTPPlotSymbol(TPainter& painter, double x, double y, JKQTPGraphSymbols symbol, double symbolSize, double symbolLineWidth, QColor color, QColor fillColor) {
|
||||
if (symbol==JKQTPNoSymbol) return;
|
||||
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
||||
QPen p=painter.pen();
|
||||
p.setColor(color);
|
||||
|
@ -23,7 +23,9 @@
|
||||
#include "jkqtplotter/jkqtpbaseplotter.h"
|
||||
#include <stdlib.h>
|
||||
#include <QDebug>
|
||||
#include <QMarginsF>
|
||||
#include <iostream>
|
||||
#include "jkqtcommon/jkqtpdrawingtools.h"
|
||||
#include "jkqtplotter/jkqtptools.h"
|
||||
#include "jkqtplotter/jkqtpimagetools.h"
|
||||
#include "jkqtplotter/graphs/jkqtpimage.h"
|
||||
@ -47,10 +49,10 @@ JKQTPXYLineGraph::JKQTPXYLineGraph(JKQTPlotter* parent):
|
||||
}
|
||||
|
||||
JKQTPXYLineGraph::JKQTPXYLineGraph(JKQTBasePlotter* parent):
|
||||
JKQTPXYGraph(parent)
|
||||
JKQTPXYGraph(parent),
|
||||
drawLine(true)
|
||||
{
|
||||
sortData=JKQTPXYGraph::Unsorted;
|
||||
drawLine=true;
|
||||
|
||||
initLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
|
||||
initSymbolStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
|
||||
@ -74,6 +76,14 @@ void JKQTPXYLineGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
|
||||
const QPen p=getLinePen(painter, parent);
|
||||
const QPen penSelection=getHighlightingLinePen(painter, parent);
|
||||
const auto symType=getSymbolType();
|
||||
const double xmin=transformX(parent->getXAxis()->getMin());
|
||||
const double xmax=transformX(parent->getXAxis()->getMax());
|
||||
const double ymin=transformY(parent->getYAxis()->getMin());
|
||||
const double ymax=transformY(parent->getYAxis()->getMax());
|
||||
const double symbolSize=parent->pt2px(painter, getSymbolSize());
|
||||
const QMarginsF clipMargins=(symType==JKQTPNoSymbol)?QMarginsF(0,0,0,0):QMarginsF(symbolSize,symbolSize,symbolSize,symbolSize);
|
||||
const QRectF cliprect=QRectF(qMin(xmin,xmax),qMin(ymin,ymax),fabs(xmax-xmin),fabs(ymax-ymin))+clipMargins;
|
||||
|
||||
|
||||
int imax=0;
|
||||
@ -81,8 +91,8 @@ void JKQTPXYLineGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
if (getIndexRange(imin, imax)) {
|
||||
|
||||
|
||||
std::vector<QPolygonF> vec_linesP;
|
||||
vec_linesP.push_back(QPolygonF());
|
||||
QList<QList<QPointF>> vec_linesP;
|
||||
vec_linesP.push_back(QList<QPointF>());
|
||||
intSortData();
|
||||
for (int iii=imin; iii<imax; iii++) {
|
||||
const int i=qBound(imin, getDataIndex(iii), imax);
|
||||
@ -93,31 +103,41 @@ void JKQTPXYLineGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): (xv, yv) = ( "<<xv<<", "<<yv<<" )";
|
||||
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv) && JKQTPIsOKFloat(x) && JKQTPIsOKFloat(y)) {
|
||||
|
||||
if (isHighlighted() && getSymbolType()!=JKQTPNoSymbol) {
|
||||
//if (isHighlighted() && getSymbolType()!=JKQTPNoSymbol) {
|
||||
//JKQTPPlotSymbol(painter, x, y, JKQTPFilledCircle, parent->pt2px(painter, symbolSize*1.5), parent->pt2px(painter, symbolWidth*parent->getLineWidthMultiplier()), penSelection.color(), penSelection.color());
|
||||
}
|
||||
//}
|
||||
if ((!parent->getXAxis()->isLogAxis() || xv>0.0) && (!parent->getYAxis()->isLogAxis() || yv>0.0) ) {
|
||||
plotStyledSymbol(parent, painter, x, y);
|
||||
if (symType!=JKQTPNoSymbol && cliprect.contains(x,y)) plotStyledSymbol(parent, painter, x, y);
|
||||
if (drawLine) {
|
||||
vec_linesP[vec_linesP.size()-1] << QPointF(x,y);
|
||||
vec_linesP.last() << QPointF(x,y);
|
||||
}
|
||||
} else {
|
||||
vec_linesP.push_back(QPolygonF());
|
||||
if (drawLine) {
|
||||
if (vec_linesP.size()==0 || vec_linesP.last().size()>0)
|
||||
vec_linesP.push_back(QList<QPointF>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): "<<4<<" lines="<<lines.size();
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): "<<5<<" p="<<painter.pen();
|
||||
for (auto &linesP : vec_linesP) {
|
||||
if (drawLine) {
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): vec_linesP.size()=="<<vec_linesP.size();
|
||||
|
||||
const QList<QList<QPointF>> linesToDraw=JKQTPClipPolyLines(vec_linesP, cliprect);
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): linesToDraw.size()=="<<linesToDraw.size()<<", clip: x="<<xmin<<".."<<xmax<<", y="<<ymin<<".."<<ymax;
|
||||
for (const auto &linesPFromV : linesToDraw) {
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): linesPFromV.size()=="<<linesPFromV.size()<<" useNonvisibleLineCompression="<<getUseNonvisibleLineCompression();
|
||||
const QList<QPointF> linesP=getUseNonvisibleLineCompression()?JKQTPSimplifyPolyLines(linesPFromV, p.widthF()*getNonvisibleLineCompressionAgressiveness()):linesPFromV;
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): --> linesP.size()=="<<linesP.size();
|
||||
if (linesP.size()>0) {
|
||||
if (isHighlighted()) {
|
||||
painter.setPen(penSelection);
|
||||
//painter.drawLines(lines);
|
||||
painter.drawPolyline(linesP);
|
||||
painter.drawPolyline(linesP.data(), linesP.size());
|
||||
}
|
||||
painter.setPen(p);
|
||||
//painter.drawLines(lines);
|
||||
painter.drawPolyline(linesP);
|
||||
painter.drawPolyline(linesP.data(), linesP.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
//qDebug()<<"JKQTPXYLineGraph::draw(): "<<6;
|
||||
@ -174,6 +194,7 @@ void JKQTPXYLineGraph::setColor(QColor c)
|
||||
}
|
||||
|
||||
|
||||
|
||||
JKQTPXYLineErrorGraph::JKQTPXYLineErrorGraph(JKQTBasePlotter *parent):
|
||||
JKQTPXYLineGraph(parent)
|
||||
{
|
||||
|
@ -51,9 +51,14 @@ class JKQTPDatastore;
|
||||
|
||||
\image html plot_lineplots.png
|
||||
|
||||
\see \ref JKQTPlotterAdvancedLineAndFillStyling, \ref JKQTPlotterSimpleTest, \ref JKQTPlotterSymbolsAndStyles, jkqtpstatAddVKDE1D(), jkqtpstatAddVKDE1DAutoranged(), jkqtpstatAddHKDE1D(), jkqtpstatAddHKDE1DAutoranged()
|
||||
\note This classes can (and does by default) apply a line-compression strategy that improves plotting speed
|
||||
but reduces accuracy a bit. See JKQTPGraphLinesCompressionMixin for details.
|
||||
|
||||
\see \ref JKQTPlotterAdvancedLineAndFillStyling, \ref JKQTPlotterSimpleTest, \ref JKQTPlotterSymbolsAndStyles,
|
||||
jkqtpstatAddVKDE1D(), jkqtpstatAddVKDE1DAutoranged(), jkqtpstatAddHKDE1D(), jkqtpstatAddHKDE1DAutoranged(),
|
||||
JKQTPGraphLinesCompressionMixin
|
||||
*/
|
||||
class JKQTPLOTTER_LIB_EXPORT JKQTPXYLineGraph: public JKQTPXYGraph, public JKQTPGraphLineStyleMixin, public JKQTPGraphSymbolStyleMixin {
|
||||
class JKQTPLOTTER_LIB_EXPORT JKQTPXYLineGraph: public JKQTPXYGraph, public JKQTPGraphLineStyleMixin, public JKQTPGraphSymbolStyleMixin, public JKQTPGraphLinesCompressionMixin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/** \brief class constructor */
|
||||
@ -76,13 +81,14 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYLineGraph: public JKQTPXYGraph, public JKQTP
|
||||
/** \brief set color of line and symbol */
|
||||
void setColor(QColor c);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/** \brief indicates whether to draw a line or not */
|
||||
bool drawLine;
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -100,7 +106,9 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYLineGraph: public JKQTPXYGraph, public JKQTP
|
||||
|
||||
\image html paramscatterplot.png "Different Styles of Parametrized Scatter/Line Graphs"
|
||||
|
||||
\image html paramscatterplot_image_star.png "JKQTPXYParametrizedScatterGraph with symbols organized in a grid"
|
||||
\note This classes is meant for cases where you want to change the color/size/... of single symbols, in dependence
|
||||
of data. If you are looking for a simple scatter-plot without data-dependent properties, use JKQTPXYLineGraph
|
||||
instead, which is faster.
|
||||
|
||||
\note For the size, line width and symbol type columns, you can also set a functor, which converts the column value (optionally based
|
||||
also on the x- and y-location of the data point) into the local symbol size, symbol type or line width. Use the functions
|
||||
@ -110,6 +118,8 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPXYLineGraph: public JKQTPXYGraph, public JKQTP
|
||||
\image html JKQTPXYParametrizedScatterGraph_LinewidthFunctor.png
|
||||
\image html JKQTPXYParametrizedScatterGraph_SymbolFunctor.png
|
||||
|
||||
\image html paramscatterplot_image_star.png "JKQTPXYParametrizedScatterGraph with symbols organized in a grid"
|
||||
|
||||
|
||||
\see JKQTPXYParametrizedErrorScatterGraph, \ref JKQTPlotterParamScatter , \ref JKQTPlotterParamScatterImage, \ref JKQTPlotterParametricCurves
|
||||
*/
|
||||
|
@ -128,6 +128,14 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
QPen ph=getHighlightingLinePen(painter, parent);
|
||||
QPen np(Qt::NoPen);
|
||||
QBrush b=getFillBrush(painter, parent);
|
||||
const double xmin=transformX(parent->getXAxis()->getMin());
|
||||
const double xmax=transformX(parent->getXAxis()->getMax());
|
||||
const double ymin=transformY(parent->getYAxis()->getMin());
|
||||
const double ymax=transformY(parent->getYAxis()->getMax());
|
||||
const auto symType=getSymbolType();
|
||||
const double symbolSize=parent->pt2px(painter, getSymbolSize());
|
||||
const QMarginsF clipMargins=(symType==JKQTPNoSymbol)?QMarginsF(0,0,0,0):QMarginsF(symbolSize,symbolSize,symbolSize,symbolSize);
|
||||
const QRectF cliprect=QRectF(qMin(xmin,xmax),qMin(ymin,ymax),fabs(xmax-xmin),fabs(ymax-ymin))+clipMargins;
|
||||
|
||||
int imax=0;
|
||||
int imin=0;
|
||||
@ -135,18 +143,17 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
if (getIndexRange(imin, imax)) {
|
||||
|
||||
|
||||
QPainterPath pl, pf;
|
||||
QVector<QPointF> ps;
|
||||
QPolygonF pl, pf;
|
||||
QList<QPointF> ps;
|
||||
|
||||
double xold=-1;
|
||||
double yold=-1;
|
||||
double y0=transformY(getBaseline());
|
||||
if (parent->getYAxis()->isLogAxis()) {
|
||||
y0=transformY(parent->getYAxis()->getMin());
|
||||
if (getBaseline()>0 && getBaseline()>parent->getYAxis()->getMin()) y0=transformY(getBaseline());
|
||||
else y0=transformY(parent->getYAxis()->getMin());
|
||||
}
|
||||
bool subsequentItem=false;
|
||||
bool firstPoint=true;
|
||||
intSortData();
|
||||
for (int iii=imin; iii<imax; iii++) {
|
||||
const int i=qBound(imin, getDataIndex(iii), imax);
|
||||
@ -158,7 +165,7 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
const double y=transformY(yv);
|
||||
if (JKQTPIsOKFloat(x) && JKQTPIsOKFloat(y)) {
|
||||
ps.append(QPointF(x,y));
|
||||
if (subsequentItem) {
|
||||
if (!firstPoint) {
|
||||
//double xl1=xold;
|
||||
//double yl1=yold;
|
||||
//double xl2=x;
|
||||
@ -171,13 +178,13 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// *--------|
|
||||
// xold/yold
|
||||
const double d=(x-xold);
|
||||
pf.lineTo(xold+d/2.0, yold);
|
||||
pf.lineTo(xold+d/2.0, y);
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(xold+d/2.0, yold);
|
||||
pf<<QPointF(xold+d/2.0, y);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(xold+d/2.0, yold);
|
||||
pl.lineTo(xold+d/2.0, y);
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(xold+d/2.0, yold);
|
||||
pl<<QPointF(xold+d/2.0, y);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPStepLeft) {
|
||||
// x/y
|
||||
@ -185,11 +192,11 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// |
|
||||
// *
|
||||
// xold/yold
|
||||
pf.lineTo(xold, y);
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(xold, y);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(xold, y);
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(xold, y);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPStepRight) {
|
||||
// x/y
|
||||
@ -197,11 +204,11 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// |
|
||||
// *----------------|
|
||||
// xold/yold
|
||||
pf.lineTo(x, yold);
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(x, yold);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(x, yold);
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(x, yold);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPStepAverage) {
|
||||
// x/y
|
||||
@ -213,13 +220,13 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// xold/yold
|
||||
//const double d=(x-xold);
|
||||
const double h=(y-yold);
|
||||
pf.lineTo(xold, yold+h/2.0);
|
||||
pf.lineTo(x, yold+h/2.0);
|
||||
pf.lineTo(x,y);
|
||||
pf<<QPointF(xold, yold+h/2.0);
|
||||
pf<<QPointF(x, yold+h/2.0);
|
||||
pf<<QPointF(x,y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(xold, yold+h/2.0);
|
||||
pl.lineTo(x, yold+h/2.0);
|
||||
pl.lineTo(x,y);
|
||||
pl<<QPointF(xold, yold+h/2.0);
|
||||
pl<<QPointF(x, yold+h/2.0);
|
||||
pl<<QPointF(x,y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPDirectLine) {
|
||||
// x/y
|
||||
@ -227,54 +234,64 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// /----/
|
||||
// *----/
|
||||
// xold/yold
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout<<"line ("<<xl1<<", "<<yl1<<") -- ("<<xl2<<", "<<yl2<<")"<<std::endl;
|
||||
} else {
|
||||
if (getDrawLine()) pl.moveTo(x,y);
|
||||
pf.moveTo(x, y0);
|
||||
pf.lineTo(x, y);
|
||||
if (getDrawLine()) pl<<QPointF(x,y);
|
||||
pf<<QPointF(x, y0);
|
||||
pf<<QPointF(x, y);
|
||||
//xstart=x;
|
||||
//ystart=y0;
|
||||
}
|
||||
xold=x;
|
||||
yold=y;
|
||||
subsequentItem=true;
|
||||
firstPoint=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getFillCurve()) {
|
||||
pf.lineTo(xold, y0);
|
||||
pf.closeSubpath();
|
||||
pf<<QPointF(xold, y0);
|
||||
}
|
||||
painter.save();
|
||||
auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
||||
|
||||
if (getFillCurve()) {
|
||||
painter.fillPath(pf, b);
|
||||
painter.setBrush(b);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawPolygon(pf.intersected(cliprect));
|
||||
}
|
||||
|
||||
QList<QList<QPointF>> pl_fordrawing;
|
||||
if (isHighlighted() || getDrawLine()) {
|
||||
pl_fordrawing=JKQTPClipPolyLine(pl, cliprect);
|
||||
}
|
||||
|
||||
if (isHighlighted()) {
|
||||
painter.setBrush(QBrush(Qt::transparent));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(ph);
|
||||
painter.drawPath(pl);
|
||||
for (const auto &lines : pl_fordrawing) {
|
||||
painter.drawPolyline(lines);
|
||||
}
|
||||
}
|
||||
|
||||
if (getDrawLine()) {
|
||||
painter.setBrush(QBrush(Qt::transparent));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(p);
|
||||
painter.drawPath(pl);
|
||||
for (const auto &lines : pl_fordrawing) {
|
||||
painter.drawPolyline(lines);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_drawSymbols) {
|
||||
painter.save();
|
||||
auto __finalpaintsym=JKQTPFinally([&painter]() {painter.restore();});
|
||||
for (auto& ppoint: ps) {
|
||||
plotStyledSymbol(parent, painter, ppoint.x(), ppoint.y());
|
||||
if (cliprect.contains(ppoint)) plotStyledSymbol(parent, painter, ppoint.x(), ppoint.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -317,6 +334,14 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
QPen ph=getHighlightingLinePen(painter, parent);
|
||||
QPen np(Qt::NoPen);
|
||||
QBrush b=getFillBrush(painter, parent);
|
||||
const double xmin=transformX(parent->getXAxis()->getMin());
|
||||
const double xmax=transformX(parent->getXAxis()->getMax());
|
||||
const double ymin=transformY(parent->getYAxis()->getMin());
|
||||
const double ymax=transformY(parent->getYAxis()->getMax());
|
||||
const auto symType=getSymbolType();
|
||||
const double symbolSize=parent->pt2px(painter, getSymbolSize());
|
||||
const QMarginsF clipMargins=(symType==JKQTPNoSymbol)?QMarginsF(0,0,0,0):QMarginsF(symbolSize,symbolSize,symbolSize,symbolSize);
|
||||
const QRectF cliprect=QRectF(qMin(xmin,xmax),qMin(ymin,ymax),fabs(xmax-xmin),fabs(ymax-ymin))+clipMargins;
|
||||
|
||||
int imax=0;
|
||||
int imin=0;
|
||||
@ -324,8 +349,8 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
if (getIndexRange(imin, imax)) {
|
||||
|
||||
|
||||
QPainterPath pl, pf;
|
||||
QVector<QPointF> ps;
|
||||
QPolygonF pl, pf;
|
||||
QList<QPointF> ps;
|
||||
|
||||
double xold=-1;
|
||||
double yold=-1;
|
||||
@ -354,27 +379,27 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
|
||||
if (m_specialLineType==JKQTPStepCenter) {
|
||||
double d=(y-yold);
|
||||
pf.lineTo(xold, yold+d/2.0);
|
||||
pf.lineTo(x, yold+d/2.0);
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(xold, yold+d/2.0);
|
||||
pf<<QPointF(x, yold+d/2.0);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(xold, yold+d/2.0);
|
||||
pl.lineTo(x, yold+d/2.0);
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(xold, yold+d/2.0);
|
||||
pl<<QPointF(x, yold+d/2.0);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPStepLeft) {
|
||||
pf.lineTo(x, yold);
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(x, yold);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(x, yold);
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(x, yold);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPStepRight) {
|
||||
pf.lineTo(xold, y);
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(xold, y);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(xold, y);
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(xold, y);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPStepAverage) {
|
||||
// x/y
|
||||
@ -386,13 +411,13 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// xold/yold
|
||||
const double d=(x-xold);
|
||||
//const double h=(y-yold);
|
||||
pf.lineTo(xold+d/2.0, yold);
|
||||
pf.lineTo(xold+d/2.0, y);
|
||||
pf.lineTo(x,y);
|
||||
pf<<QPointF(xold+d/2.0, yold);
|
||||
pf<<QPointF(xold+d/2.0, y);
|
||||
pf<<QPointF(x,y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(xold+d/2.0, yold);
|
||||
pl.lineTo(xold+d/2.0, y);
|
||||
pl.lineTo(x,y);
|
||||
pl<<QPointF(xold+d/2.0, yold);
|
||||
pl<<QPointF(xold+d/2.0, y);
|
||||
pl<<QPointF(x,y);
|
||||
}
|
||||
} else if (m_specialLineType==JKQTPDirectLine) {
|
||||
// x/y
|
||||
@ -400,17 +425,17 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
// /----/
|
||||
// *----/
|
||||
// xold/yold
|
||||
pf.lineTo(x, y);
|
||||
pf<<QPointF(x, y);
|
||||
if (getDrawLine()) {
|
||||
pl.lineTo(x, y);
|
||||
pl<<QPointF(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout<<"line ("<<xl1<<", "<<yl1<<") -- ("<<xl2<<", "<<yl2<<")"<<std::endl;
|
||||
} else {
|
||||
if (getDrawLine()) pl.moveTo(x,y);
|
||||
pf.moveTo(x0, y);
|
||||
pf.lineTo(x, y);
|
||||
if (getDrawLine()) pl<<QPointF(x,y);
|
||||
pf<<QPointF(x0, y);
|
||||
pf<<QPointF(x, y);
|
||||
}
|
||||
xold=x;
|
||||
yold=y;
|
||||
@ -418,31 +443,41 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
|
||||
}
|
||||
}
|
||||
}
|
||||
pf.lineTo(x0, yold);
|
||||
pf.closeSubpath();
|
||||
pf<<QPointF(x0, yold);
|
||||
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
||||
|
||||
if (getFillCurve()) {
|
||||
painter.fillPath(pf, b);
|
||||
painter.setBrush(b);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawPolygon(pf.intersected(cliprect));
|
||||
}
|
||||
|
||||
QList<QList<QPointF>> pl_fordrawing;
|
||||
if (isHighlighted() || getDrawLine()) {
|
||||
pl_fordrawing=JKQTPClipPolyLine(pl, cliprect);
|
||||
}
|
||||
|
||||
if (isHighlighted()) {
|
||||
painter.setBrush(QBrush(Qt::transparent));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(ph);
|
||||
painter.drawPath(pl);
|
||||
for (const auto &lines : pl_fordrawing) {
|
||||
painter.drawPolyline(lines);
|
||||
}
|
||||
}
|
||||
|
||||
if (getDrawLine()) {
|
||||
painter.setBrush(QBrush(Qt::transparent));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(p);
|
||||
painter.drawPath(pl);
|
||||
for (const auto &lines : pl_fordrawing) {
|
||||
painter.drawPolyline(lines);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_drawSymbols) {
|
||||
painter.save();
|
||||
auto __finalpaintsym=JKQTPFinally([&painter]() {painter.restore();});
|
||||
for (auto& point: ps) {
|
||||
plotStyledSymbol(parent, painter, point.x(), point.y());
|
||||
for (auto& ppoint: ps) {
|
||||
if (cliprect.contains(ppoint)) plotStyledSymbol(parent, painter, ppoint.x(), ppoint.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,6 +272,13 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
if (!visX&&!visY) return;
|
||||
//std::cout<<" JKQTPGraphErrors::intPlotXYErrorIndicators(p, "<<parent<<", "<<xColumn<<", "<<yColumn<<", "<<xErrorColumn<<", "<<yErrorColumn<<", ...)\n";
|
||||
|
||||
const double xmin=parentGraph->transformX(parent->getXAxis()->getMin());
|
||||
const double xmax=parentGraph->transformX(parent->getXAxis()->getMax());
|
||||
const double ymin=parentGraph->transformY(parent->getYAxis()->getMin());
|
||||
const double ymax=parentGraph->transformY(parent->getYAxis()->getMax());
|
||||
const QMarginsF clipMargins(50,50,50,50);
|
||||
const QRectF cliprect=QRectF(qMin(xmin,xmax),qMin(ymin,ymax),fabs(xmax-xmin),fabs(ymax-ymin))+clipMargins;
|
||||
|
||||
QBrush b=getErrorFillBrush(painter, parent);
|
||||
QPen p=getErrorLinePen(painter, parent);
|
||||
QPen pr=getErrorLinePenForRects(painter, parent);
|
||||
@ -394,28 +401,32 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
QPen pp=p;
|
||||
if (!defaultErrorColor) pp.setColor(terrCol);
|
||||
painter.setPen(pp);
|
||||
QList<QLineF> elines;
|
||||
if (x0ok&&x1ok&&xok&&yok) {
|
||||
painter.drawLine(QLineF(x0, y, x1, y));
|
||||
elines<<QLineF(x0, y, x1, y);
|
||||
if (xErrorStyle==JKQTPErrorBars || xErrorStyle==JKQTPErrorBarsLines|| xErrorStyle==JKQTPErrorBarsPolygons) {
|
||||
if (plotlowerbarx) painter.drawLine(QLineF(x0,y-ebs_px/2.0,x0,y+ebs_px/2.0));
|
||||
if (plotupperbarx) painter.drawLine(QLineF(x1,y-ebs_px/2.0,x1,y+ebs_px/2.0));
|
||||
if (plotlowerbarx) elines<<QLineF(x0,y-ebs_px/2.0,x0,y+ebs_px/2.0);
|
||||
if (plotupperbarx) elines<<QLineF(x1,y-ebs_px/2.0,x1,y+ebs_px/2.0);
|
||||
}
|
||||
} else if (x0ok&&!x1ok&&xok&&yok) {
|
||||
painter.drawLine(QLineF(x0, y, x, y));
|
||||
elines<<QLineF(x0, y, x, y);
|
||||
if (xErrorStyle==JKQTPErrorBars || xErrorStyle==JKQTPErrorBarsLines|| xErrorStyle==JKQTPErrorBarsPolygons) {
|
||||
if (plotlowerbarx) painter.drawLine(QLineF(x0,y-ebs_px/2.0,x0,y+ebs_px/2.0));
|
||||
if (plotlowerbarx) elines<<QLineF(x0,y-ebs_px/2.0,x0,y+ebs_px/2.0);
|
||||
}
|
||||
if (x0<x) painter.drawLine(QLineF(x,y,parentGraph->transformX(parent->getXMax()),y));
|
||||
else painter.drawLine(QLineF(x,y,parentGraph->transformX(parent->getXMin()),y));
|
||||
if (x0<x) elines<<QLineF(x,y,parentGraph->transformX(parent->getXMax()),y);
|
||||
else elines<<QLineF(x,y,parentGraph->transformX(parent->getXMin()),y);
|
||||
} else if (!x0ok&&x1ok&&xok&&yok) {
|
||||
painter.drawLine(QLineF(x1, y, x, y));
|
||||
elines<<QLineF(x1, y, x, y);
|
||||
if (xErrorStyle==JKQTPErrorBars || xErrorStyle==JKQTPErrorBarsLines|| xErrorStyle==JKQTPErrorBarsPolygons) {
|
||||
if (plotupperbarx) painter.drawLine(QLineF(x1,y-ebs_px/2.0,x1,y+ebs_px/2.0));
|
||||
if (plotupperbarx) elines<<QLineF(x1,y-ebs_px/2.0,x1,y+ebs_px/2.0);
|
||||
}
|
||||
if (x1<x) painter.drawLine(QLineF(x,y,parentGraph->transformX(parent->getXMin()),y));
|
||||
else painter.drawLine(QLineF(x,y,parentGraph->transformX(parent->getXMax()),y));
|
||||
if (x1<x) elines<<QLineF(x,y,parentGraph->transformX(parent->getXMin()),y);
|
||||
else elines<<QLineF(x,y,parentGraph->transformX(parent->getXMax()),y);
|
||||
}
|
||||
for (QLineF& l: elines) {
|
||||
l=JKQTPClipLine(l, cliprect);
|
||||
if (l.length()>0) painter.drawLine(l);
|
||||
}
|
||||
|
||||
}
|
||||
// y-errorbars
|
||||
if ((yErrorColumn>=0 || yErrorColumnLower>=0) && (yErrorStyle==JKQTPErrorBars || yErrorStyle==JKQTPErrorBarsLines || yErrorStyle==JKQTPErrorBarsPolygons
|
||||
@ -426,29 +437,32 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
QPen pp=p;
|
||||
if (!defaultErrorColor) pp.setColor(terrCol);
|
||||
painter.setPen(pp);
|
||||
QList<QLineF> elines;
|
||||
if (y0ok&&y1ok&&xok&&yok) {
|
||||
painter.drawLine(QLineF(x, y0, x, y1));
|
||||
elines<<QLineF(x, y0, x, y1);
|
||||
if (yErrorStyle==JKQTPErrorBars || yErrorStyle==JKQTPErrorBarsLines || yErrorStyle==JKQTPErrorBarsPolygons) {
|
||||
if (plotlowerbary) painter.drawLine(QLineF(x-ebs_px/2.0,y0,x+ebs_px/2.0,y0));
|
||||
if (plotupperbary) painter.drawLine(QLineF(x-ebs_px/2.0,y1,x+ebs_px/2.0,y1));
|
||||
if (plotlowerbary) elines<<QLineF(x-ebs_px/2.0,y0,x+ebs_px/2.0,y0);
|
||||
if (plotupperbary) elines<<QLineF(x-ebs_px/2.0,y1,x+ebs_px/2.0,y1);
|
||||
}
|
||||
} else if (y0ok&&!y1ok&&xok&&yok) { // upper errorbar OK, lower errorbar NAN
|
||||
painter.drawLine(QLineF(x, y0, x, y));
|
||||
elines<<QLineF(x, y0, x, y);
|
||||
if (yErrorStyle==JKQTPErrorBars || yErrorStyle==JKQTPErrorBarsLines || yErrorStyle==JKQTPErrorBarsPolygons) {
|
||||
if (plotlowerbary) painter.drawLine(QLineF(x-ebs_px/2.0,y0,x+ebs_px/2.0,y0));
|
||||
if (plotlowerbary) elines<<QLineF(x-ebs_px/2.0,y0,x+ebs_px/2.0,y0);
|
||||
}
|
||||
if (y0<y) painter.drawLine(QLineF(x,y,x,parentGraph->transformY(parent->getYMin())));
|
||||
else painter.drawLine(QLineF(x,y,x,parentGraph->transformY(parent->getYMax()))); // inverted axis!
|
||||
if (y0<y) elines<<QLineF(x,y,x,parentGraph->transformY(parent->getYMin()));
|
||||
else elines<<QLineF(x,y,x,parentGraph->transformY(parent->getYMax())); // inverted axis!
|
||||
} else if (!y0ok&&y1ok&&xok&&yok) {
|
||||
painter.drawLine(QLineF(x, y1, x, y));
|
||||
elines<<QLineF(x, y1, x, y);
|
||||
if (yErrorStyle==JKQTPErrorBars || yErrorStyle==JKQTPErrorBarsLines || yErrorStyle==JKQTPErrorBarsPolygons) {
|
||||
if (plotupperbary) painter.drawLine(QLineF(x-ebs_px/2.0,y1,x+ebs_px/2.0,y1));
|
||||
if (plotupperbary) elines<<QLineF(x-ebs_px/2.0,y1,x+ebs_px/2.0,y1);
|
||||
}
|
||||
if (y1<y) painter.drawLine(QLineF(x,y,x,parentGraph->transformY(parent->getYMax())));
|
||||
else painter.drawLine(QLineF(x,y,x,parentGraph->transformY(parent->getYMin())));
|
||||
if (y1<y) elines<<QLineF(x,y,x,parentGraph->transformY(parent->getYMax()));
|
||||
else elines<<QLineF(x,y,x,parentGraph->transformY(parent->getYMin()));
|
||||
}
|
||||
for (QLineF& l: elines) {
|
||||
l=JKQTPClipLine(l, cliprect);
|
||||
if (l.length()>0) painter.drawLine(l);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// error boxes
|
||||
@ -465,8 +479,8 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
if (!defaultErrorColor) bb.setColor(terrFillCol);
|
||||
painter.setBrush(bb);
|
||||
|
||||
QRectF errRect=QRectF(QPointF(x0,y0), QPointF(x1,y1));
|
||||
if ((y0ok&&y1ok)||(x0ok&&x1ok)) {
|
||||
const QRectF errRect=QRectF(QPointF(x0,y0), QPointF(x1,y1));
|
||||
if (((y0ok&&y1ok)||(x0ok&&x1ok))&&cliprect.intersects(errRect)) {
|
||||
if (yErrorStyle==JKQTPErrorEllipses || xErrorStyle==JKQTPErrorEllipses) painter.drawEllipse(errRect);
|
||||
else painter.drawRect(errRect);
|
||||
}
|
||||
@ -487,10 +501,12 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
if (!defaultErrorColor) pp.setColor(terrCol);
|
||||
painter.setPen(pp);
|
||||
if (JKQTPIsOKFloat(xl1m)&&JKQTPIsOKFloat(yl1)&&JKQTPIsOKFloat(xl2m)&&JKQTPIsOKFloat(yl2)) {
|
||||
painter.drawLine(QLineF(xl1m, yl1, xl2m, yl2));
|
||||
const QLineF l=JKQTPClipLine(QLineF(xl1m, yl1, xl2m, yl2),cliprect);
|
||||
if (l.length()>0) painter.drawLine(l);
|
||||
}
|
||||
if (JKQTPIsOKFloat(xl1p)&&JKQTPIsOKFloat(yl1)&&JKQTPIsOKFloat(xl2p)&&JKQTPIsOKFloat(yl2)) {
|
||||
painter.drawLine(QLineF(xl1p, yl1, xl2p, yl2));
|
||||
const QLineF l=JKQTPClipLine(QLineF(xl1p, yl1, xl2p, yl2),cliprect);
|
||||
if (l.length()>0) painter.drawLine(l);
|
||||
}
|
||||
|
||||
}
|
||||
@ -509,10 +525,12 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
if (!defaultErrorColor) pp.setColor(terrCol);
|
||||
painter.setPen(pp);
|
||||
if (JKQTPIsOKFloat(xl1)&&JKQTPIsOKFloat(yl1m)&&JKQTPIsOKFloat(xl2)&&JKQTPIsOKFloat(yl2m)) {
|
||||
painter.drawLine(QLineF(xl1, yl1m, xl2, yl2m));
|
||||
const QLineF l=JKQTPClipLine(QLineF(xl1, yl1m, xl2, yl2m),cliprect);
|
||||
if (l.length()>0) painter.drawLine(l);
|
||||
}
|
||||
if (JKQTPIsOKFloat(xl1)&&JKQTPIsOKFloat(yl1p)&&JKQTPIsOKFloat(xl2)&&JKQTPIsOKFloat(yl2p)) {
|
||||
painter.drawLine(QLineF(xl1, yl1p, xl2, yl2p));
|
||||
const QLineF l=JKQTPClipLine(QLineF(xl1, yl1p, xl2, yl2p),cliprect);
|
||||
if (l.length()>0) painter.drawLine(l);
|
||||
}
|
||||
|
||||
}
|
||||
@ -541,7 +559,7 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
for (int i=polyXBottomPoints.size()-1; i>=0; i--) {
|
||||
poly<<polyXBottomPoints[i];
|
||||
}
|
||||
painter.drawConvexPolygon(poly);
|
||||
painter.drawConvexPolygon(poly.intersected(cliprect));
|
||||
|
||||
}
|
||||
if ((polyYTopPoints.size()>0 || polyYBottomPoints.size()>0) && (yErrorStyle==JKQTPErrorPolygons || yErrorStyle==JKQTPErrorBarsPolygons || yErrorStyle==JKQTPErrorSimpleBarsPolygons)) {
|
||||
@ -556,7 +574,8 @@ void JKQTPGraphErrorStyleMixin::intPlotXYErrorIndicators(JKQTPEnhancedPainter& p
|
||||
for (int i=polyYBottomPoints.size()-1; i>=0; i--) {
|
||||
poly<<polyYBottomPoints[i];
|
||||
}
|
||||
painter.drawConvexPolygon(poly);
|
||||
|
||||
painter.drawConvexPolygon(poly.intersected(cliprect));
|
||||
|
||||
}
|
||||
//std::cout<<"end\n";
|
||||
|
@ -155,6 +155,38 @@ QPen JKQTPGraphLineStyleMixin::getLinePenForRects(JKQTPEnhancedPainter &painter,
|
||||
|
||||
|
||||
|
||||
JKQTPGraphLinesCompressionMixin::JKQTPGraphLinesCompressionMixin():
|
||||
m_useNonvisibleLineCompression(true),
|
||||
m_nonvisibleLineCompressionAgressiveness(1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JKQTPGraphLinesCompressionMixin::~JKQTPGraphLinesCompressionMixin()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void JKQTPGraphLinesCompressionMixin::setUseNonvisibleLineCompression(bool _useNonvisibleLineCompression)
|
||||
{
|
||||
m_useNonvisibleLineCompression=_useNonvisibleLineCompression;
|
||||
}
|
||||
|
||||
bool JKQTPGraphLinesCompressionMixin::getUseNonvisibleLineCompression() const
|
||||
{
|
||||
return m_useNonvisibleLineCompression;
|
||||
}
|
||||
|
||||
void JKQTPGraphLinesCompressionMixin::setNonvisibleLineCompressionAgressiveness(double Agressiveness)
|
||||
{
|
||||
m_nonvisibleLineCompressionAgressiveness=Agressiveness;
|
||||
}
|
||||
|
||||
double JKQTPGraphLinesCompressionMixin::getNonvisibleLineCompressionAgressiveness() const
|
||||
{
|
||||
return m_nonvisibleLineCompressionAgressiveness;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -47,7 +47,9 @@ class JKQTPlotter; // forward
|
||||
.
|
||||
*/
|
||||
class JKQTPLOTTER_LIB_EXPORT JKQTPGraphLineStyleMixin {
|
||||
#ifndef JKQTPLOTTER_WORKAROUND_QGADGET_BUG
|
||||
Q_GADGET
|
||||
#endif
|
||||
public:
|
||||
/** \brief class constructor */
|
||||
JKQTPGraphLineStyleMixin();
|
||||
@ -145,6 +147,76 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPGraphLineStyleMixin {
|
||||
|
||||
|
||||
|
||||
/*! \brief This Mix-In class provides setter/getter methods, storage and other facilities for a line-graph compression algorithm
|
||||
\ingroup jkqtplotter_basegraphs_stylemixins
|
||||
|
||||
supported properties:
|
||||
- activate compression (improves plotting speed, but decreases detail level)
|
||||
- level of retained details
|
||||
.
|
||||
|
||||
\image html JKQTPSimplifyPolyLines_agressive.png
|
||||
*/
|
||||
class JKQTPLOTTER_LIB_EXPORT JKQTPGraphLinesCompressionMixin {
|
||||
#ifndef JKQTPLOTTER_WORKAROUND_QGADGET_BUG
|
||||
Q_GADGET
|
||||
#endif
|
||||
public:
|
||||
/** \brief class constructor */
|
||||
JKQTPGraphLinesCompressionMixin();
|
||||
|
||||
virtual ~JKQTPGraphLinesCompressionMixin();
|
||||
|
||||
|
||||
/** \copydoc useNonvisibleLineCompression */
|
||||
void setUseNonvisibleLineCompression(bool _useNonvisibleLineCompression);
|
||||
/** \copydoc useNonvisibleLineCompression */
|
||||
bool getUseNonvisibleLineCompression() const;
|
||||
/** \copydoc useNonvisibleLineCompression */
|
||||
void setNonvisibleLineCompressionAgressiveness(double Agressiveness);
|
||||
/** \copydoc useNonvisibleLineCompression */
|
||||
double getNonvisibleLineCompressionAgressiveness() const;
|
||||
|
||||
|
||||
#ifndef JKQTPLOTTER_WORKAROUND_QGADGET_BUG
|
||||
Q_PROPERTY(bool useNonvisibleLineCompression MEMBER useNonvisibleLineCompression READ getUseNonvisibleLineCompression WRITE setUseNonvisibleLineCompression)
|
||||
Q_PROPERTY(double nonvisibleLineCompressionAgressiveness MEMBER nonvisibleLineCompressionAgressiveness READ getNonvisibleLineCompressionAgressiveness WRITE setNonvisibleLineCompressionAgressiveness)
|
||||
#endif
|
||||
private:
|
||||
/** \brief use an optimization algorithm that tries to reduce the number of lines that
|
||||
* overlap each other (i.e. for noisy data or a low zoom) and thus improves
|
||||
* drawing speed
|
||||
*
|
||||
* When the property useNonvisibleLineCompression is activated (\c true ), the graph class
|
||||
* uses the algorithm implemented in JKQTPSimplifyPolyLines() to simplify the task of plotting.
|
||||
*
|
||||
* \image html JKQTPSimplifyPolyLines.png
|
||||
*
|
||||
* \note This option is designed to not alter the plot representation significantly,
|
||||
* but of course it may ...
|
||||
*
|
||||
* \see JKQTPSimplifyPolyLines() setUseNonvisibleLineCompression(), getUseNonvisibleLineCompression()
|
||||
*/
|
||||
bool m_useNonvisibleLineCompression;
|
||||
|
||||
/** \brief this sets the agressiveness of the option useNonvisibleLineCompression
|
||||
*
|
||||
* Basically the compressed groups will have a size of nonvisibleLineCompressionAgressiveness*pen.linewidth
|
||||
*
|
||||
* The default setting is \c 1.0 , larger settings will lead to better compression (and faster plotting), but less detailed
|
||||
* plots, whereas smaller settings will increase the detail-level, but also increase plotting time.
|
||||
*
|
||||
* \image html JKQTPSimplifyPolyLines_agressive.png
|
||||
*
|
||||
* \note This option is designed to not alter the plot representation significantly,
|
||||
* but of course it may ...
|
||||
*
|
||||
* \see JKQTPSimplifyPolyLines() setUseNonvisibleLineCompression(), getUseNonvisibleLineCompression()
|
||||
*/
|
||||
double m_nonvisibleLineCompressionAgressiveness;
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user