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
}
}