/// <summary> /// Initializes a new instance of the <see cref="GraphController{TDataSeries, TXDataPoint, TYDataPoint}"/> class. /// </summary> public GraphController() { Renderer = new ScrollingLineRenderer <TDataSeries>(); DataSeriesCollection = new ObservableCollection <TDataSeries>(); Range = new GraphRange <TXDataPoint, TYDataPoint>(); _last_render_time = DateTime.Now; _to_render = new Dictionary <TDataSeries, PendingSeries>(); _pending_series_collection = new GraphDataQueue <List <PendingSeries> >(); RefreshRate = TimeSpan.FromMilliseconds(50); ClearCommand = new GraphCommand(Clear); _render_thread = new Thread(RenderThreadMethod); _render_thread.IsBackground = true; _render_thread.Start(); }
/// <summary> /// The rendering thread method. /// </summary> private void RenderThreadMethod() { while (true) { if (!IsPaused) { var pending_list = _pending_series_collection.BlockDequeue(); foreach (var pending_series in pending_list) { if (pending_series.IsClearSeries) { _pending_series_collection = new GraphDataQueue <List <PendingSeries> >(); _to_render.Clear(); break; } if (_to_render.ContainsKey(pending_series.Series)) { var s = _to_render[pending_series.Series]; s.XX.AddRange(pending_series.XX); s.YY.AddRange(pending_series.YY); } else { _to_render[pending_series.Series] = pending_series; } } if (DateTime.Now > _last_render_time.AddMilliseconds(RefreshRate.TotalMilliseconds) && _to_render.Count > 0) { GraphDataPoint min_x = _range.MaximumX - _range.MaximumX; GraphDataPoint max_x = _range.MaximumX; GraphDataPoint min_y = _range.MinimumY; GraphDataPoint max_y = _range.MaximumY; min_x = _to_render.First().Value.XX.First(); max_x = _to_render.First().Value.XX.Last(); if (_range.AutoY) { min_y = _to_render.Select(x => x.Value).SelectMany(x => x.YY).Min(); max_y = _to_render.Select(x => x.Value).SelectMany(x => x.YY).Max(); } if (min_y == max_y) { min_y = _range.MinimumY; max_y = _range.MaximumY; } EffectiveMinimumX = min_x; EffectiveMaximumX = max_x; EffectiveMinimumY = min_y; EffectiveMaximumY = max_y; VirtualMinimumX = EffectiveMinimumX; VirtualMaximumX = EffectiveMaximumX; VirtualMinimumY = EffectiveMinimumY; VirtualMaximumY = EffectiveMaximumY; _last_render_time = DateTime.Now; if (Surface != null) { var surface_size = Surface.GetSize(); var zoom_rect = Surface.GetZoomRect(); Surface.BeginDraw(); if (zoom_rect.Width > 0 && zoom_rect.Height > 0) { var zoom_rect_top_percentage = zoom_rect.Top / surface_size.Height; var zoom_rect_bottom_percentage = zoom_rect.Bottom / surface_size.Height; var zoom_rect_left_percentage = zoom_rect.Left / surface_size.Width; var zoom_rect_right_percentage = zoom_rect.Right / surface_size.Width; VirtualMinimumY = EffectiveMaximumY - GraphDataPointHelper.ComputeAbsolutePosition(EffectiveMinimumY, EffectiveMaximumY, zoom_rect_bottom_percentage); VirtualMaximumY = EffectiveMaximumY - GraphDataPointHelper.ComputeAbsolutePosition(EffectiveMinimumY, EffectiveMaximumY, zoom_rect_top_percentage); VirtualMinimumX = GraphDataPointHelper.ComputeAbsolutePosition(EffectiveMinimumX, EffectiveMaximumX, zoom_rect_left_percentage); VirtualMaximumX = GraphDataPointHelper.ComputeAbsolutePosition(EffectiveMinimumX, EffectiveMaximumX, zoom_rect_right_percentage); GraphTransform transform = new GraphTransform(); var scale_x = (float)(surface_size.Width / zoom_rect.Width); var scale_y = (float)(surface_size.Height / zoom_rect.Height); var translate_x = (float)-zoom_rect.Left * scale_x; var translate_y = (float)-zoom_rect.Top * scale_y; transform = new GraphTransform(); transform.TranslateX = translate_x; transform.TranslateY = translate_y; transform.ScaleX = scale_x; transform.ScaleY = scale_y; Surface.SetTransform(transform); } List <Tuple <TDataSeries, IEnumerable <PointF> > > to_draw = new List <Tuple <TDataSeries, IEnumerable <PointF> > >(); var to_render = _to_render.Select(x => x.Value).ToList(); foreach (var item in to_render) { var points = Renderer.Render(Surface, item.Series, _range, item.XX, item.YY, min_x, max_x, min_y, max_y); to_draw.Add(new Tuple <TDataSeries, IEnumerable <PointF> >(item.Series, points)); } for (int i = 0; i < to_draw.Count; i++) { if (to_draw[i].Item2.Count() > 2) { if (to_draw[i].Item1.IsVisible) { Renderer.Draw(Surface, to_draw[i].Item1, to_draw[i].Item2, i, to_draw.Count); } } } Surface.EndDraw(); } OnEffectiveRangeChanged(EffectiveMinimumX, EffectiveMaximumX, EffectiveMinimumY, EffectiveMaximumY); OnVirtualRangeChanged(VirtualMinimumX, VirtualMaximumX, VirtualMinimumY, VirtualMaximumY); } } else { Thread.Sleep(RefreshRate); } } }
/// <summary> /// The rendering thread method. /// </summary> private void RenderThreadMethod() { while (true) { if (!IsPaused && !DisableRendering) { try { List <List <PendingSeries> > pending_lists = new List <List <PendingSeries> >(); var pending_list_first = _pending_series_collection.BlockDequeue(); pending_lists.Add(pending_list_first); while (_pending_series_collection.Count > 0) { var pending_list = _pending_series_collection.BlockDequeue(); pending_lists.Add(pending_list); } foreach (var pending_list in pending_lists) { foreach (var pending_series in pending_list) { if (pending_series.IsClearSeries) { _pending_series_collection = new GraphDataQueue <List <PendingSeries> >(); _to_render.Clear(); } else if (!pending_series.IsUpdateSeries) { if (_to_render.ContainsKey(pending_series.Series)) { var s = _to_render[pending_series.Series]; s.XX.AddRange(pending_series.XX); s.YY.AddRange(pending_series.YY); } else { _to_render[pending_series.Series] = pending_series; } } } } Render(); } catch (Exception ex) { Debug.WriteLine($"Error in RealTimeGraphX:\n{ex.ToString()}"); } } else if (IsPaused && !DisableRendering) { Render(); Thread.Sleep(RefreshRate); } else { Thread.Sleep(RefreshRate); } } }