using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using RealTimeGraphX.EventArguments; namespace RealTimeGraphX { /// /// Represents an base class. /// /// The type of the graph data series. /// The type of the x-axis data point. /// The type of the y-axis data point. /// /// public abstract class GraphControllerBase : GraphOutputComponentBase>, IGraphController where XDataPoint : GraphDataPointBase where YDataPoint : GraphDataPointBase where TDataSeries : IGraphDataSeries { #region Events /// /// Occurs when one of the properties was modified. /// public event EventHandler> RangeChanged; /// /// Occurs when one of the properties was modified. /// event EventHandler IGraphController.RangeChanged { add { RangeChanged += new EventHandler>((sender, range) => { value.Invoke(sender, Range); }); } remove { RangeChanged -= new EventHandler>((sender, range) => { value.Invoke(sender, Range); }); } } /// /// Occurs when the connected has changed. /// event EventHandler>> IGraphOutputComponent>.OutputChanged { add { OutputChanged += new EventHandler>>((sender, output) => { value.Invoke(sender, new OutputChangedEventArgs>(output.Output)); }); } remove { OutputChanged -= new EventHandler>>((sender, output) => { value.Invoke(sender, new OutputChangedEventArgs>(output.Output)); }); } } #endregion #region Properties /// /// Gets the data series collection. /// public ReadOnlyObservableCollection DataSeriesCollection { get; private set; } /// /// Gets the graph range (limits). /// public GraphRange Range { get; private set; } /// /// Gets the graph range (limits). /// IGraphRange IGraphController.Range { get { return Range; } } /// /// Gets the connected output . /// IGraphRenderer IGraphOutputComponent>.Output { get { return Output; } } private bool _isPaused; /// /// Gets or sets a value indicating whether to pause the graph movement. /// public bool IsPaused { get { return _isPaused; } set { _isPaused = value; RaisePropertyChangedAuto(); if (Output != null) { Output.IsPaused = value; } } } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public GraphControllerBase() { DataSeriesCollection = new ReadOnlyObservableCollection(new ObservableCollection()); Range = new GraphRange(); Range.PropertyChanged += (x, e) => OnRangeChanged(); } #endregion #region Public Methods /// /// Clears the graph data. /// public void Clear() { if (Output != null) { Output.Clear(); } } /// /// Submits a matrix of x and y data points. Meaning each data series should process a single collection of x/y data points. /// /// X matrix. /// Y matrix. public void PushData(IEnumerable> xxxx, IEnumerable> yyyy) { if (DataSeriesCollection.Count == 0) return; Output.Render(DataSeriesCollection, xxxx, yyyy); } /// /// Submits the specified collections of x and y data points. /// If the controller has more than one data series the data points will be distributed evenly. /// /// X data point collection. /// Y data point collection. public void PushData(IEnumerable xx, IEnumerable yy) { if (DataSeriesCollection.Count == 0) return; var xList = xx.ToList(); var yList = yy.ToList(); List> xxList = new List>(); List> yyList = new List>(); foreach (var series in DataSeriesCollection.ToList()) { xxList.Add(new List()); yyList.Add(new List()); } int counter = 0; for (int i = 0; i < xList.Count; i++) { xxList[counter].Add(xList[i]); yyList[counter].Add(yList[i]); counter++; if (counter >= xxList.Count) { counter = 0; } } Output.Render(DataSeriesCollection, xxList, yyList); } /// /// Submits the specified x and y data points the controller. /// If the controller has more than one data series the data points will be duplicated. /// /// X data point. /// Y data point. public void PushData(XDataPoint x, YDataPoint y) { if (DataSeriesCollection.Count == 0) return; List> xxList = new List>(); List> yyList = new List>(); foreach (var series in DataSeriesCollection.ToList()) { xxList.Add(new List() { x }); yyList.Add(new List() { y }); } Output.Render(DataSeriesCollection, xxList, yyList); } /// /// Adds a new data series. /// /// The series. public void AddDataSeries(TDataSeries series) { var current_collection = DataSeriesCollection.ToList(); current_collection.Add(series); DataSeriesCollection = new ReadOnlyObservableCollection(new ObservableCollection(current_collection)); } /// /// Removed the specified data series /// /// The series. public void RemoveDataSeries(TDataSeries series) { var current_collection = DataSeriesCollection.ToList(); current_collection.Remove(series); DataSeriesCollection = new ReadOnlyObservableCollection(new ObservableCollection(current_collection)); } /// /// Connects this controller to the specified . /// /// The renderer. /// Specifies whether this call was made from an component. public override void ConnectOutput(IGraphRenderer renderer, bool fromOutput = false) { renderer.ThrowIfNull("Cannot connect a null renderer."); Output = renderer; if (!fromOutput) { Output.ConnectInput(this, true); } } /// /// Disconnects this component from the connected . /// /// Specifies whether this call was made from an component. public override void DisconnectOutput(bool fromOutput = false) { if (Output != null) { if (!fromOutput) { (Output as IGraphInputComponent>).DisconnectInput(true); } Output = null; } } /// /// Connects this controller to the specified . /// /// The renderer. /// Specifies whether this call was made from the output . void IGraphOutputComponent>.ConnectOutput(IGraphRenderer renderer, bool fromOutput) { ConnectOutput(renderer as IGraphRenderer); } /// /// Disconnects the current connected output . /// /// Specifies whether this call was made from the output . void IGraphOutputComponent>.DisconnectOutput(bool fromOutput) { DisconnectOutput(); } #endregion #region Protected Methods /// /// Raises the event. /// protected virtual void OnRangeChanged() { RangeChanged?.Invoke(this, Range); } #endregion } }