WPF编程,曲线控件DynamicDataDisplay的源码分析

扣扣技术交流群:460189483

源码下载地址

https://archive.codeplex.com/?p=dynamicdatadisplay

目录

LineTestSample

IsolineSampleApp

​Simulation

PlotterLayoutPanels

5x1000Charts

​ArcSegmentPlotting

DateTimeAxisBug 时间轴

DescriptionChangeOnTheFly 动态更改线的粗细

HandledRepro 图中图

LineGraphUpdateOnDataSourceChange

SyncedWidthSample 同步两个图的x轴宽度

TwoAxisSample 两条x轴

VerticalRangeNotVisibleRepro 添加随机垂直range

ViewportPanelBugRepro

ZeroDifferenceRepro 零点

PointsOnMapSampleApp

​HideAxisSample 隐藏轴

AxisColoringSample 轴更改样式

Animation 动态图

ImageHistogram

 

LineTestSample 

<Button DockPanel.Dock="Top" Content="Add data source" Name="addDataSourceBtn" Click="addDataSourceBtn_Click"/>
<d3:ChartPlotter Name="plotter" Margin="0,5,0,0">
    <d3:LineGraph Name="lineGraph" Stroke="OrangeRed" StrokeThickness="1"/>
 
    <d3:VerticalLine Value="{Binding ElementName=dataFollowChart, Path=MarkerPosition.X}" StrokeThickness="2" Stroke="Violet" StrokeDashArray="4,5"/>
    <d3:HorizontalLine Value="{Binding ElementName=dataFollowChart, Path=MarkerPosition.Y}" StrokeThickness="2" Stroke="Violet" StrokeDashArray="4,5"/>
    
    <d3:ViewportPanel>
        <Rectangle Width="35" Height="35" Fill="Yellow" Stroke="Orange"
                   d3:ViewportPanel.X="{Binding ElementName=dataFollowChart, Path=MarkerPosition.X}"
                   d3:ViewportPanel.Y="{Binding ElementName=dataFollowChart, Path=MarkerPosition.Y}"/>
    </d3:ViewportPanel>
    
    <d3:DataFollowChart Name="dataFollowChart" PointSource="{Binding ElementName=lineGraph}">
        <DataTemplate>
            <Grid d3:ViewportPanel.ScreenOffsetY="16">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Rectangle RadiusX="3" RadiusY="3" Stroke="LightGray" Fill="#99FFFFFF" Grid.Row="0" Grid.RowSpan="2"/>
                <Ellipse Width="10" Height="10" Fill="LightGreen" Stroke="Green" Grid.Row="0"/>
                <TextBlock Name="tb" Margin="2,15,2,0" Grid.Row="1">
                    <TextBlock Text="{Binding Position.X, StringFormat=G3}"/>
                    <TextBlock Text="{Binding Position.Y, StringFormat=G3}"/>
                </TextBlock>
            </Grid>
        </DataTemplate>
    </d3:DataFollowChart>
</d3:ChartPlotter>
DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(20) };
public Window1()
{	
	InitializeComponent();
	plotter.Viewport.Domain = new Rect(-1, -1.2, 20, 2.4);
	plotter.Children.Add(new HorizontalScrollBar());	
	plotter.AxisGrid.DrawHorizontalMinorTicks = false;
	plotter.AxisGrid.DrawVerticalMinorTicks = false;
	lineGraph.DataSource = CreateSineDataSource(1.0);
	timer.Tick += new EventHandler(timer_Tick);
}
 
TimeSpan elapsed = new TimeSpan();
void timer_Tick(object sender, EventArgs e)
{
	elapsed = elapsed.Add(timer.Interval);
	double millis = elapsed.TotalMilliseconds;
	double x = ((millis % 1000) - 500) / 500;
	var visible = plotter.Viewport.Visible;
	visible.XMin = x;
	plotter.Visible = visible;
	if (millis > 10000)
		Close();
}
 
Random rnd = new Random();
private void addDataSourceBtn_Click(object sender, RoutedEventArgs e)
{
	var line = plotter.AddLineGraph(CreateSineDataSource(rnd.NextDouble()));
	DataFollowChart followChart = new DataFollowChart(line);
	plotter.Children.Add(followChart);
}
 
private IPointDataSource CreateSineDataSource(double phase)
{
	const int N = 100;
	Point[] pts = new Point[N];
	for (int i = 0; i < N; i++)
	{
		double x = i / (N / 10.0) + phase;
		pts[i] = new Point(x, Math.Sin(x - phase));
	}
	var ds = new EnumerableDataSource<Point>(pts);
	ds.SetXYMapping(pt => pt);
	return ds;
}
 
private IPointDataSource CreateLineDataSource(double phase)
{
	const int N = 100;
	Point[] pts = new Point[N];
	for (int i = 0; i < N; i++)
	{
		double x = i / (N / 10.0);
		pts[i] = new Point(x, x * phase + phase);
	}
	var ds = new EnumerableDataSource<Point>(pts);
	ds.SetXYMapping(pt => pt);
	return ds;
}

IsolineSampleApp

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
 
		Loaded += new RoutedEventHandler(Window1_Loaded);
	}
 
	private void Window1_Loaded(object sender, RoutedEventArgs e)
	{
		LoadField();
	}
 
	private static string[] ParseDataString(string str)
	{
		str = str.TrimEnd(' ');
		return str.Split(' ');
	}
 
	private static string[] ParseGridString(string str)
	{
		return str.TrimEnd(' ').
			Substring(0, str.Length - 3).
			TrimStart('{').
			Split(new string[] { " } {" }, StringSplitOptions.None);
	}
 
	private void LoadField()
	{
		var assembly = Assembly.GetExecutingAssembly();
 
		List<string> strings = new List<string>();
		using (Stream stream = assembly.GetManifestResourceStream("IsolineSampleApp.SampleData.txt"))
		{
			using (StreamReader reader = new StreamReader(stream))
			{
				while (!reader.EndOfStream)
				{
					string str = reader.ReadLine();
					if (str == "Data:")
					{
						// do nothing
					}
					else if (str == "Grid:")
					{
						// do nothing too
					}
					else
					{
						strings.Add(str);
					}
				}
			}
		}
 
		// data
		string[] nums = ParseDataString(strings[0]);
		int width = nums.Length;
		int height = strings.Count / 2;
 
		CultureInfo culture = new CultureInfo("ru-RU");
 
		double[,] data = new double[width, height];
		for (int row = 0; row < height; row++)
		{
			nums = ParseDataString(strings[row]);
			for (int column = 0; column < width; column++)
			{
				double d = Double.Parse(nums[column], culture);
				data[column, row] = d;
			}
		}
 
		Point[,] gridData = new Point[width, height];
		for (int row = 0; row < height; row++)
		{
			string str = strings[row + height];
			nums = ParseGridString(str);
			for (int column = 0; column < width; column++)
			{
				string[] vecStrs = nums[column].Split(new string[] { "; " }, StringSplitOptions.None);
				gridData[column, row] = new Point(
					Double.Parse(vecStrs[0], culture),
					Double.Parse(vecStrs[1], culture));
			}
		}
 
		WarpedDataSource2D<double> dataSource = new WarpedDataSource2D<double>(data, gridData);
		isolineGraph.DataSource = dataSource;
		trackingGraph.DataSource = dataSource;
 
		DataRect visible = dataSource.GetGridBounds();
		plotter.Viewport.Visible = visible;
	}
}
<d3:ChartPlotter Name="plotter">
	<d3:FastIsolineDisplay Name="isolineGraph" DrawLabels="True" WayBeforeTextMultiplier="20"/>
	<d3:IsolineTrackingGraph Name="trackingGraph"/>
	<d3:CursorCoordinateGraph/>
