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