C# 基于点值的LiveCharts线图样式
我使用C# 基于点值的LiveCharts线图样式,c#,wpf,linegraph,livecharts,C#,Wpf,Linegraph,Livecharts,我使用LiveCharts和WPF绘制了一组折线图,其中折线图的内容和数量在运行时确定。所以我事先不知道会有多少个LineSeries,它们的值是多少。但是,我知道每个系列的良好范围。例如,一个系列,我们称之为S1的范围为2+/-1。所以1到3之间的任何东西都被认为是好的。类似地,也可以有另一个,比如说S2,其中范围为30+/-2,所以28到32之间的任何值都是好的 我想画一个折线图,这样在范围内的部分就画成一条实线,但是如果一个部分在范围之外,它就会是一条虚线/虚线。由于我有多个线系列,因此我
LiveCharts
和WPF
绘制了一组折线图,其中折线图的内容和数量在运行时确定。所以我事先不知道会有多少个LineSeries
,它们的值是多少。但是,我知道每个系列的良好范围。例如,一个系列,我们称之为S1
的范围为2+/-1。所以1到3之间的任何东西都被认为是好的。类似地,也可以有另一个,比如说S2
,其中范围为30+/-2,所以28到32之间的任何值都是好的
我想画一个折线图,这样在范围内的部分就画成一条实线,但是如果一个部分在范围之外,它就会是一条虚线/虚线。由于我有多个线系列
,因此我在其各自的Y轴上绘制了每个线系列。我的XAML
代码如下所示:
<Grid>
<lvc:CartesianChart Name="MyChart" Margin="4"
Series="{Binding SeriesCollection}"/>
</Grid>
代码隐藏:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public SeriesCollection SeriesCollection { get; set; }
public MainWindow()
{
InitializeComponent();
PlotGraph();
}
private void PlotGraph()
{
SeriesCollection = new SeriesCollection();
var lineSeries1 = new LineSeries
{
Title = "S1",
Values = new ChartValues<double>() { 2.3, 2.0, 3.1, 1.3, 0.5, 3.8, 7.3, 2.4, 1.2, 0.1 },
DataLabels = true,
Stroke = Brushes.Green,
Fill = Brushes.Transparent,
ScalesYAt = 0
};
var lineSeries2 = new LineSeries
{
Title = "S2",
Values = new ChartValues<double>() { 32.5, 34.5, 29.5, 26.0, 25.8, 30.5, 32.1, 36.5, 32.4, 24.5 },
DataLabels = true,
Stroke = Brushes.HotPink,
Fill = Brushes.Transparent,
ScalesYAt = 1
};
SeriesCollection.Add(lineSeries1);
SeriesCollection.Add(lineSeries2);
MyChart.AxisY.Add(new Axis());
MyChart.AxisY.Add(new Axis());
DataContext = this;
}
}
public分部类主窗口:窗口,INotifyPropertyChanged
{
公共序列集合序列集合{get;set;}
公共主窗口()
{
初始化组件();
绘图仪();
}
专用空心绘图仪()
{
SeriesCollection=新的SeriesCollection();
var lineSeries1=新的LineSeries
{
Title=“S1”,
Values=新的ChartValues(){2.3,2.0,3.1,1.3,0.5,3.8,7.3,2.4,1.2,0.1},
DataLabels=true,
笔划=画笔。绿色,
填充=画笔。透明,
ScaleShat=0
};
var lineSeries2=新的LineSeries
{
Title=“S2”,
Values=新的ChartValues(){32.5,34.5,29.5,26.0,25.8,30.5,32.1,36.5,32.4,24.5},
DataLabels=true,
笔划=画笔。粉红色,
填充=画笔。透明,
ScalesYAt=1
};
SeriesCollection.Add(lineSeries1);
SeriesCollection.Add(lineSeries2);
MyChart.AxisY.Add(newaxis());
MyChart.AxisY.Add(newaxis());
DataContext=this;
}
}
PointState
是基于值着色的,但它不适合我,因为我在一个序列中绘制了多个序列。此外,我的图形有数千个点,因此我禁用了PointGeometry
,因为如果启用它们,无论如何都很难看到它们
我想要的一切可能吗?我找到了一个解决方案,事实上是2:要么根据问题定制livechart,要么像下面那样重新计算不同点:
这只是回答你问题的一种方式,一个想法,所有的事情都是可能的,但需要一些代码
绘图仪法
private void PlotGraph()
{
var points = new List<Point>() { new Point(0, 2.3), new Point(1, 2.0),
new Point(2, 3.1), new Point(3, 1.3),
new Point(4, 0.5), new Point(5, 3.8),
new Point(6, 7.3), new Point(7, 2.4),
new Point(8, 1.2), new Point(9, 0.1)};
var range1 = new double[] { 1d, 3d };
var otherpoints = CurvesMath.GetInterpolatedCubicSplinedCurve(points);
var pointscurve = otherpoints.Select(p => p.Y).ToArray();
SeriesCollection = new SeriesCollection();
var lineSeries1 = new LineSeries
{
Title = "S1",
Values = new ChartValues<double>(pointscurve),
DataLabels = false,
Stroke = Brushes.Transparent,
Fill = Brushes.Transparent,
ScalesYAt = 0,
PointGeometrySize = 2,
Configuration = Mappers.Xy<double>()
.X((value, index) => index)
.Y((value, index) => value)
.Stroke((value, index) => value <= range1[0] || value >= range1[1] ? Brushes.Red : Brushes.Blue)
.Fill((value, index) => value <= range1[0] || value >= range1[1] ? Brushes.Red : Brushes.Blue)
};
points = new List<Point>() { new Point(0, 32.5), new Point(1, 34.5),
new Point(2, 29.5), new Point(3, 26.0),
new Point(4, 25.8), new Point(5, 30.5),
new Point(6, 32.1), new Point(7, 36.5),
new Point(8, 32.4), new Point(9, 24.5)};
var range2 = new double[] { 28d, 32d };
otherpoints = CurvesMath.GetInterpolatedCubicSplinedCurve(points);
pointscurve = otherpoints.Select(p => p.Y).ToArray();
var lineSeries2 = new LineSeries
{
Title = "S2",
Values = new ChartValues<double>(pointscurve),
DataLabels = false,
Stroke = Brushes.Transparent,
Fill = Brushes.Transparent,
ScalesYAt = 1,
PointGeometrySize = 2,
Configuration = Mappers.Xy<double>()
.X((value, index) => index)
.Y((value, index) => value)
.Stroke((value, index) => value <= range2[0] || value >= range2[1] ? Brushes.Red : Brushes.Green)
.Fill((value, index) => value <= range2[0] || value >= range2[1] ? Brushes.Red : Brushes.Green)
};
SeriesCollection.Add(lineSeries1);
SeriesCollection.Add(lineSeries2);
MyChart.AxisY.Add(new Axis());
MyChart.AxisY.Add(new Axis());
DataContext = this;
}
private void PlotGraph()
{
var points=new List(){新点(0,2.3),新点(1,2.0),
新的点(2,3.1),新的点(3,1.3),
新的点(4,0.5),新的点(5,3.8),
新的点(6,7.3),新的点(7,2.4),
新点(8,1.2),新点(9,0.1)};
var range1=新的双[]{1d,3d};
var otherpoints=CurvesMath.GetInterpolatedCubicSplinedCurve(点);
var pointscurve=otherpoints.Select(p=>p.Y).ToArray();
SeriesCollection=新的SeriesCollection();
var lineSeries1=新的LineSeries
{
Title=“S1”,
值=新图表值(点曲线),
DataLabels=false,
笔划=画笔。透明,
填充=画笔。透明,
ScalesYAt=0,
PointGeometrySize=2,
配置=Mappers.Xy()
.X((值,索引)=>索引)
.Y((值,索引)=>值)
.Stroke((值,索引)=>value=range1[1]?画笔。红色:画笔。蓝色)
.Fill((值,索引)=>value=range1[1]?画笔。红色:画笔。蓝色)
};
点=新列表(){新点(0,32.5),新点(1,34.5),
新的点(2,29.5),新的点(3,26.0),
新的点(4,25.8),新的点(5,30.5),
新的点(6,32.1),新的点(7,36.5),
新的点(8,32.4),新的点(9,24.5)};
var range2=新的双[]{28d,32d};
其他点=曲线路径。GetInterpolatedCubicSplinedCurve(点);
pointscurve=otherpoints.Select(p=>p.Y).ToArray();
var lineSeries2=新的LineSeries
{
Title=“S2”,
值=新图表值(点曲线),
DataLabels=false,
笔划=画笔。透明,
填充=画笔。透明,
ScalesYAt=1,
PointGeometrySize=2,
配置=Mappers.Xy()
.X((值,索引)=>索引)
.Y((值,索引)=>值)
.Stroke((值,索引)=>value=range2[1]?画笔。红色:画笔。绿色)
.Fill((值,索引)=>value=range2[1]?画笔。红色:画笔。绿色)
};
SeriesCollection.Add(lineSeries1);
SeriesCollection.Add(lineSeries2);
MyChart.AxisY.Add(newaxis());
MyChart.AxisY.Add(newaxis());
DataContext=this;
}
xaml文件:
<Grid>
<lvc:CartesianChart Name="MyChart" Margin="4"
Series="{Binding SeriesCollection}" >
</lvc:CartesianChart>
</Grid>
插值三次样条曲线(或贝塞尔曲线)
使用System.Collections.Generic;
使用System.Linq;
使用System.Windows;
命名空间WpfApp2
{
公共静态类CurveMath
{
专用常量int精度=80;
公共静态列表GetInterpolatedCubicSplinedCurve(IList点)
{
var输出=n
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace WpfApp2
{
public static class CurvesMath
{
private const int precision = 80;
public static List<Point> GetInterpolatedCubicSplinedCurve(IList<Point> points)
{
var output = new List<Point>();
int np = points.Count; // number of points
double[] yCoords = new double[np]; // Newton form coefficients
double[] xCoords = new double[np]; // x-coordinates of nodes
double y;
double x;
if (np > 0)
{
for (int i = 0; i < np; i++)
{
var p = points[i];
xCoords[i] = p.X;
yCoords[i] = p.Y;
}
if (np > 1)
{
double[] a = new double[np];
double x1;
double x2;
double[] h = new double[np];
for (int i = 1; i <= np - 1; i++)
{
h[i] = xCoords[i] - xCoords[i - 1];
}
if (np > 2)
{
double[] sub = new double[np - 1];
double[] diag = new double[np - 1];
double[] sup = new double[np - 1];
for (int i = 1; i <= np - 2; i++)
{
diag[i] = (h[i] + h[i + 1]) / 3;
sup[i] = h[i + 1] / 6;
sub[i] = h[i] / 6;
a[i] = (yCoords[i + 1] - yCoords[i]) / h[i + 1] - (yCoords[i] - yCoords[i - 1]) / h[i];
}
SolveTridiag(sub, diag, sup, ref a, np - 2);
}
output.Add(points.First());
for (int i = 1; i <= np - 1; i++)
{
// loop over intervals between nodes
for (int j = 1; j <= precision; j++)
{
x1 = (h[i] * j) / precision;
x2 = h[i] - x1;
y = ((-a[i - 1] / 6 * (x2 + h[i]) * x1 + yCoords[i - 1]) * x2 +
(-a[i] / 6 * (x1 + h[i]) * x2 + yCoords[i]) * x1) / h[i];
x = xCoords[i - 1] + x1;
output.Add(new Point(x, y));
}
}
}
}
return output;
}
public static double SolveCubicSpline(IList<Point> knownSamples, double z)
{
int np = knownSamples.Count;
if (np > 1)
{
if (knownSamples[0].X == z) return knownSamples[0].Y;
double[] a = new double[np];
double x1;
double x2;
double y;
double[] h = new double[np];
for (int i = 1; i <= np - 1; i++)
{
h[i] = knownSamples[i].X - knownSamples[i - 1].X;
}
if (np > 2)
{
double[] sub = new double[np - 1];
double[] diag = new double[np - 1];
double[] sup = new double[np - 1];
for (int i = 1; i <= np - 2; i++)
{
diag[i] = (h[i] + h[i + 1]) / 3;
sup[i] = h[i + 1] / 6;
sub[i] = h[i] / 6;
a[i] = (knownSamples[i + 1].Y - knownSamples[i].Y) / h[i + 1] -
(knownSamples[i].Y - knownSamples[i - 1].Y) / h[i];
}
// SolveTridiag is a support function, see Marco Roello's original code
// for more information at
// http://www.codeproject.com/useritems/SplineInterpolation.asp
SolveTridiag(sub, diag, sup, ref a, np - 2);
}
int gap = 0;
double previous = double.MinValue;
// At the end of this iteration, "gap" will contain the index of the interval
// between two known values, which contains the unknown z, and "previous" will
// contain the biggest z value among the known samples, left of the unknown z
for (int i = 0; i < knownSamples.Count; i++)
{
if (knownSamples[i].X < z && knownSamples[i].X > previous)
{
previous = knownSamples[i].X;
gap = i + 1;
}
}
x1 = z - previous;
if (gap > h.Length - 1)
return z;
x2 = h[gap] - x1;
if (gap == 0)
return 0.0;
y = ((-a[gap - 1] / 6 * (x2 + h[gap]) * x1 + knownSamples[gap - 1].Y) * x2 +
(-a[gap] / 6 * (x1 + h[gap]) * x2 + knownSamples[gap].Y) * x1) / h[gap];
return y;
}
return 0;
}
private static void SolveTridiag(double[] sub, double[] diag, double[] sup, ref double[] b, int n)
{
/* solve linear system with tridiagonal n by n matrix a
using Gaussian elimination *without* pivoting
where a(i,i-1) = sub[i] for 2<=i<=n
a(i,i) = diag[i] for 1<=i<=n
a(i,i+1) = sup[i] for 1<=i<=n-1
(the values sub[1], sup[n] are ignored)
right hand side vector b[1:n] is overwritten with solution
NOTE: 1...n is used in all arrays, 0 is unused */
int i;
/* factorization and forward substitution */
for (i = 2; i <= n; i++)
{
sub[i] = sub[i] / diag[i - 1];
diag[i] = diag[i] - sub[i] * sup[i - 1];
b[i] = b[i] - sub[i] * b[i - 1];
}
b[n] = b[n] / diag[n];
for (i = n - 1; i >= 1; i--)
{
b[i] = (b[i] - sup[i] * b[i + 1]) / diag[i];
}
}
}
}