</d3:ChartPlotter>

Simulation

/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    // Three observable data sources. Observable data source contains
    // inside ObservableCollection. Modification of collection instantly modify
    // visual representation of graph. 
    ObservableDataSource<Point> source1 = null;
    ObservableDataSource<Point> source2 = null;
    ObservableDataSource<Point> source3 = null;
 
    public Window1()
    {
        InitializeComponent();
    }
 
    private void Simulation()
    {
        CultureInfo culture = CultureInfo.InvariantCulture;
        Assembly executingAssembly = Assembly.GetExecutingAssembly();
        // load spim-generated data from embedded resource file
        const string spimDataName = "Simulation.Repressilator.txt";
        using (Stream spimStream = executingAssembly.GetManifestResourceStream(spimDataName))
        {
            using (StreamReader r = new StreamReader(spimStream))
            {
                string line = r.ReadLine();
                while (!r.EndOfStream)
                {
                    line = r.ReadLine();
                    string[] values = line.Split(',');
 
                    double x = Double.Parse(values[0], culture);
                    double y1 = Double.Parse(values[1], culture);
                    double y2 = Double.Parse(values[2], culture);
                    double y3 = Double.Parse(values[3], culture);
 
                    Point p1 = new Point(x, y1);
                    Point p2 = new Point(x, y2);
                    Point p3 = new Point(x, y3);
 
                    source1.AppendAsync(Dispatcher, p1);
                    source2.AppendAsync(Dispatcher, p2);
                    source3.AppendAsync(Dispatcher, p3);
 
                    Thread.Sleep(10); // Long-long time for computations...
                }
            }
        }
    }
 
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // Create first source
        source1 = new ObservableDataSource<Point>();
        // Set identity mapping of point in collection to point on plot
        source1.SetXYMapping(p => p);
 
        // Create second source
        source2 = new ObservableDataSource<Point>();
        // Set identity mapping of point in collection to point on plot
        source2.SetXYMapping(p => p);
 
        // Create third source
        source3 = new ObservableDataSource<Point>();
        // Set identity mapping of point in collection to point on plot
        source3.SetXYMapping(p => p);
 
        // Add all three graphs. Colors are not specified and chosen random
        plotter.AddLineGraph(source1, 2, "Data row 1");
        plotter.AddLineGraph(source2, 2, "Data row 2");
        plotter.AddLineGraph(source3, 2, "Data row 3");
 
        // Start computation process in second thread
        Thread simThread = new Thread(new ThreadStart(Simulation));
        simThread.IsBackground = true;
        simThread.Start();
    }
}
<Grid>
    <d3:ChartPlotter Name="plotter"/>
</Grid>

PlotterLayoutPanels

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
	}
 
	private void ChartPlotter_Loaded(object sender, RoutedEventArgs e)
	{
		plotter.LeftPanel.Background = Brushes.DodgerBlue.MakeTransparent(0.4);
		plotter.RightPanel.Background = Brushes.Salmon.MakeTransparent(0.4);
		plotter.BottomPanel.Background = Brushes.PaleGreen;
		plotter.TopPanel.Background = Brushes.Gold;
 
		plotter.HeaderPanel.Background = Brushes.GreenYellow;
		plotter.FooterPanel.Background = Brushes.DarkOrchid.MakeTransparent(0.4);
 
		//NumericAxis horizAxis = plotter.MainHorizontalAxis as NumericAxis;
		//horizAxis.AxisControl.IsStaticAxis = true;
 
		//NumericAxis vertAxis = plotter.MainVerticalAxis as NumericAxis;
		//vertAxis.AxisControl.IsStaticAxis = true;
	}
}
<Grid>
	<d3:ChartPlotter Name="plotter" Loaded="ChartPlotter_Loaded">
		<d3:ChartPlotter.Template>
			<ControlTemplate TargetType="{x:Type d3:Plotter}">
				<Grid Name="PART_ContentsGrid" Background="{TemplateBinding Background}" DataContext="{TemplateBinding DataContext}">
					<Grid.RowDefinitions>
						<RowDefinition Height="auto"/>
						<RowDefinition/>
						<RowDefinition Height="auto"/>
					</Grid.RowDefinitions>
 
					<StackPanel Name="PART_HeaderPanel" Orientation="Vertical" Grid.Row="0"/>
 
					<Grid Name="PART_MainGrid" Grid.Row="1">
						<Grid.RowDefinitions>
							<RowDefinition Height="auto"/>
							<RowDefinition/>
							<RowDefinition Height="auto"/>
						</Grid.RowDefinitions>
						<Grid.ColumnDefinitions>
							<ColumnDefinition Width="auto"/>
							<ColumnDefinition/>
							<ColumnDefinition Width="auto"/>
						</Grid.ColumnDefinitions>
 
						<StackPanel Name="PART_LeftPanel" Grid.Column="0" Grid.Row="1" Orientation="Horizontal" Background="Transparent"/>
						<StackPanel Name="PART_RightPanel" Grid.Column="2" Grid.Row="1" Orientation="Horizontal" Background="Transparent"/>
						<StackPanel Name="PART_BottomPanel" Grid.Column="1" Grid.Row="2" Orientation="Vertical" Background="Transparent"/>
						<StackPanel Name="PART_TopPanel" Grid.Column="1" Grid.Row="0" Orientation="Vertical" Background="Transparent"/>
 
						<!-- Border of viewport -->
						<Rectangle Name="Border" Grid.Column="1" Grid.Row="1" Stroke="{TemplateBinding BorderBrush}" 
								 StrokeThickness="{TemplateBinding BorderThickness}"/>
 
						<Grid Name="PART_CentralGrid" Grid.Column="1" Grid.Row="1" Background="Transparent">
							<Rectangle Stroke="DarkGray" StrokeThickness="3" RadiusX="4" RadiusY="4" Panel.ZIndex="1" Fill="#70FFFFA0">
								<Rectangle.RenderTransform>
									<TranslateTransform X="10" Y="10"/>
								</Rectangle.RenderTransform>
							</Rectangle>
							<Rectangle Stroke="Black" StrokeThickness="3" RadiusX="4" RadiusY="4" Panel.ZIndex="1" Fill="#50FFA0FF">
								<Rectangle.RenderTransform>
									<TranslateTransform X="30" Y="30"/>
								</Rectangle.RenderTransform>
							</Rectangle>
							<Canvas Panel.ZIndex="2">
								<TextBlock Text="CentralGrid" FontSize="12" Canvas.Top="12" Canvas.Left="14"/>
								<TextBlock Text="MainCanvas" FontSize="12" Canvas.Top="32" Canvas.Left="34"/>
							</Canvas>
						</Grid>
						<Canvas Name="PART_MainCanvas" Grid.Column="1" Grid.Row="1" ClipToBounds="True"/>
 
					</Grid>
 
					<Canvas Name="PART_ParallelCanvas" Grid.Column="1" Grid.Row="1"/>
 
					<StackPanel Name="PART_FooterPanel" Orientation="Vertical" Grid.Row="2"/>
 
					<Rectangle Stroke="DarkGray" StrokeThickness="3" Grid.RowSpan="3"/>
				</Grid>
			</ControlTemplate>
		</d3:ChartPlotter.Template>
		<d3:Header>HeaderPanel</d3:Header>
 
		<d3:HorizontalAxis Placement="Top"/>
		<d3:VerticalAxis Placement="Right"/>
		
		<d3:HorizontalAxisTitle Placement="Bottom">BottomPanel</d3:HorizontalAxisTitle>
		<d3:HorizontalAxisTitle Placement="Top">TopPanel</d3:HorizontalAxisTitle>
		<d3:VerticalAxisTitle Placement="Left">LeftPanel</d3:VerticalAxisTitle>
		<d3:VerticalAxisTitle Placement="Right">RightPanel</d3:VerticalAxisTitle>
		
		<d3:Footer>FooterPanel</d3:Footer>
	</d3:ChartPlotter>
