using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using RealTimeGraphX.EventArguments; namespace RealTimeGraphX { /// /// Represents an base class. /// /// The type of the data series. /// The type of the image. /// /// public abstract class GraphPainterBase : GraphInputOutputComponentBase, IGraphSurface>, IGraphPainter where TDataSeries : IGraphDataSeries { private bool _size_changed; #region Events /// /// Occurs when the painter has a new graph image available. /// public event EventHandler> PaintingCompleted; /// /// Occurs when the current painter transformation (Scale/Translate) has changed. /// public event EventHandler TransformChanged; /// /// Occurs when the current virtual (effective minimum/maximum after transformation) x-axis minimum/maximum has changed. /// public event EventHandler VirtualRangeXChanged; /// /// Occurs when the current virtual (effective minimum/maximum after transformation) y-axis minimum/maximum has changed. /// public event EventHandler VirtualRangeYChanged; #endregion #region Properties /// /// Gets the current graph image. /// public abstract TImage Image { get; protected set; } private GraphTransform _transform; /// /// Gets the current painter transform object. /// The transform object is constructed based on the current . /// public GraphTransform Transform { get { return _transform; } private set { _transform = value; RaisePropertyChangedAuto(); } } private GraphRect _zoomRect; /// /// Gets the last zooming rectangle that was passed on . /// public GraphRect ZoomRect { get { return _zoomRect; } private set { _zoomRect = value; RaisePropertyChangedAuto(); } } private GraphDataPointBase _virtualMinimumX; /// /// Gets the current virtual (effective minimum/maximum after transformation) x-axis minimum. /// public GraphDataPointBase VirtualMinimumX { get { return _virtualMinimumX; } private set { _virtualMinimumX = value; RaisePropertyChangedAuto(); } } private GraphDataPointBase _virtualMaximumX; /// /// Gets the current virtual (effective minimum/maximum after transformation) x-axis maximum. /// public GraphDataPointBase VirtualMaximumX { get { return _virtualMaximumX; } private set { _virtualMaximumX = value; RaisePropertyChangedAuto(); } } private GraphDataPointBase _virtualMinimumY; /// /// Gets the current virtual (effective minimum/maximum after transformation) y-axis minimum. /// public GraphDataPointBase VirtualMinimumY { get { return _virtualMinimumY; } private set { _virtualMinimumY = value; RaisePropertyChangedAuto(); } } private GraphDataPointBase _virtualMaximumY; /// /// Gets the current virtual (effective minimum/maximum after transformation) y-axis maximum. /// public GraphDataPointBase VirtualMaximumY { get { return _virtualMaximumY; } private set { _virtualMaximumY = value; RaisePropertyChangedAuto(); } } /// /// Gets the width of the connected output surface. /// public double SurfaceWidth { get; private set; } /// /// Gets the height of the connected output surface. /// public double SurfaceHeight { get; private set; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public GraphPainterBase() { Transform = new GraphTransform(); //VirtualMinimumX = GraphDataPointHelper.Init(); //VirtualMinimumY = GraphDataPointHelper.Init(); //VirtualMaximumX = GraphDataPointHelper.Init(); //VirtualMaximumY = GraphDataPointHelper.Init(); } #endregion #region Public Methods /// /// Sets the painter zooming rectangle. /// The zoom rectangle must be smaller than the size of the connected size. /// /// The rectangle. public void SetZoomRect(GraphRect rect) { ZoomRect = rect; Input.Invalidate(); } /// /// Resets the painter zooming rectangle. /// public void ResetZoomRect() { ZoomRect = new GraphRect(); OnVirtualRangeXChanged(); OnVirtualRangeYChanged(); Input.Invalidate(); } /// The entire series collection. /// The data series. /// The points. public void Draw(IEnumerable seriesCollection, TDataSeries series, IEnumerable points) { OnDraw(seriesCollection, series, points, _size_changed); _size_changed = false; } #endregion #region Protected Methods /// /// Called when the connected input invoked the method. /// /// The series to draw. /// The points to draw. /// Specifies whether there is a need to reinitialize the image because surface size change. protected abstract void OnDraw(IEnumerable seriesCollection, TDataSeries series, IEnumerable points, bool sizeChanged); /// /// Called when the connected surface size had changed. /// /// The instance containing the event data. protected virtual void OnSurfaceSizeChanged(SurfaceSizeChangedEventArgs e) { SurfaceWidth = e.Width; SurfaceHeight = e.Height; } /// /// Called when the virtual x-axis minimum/maximum range has changed. /// protected virtual void OnVirtualRangeXChanged() { if (ZoomRect.Width > 0 || ZoomRect.Height > 0) { var zoom_rect_left_percentage = ZoomRect.Left / Output.SurfaceWidth; var zoom_rect_right_percentage = ZoomRect.Right / Output.SurfaceWidth; VirtualMinimumX = GraphDataPointHelper.ComputeAbsolutePosition(Input.EffectiveMinimumX, Input.EffectiveMaximumX, zoom_rect_left_percentage) as GraphDataPointBase; VirtualMaximumX = GraphDataPointHelper.ComputeAbsolutePosition(Input.EffectiveMinimumX, Input.EffectiveMaximumX, zoom_rect_right_percentage) as GraphDataPointBase; } else { VirtualMinimumX = Input.EffectiveMinimumX; VirtualMaximumX = Input.EffectiveMaximumX; } VirtualRangeXChanged?.Invoke(this, new RangeChangedEventArgs(VirtualMinimumX, VirtualMaximumX)); } /// /// Called when the virtual y-axis minimum/maximum range has changed. /// protected virtual void OnVirtualRangeYChanged() { if (ZoomRect.Width > 0 || ZoomRect.Height > 0) { var zoom_rect_top_percentage = ZoomRect.Top / Output.SurfaceHeight; var zoom_rect_bottom_percentage = ZoomRect.Bottom / Output.SurfaceHeight; VirtualMinimumY = Input.EffectiveMaximumY - GraphDataPointHelper.ComputeAbsolutePosition(Input.EffectiveMinimumY, Input.EffectiveMaximumY, zoom_rect_bottom_percentage) as GraphDataPointBase; VirtualMaximumY = Input.EffectiveMaximumY - GraphDataPointHelper.ComputeAbsolutePosition(Input.EffectiveMinimumY, Input.EffectiveMaximumY, zoom_rect_top_percentage) as GraphDataPointBase; } else { VirtualMinimumY = Input.EffectiveMinimumY; VirtualMaximumY = Input.EffectiveMaximumY; } VirtualRangeYChanged?.Invoke(this, new RangeChangedEventArgs(VirtualMinimumY, VirtualMaximumY)); } /// /// Called when the painter transformation has changed. /// /// The instance containing the event data. protected virtual void OnTransformChanged(TransformChangedEventArgs e) { Transform = e.Transform; TransformChanged?.Invoke(this, e); } /// /// Returns a closed polygon points which can be used to fill a graph polygon/>. /// /// The points. /// protected virtual IEnumerable GetFillPoints(IEnumerable points) { List closed = points.ToList(); closed.Add(new GraphPoint(points.Last().X, SurfaceWidth)); closed.Add(new GraphPoint(0, SurfaceHeight)); return closed; } /// /// Raises the event. /// /// The image. protected virtual void OnPaintingCompleted(TImage image) { Output.InvokeOnSurface(() => { Image = image; PaintingCompleted?.Invoke(this, new PaintingCompletedEventArgs() { Image = image }); }); } #endregion #region Event Handlers private void Input_EffectiveRangeYChanged(object sender, RangeChangedEventArgs e) { OnVirtualRangeYChanged(); } private void Input_EffectiveRangeXChanged(object sender, RangeChangedEventArgs e) { OnVirtualRangeXChanged(); } private void _surface_SurfaceSizeChanged(object sender, SurfaceSizeChangedEventArgs e) { _size_changed = true; OnSurfaceSizeChanged(e); } #endregion #region Public Methods /// /// Connects this painter to the specified . /// /// The renderer. /// Specifies whether this call was made from the input . public override void ConnectInput(IGraphRenderer renderer, bool fromInput = false) { renderer.ThrowIfNull("Cannot connect a null renderer."); DisconnectInput(); Input = renderer; if (!fromInput) { Input.ConnectOutput(this, true); } Input.EffectiveRangeXChanged += Input_EffectiveRangeXChanged; Input.EffectiveRangeYChanged += Input_EffectiveRangeYChanged; } /// /// Disconnects the connected input . /// /// Specifies whether this call was made from the connected input . public override void DisconnectInput(bool fromInput = false) { if (Input != null) { if (!fromInput) { Input.DisconnectOutput(true); } Input.EffectiveRangeXChanged -= Input_EffectiveRangeXChanged; Input.EffectiveRangeYChanged -= Input_EffectiveRangeYChanged; Input = null; } } /// /// Connects this painter to the specified . /// /// The graph surface. /// Specifies whether this call was made from the connected output . public override void ConnectOutput(IGraphSurface surface, bool fromOutput = false) { surface.ThrowIfNull("Cannot connect a null surface."); DisconnectOutput(); Output = surface; Output.SurfaceSizeChanged += _surface_SurfaceSizeChanged; if (!fromOutput) { Output.ConnectInput(this, true); } _size_changed = true; } /// /// Disconnects this painter from the current connected . /// /// Specifies whether this call was made the output . public override void DisconnectOutput(bool fromOutput = false) { if (Output != null) { if (!fromOutput) { Output.DisconnectInput(true); } Output.SurfaceSizeChanged -= _surface_SurfaceSizeChanged; Output = null; } } #endregion } }