fslaborg / FSharp.Charting

Charting library suitable for interactive F# scripting
http://fslab.org/FSharp.Charting/
Other
216 stars 67 forks source link

Exception when using ExtCore's async sequences to specify incremental data #33

Closed ghost closed 8 years ago

ghost commented 10 years ago

I was experimenting using ExtCore async sequences as specifications of incrementally growing data sets. THis works when I use event-based sources of IObservables.

However for the asyncSeq example below I get a background exception:

#r "packages/ExtCore/lib/net40/ExtCore.dll"
#load "packages/FSharp.Charting/FSharp.Charting.fsx"

open FSharp.Charting

asyncSeq { 
    yield (1,10) 
    for i in 0 .. 100 do 
       do! Async.Sleep 100
       yield (i,i*i) 
    }
 |> AsyncSeq.toObservable
 |> LiveChart.LineIncremental

val it : ChartTypes.GenericChart = (Chart)

System.InvalidOperationException: Collection was modified; enumeration operation may not execute. at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) at System.Collections.Generic.List1.Enumerator.MoveNextRare() at System.Collections.Generic.List1.Enumerator.MoveNext() at System.Windows.Forms.DataVisualization.Charting.ChartArea.GetPointsInterval(List`1 seriesList, Boolean isLogarithmic, Double logarithmicBase, Boolean checkSameInterval, Boolean& sameInterval, Series& series) at System.Windows.Forms.DataVisualization.Charting.ChartArea.SetDefaultFromData(Axis axis) at System.Windows.Forms.DataVisualization.Charting.ChartArea.SetDefaultFromIndexesOrData(Axis axis, AxisType axisType) at System.Windows.Forms.DataVisualization.Charting.ChartArea.SetDefaultAxesValues() at System.Windows.Forms.DataVisualization.Charting.ChartArea.SetData(Boolean initializeAxes, Boolean checkIndexedAligned) at System.Windows.Forms.DataVisualization.Charting.ChartArea.ReCalcInternal() at System.Windows.Forms.DataVisualization.Charting.ChartPicture.Paint(Graphics graph, Boolean paintTopLevelElementOnly) at System.Windows.Forms.DataVisualization.Charting.Chart.OnPaint(PaintEventArgs e) at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer) at System.Windows.Forms.Control.WmPaint(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) Stopped due to error

rookboom commented 10 years ago

I see the same behavior when trying to plot a sequence that is lazily created:

    let chart(points:(int*float32) seq) = 
        let obs = (Observable.ofSeq points).SubscribeOn(ThreadPoolScheduler.Instance)
        LiveChart.FastLineIncremental(obs)

If I don't subscribe on the threadpool, I don't get any exception, but I only see the chart after all the entire sequence has been evaluated, which defeats the purpose of a live chart...

rookboom commented 10 years ago

I found a workaround that at least avoids the exception:

    let chart(points:(int*float32) seq) = 
        let obs = (Observable.ofSeq points)
                           .SubscribeOn(ThreadPoolScheduler.Instance)
                           .ObserveOn(WindowsFormsSynchronizationContext.Current)
        LiveChart.FastLineIncremental(obs)

The events has to be observed on the WindowsForms thread, but can be produced on a separate thread, It is still not ideal though. The form only seems to update once per second or so and only displays one new observation at a time. Not ideal when producing many values.