</Grid>

5x1000Charts

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
	}
 
	private double[] xs;
	private double[] y1;
	private double[] y2;
	private double[] y3;
	private double[] y4;
	private double[] y5;
 
	private const int count = 1000;
	bool useFilters = true;
 
	private readonly Random rnd = new Random();
 
	private void AddChartsBtn_Click(object sender, RoutedEventArgs e)
	{
		using (new DisposableTimer("adding charts"))
		{
			xs = Enumerable.Range(0, count).Select(i => (double)i).ToArray();
 
			y1 = Enumerable.Range(0, count).Select(i => 1 + rnd.NextDouble()).ToArray();
			y2 = Enumerable.Range(0, count).Select(i => 2 + rnd.NextDouble()).ToArray();
			y3 = Enumerable.Range(0, count).Select(i => 3 + rnd.NextDouble()).ToArray();
			y4 = Enumerable.Range(0, count).Select(i => 4 + rnd.NextDouble()).ToArray();
			y5 = Enumerable.Range(0, count).Select(i => 5 + rnd.NextDouble()).ToArray();
 
			var xds = xs.AsXDataSource();
			var ds1 = xds.Join(y1.AsYDataSource());
			var ds2 = xds.Join(y2.AsYDataSource());
			var ds3 = xds.Join(y3.AsYDataSource());
			var ds4 = xds.Join(y4.AsYDataSource());
			var ds5 = xds.Join(y5.AsYDataSource());
 
			plotter.Visible = new DataRect(-100, 0, 1200, 7);
 
			if (!useFilters)
			{
				plotter.Children.Add(new LineGraph { DataSource = ds1, Stroke = ColorHelper.RandomBrush });
				plotter.Children.Add(new LineGraph { DataSource = ds2, Stroke = ColorHelper.RandomBrush });
				plotter.Children.Add(new LineGraph { DataSource = ds3, Stroke = ColorHelper.RandomBrush });
				plotter.Children.Add(new LineGraph { DataSource = ds4, Stroke = ColorHelper.RandomBrush });
				plotter.Children.Add(new LineGraph { DataSource = ds5, Stroke = ColorHelper.RandomBrush });
			}
			else
			{
				plotter.AddLineGraph(ds1);
				plotter.AddLineGraph(ds2);
				plotter.AddLineGraph(ds3);
				plotter.AddLineGraph(ds4);
				plotter.AddLineGraph(ds5);
			}
		}
	}
}	
<Grid>
	<d3:ChartPlotter Name="plotter">
		<Button Content="Add charts" Click="AddChartsBtn_Click"/>
	</d3:ChartPlotter>
</Grid>

ArcSegmentPlotting

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
	}
 
	private void Window_Loaded(object sender, RoutedEventArgs e)
	{
		AddLineGraph(new Point(0, 0), new Point(0, 50));
		AddCircularArcGraph(new Point(0, 0), new Point(50, -50), new Size(50, 50));
		AddLineGraph(new Point(50, 50), new Point(0, 50));
	}
 
	private void AddLineGraph(Point startPoint, Point endPoint)
	{
		var ds = new EnumerableDataSource<Point>(new List<Point> {
    new Point { X = startPoint.X, Y = startPoint.Y }, 
    new Point { X = endPoint.X, Y = endPoint.Y } 
  });
		ds.SetXYMapping(pt => pt);
 
		plotter.AddLineGraph(ds);
	}
 
	private void AddCircularArcGraph(Point startPoint, Point endPoint, Size size)
	{
		PathFigure pf = new PathFigure();
		pf.StartPoint = new Point(startPoint.X, startPoint.Y);
 
		ArcSegment arcSegment = new ArcSegment();
		arcSegment.Point = new Point(endPoint.X, endPoint.Y);
		arcSegment.Size = size;
		arcSegment.SweepDirection = SweepDirection.Counterclockwise;
 
		PathSegmentCollection psc = new PathSegmentCollection();
		psc.Add(arcSegment);
 
		pf.Segments = psc;
 
		PathFigureCollection pfc = new PathFigureCollection();
		pfc.Add(pf);
 
		PathGeometry pg = new PathGeometry();
		pg.Figures = pfc;
 
		var path = new Path();
		path.Stroke = Brushes.Black;
		path.StrokeThickness = 1;
		path.Data = pg;
		path.Fill = Brushes.Orange;
		path.Stretch = Stretch.Fill;
 
		var viewportPanel = new ViewportHostPanel();
		ViewportPanel.SetViewportBounds(path, new DataRect(0, 0, 50, 50));
		viewportPanel.Children.Add(path);
		plotter.Children.Add(viewportPanel);
	}
}
<Grid>
	<d3:ChartPlotter Name="plotter"></d3:ChartPlotter>
</Grid>

DateTimeAxisBug

