You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Chart: a frame can stroke a pen against a half-swapped axis pair #64
A pen's line can be drawn for one frame with part of its points scaled through the old Y axis and part through the new one: a step in the line, or a stretch of it outside the data area, gone on the next frame. It shows as a flicker at the moment the axis assignment changes: a pen regrouped by unit, a manual/auto scale switch, a pen added.
Not reproduced by hand yet. A drag alone does not trigger it because the Y axes do not change during a drag.
Cause
ChartAxisBinder.AssignPensToAxis (SemiPlot/SemiPlot.UI/Chart/ChartAxisBinder.cs:70) writes state.Line.Axes.YAxis = axis on the UI thread on every ApplyAxisModel, which runs after each history apply and each window change.
EnvelopeLine.Render reads Axes on Avalonia's render thread outside any lock: Axes.XAxis.Min/Max for the visible range (EnvelopeLine.cs:82) and Axes.GetPixel per point (EnvelopeLine.cs:94). Axes is ScottPlot's plain object with two reference properties; the column-buffer lock from #59 does not cover it.
A reference write is atomic, so nothing throws. A frame is not atomic: a write landing between two GetPixel calls leaves that frame drawn against two axes.
Fix options
Render snapshots Axes.XAxis and Axes.YAxis into locals once at the top and uses them for the whole frame. No lock, no UI-thread blocking; one frame sees one consistent pair. Smallest change.
TrendChartViewModel.RemovePen (TrendChartViewModel.cs:248) calls Plot.Remove(state.Line), which mutates ScottPlot's PlottableList while the render thread enumerates it with foreach: InvalidOperationException (collection modified) if it ever runs during a frame. Latent today because RemovePen has no production caller and AddPen runs once at startup before the window is shown. Decide with the behaviour/UX round: delete the method with its tests, or reimplement pen removal through IsVisible rather than Plot.Remove.
Provenance
Found during the review of #63. Present on master since the culled polyline plottable (013f491, #57).
Obsolete on master (823c687). EnvelopeLine.Render reads Axes.YAxis into a local once per frame (EnvelopeLine.cs:86), the fix this issue proposed; it landed with #113. Each pen also keeps one Y axis for its lifetime (ChartAxisBinder.ResolveAxis), so the assignment at ChartAxisBinder.cs:41 rewrites the same reference and no swap occurs. The related TrendChartViewModel.RemovePen and its Plot.Remove call are gone; a removed pen's axis is hidden through HideAxis.
Symptom
A pen's line can be drawn for one frame with part of its points scaled through the old Y axis and part through the new one: a step in the line, or a stretch of it outside the data area, gone on the next frame. It shows as a flicker at the moment the axis assignment changes: a pen regrouped by unit, a manual/auto scale switch, a pen added.
Not reproduced by hand yet. A drag alone does not trigger it because the Y axes do not change during a drag.
Cause
ChartAxisBinder.AssignPensToAxis(SemiPlot/SemiPlot.UI/Chart/ChartAxisBinder.cs:70) writesstate.Line.Axes.YAxis = axison the UI thread on everyApplyAxisModel, which runs after each history apply and each window change.EnvelopeLine.RenderreadsAxeson Avalonia's render thread outside any lock:Axes.XAxis.Min/Maxfor the visible range (EnvelopeLine.cs:82) andAxes.GetPixelper point (EnvelopeLine.cs:94).Axesis ScottPlot's plain object with two reference properties; the column-buffer lock from #59 does not cover it.A reference write is atomic, so nothing throws. A frame is not atomic: a write landing between two
GetPixelcalls leaves that frame drawn against two axes.Fix options
RendersnapshotsAxes.XAxisandAxes.YAxisinto locals once at the top and uses them for the whole frame. No lock, no UI-thread blocking; one frame sees one consistent pair. Smallest change.EnvelopeLinethat takes the existing_columnsLock, and readAxesunder the same lock inRender. Consistent with Chart crashes on drag: EnvelopeLine renders a pen's column list while the UI thread rewrites it #59 but blocks the UI thread for nothing the snapshot does not already give.Option 1 is enough.
Related
TrendChartViewModel.RemovePen(TrendChartViewModel.cs:248) callsPlot.Remove(state.Line), which mutates ScottPlot'sPlottableListwhile the render thread enumerates it withforeach:InvalidOperationException(collection modified) if it ever runs during a frame. Latent today becauseRemovePenhas no production caller andAddPenruns once at startup before the window is shown. Decide with the behaviour/UX round: delete the method with its tests, or reimplement pen removal throughIsVisiblerather thanPlot.Remove.Provenance
Found during the review of #63. Present on
mastersince the culled polyline plottable (013f491, #57).