Initial Commit
This commit is contained in:
333
Charts/LineGraph.cs
Normal file
333
Charts/LineGraph.cs
Normal file
@@ -0,0 +1,333 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts;
|
||||
using Microsoft.Research.DynamicDataDisplay.Charts.Legend_items;
|
||||
using Microsoft.Research.DynamicDataDisplay.DataSources;
|
||||
using Microsoft.Research.DynamicDataDisplay.Filters;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
|
||||
namespace Microsoft.Research.DynamicDataDisplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a series of points connected by one polyline.
|
||||
/// </summary>
|
||||
public class LineGraph : PointsGraphBase
|
||||
{
|
||||
static LineGraph()
|
||||
{
|
||||
Type thisType = typeof(LineGraph);
|
||||
|
||||
NewLegend.DescriptionProperty.OverrideMetadata(thisType, new FrameworkPropertyMetadata("LineGraph"));
|
||||
NewLegend.LegendItemsBuilderProperty.OverrideMetadata(thisType, new FrameworkPropertyMetadata(new LegendItemsBuilder(DefaultLegendItemsBuilder)));
|
||||
}
|
||||
|
||||
private static IEnumerable<FrameworkElement> DefaultLegendItemsBuilder(IPlotterElement plotterElement)
|
||||
{
|
||||
LineGraph lineGraph = (LineGraph)plotterElement;
|
||||
|
||||
Line line = new Line { X1 = 0, Y1 = 10, X2 = 20, Y2 = 0, Stretch = Stretch.Fill, DataContext = lineGraph };
|
||||
line.SetBinding(Line.StrokeProperty, "Stroke");
|
||||
line.SetBinding(Line.StrokeThicknessProperty, "StrokeThickness");
|
||||
NewLegend.SetVisualContent(lineGraph, line);
|
||||
|
||||
var legendItem = LegendItemsHelper.BuildDefaultLegendItem(lineGraph);
|
||||
yield return legendItem;
|
||||
}
|
||||
|
||||
private readonly FilterCollection filters = new FilterCollection();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LineGraph"/> class.
|
||||
/// </summary>
|
||||
public LineGraph()
|
||||
{
|
||||
Legend.SetVisibleInLegend(this, true);
|
||||
ManualTranslate = true;
|
||||
|
||||
filters.CollectionChanged += filters_CollectionChanged;
|
||||
}
|
||||
|
||||
private void filters_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
filteredPoints = null;
|
||||
Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LineGraph"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pointSource">The point source.</param>
|
||||
public LineGraph(IPointDataSource pointSource)
|
||||
: this()
|
||||
{
|
||||
DataSource = pointSource;
|
||||
}
|
||||
|
||||
protected override Description CreateDefaultDescription()
|
||||
{
|
||||
return new PenDescription();
|
||||
}
|
||||
|
||||
/// <summary>Provides access to filters collection</summary>
|
||||
public FilterCollection Filters
|
||||
{
|
||||
get { return filters; }
|
||||
}
|
||||
|
||||
#region Pen
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush, using which polyline is plotted.
|
||||
/// </summary>
|
||||
/// <value>The line brush.</value>
|
||||
public Brush Stroke
|
||||
{
|
||||
get { return LinePen.Brush; }
|
||||
set
|
||||
{
|
||||
if (LinePen.Brush != value)
|
||||
{
|
||||
if (!LinePen.IsSealed)
|
||||
{
|
||||
LinePen.Brush = value;
|
||||
InvalidateVisual();
|
||||
}
|
||||
else
|
||||
{
|
||||
Pen pen = LinePen.Clone();
|
||||
pen.Brush = value;
|
||||
LinePen = pen;
|
||||
}
|
||||
|
||||
RaisePropertyChanged("Stroke");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the line thickness.
|
||||
/// </summary>
|
||||
/// <value>The line thickness.</value>
|
||||
public double StrokeThickness
|
||||
{
|
||||
get { return LinePen.Thickness; }
|
||||
set
|
||||
{
|
||||
if (LinePen.Thickness != value)
|
||||
{
|
||||
if (!LinePen.IsSealed)
|
||||
{
|
||||
LinePen.Thickness = value; InvalidateVisual();
|
||||
}
|
||||
else
|
||||
{
|
||||
Pen pen = LinePen.Clone();
|
||||
pen.Thickness = value;
|
||||
LinePen = pen;
|
||||
}
|
||||
|
||||
RaisePropertyChanged("StrokeThickness");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the line pen.
|
||||
/// </summary>
|
||||
/// <value>The line pen.</value>
|
||||
[NotNull]
|
||||
public Pen LinePen
|
||||
{
|
||||
get { return (Pen)GetValue(LinePenProperty); }
|
||||
set { SetValue(LinePenProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LinePenProperty =
|
||||
DependencyProperty.Register(
|
||||
"LinePen",
|
||||
typeof(Pen),
|
||||
typeof(LineGraph),
|
||||
new FrameworkPropertyMetadata(
|
||||
new Pen(Brushes.Blue, 1),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender
|
||||
),
|
||||
OnValidatePen);
|
||||
|
||||
private static bool OnValidatePen(object value)
|
||||
{
|
||||
return value != null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnOutputChanged(Rect newRect, Rect oldRect)
|
||||
{
|
||||
filteredPoints = null;
|
||||
base.OnOutputChanged(newRect, oldRect);
|
||||
}
|
||||
|
||||
protected override void OnDataChanged()
|
||||
{
|
||||
filteredPoints = null;
|
||||
base.OnDataChanged();
|
||||
}
|
||||
|
||||
protected override void OnDataSourceChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
filteredPoints = null;
|
||||
base.OnDataSourceChanged(args);
|
||||
}
|
||||
|
||||
protected override void OnVisibleChanged(DataRect newRect, DataRect oldRect)
|
||||
{
|
||||
if (newRect.Size != oldRect.Size)
|
||||
{
|
||||
filteredPoints = null;
|
||||
}
|
||||
|
||||
base.OnVisibleChanged(newRect, oldRect);
|
||||
}
|
||||
|
||||
private FakePointList filteredPoints;
|
||||
protected FakePointList FilteredPoints
|
||||
{
|
||||
get { return filteredPoints; }
|
||||
set { filteredPoints = value; }
|
||||
}
|
||||
|
||||
protected override void UpdateCore()
|
||||
{
|
||||
if (DataSource == null) return;
|
||||
if (Plotter == null) return;
|
||||
|
||||
Rect output = Viewport.Output;
|
||||
var transform = GetTransform();
|
||||
|
||||
if (filteredPoints == null || !(transform.DataTransform is IdentityTransform))
|
||||
{
|
||||
IEnumerable<Point> points = GetPoints();
|
||||
|
||||
var bounds = BoundsHelper.GetViewportBounds(points, transform.DataTransform);
|
||||
Viewport2D.SetContentBounds(this, bounds);
|
||||
|
||||
// getting new value of transform as it could change after calculating and setting content bounds.
|
||||
transform = GetTransform();
|
||||
List<Point> transformedPoints = transform.DataToScreenAsList(points);
|
||||
|
||||
// Analysis and filtering of unnecessary points
|
||||
filteredPoints = new FakePointList(FilterPoints(transformedPoints),
|
||||
output.Left, output.Right);
|
||||
|
||||
if (ProvideVisiblePoints)
|
||||
{
|
||||
List<Point> viewportPointsList = new List<Point>(transformedPoints.Count);
|
||||
if (transform.DataTransform is IdentityTransform)
|
||||
{
|
||||
viewportPointsList.AddRange(points);
|
||||
}
|
||||
else
|
||||
{
|
||||
var viewportPoints = points.DataToViewport(transform.DataTransform);
|
||||
viewportPointsList.AddRange(viewportPoints);
|
||||
}
|
||||
|
||||
SetVisiblePoints(this, new ReadOnlyCollection<Point>(viewportPointsList));
|
||||
}
|
||||
|
||||
Offset = new Vector();
|
||||
}
|
||||
else
|
||||
{
|
||||
double left = output.Left;
|
||||
double right = output.Right;
|
||||
double shift = Offset.X;
|
||||
left -= shift;
|
||||
right -= shift;
|
||||
|
||||
filteredPoints.SetXBorders(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
StreamGeometry streamGeometry = new StreamGeometry();
|
||||
protected override void OnRenderCore(DrawingContext dc, RenderState state)
|
||||
{
|
||||
if (DataSource == null) return;
|
||||
|
||||
if (filteredPoints.HasPoints)
|
||||
{
|
||||
|
||||
using (StreamGeometryContext context = streamGeometry.Open())
|
||||
{
|
||||
context.BeginFigure(filteredPoints.StartPoint, false, false);
|
||||
context.PolyLineTo(filteredPoints, true, smoothLinesJoin);
|
||||
}
|
||||
|
||||
Brush brush = null;
|
||||
Pen pen = LinePen;
|
||||
|
||||
bool isTranslated = IsTranslated;
|
||||
if (isTranslated)
|
||||
{
|
||||
dc.PushTransform(new TranslateTransform(Offset.X, Offset.Y));
|
||||
}
|
||||
dc.DrawGeometry(brush, pen, streamGeometry);
|
||||
if (isTranslated)
|
||||
{
|
||||
dc.Pop();
|
||||
}
|
||||
|
||||
#if __DEBUG
|
||||
FormattedText text = new FormattedText(filteredPoints.Count.ToString(),
|
||||
CultureInfo.InvariantCulture, FlowDirection.LeftToRight,
|
||||
new Typeface("Arial"), 12, Brushes.Black);
|
||||
dc.DrawText(text, Viewport.Output.GetCenter());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private bool filteringEnabled = true;
|
||||
public bool FilteringEnabled
|
||||
{
|
||||
get { return filteringEnabled; }
|
||||
set
|
||||
{
|
||||
if (filteringEnabled != value)
|
||||
{
|
||||
filteringEnabled = value;
|
||||
filteredPoints = null;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool smoothLinesJoin = true;
|
||||
public bool SmoothLinesJoin
|
||||
{
|
||||
get { return smoothLinesJoin; }
|
||||
set
|
||||
{
|
||||
smoothLinesJoin = value;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Point> FilterPoints(List<Point> points)
|
||||
{
|
||||
if (!filteringEnabled)
|
||||
return points;
|
||||
|
||||
var filteredPoints = filters.Filter(points, Viewport.Output);
|
||||
|
||||
return filteredPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user