<Grid>
    <d3:ChartPlotter Name="plotter">
        <d3:ChartPlotter.MainHorizontalAxis>
            <d3:HorizontalDateTimeAxis/>
        </d3:ChartPlotter.MainHorizontalAxis>
		<d3:ChartPlotter.MainVerticalAxis>
			<d3:VerticalDateTimeAxis/>
		</d3:ChartPlotter.MainVerticalAxis>
    </d3:ChartPlotter>
</Grid>

DescriptionChangeOnTheFly

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
 
		Loaded += new RoutedEventHandler(Window1_Loaded);
	}
 
	LineGraph chart;
	void Window1_Loaded(object sender, RoutedEventArgs e)
	{
		double[] xs = new double[] { 1, 2, 3 };
		double[] ys = new double[] { 0.1, 0.4, 0.2 };
 
		var ds = xs.AsXDataSource().Join(ys.AsYDataSource());
 
		chart = plotter.AddLineGraph(ds, "Chart1"); 
	}
 
	private void changeDescrBtn_Click(object sender, RoutedEventArgs e)
	{
		chart.Stroke = Brushes.Red;
		chart.StrokeThickness = 3;
		chart.Description = new PenDescription("Chart2");
	}
}
<Grid>
	<d3:ChartPlotter Name="plotter">
		<Button Canvas.Bottom="10" Canvas.Right="10" Content="Change description" Name="changeDescrBtn" Click="changeDescrBtn_Click"/>
	</d3:ChartPlotter>
</Grid>

HandledRepro

<Grid>
	<d3:ChartPlotter Name="plotter">
		<d3:VerticalLine Value="0.1"/>
		<d3:ViewportHostPanel>
			<Rectangle Width="100" Height="100" Fill="DarkOliveGreen" d3:ViewportPanel.X="0.2" d3:ViewportPanel.Y="0.2"/>
		</d3:ViewportHostPanel>
 
		<d3:ViewportHostPanel>
			<Border d3:ViewportPanel.X="0.6" d3:ViewportPanel.Y="0.6" BorderThickness="1" BorderBrush="Black">
				<d3:ChartPlotter Width="400" Height="400">
					<d3:VerticalLine Value="0.5"/>
				</d3:ChartPlotter>
			</Border>
		</d3:ViewportHostPanel>
 
	</d3:ChartPlotter>
</Grid>	

LineGraphUpdateOnDataSourceChange

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }
 
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        var xs = Enumerable.Range(0, 100).Select(i => i * 0.1);
 
        var xDS = xs.AsXDataSource();
 
        var yDS1 = xs.Select(x => Math.Sin(x)).AsYDataSource();
        var yDS2 = xs.Select(x => Math.Cos(x) + 2).AsYDataSource();
 
        var ds1 = xDS.Join(yDS1);
        var ds2 = xDS.Join(yDS2);
 
        var list = new List<IPointDataSource>();
        list.Add(ds1);
        list.Add(ds2);
 
        DataContext = list;
    }
}
<Grid>
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="Auto"/>
		<ColumnDefinition Width="*"/>
	</Grid.ColumnDefinitions>
	
	<ListBox Name="listBox" Grid.Column="0" ItemsSource="{Binding}"/>
	
	<ContentControl DataContext="{Binding SelectedItem, ElementName=listBox}" Grid.Column="1">
		<ContentControl.Template>
			<ControlTemplate>
				<Grid>
					<d3:ChartPlotter>
						<d3:LineGraph Stroke="Red" StrokeThickness="2" DataSource="{Binding}"/>
					</d3:ChartPlotter>
				</Grid>
			</ControlTemplate>
		</ContentControl.Template>
	</ContentControl>
</Grid>

SyncedWidthSample

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
	}
 
	private void StackPanel_Loaded(object sender, RoutedEventArgs e)
	{
		plotter2.LeftPanel.SetBinding(Panel.WidthProperty, new Binding("Width") { Source = plotter1.LeftPanel });
	}
}
<StackPanel Loaded="StackPanel_Loaded">
	<d3:ChartPlotter Visible="-25000,-14000,50000,28000" Name="plotter1"/>
	<d3:ChartPlotter Visible="-25000,-2,50000,4" Name="plotter2">
		<d3:WidthSpring SourcePanel="{Binding LeftPanel, ElementName=plotter1}"/>
	</d3:ChartPlotter>
</StackPanel>

TwoAxisSample

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
 
		VerticalAxis axis = new VerticalAxis();
		axis.SetConversion(0, 100, 100, 0);
 
		plotter.Children.Add(axis);
		// this is only an example of visible rectange. Use here rect you actually need.
		plotter.Viewport.Visible = new Rect(0, 0, 1, 100);
	}
}
<Grid>
    <d3:ChartPlotter Name="plotter">
    </d3:ChartPlotter>
</Grid>

VerticalRangeNotVisibleRepro

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
	}
 
	private readonly Random rnd = new Random();
	private void AddRandomRangeBtn_Click(object sender, RoutedEventArgs e)
	{
		double min = rnd.NextDouble();
		double max = min + rnd.NextDouble();
		VerticalRange range = new VerticalRange { Value1 = min, Value2 = max, Fill = Brushes.Green };
 
		plotter.Children.Add(range);
	}
}
<Grid>
	<d3:ChartPlotter Name="plotter">
		<Button Content="Add random range" Name="AddRandomRangeBtn" Click="AddRandomRangeBtn_Click"/>
	</d3:ChartPlotter>
</Grid>

ViewportPanelBugRepro

<Grid>
	<d3:ChartPlotter>
		<d3:ViewportHostPanel>
			<d3:ViewportPanel d3:ViewportPanel.ViewportBounds="0,0,1,1" Background="#90A0B0C0">
 
				<Rectangle Fill="Green" Stroke="Black" StrokeThickness="4" Stretch="Fill"
						   d3:ViewportPanel.ViewportVerticalAlignment="Bottom"
							  d3:ViewportPanel.X="0.5"
							  d3:ViewportPanel.Y="0.5"
							  d3:ViewportPanel.ViewportHorizontalAlignment="Center"
							  d3:ViewportPanel.ViewportHeight="0.5"
							  d3:ViewportPanel.ViewportWidth="1">
				</Rectangle>
 
				<Path Name="crown" Stretch="Fill" Fill="Red" Stroke="Black" StrokeThickness="4"
					  d3:ViewportPanel.ViewportVerticalAlignment="Bottom"
					  d3:ViewportPanel.X="0.5"
					  d3:ViewportPanel.Y="0.5"
					  d3:ViewportPanel.ViewportHorizontalAlignment="Center"
					  d3:ViewportPanel.ViewportHeight="0.5"
					  d3:ViewportPanel.ViewportWidth="1">
					<Path.Data>
						<PathGeometry>
							<PathFigure StartPoint="0,1" IsClosed="True" IsFilled="True">
								<LineSegment Point="0.5,0"/>
								<LineSegment Point="1,1"/>
								<LineSegment Point="0.5,0.5"/>
								<LineSegment Point="0,1"/>
							</PathFigure>
						</PathGeometry>
					</Path.Data>
				</Path>
 
			</d3:ViewportPanel>
		</d3:ViewportHostPanel>
	</d3:ChartPlotter>
