diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox
index 913d067f1b..593dd53f1b 100644
--- a/doc/dox/whatsnew.dox
+++ b/doc/dox/whatsnew.dox
@@ -33,6 +33,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
NEW/BREAKING CHANGE: changed JKQTPColorDerivationMode into a struct, which extends its capabilities above the previously available few enum-items
NEW: added debug-feature to show boxes around text in the plot
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 #81, many thanks to user:sufuk for contributing part of the code and supplying the idea!
+ NEW: improved plotting speed for line-graphs by drawing a series of single lines instead of a polyline in some cases
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)
diff --git a/examples/speed/speedtestplot.cpp b/examples/speed/speedtestplot.cpp
index e1269889d5..5487c12034 100644
--- a/examples/speed/speedtestplot.cpp
+++ b/examples/speed/speedtestplot.cpp
@@ -41,13 +41,16 @@ SpeedTestPlot::SpeedTestPlot():
graph->setYColumn(columnY);
graph->setTitle(QObject::tr("live sin() graph"));
graph->setLineWidth(1);
+ graph->setSymbolType(JKQTPNoSymbol);
addGraph(graph);
graph2=new JKQTPXYLineGraph(this);
graph2->setXColumn(columnX);
graph2->setYColumn(columnY2);
graph2->setTitle(QObject::tr("live cos() graph"));
- graph2->setLineWidth(1);
+ graph2->setLineWidth(2);
+ graph2->setSymbolType(JKQTPNoSymbol);
+
addGraph(graph2);
// 6. scale the plot so the graph is contained
@@ -87,7 +90,7 @@ SpeedTestPlot::SpeedTestPlot():
actSymbols=new QAction(QObject::tr("Show Graph Symbols"));
actSymbols->setCheckable(true);
- actSymbols->setChecked(true);
+ actSymbols->setChecked(false);
connect(actSymbols, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2,SpeedTestPlot* p){
g->setSymbolType(enabled?JKQTPCross:JKQTPNoSymbol);
g2->setSymbolType(enabled?JKQTPCircle:JKQTPNoSymbol);
diff --git a/lib/jkqtcommon/jkqtpenhancedpainter.cpp b/lib/jkqtcommon/jkqtpenhancedpainter.cpp
index 36a3b4bdb4..d37ea77023 100644
--- a/lib/jkqtcommon/jkqtpenhancedpainter.cpp
+++ b/lib/jkqtcommon/jkqtpenhancedpainter.cpp
@@ -23,7 +23,8 @@
JKQTPEnhancedPainter::JKQTPEnhancedPainter(QPaintDevice *device):
- QPainter(device)
+ QPainter(device),
+ m_flags(DefaultPaintMode)
{
initQEnhacedPainter();
}
@@ -34,6 +35,51 @@ JKQTPEnhancedPainter::JKQTPEnhancedPainter():
initQEnhacedPainter();
}
+void JKQTPEnhancedPainter::setPainterFlag(PainterFlag flag, bool enabled)
+{
+ if (!enabled && m_flags.testFlag(flag)) m_flags &= ~flag;
+ else if (enabled && !m_flags.testFlag(flag)) m_flags |= flag;
+}
+
+void JKQTPEnhancedPainter::setPainterFlag(PainterFlags flags)
+{
+ m_flags=flags;
+}
+
+void JKQTPEnhancedPainter::drawPolylineFast(const QPointF *points, int pointCount)
+{
+ if (!points || pointCount<2) return;
+ if (m_flags.testFlag(VectorPainting) || pen().style()!=Qt::SolidLine || pen().widthF()>device()->devicePixelRatioF()+0.01 || pen().widthF()devicePixelRatioF()*0.8) {
+ //qDebug()<<"drawFastPolyline(): widthF="<0) drawLine(l);
+ }
+ }
+}
+
+void JKQTPEnhancedPainter::drawPolylineFast(const QPoint *points, int pointCount)
+{
+ if (!points || pointCount<2) return;
+ if (m_flags.testFlag(VectorPainting) || pen().style()!=Qt::SolidLine || pen().widthF()>1.01) {
+ drawPolyline(points, pointCount);
+ } else {
+ // we use a specialized algorithm only in a very defined circumstances
+ for (int i=1; i0||l.dy()>0)) drawLine(l);
+ }
+ }
+}
+
+JKQTPEnhancedPainter::PainterFlags JKQTPEnhancedPainter::painterFlags() const {
+ return m_flags;
+}
+
void JKQTPEnhancedPainter::initQEnhacedPainter()
{
diff --git a/lib/jkqtcommon/jkqtpenhancedpainter.h b/lib/jkqtcommon/jkqtpenhancedpainter.h
index 57146bb919..28f3f230e5 100644
--- a/lib/jkqtcommon/jkqtpenhancedpainter.h
+++ b/lib/jkqtcommon/jkqtpenhancedpainter.h
@@ -32,17 +32,46 @@
*/
class JKQTCOMMON_LIB_EXPORT JKQTPEnhancedPainter : public QPainter {
public:
+
+ enum PainterFlag { DefaultPaintMode = 0x00, /*!< \brief the default mode, the JKQTPEnhancedPainter draws on a pixel-device */
+ VectorPainting = 0x01, /*!< \brief if set, the JKQTPEnhancedPainter draws onto a vector-device, like a printer, PDF or SVG-output */
+ };
+ Q_ENUMS(PainterFlag)
+ Q_FLAGS(PainterFlags)
+ Q_DECLARE_FLAGS(PainterFlags, PainterFlag)
+
JKQTPEnhancedPainter(QPaintDevice* device);
JKQTPEnhancedPainter();
+ /** \copydoc m_flags */
+ PainterFlags painterFlags() const ;
+ /** \copydoc m_flags */
+ void setPainterFlag(PainterFlag flag, bool enabled=true);
+ /** \copydoc m_flags */
+ void setPainterFlag(PainterFlags flags);
+ /** \brief faster variant of QPainter::drawPolyline(),it turns out that drawing single lines is way faster than drawing with drawPolyLine()
+ *
+ * At least for thin (1 Pixel) lines on non-vector-devices, this does not make much difference in appearance
+ */
+ void drawPolylineFast(const QPointF *points, int pointCount);
+ inline void drawPolylineFast(const QPolygonF &polyline) {
+ drawPolylineFast(polyline.constData(), int(polyline.size()));
+ }
+ void drawPolylineFast(const QPoint *points, int pointCount);
+ inline void drawPolylineFast(const QPolygon &polyline) {
+ drawPolylineFast(polyline.constData(), int(polyline.size()));
+ }
protected:
void initQEnhacedPainter();
private:
-
+ /** \brief flags, specifying how the JKQTPEnhancedPainter works
+ *
+ * \see PainterFlags */
+ PainterFlags m_flags;
};
-
+Q_DECLARE_OPERATORS_FOR_FLAGS(JKQTPEnhancedPainter::PainterFlags)
diff --git a/lib/jkqtplotter/graphs/jkqtpcontour.cpp b/lib/jkqtplotter/graphs/jkqtpcontour.cpp
index f0604f4b43..09941ba60a 100644
--- a/lib/jkqtplotter/graphs/jkqtpcontour.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpcontour.cpp
@@ -126,7 +126,7 @@ void JKQTPContourPlot::draw(JKQTPEnhancedPainter &painter)
//qDebug()<(errorPlotFunction))) {
painter.save(); auto __finalpainterrline=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(ep);
- painter.drawPolyline(errorLineTop);
- painter.drawPolyline(errorLineBottom);
+ painter.drawPolylineFast(errorLineTop);
+ painter.drawPolylineFast(errorLineBottom);
}
@@ -425,14 +425,14 @@ void JKQTPEvaluatedFunctionWithErrorsGraphDrawingBase::drawYGraph(JKQTPEnhancedP
if (getDrawLine()) {
painter.save(); auto __finalpaintline=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(p);
- painter.drawPolyline(linePolygon);
+ painter.drawPolylineFast(linePolygon);
}
if (drawErrorLines && (static_cast(errorPlotFunction))) {
painter.save(); auto __finalpainterrline=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(ep);
- painter.drawPolyline(errorLineTop);
- painter.drawPolyline(errorLineBottom);
+ painter.drawPolylineFast(errorLineTop);
+ painter.drawPolylineFast(errorLineBottom);
}
diff --git a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp
index 5e045d59ba..b3b37cb3be 100644
--- a/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpevaluatedparametriccurve.cpp
@@ -108,7 +108,7 @@ void JKQTPXYFunctionLineGraphBase::draw(JKQTPEnhancedPainter& painter) {
{
painter.save(); auto __finalpaintline=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(p);
- painter.drawPolyline(data);
+ painter.drawPolylineFast(data);
}
diff --git a/lib/jkqtplotter/graphs/jkqtpfilledcurve.cpp b/lib/jkqtplotter/graphs/jkqtpfilledcurve.cpp
index 12744129fb..9c620d5f3d 100644
--- a/lib/jkqtplotter/graphs/jkqtpfilledcurve.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpfilledcurve.cpp
@@ -466,14 +466,14 @@ void JKQTPFilledVerticalRangeGraph::draw(JKQTPEnhancedPainter &painter)
if (isHighlighted()) {
painter.setPen(ps);
- painter.drawPolyline(phigh);
- painter.drawPolyline(plow);
+ painter.drawPolylineFast(phigh);
+ painter.drawPolylineFast(plow);
}
painter.setPen(p);
- painter.drawPolyline(phigh);
- painter.drawPolyline(plow);
+ painter.drawPolylineFast(phigh);
+ painter.drawPolylineFast(plow);
}
}
@@ -581,14 +581,14 @@ void JKQTPFilledHorizontalRangeGraph::draw(JKQTPEnhancedPainter &painter)
if (isHighlighted()) {
painter.setPen(ps);
- painter.drawPolyline(phigh);
- painter.drawPolyline(plow);
+ painter.drawPolylineFast(phigh);
+ painter.drawPolylineFast(plow);
}
painter.setPen(p);
- painter.drawPolyline(phigh);
- painter.drawPolyline(plow);
+ painter.drawPolylineFast(phigh);
+ painter.drawPolylineFast(plow);
}
}
diff --git a/lib/jkqtplotter/graphs/jkqtpgeolines.cpp b/lib/jkqtplotter/graphs/jkqtpgeolines.cpp
index a08d5d93c6..bfda70af91 100644
--- a/lib/jkqtplotter/graphs/jkqtpgeolines.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpgeolines.cpp
@@ -142,7 +142,7 @@ void JKQTPGeoLine::draw(JKQTPEnhancedPainter& painter) {
if (drawHead) JKQTPPlotLineDecorator(painter, xx2.x(), xx2.y(), angle2, getHeadDecoratorStyle(), calcHeadDecoratorSize(getLinePen(painter, getParent()).widthF()));//, &lx2);
//points[0]=lx1;
//points[points.size()-1]=lx2;
- painter.drawPolyline(points.data(), points.size());
+ painter.drawPolylineFast(points.data(), points.size());
/*for (auto& p: points) {
JKQTPPlotSymbol(painter, p.x(), p.y(), JKQTPPlus, 5, 1, QColor("green"), QColor("darkgreen"));
}*/
@@ -421,7 +421,7 @@ void JKQTPGeoInfiniteLine::draw(JKQTPEnhancedPainter& painter) {
xx1=points[0];
const QPointF xx1p=points[1];
angle1=atan2(xx1p.y()-xx1.y(), xx1p.x()-xx1.x());
- painter.drawPolyline(points.data(), points.size());
+ painter.drawPolylineFast(points.data(), points.size());
/*for (auto& p: points) {
JKQTPPlotSymbol(painter, p.x(), p.y(), JKQTPPlus, 5, 1, QColor("green"), QColor("darkgreen"));
}*/
@@ -592,7 +592,7 @@ void JKQTPGeoPolyLines::draw(JKQTPEnhancedPainter& painter) {
xx1=path[0];
xx2=path[path.size()-1];
// draw corrected line
- painter.drawPolyline(path.data(), path.size());
+ painter.drawPolylineFast(path.data(), path.size());
doDrawDecorator=true;
} else {
// for non-linear axes, a line might not be drawn as a line, so we need to segment the line (i.e. linear function in coordinate space)
@@ -607,7 +607,7 @@ void JKQTPGeoPolyLines::draw(JKQTPEnhancedPainter& painter) {
xx2=points_poly[points_poly.size()-1];
const QPointF xx2p=points_poly[points_poly.size()-2];
angle2=atan2(xx2p.y()-xx2.y(), xx2p.x()-xx2.x());
- painter.drawPolyline(points_poly.data(), points_poly.size());
+ painter.drawPolylineFast(points_poly.data(), points_poly.size());
doDrawDecorator=true;
/*for (auto& p: points_poly) {
JKQTPPlotSymbol(painter, p.x(), p.y(), JKQTPPlus, 5, 1, QColor("green"), QColor("darkgreen"));
@@ -719,7 +719,7 @@ void JKQTPGeoArc::draw(JKQTPEnhancedPainter& painter) {
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
- painter.drawPolyline(rect);
+ painter.drawPolylineFast(rect);
}
void JKQTPGeoArc::setAngleStart(double __value)
diff --git a/lib/jkqtplotter/graphs/jkqtplines.cpp b/lib/jkqtplotter/graphs/jkqtplines.cpp
index dbcb70b2bf..96fc337472 100644
--- a/lib/jkqtplotter/graphs/jkqtplines.cpp
+++ b/lib/jkqtplotter/graphs/jkqtplines.cpp
@@ -134,10 +134,10 @@ void JKQTPXYLineGraph::draw(JKQTPEnhancedPainter& painter) {
if (linesP.size()>0) {
if (isHighlighted()) {
painter.setPen(penSelection);
- painter.drawPolyline(linesP);
+ painter.drawPolylineFast(linesP);
}
painter.setPen(p);
- painter.drawPolyline(linesP);
+ painter.drawPolylineFast(linesP);
}
}
}
diff --git a/lib/jkqtplotter/graphs/jkqtpscatter.cpp b/lib/jkqtplotter/graphs/jkqtpscatter.cpp
index 5163de737f..93179d439d 100644
--- a/lib/jkqtplotter/graphs/jkqtpscatter.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpscatter.cpp
@@ -393,7 +393,7 @@ void JKQTPXYParametrizedScatterGraph::draw(JKQTPEnhancedPainter &painter)
} else {
pp.setColor(getHighlightingLineColor());
painter.setPen(pp);
- painter.drawPolyline(linesP);
+ painter.drawPolylineFast(linesP);
}
}
QPen pp=p;
@@ -407,7 +407,7 @@ void JKQTPXYParametrizedScatterGraph::draw(JKQTPEnhancedPainter &painter)
} else {
pp.setColor(getHighlightingLineColor());
painter.setPen(pp);
- painter.drawPolyline(linesP);
+ painter.drawPolylineFast(linesP);
}
}
diff --git a/lib/jkqtplotter/graphs/jkqtpspecialline.cpp b/lib/jkqtplotter/graphs/jkqtpspecialline.cpp
index 946eea7af0..136cfb045d 100644
--- a/lib/jkqtplotter/graphs/jkqtpspecialline.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpspecialline.cpp
@@ -276,7 +276,7 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
painter.setBrush(Qt::NoBrush);
painter.setPen(ph);
for (const QPolygonF& lines : pl_fordrawing) {
- painter.drawPolyline(lines);
+ painter.drawPolylineFast(lines);
}
}
@@ -284,7 +284,7 @@ void JKQTPSpecialLineHorizontalGraph::draw(JKQTPEnhancedPainter& painter) {
painter.setBrush(Qt::NoBrush);
painter.setPen(p);
for (const QPolygonF& lines : pl_fordrawing) {
- painter.drawPolyline(lines);
+ painter.drawPolylineFast(lines);
}
}
@@ -464,7 +464,7 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
painter.setBrush(Qt::NoBrush);
painter.setPen(ph);
for (const QPolygonF& lines : pl_fordrawing) {
- painter.drawPolyline(lines);
+ painter.drawPolylineFast(lines);
}
}
@@ -472,7 +472,7 @@ void JKQTPSpecialLineVerticalGraph::draw(JKQTPEnhancedPainter& painter) {
painter.setBrush(Qt::NoBrush);
painter.setPen(p);
for (const QPolygonF& lines : pl_fordrawing) {
- painter.drawPolyline(lines);
+ painter.drawPolylineFast(lines);
}
}
diff --git a/lib/jkqtplotter/jkqtpbaseplotter.cpp b/lib/jkqtplotter/jkqtpbaseplotter.cpp
index 63e752d9b1..08ab8bbff8 100644
--- a/lib/jkqtplotter/jkqtpbaseplotter.cpp
+++ b/lib/jkqtplotter/jkqtpbaseplotter.cpp
@@ -1965,6 +1965,7 @@ void JKQTBasePlotter::printpreviewPaintRequested(QPrinter* printer) {
}
JKQTPEnhancedPainter painter;
+ painter.setPainterFlag(JKQTPEnhancedPainter::VectorPainting);
painter.begin(printer);
if (!printSetAbsolutePageSize) {
#ifdef SHOW_JKQTPLOTTER_DEBUG
@@ -2065,6 +2066,7 @@ void JKQTBasePlotter::printpreviewPaintRequestedNewPaintDevice(QPaintDevice *pai
if (printer) painter.begin(printer);
else if (svg) painter.begin(svg);
else painter.begin(paintDevice);
+ if (printer||svg) painter.setPainterFlag(JKQTPEnhancedPainter::VectorPainting);
#else
painter.begin(paintDevice);
#endif
@@ -3916,6 +3918,7 @@ void JKQTBasePlotter::copyPixelImage(bool showPreview) {
svg->setSize(size);
svg->setOutputDevice(&buffer);
JKQTPEnhancedPainter painter;
+ painter.setPainterFlag(JKQTPEnhancedPainter::VectorPainting);
painter.begin(svg);
painter.scale(factor,factor);
printAspect=printSizeY_Millimeter/printSizeX_Millimeter;
diff --git a/screenshots/dateaxes.png b/screenshots/dateaxes.png
index 8a0faeaa06..ba1390515e 100644
Binary files a/screenshots/dateaxes.png and b/screenshots/dateaxes.png differ
diff --git a/screenshots/dateaxes_timeaxis.png b/screenshots/dateaxes_timeaxis.png
index fccea7a732..8045b68bcf 100644
Binary files a/screenshots/dateaxes_timeaxis.png and b/screenshots/dateaxes_timeaxis.png differ
diff --git a/screenshots/stepplots.png b/screenshots/stepplots.png
index 51db7ff09e..8e926ee649 100644
Binary files a/screenshots/stepplots.png and b/screenshots/stepplots.png differ