diff --git a/common/widgets/mathplot.cpp b/common/widgets/mathplot.cpp index 48517ade8..10274ec6c 100644 --- a/common/widgets/mathplot.cpp +++ b/common/widgets/mathplot.cpp @@ -647,10 +647,11 @@ void mpFXY::Plot( wxDC& dc, mpWindow& w ) if( !m_continuous ) { - // for some reason DrawPoint does not use the current pen, - // so we use DrawLine for fat pens - if( m_pen.GetWidth() <= 1 ) + if( m_dirty ) { + m_cachedPoints.clear(); + m_cachedLines.clear(); + while( GetNextXY( x, y ) ) { double px = m_scaleX->TransformToPlot( x ); @@ -663,82 +664,88 @@ void mpFXY::Plot( wxDC& dc, mpWindow& w ) || ( (ix >= startPx) && (ix <= endPx) && (iy >= minYpx) && (iy <= maxYpx) ) ) { - dc.DrawPoint( ix, iy ); - UpdateViewBoundary( ix, iy ); + m_cachedPoints.insert( wxPoint( ix, iy ) ); } } + m_dirty = false; } - else + + for( auto& p : m_cachedPoints ) { - while( GetNextXY( x, y ) ) + // for some reason DrawPoint does not use the current pen, + // so we use DrawLine for fat pens + if( m_pen.GetWidth() <= 1 ) { - double px = m_scaleX->TransformToPlot( x ); - double py = m_scaleY->TransformToPlot( y ); - - ix = w.x2p( px ); - iy = w.y2p( py ); - - if( m_drawOutsideMargins - || ( (ix >= startPx) && (ix <= endPx) && (iy >= minYpx) - && (iy <= maxYpx) ) ) - { - dc.DrawLine( ix, iy, ix, iy ); - UpdateViewBoundary( ix, iy ); - } - - // dc.DrawLine(cx, cy, cx, cy); + dc.DrawPoint( p.x, p.y ); + } + else + { + dc.DrawLine( p.x, p.y, p.x, p.y ); } + UpdateViewBoundary( p.x, p.y ); } } else { - int n = 0; - // Old code - wxCoord x0 = 0, c0 = 0; - bool first = true; - - while( GetNextXY( x, y ) ) + if( m_dirty ) { - double px = m_scaleX->TransformToPlot( x ); - double py = m_scaleY->TransformToPlot( y ); + m_cachedPoints.clear(); + m_cachedLines.clear(); - if( py >= 0.0 && py <= 1.0 ) - n++; + int n = 0; + // Old code + wxCoord x0 = 0, c0 = 0; + bool first = true; - // printf("py %.1f\n", py); + while( GetNextXY( x, y ) ) + { + double px = m_scaleX->TransformToPlot( x ); + double py = m_scaleY->TransformToPlot( y ); - wxCoord x1 = w.x2p( px ); - wxCoord c1 = w.y2p( py ); + if( py >= 0.0 && py <= 1.0 ) + n++; - // printf("px %.10f py %.10f c1 %d\n", px, py, c1); + // printf("py %.1f\n", py); - // wxCoord x1 = XScale().X2p(w,x); // (wxCoord) ((x - w.GetPosX()) * w.GetScaleX()); - // wxCoord c1 = YScale().X2p(w,y); // (wxCoord) ((w.GetPosY() - y) * w.GetScaleY()); + wxCoord x1 = w.x2p( px ); + wxCoord c1 = w.y2p( py ); - if( first ) - { - first = false; - x0 = x1; c0 = c1; - } + // printf("px %.10f py %.10f x1 %d c1 %d\n", px, py, x1, c1); - bool outUp, outDown; + // wxCoord x1 = XScale().X2p(w,x); // (wxCoord) ((x - w.GetPosX()) * w.GetScaleX()); + // wxCoord c1 = YScale().X2p(w,y); // (wxCoord) ((w.GetPosY() - y) * w.GetScaleY()); - if( (x1 >= startPx)&&(x0 <= endPx) ) - { - outDown = (c0 > maxYpx) && (c1 > maxYpx); - outUp = (c0 < minYpx) && (c1 < minYpx); + if( first ) + { + first = false; + x0 = x1; c0 = c1; + } - if( !outUp && !outDown ) + bool outUp, outDown; + + if( (x1 >= startPx)&&(x0 <= endPx) ) { - dc.DrawLine( x0, c0, x1, c1 ); - // UpdateViewBoundary(x1, c1); + outDown = (c0 > maxYpx) && (c1 > maxYpx); + outUp = (c0 < minYpx) && (c1 < minYpx); + + if( !outUp && !outDown ) + { + m_cachedLines.insert( std::make_pair( wxPoint( x0, c0 ), wxPoint( x1, c1 ) ) ); + } } + + x0 = x1; c0 = c1; } - x0 = x1; c0 = c1; + // printf("n %s %d\n", (const char *) m_name.c_str(), n); + m_dirty = false; } - // printf("n %s %d\n", (const char *) m_name.c_str(), n); + for( auto& p : m_cachedLines ) + { + dc.DrawLine( p.first.x, p.first.y, p.second.x, p.second.y ); + //UpdateViewBoundary( p.second.x, p.second.y ); + } } if( !m_name.IsEmpty() && m_showName ) @@ -2854,6 +2861,10 @@ void mpWindow::UpdateAll() } } + for( auto& layer: m_layers ) + { + layer->MakeDirty(); + } Refresh( false ); } @@ -3411,6 +3422,8 @@ void mpFXYVector::SetData( const std::vector& xs, const std::vector +#include +#include // #include #include @@ -308,6 +310,8 @@ public: * @param brush brush, will be copied to internal class member */ void SetBrush( wxBrush brush ) { m_brush = brush; }; + /** Makes this layer's data cache dirty. */ + void MakeDirty() { m_dirty = true; }; protected: wxFont m_font; // !< Layer's font @@ -319,6 +323,17 @@ protected: bool m_drawOutsideMargins; // !< select if the layer should draw only inside margins or over all DC mpLayerType m_type; // !< Define layer type, which is assigned by constructor bool m_visible; // !< Toggles layer visibility + + /** Is this layer's data cache dirty? + * + * Whether or not this layer's data has changed since the last time it's + * been plotted. If it has, the next time this layer is plotted it will + * recreate its cache. + * + * If the layer doesn't support caching then this flag will always equal + * true. */ + bool m_dirty = true; + DECLARE_DYNAMIC_CLASS( mpLayer ) }; @@ -585,6 +600,22 @@ protected: * to the constructor mpFXY::mpFXY. If the layer name is empty, no label will be plotted. */ +struct wxPointCmp { + inline bool operator() (const wxPoint& a, const wxPoint& b) const { + return a.x == b.x ? a.y < b.y : a.x < b.x; + } +}; + +struct wxPointPairCmp { + using T = std::pair; + inline bool operator() (const T& a, const T& b) const { + wxPointCmp cmp; + return ( cmp( a.first, b.first ) == cmp( b.first, a.first ) ) + ? cmp( a.second, b.second ) + : cmp( a.first, b.first ); + } +}; + class WXDLLIMPEXP_MATHPLOT mpFXY : public mpLayer { public: @@ -636,6 +667,10 @@ protected: void UpdateViewBoundary( wxCoord xnew, wxCoord ynew ); DECLARE_DYNAMIC_CLASS( mpFXY ) + +private: + std::set m_cachedPoints; + std::set, wxPointPairCmp> m_cachedLines; }; /** Abstract base class providing plot and labeling functionality for functions F:Y->X.