</Grid>

ZeroDifferenceRepro

#define old
 
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
 
#if old
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.Charts;
#else
#if !old
using Microsoft.Research.DynamicDataDisplay.Charts.NewLine;
#endif
using Microsoft.Research.DynamicDataDisplay.Charts;
using Microsoft.Research.DynamicDataDisplay.Charts.Axes.Numeric;
#if !old
using Microsoft.Research.DynamicDataDisplay.Charts.NewLine.Functional;
using Microsoft.Research.DynamicDataDisplay.Charts.NewLine.Filters;
#endif
#endif
 
namespace ZeroDifferenceRepro
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
	{
		public Window1()
		{
			InitializeComponent();
 
			Loaded += new RoutedEventHandler(Window1_Loaded);
		}
 
		void Window1_Loaded(object sender, RoutedEventArgs e)
		{
			plotter.Children.Add(new HorizontalLine { Value = 0, Stroke = Brushes.DarkGreen.MakeTransparent(0.2) });
			plotter.Children.Add(new VerticalLine { Value = 0, Stroke = Brushes.DarkGreen.MakeTransparent(0.2) });
 
#if old
			var xs = Enumerable.Range(0, 500).Select(i => (i - 250) * 0.02);
			var sineYDS = xs.Select(x => Math.Sin(x)).AsYDataSource();
			var atanYDS = xs.Select(x => Math.Atan(x)).AsYDataSource();
 
			var sineDS = new CompositeDataSource(xs.AsXDataSource(), sineYDS);
			var atanDS = new CompositeDataSource(xs.AsXDataSource(), atanYDS);
 
			var sineChart = plotter.AddLineGraph(sineDS);
			var atanChart = plotter.AddLineGraph(atanDS);
 
			//sineChart.Filters.Clear();
			//atanChart.Filters.Clear();
#else
			var xs = Enumerable.Range(0, 500).Select(i => (i - 250) * 0.02);
			var sineDS = xs.Select(x => new Point(x, Math.Sin(x))).AsDataSource();
			var atanDS = xs.Select(x => new Point(x, Math.Atan(x))).AsDataSource();
			var sincDS = Enumerable.Range(-5000, 10001).Select(i =>
			{
				double x = Math.PI * i / 1000;
				double y;
				if (i == 0)
					y = 100;
				else
					y = Math.Sin(x * 100);
				return new Point(x, y);
			}).AsDataSource();
 
			LineChart sincChart = new LineChart { Stroke = ColorHelper.RandomBrush, DataSource = sincDS };
			//plotter.Children.Add(sincChart);
 
 
			LineChart sineChart = new LineChart { Stroke = ColorHelper.RandomBrush, DataSource = sineDS };
			plotter.Children.Add(sineChart);
			LineChart atanChart = new LineChart { Stroke = ColorHelper.RandomBrush, DataSource = atanDS };
			plotter.Children.Add(atanChart);
#endif
		}
	}
}
<Grid>
    <d3:ChartPlotter Name="plotter">
    </d3:ChartPlotter>
</Grid>

PointsOnMapSampleApp

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
 
		dataTypeCombobox.ItemsSource = new List<DataType> 
		{ 
			DataType.Temp, DataType.Rainfall, DataType.SoilDepth 
		};
 
		Loaded += Window1_Loaded;
	}
 
	void Window1_Loaded(object sender, RoutedEventArgs e)
	{
		LoadData();
 
		dataTypeCombobox.SelectedIndex = 0;
		bool res = dataTypeCombobox.Focus();
	}
 
	private EnumerableDataSource<DataPoint> CreateDataSource(IEnumerable<DataPoint> data)
	{
		EnumerableDataSource<DataPoint> ds = new EnumerableDataSource<DataPoint>(data);
 
		MercatorTransform transform = new MercatorTransform();
 
		ds.SetXMapping(p => p.X);
		ds.SetYMapping(p => transform.DataToViewport(new Point(0, p.Y)).Y);
		ds.AddMapping(CirclePointMarker.FillProperty, dp =>
		{
			double alpha = (dp.Data - currentRange.Min) / (currentRange.Max - currentRange.Min);
 
			Debug.Assert(0 <= alpha && alpha <= 1);
 
			const double hueWidth = 100;
			double hue = hueWidth * (alpha - 0.5) + hueSlider.Value;
 
			if (hue > 360) hue -= 360;
			else if (hue < 0) hue += 360;
 
			Debug.Assert(0 <= hue && hue <= 360);
 
			Color mainColor = new HsbColor(hue, 1, 0 + 1 * alpha, 0.3 + 0.7 * alpha).ToArgbColor();
 
			const int colorCount = 5;
			GradientStopCollection colors = new GradientStopCollection(colorCount);
 
			double step = 1.0 / (colorCount - 1);
			for (int i = 0; i < colorCount; i++)
			{
				Color color = mainColor;
				double x = attSlider.Value * step * i;
				color.A = (byte)(255 * Math.Exp(-x * x));
				colors.Add(new GradientStop(color, step * i));
			}
 
			return new RadialGradientBrush(colors);
		});
		return ds;
	}
 
	private List<SampleDataPoint> loadedData = new List<SampleDataPoint>();
	private void LoadData()
	{
		string[] strings = File.ReadAllLines("example_for_visualization.csv");
 
		// skipping 1st line, parsing all other lines
		for (int i = 1; i < strings.Length; i++)
		{
			SampleDataPoint point = ParseDataPoint(strings[i]);
			loadedData.Add(point);
		}
 
		tempRange.Min = loadedData.Min(p => p.Temp);
		tempRange.Max = loadedData.Max(p => p.Temp);
 
		rainfallRange.Min = loadedData.Min(p => p.RainFall);
		rainfallRange.Max = loadedData.Max(p => p.RainFall);
 
		soildepthRange.Min = loadedData.Min(p => p.SoilDepth);
		soildepthRange.Max = loadedData.Max(p => p.SoilDepth);
	}
 
	MinMax tempRange = new MinMax();
	MinMax rainfallRange = new MinMax();
	MinMax soildepthRange = new MinMax();
 
	MinMax currentRange;
	private SampleDataPoint ParseDataPoint(string str)
	{
		var pieces = str.Split(',');
 
		SampleDataPoint res = new SampleDataPoint();
 
		res.Lat = Double.Parse(pieces[0], CultureInfo.InvariantCulture);
		res.Lon = Double.Parse(pieces[1], CultureInfo.InvariantCulture);
 
		res.Temp = Double.Parse(pieces[2], CultureInfo.InvariantCulture);
		res.RainFall = Double.Parse(pieces[3], CultureInfo.InvariantCulture);
		res.SoilDepth = Double.Parse(pieces[4], CultureInfo.InvariantCulture);
 
		return res;
	}
 
	private IEnumerable<DataPoint> GetSampleData(DataType dataType)
	{
		switch (dataType)
		{
			case DataType.Temp:
				currentRange = tempRange;
				return loadedData.Select(dp => new DataPoint { X = dp.Lon, Y = dp.Lat, Data = dp.Temp }).ToList();
			case DataType.Rainfall:
				currentRange = rainfallRange;
				return loadedData.Select(dp => new DataPoint { X = dp.Lon, Y = dp.Lat, Data = dp.RainFall }).ToList();
			case DataType.SoilDepth:
				currentRange = soildepthRange;
				return loadedData.Select(dp => new DataPoint { X = dp.Lon, Y = dp.Lat, Data = dp.SoilDepth }).ToList();
			default:
				throw new InvalidOperationException();
		}
	}
 
	private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
	{
		if (plotter == null)
			return;
 
		var graph = plotter.Children.OfType<MarkerPointsGraph>().FirstOrDefault();
		if (graph != null)
			graph.InvalidateVisual();
	}
 
	private void dataTypeCombobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
	{
		if (graph == null || plotter == null)
			return;
 
		graph.DataSource = CreateDataSource(GetSampleData((DataType)dataTypeCombobox.SelectedValue));
	}
 
	private void Hyperlink_Click(object sender, RoutedEventArgs e)
	{
		Hyperlink link = (Hyperlink)sender;
		Process.Start(link.NavigateUri.ToString());
	}
}
 
class MinMax
{
	public double Min { get; set; }
	public double Max { get; set; }
}
 
class SampleDataPoint
{
	public double Lat { get; set; }
	public double Lon { get; set; }
	public double Temp { get; set; }
	public double RainFall { get; set; }
	public double SoilDepth { get; set; }
}
 
class DataPoint
{
	public double X { get; set; }
	public double Y { get; set; }
 
	public double Data { get; set; }
}
 
enum DataType
{
	Temp,
	Rainfall,
	SoilDepth
}
<Grid>
	<Grid.Resources>
		<Style TargetType="{x:Type TextBlock}">
			<Setter Property="Margin" Value="0,0,10,0"/>
		</Style>
 
		<Style TargetType="{x:Type Slider}">
			<Setter Property="TickPlacement" Value="BottomRight"/>
			<Setter Property="AutoToolTipPlacement" Value="BottomRight"/>
		</Style>
 
		<Style TargetType="{x:Type DockPanel}">
			<Setter Property="Margin" Value="3,5,3,5"/>
		</Style>
	</Grid.Resources>
 
	<d3:ChartPlotter Name="plotter">
		
 
		<d3:MarkerPointsGraph Name="graph" IsHitTestVisible="False">
			<d3:MarkerPointsGraph.Marker>
				<d3:CirclePointMarker Size="20"/>
			</d3:MarkerPointsGraph.Marker>
		</d3:MarkerPointsGraph>
 
		<d3:CursorCoordinateGraph/>
 
		<Grid Width="300" Canvas.Bottom="10" Canvas.Right="10">
			<Rectangle Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Grid.RowSpan="3" RadiusX="5" RadiusY="5" Fill="#A9FFFFFF" Stroke="LightGray"/>
		
			<Grid Margin="5">
				<Grid.RowDefinitions>
					<RowDefinition/>
					<RowDefinition/>
					<RowDefinition/>
				</Grid.RowDefinitions>
 
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="Auto"/>
					<ColumnDefinition Width="*"/>
				</Grid.ColumnDefinitions>
 
 
				<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="2">Data Type:</TextBlock>
				<ComboBox Grid.Row="0" Grid.Column="1" Name="dataTypeCombobox" SelectionChanged="dataTypeCombobox_SelectionChanged" Margin="1"/>
 
				<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="2">Text Hue:</TextBlock>
				<Slider Minimum="0" Maximum="360" Grid.Row="1" Grid.Column="1" Value="180"
				ValueChanged="Slider_ValueChanged" Name="hueSlider"/>
 
				<TextBlock Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="2">Attenuation:</TextBlock>
				<Slider Minimum="0" Maximum="4" Value="2.3" Grid.Row="2" Grid.Column="1"
					ValueChanged="Slider_ValueChanged" Name="attSlider"/>
			</Grid>
		</Grid>
		
		<TextBlock Canvas.Bottom="10" Canvas.Left="10">
			Data from <Hyperlink FontFamily="Consolas" FontSize="14" NavigateUri="http://www.nass.usda.gov" Click="Hyperlink_Click">http://www.nass.usda.gov</Hyperlink>
		</TextBlock>
 
	</d3:ChartPlotter>
</Grid>

HideAxisSample

<Grid>
    <Grid.Resources>
        <Style TargetType="{x:Type d3:Segment}">
            <Setter Property="Stroke" Value="Orange"/>
 
            <Style.Triggers>
                <EventTrigger RoutedEvent="Shape.Loaded">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation 
								From="1" 
								To="10" 
								Duration="0:0:3" 
								AutoReverse="True"
								RepeatBehavior="Forever"
								Storyboard.TargetProperty="StrokeThickness"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>
 
    <d3:ChartPlotter Name="plotter" MainHorizontalAxis="{x:Null}" MainVerticalAxis="{x:Null}">
 
 
        <d3:Segment StartPoint="{Binding ElementName=point0, Path=Position}" 
					EndPoint="{Binding ElementName=point1, Path=Position}"
					Style="{x:Null}"/>
        <d3:Segment StartPoint="{Binding ElementName=point0, Path=Position}" 
					EndPoint="{Binding ElementName=point2, Path=Position}"
					Style="{x:Null}"/>
        <d3:Segment StartPoint="{Binding ElementName=point0, Path=Position}" 
					EndPoint="{Binding ElementName=point3, Path=Position}"
					Style="{x:Null}"/>
        <d3:Segment StartPoint="{Binding ElementName=point0, Path=Position}" 
					EndPoint="{Binding ElementName=point4, Path=Position}"
					Style="{x:Null}"/>
 
        <d3:DraggablePoint Position="0.4, 0.75" Name="point0"/>
        <d3:DraggablePoint Position="0.8, 0.8" Name="point1"/>
        <d3:DraggablePoint Position="0.7, 0.6" Name="point2"/>
        <d3:DraggablePoint Position="0.7, 0.9" Name="point3"/>
        <d3:DraggablePoint Position="0.6, 0.55" Name="point4"/>
 
        <d3:HorizontalLine Value="{Binding ElementName=point0, Path=Position.Y}" Stroke="Aquamarine"/>
 
        <!-- H -->
        <d3:Segment StartPoint="0.2, 0.2" EndPoint="0.2, 0.4"/>
        <d3:Segment StartPoint="0.2, 0.3" EndPoint="0.25, 0.3"/>
        <d3:Segment StartPoint="0.25, 0.2" EndPoint="0.25, 0.4"/>
 
        <!-- E -->
        <d3:Segment StartPoint="0.3, 0.3" EndPoint="0.35, 0.4"/>
        <d3:Segment StartPoint="0.3, 0.3" EndPoint="0.35, 0.2"/>
        <d3:Segment StartPoint="0.3, 0.3" EndPoint="0.4, 0.3"/>
        <d3:Segment StartPoint="0.35, 0.4" EndPoint="0.4, 0.3"/>
 
        <!-- L -->
        <d3:Segment StartPoint="0.45, 0.2" EndPoint="0.45, 0.4"/>
        <d3:Segment StartPoint="0.45, 0.2" EndPoint="0.5, 0.2"/>
 
        <!-- L -->
        <d3:Segment StartPoint="0.55, 0.2" EndPoint="0.55, 0.4"/>
        <d3:Segment StartPoint="0.55, 0.2" EndPoint="0.6, 0.2"/>
 
        <!-- O -->
        <d3:Segment StartPoint="0.65, 0.3" EndPoint="0.7, 0.2"/>
        <d3:Segment StartPoint="0.65, 0.3" EndPoint="0.7, 0.4"/>
        <d3:Segment StartPoint="0.7, 0.2" EndPoint="0.75, 0.3"/>
        <d3:Segment StartPoint="0.7, 0.4" EndPoint="0.75, 0.3"/>
 
        <d3:ViewportPolyline Points="0.1,0.1 0.1,0.2 0.2,0.2 0.2,0.1"/>
        <d3:ViewportPolygon Points="0.3,0.6 0.3,0.7 0.35,0.6 0.2,0.8 0.4,0.65, 0.4,0.7">
            <d3:ViewportPolygon.Fill>
                <VisualBrush Viewport="0.3,0.3,0.4,0.4">
                    <VisualBrush.Visual>
                        <MediaElement Source="C:\Users\Public\Videos\Sample Videos\Bear.wmv"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </d3:ViewportPolygon.Fill>
        </d3:ViewportPolygon>
 
        <d3:CursorCoordinateGraph/>
 
    </d3:ChartPlotter>
</Grid>

AxisColoringSample

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
 
		// setting custom colors of background, axis text labels and axis ticks:
		{
			// background brush is for axis's background
			plotter.MainHorizontalAxis.Background = Brushes.Aqua.MakeTransparent(0.1);
			// foreground bruhs is for axis's labels foreground
			plotter.MainHorizontalAxis.Foreground = Brushes.DarkMagenta;
 
			plotter.MainVerticalAxis.Background = new LinearGradientBrush(Colors.White, Colors.LightBlue, 90);
			plotter.MainVerticalAxis.Foreground = Brushes.DarkGoldenrod;
 
			// stroke brush is 
			// ------ /*to rule them all*/ ------
			// for ticks' fill
			((NumericAxis)plotter.MainHorizontalAxis).AxisControl.TicksPath.Stroke = Brushes.OrangeRed;
		}
 
		// this will make the most left axis to display ticks as percents
		secondAxis.LabelProvider = new ToStringLabelProvider();
		secondAxis.LabelProvider.LabelStringFormat = "{0}%";
		secondAxis.LabelProvider.SetCustomFormatter(info => (info.Tick * 100).ToString());
		// percent values that can be divided by 50 without a remainder will be red and with bigger font size
		secondAxis.LabelProvider.SetCustomView((info, ui) =>
		{
			if (((int)Math.Round(info.Tick * 100)) % 50 == 0)
			{
				TextBlock text = (TextBlock)ui;
				text.Foreground = Brushes.Red;
				text.FontSize = 20;
			}
		});
 
		// you can add new axes not only from XAML, but from C#, too:
		HorizontalDateTimeAxis thirdAxis = new HorizontalDateTimeAxis();
		thirdAxis.LabelProvider.SetCustomFormatter(info =>
		{
			DifferenceIn dateTimeDifference = (DifferenceIn)info.Info;
			if (dateTimeDifference == DifferenceIn.Minute)
			{
				return info.Tick.ToString("%m:ss");
			}
 
			// null should be returned if you want to use default label text
			return null;
		});
 
		// let's have major labels for hours in Spanish,
		// for other time periods your Thread.CurrentThread.CurrentCulture will be used to format date.
		// You can change this default thread culture and get desired look of labels.
 
		CultureInfo culture = new CultureInfo("es-ES");
		thirdAxis.MajorLabelProvider.SetCustomFormatter(info =>
		{
			MajorLabelsInfo mInfo = (MajorLabelsInfo)info.Info;
			if ((DifferenceIn)mInfo.Info == DifferenceIn.Hour)
			{
				return info.Tick.ToString("MMMM/dd %m:ss", culture);
			}
			return null;
		});
		plotter.Children.Add(thirdAxis);
	}
}
<Grid>
    <d3:ChartPlotter Name="plotter">
        <d3:NumericAxis Placement="Left" Name="secondAxis"/>
    </d3:ChartPlotter>
</Grid>

Animation

public partial class AnimatedSampleWindow : Window
{
    double phase = 0;
    readonly double[] animatedX = new double[1000];
    readonly double[] animatedY = new double[1000];
    EnumerableDataSource<double> animatedDataSource = null;
 
    /// <summary>Programmatically created header</summary>
    Header chartHeader = new Header();
 
    /// <summary>Text contents of header</summary>
    TextBlock headerContents = new TextBlock();
 
    /// <summary>Timer to animate data</summary>
    readonly DispatcherTimer timer = new DispatcherTimer();  
 
    public AnimatedSampleWindow()
    {
        InitializeComponent();
        headerContents.FontSize = 24;
        headerContents.Text = "Phase = 0.00";
        headerContents.HorizontalAlignment = HorizontalAlignment.Center;
        chartHeader.Content = headerContents;
        plotter.Children.Add(chartHeader);
    }
 
    private void AnimatedPlot_Timer(object sender, EventArgs e)
    {
        phase += 0.01;
        if (phase > 2 * Math.PI)
            phase -= 2 * Math.PI;
        for (int i = 0; i < animatedX.Length; i++)
            animatedY[i] = Math.Sin(animatedX[i] + phase);
 
        // Here it is - signal that data is updated
        animatedDataSource.RaiseDataChanged();
        headerContents.Text = String.Format(CultureInfo.InvariantCulture, "Phase = {0:N2}", phase);
    }
 
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < animatedX.Length; i++)
        {
            animatedX[i] = 2 * Math.PI * i / animatedX.Length;
            animatedY[i] = Math.Sin(animatedX[i]);
        }
        EnumerableDataSource<double> xSrc = new EnumerableDataSource<double>(animatedX);
        xSrc.SetXMapping(x => x);
        animatedDataSource = new EnumerableDataSource<double>(animatedY);
        animatedDataSource.SetYMapping(y => y);
 
        // Adding graph to plotter
        var line = plotter.AddLineGraph(new CompositeDataSource(xSrc, animatedDataSource),
            new Pen(Brushes.Magenta, 3),                
            new PenDescription("Sin(x + phase)"));
 
		timer.Interval = TimeSpan.FromMilliseconds(10);
        timer.Tick += AnimatedPlot_Timer;
        timer.Start();
    }
}
<Grid>
    <d3:ChartPlotter Name="plotter" Background="Transparent"/>
</Grid>	

ImageHistogram

public partial class Window1 : Window
{
	public Window1()
	{
		InitializeComponent();
	}
 
	private void OnOpenImageClick(object sender, RoutedEventArgs e)
	{
		OpenFileDialog dlg = new OpenFileDialog();
		dlg.Filter = "Image|*.bmp;*.jpg;*.png;*.gif";
		if (dlg.ShowDialog(this).GetValueOrDefault(false))
		{
			OpenImage(dlg.FileName);
		}
	}
 
	private void OpenImage(string fileName)
	{
		BitmapImage bmp = new BitmapImage(new Uri(fileName));
		bmp.CacheOption = BitmapCacheOption.OnLoad;
 
		image.Source = bmp;
		ProcessImage(bmp);
	}
 
	EnumerableDataSource<int> red;
	EnumerableDataSource<int> green;
	EnumerableDataSource<int> blue;
 
	int[] reds = new int[256];
	int[] greens = new int[256];
	int[] blues = new int[256];
	byte[] pixels;
	private void ProcessImage(BitmapImage bmp)
	{
		byte[] pixels = new byte[bmp.PixelWidth * bmp.PixelHeight * 4];
		bmp.CopyPixels(pixels, bmp.PixelWidth * 4, 0);
 
		for (int i = 0; i < pixels.Length; )
		{
			//BGRA
			blues[pixels[i++]]++;
			greens[pixels[i++]]++;
			reds[pixels[i++]]++;
			i++;
		}
 
		CreateHistograms();
	}
 
	private void CreateHistograms()
	{
		EnumerableDataSource<int> x = new EnumerableDataSource<int>(Enumerable.Range(0, 256).ToArray());
		x.SetXMapping(_x => _x);
 
		Func<int, double> mapping;
		if (check.IsChecked.GetValueOrDefault())
			mapping = logMapping;
		else
			mapping = linearMapping;
 
		red = new EnumerableDataSource<int>(reds);
		red.SetYMapping(mapping);
		green = new EnumerableDataSource<int>(greens);
		green.SetYMapping(mapping);
		blue = new EnumerableDataSource<int>(blues);
		blue.SetYMapping(mapping);
 
		CompositeDataSource rDS = new CompositeDataSource(x, red);
		CompositeDataSource gDS = new CompositeDataSource(x, green);
		CompositeDataSource bDS = new CompositeDataSource(x, blue);
 
		plotter.RemoveUserElements();
		plotter.AddLineGraph(rDS, Colors.Red, 1, "Red").FilteringEnabled = false;
		plotter.AddLineGraph(gDS, Colors.Green, 1, "Green").FilteringEnabled = false;
		plotter.AddLineGraph(bDS, Colors.Blue, 1, "Blue").FilteringEnabled = false;
	}
 
	private Func<int, double> logMapping = i => i > 0 ? Math.Log10(i) : 0;
	private Func<int, double> linearMapping = i => i;
 
	private void CheckBox_Checked(object sender, RoutedEventArgs e)
	{
		Func<int, double> mapping;
		if (check.IsChecked.GetValueOrDefault())
			mapping = logMapping;
		else
			mapping = linearMapping;
 
		red.SetYMapping(mapping);
		green.SetYMapping(mapping);
		blue.SetYMapping(mapping);
	}
}
<DockPanel>
	<Menu DockPanel.Dock="Top">
		<MenuItem Header="Open image..." Click="OnOpenImageClick"/>
		<Separator/>
		<CheckBox Name="check" Content="Use log mapping" Checked="CheckBox_Checked" 
							Unchecked="CheckBox_Checked" IsChecked="False"/>
	</Menu>
	<Grid DockPanel.Dock="Bottom">
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*"/>
			<ColumnDefinition Width="3*"/>
		</Grid.ColumnDefinitions>
 
		<Image Name="image" Grid.Column="0"/>
		
		<c:ChartPlotter Name="plotter" Grid.Column="1">
			<c:Header Content="Histogram of image" FontSize="20" FontFamily="Georgia"  c:Plotter.IsDefaultElement="True"/>
			<c:HorizontalAxisTitle Content="Color [0..255]" FontFamily="Georgia" c:Plotter.IsDefaultElement="True"/>
			<c:VerticalAxisTitle Content="Distribution of pixels' color" FontFamily="Georgia" c:Plotter.IsDefaultElement="True"/>
		</c:ChartPlotter>
	</Grid>
</DockPanel>

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF(Windows Presentation Foundation)是一种用于创建和管理Windows应用程序用户界面的框架。LVC(LiveCharts)是一个在WPF中创建动态、交互式和可视化图表的库。LVC曲线控件是LVC库的一部分,用于绘制各种类型的曲线图。 要使用WPF LVC曲线控件,首先需要在项目中引用LVC库。可以在Visual Studio中使用NuGet包管理器搜索并安装"LVC"。 在安装完成后,可以在XAML文件中创建曲线控件的实例。首先,需要添加正确的命名空间引用,例如: ```xaml xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" ``` 接下来,可以使用`lvc:CartesianChart`元素来创建曲线控件,并设置要显示的数据。例如,可以使用`SeriesCollection`属性添加曲线系列。 ```xaml <lvc:CartesianChart> <lvc:CartesianChart.Series> <lvc:LineSeries Title="Series 1" Values="10, 30, 15, 40" /> <lvc:LineSeries Title="Series 2" Values="20, 5, 25, 35" /> </lvc:CartesianChart.Series> </lvc:CartesianChart> ``` 在上面的示例中,创建了两个曲线系列,并设置了它们的标题和数值。可以通过添加更多的`LineSeries`元素来创建更多的曲线系列。 可以根据具体需求自定义曲线控件的外观和行为。可以设置各种属性,例如标题、坐标轴、图例、标签等等。还可以为曲线控件添加交互功能,例如缩放、平移、提示等。 通过这些步骤,就可以使用WPF LVC曲线控件创建动态、交互式和可视化的曲线图。使用LVC库的其他功能,还可以创建其他类型的图表,如饼图、柱状图、散点图等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值