using FLY.Thick.Base.IService; using GalaSoft.MvvmLight.Command; using LiveCharts; using LiveCharts.Configurations; using LiveCharts.Wpf; using MathNet.Numerics.LinearAlgebra.Double; using Microsoft.Win32; using Misc; using OfficeOpenXml; using OfficeOpenXml.Table; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; namespace FLY.Thick.Base.UI { public class PgScanCorrVm : INotifyPropertyChanged { #region 轴调节 /// <summary> /// Y轴拖拉条 最大值 /// </summary> public double YRangeSliderMax { get; set; } = 65535; /// <summary> /// Y轴拖拉条 最小值 /// </summary> public double YRangeSliderMin { get; set; } = 0; #endregion #region 曲线 public Func<double, string> XFormatter { get; private set; } public Func<double, string> YFormatter { get; private set; } public double XMin => 0; public double XMax => PosLength/PosOfGrid; private double ymax = 65535; /// <summary> /// Y轴 最大值 /// </summary> public double YMax { get { return ymax; } set { if (value <= YMin) { return; } if (value != ymax) ymax = value; } } private double ymin = 0; /// <summary> /// Y轴 最小值 /// </summary> public double YMin { get { return ymin; } set { if (value >= YMax) { return; } if (value != ymin) ymin = value; } } public SeriesCollection Series { get; } = new SeriesCollection(); public class SeriesInfo : INotifyPropertyChanged { public string Name { get; set; } public Brush Color { get; set; } public ChartValues<double> Datas = new ChartValues<double>(); public event PropertyChangedEventHandler PropertyChanged; } public SeriesInfo[] SeriesInfos { get; set; } ChartValues<XY> KeyPointDatas = new ChartValues<XY>(); CartesianMapper<XY> Mapper { get; set; } #endregion /// <summary> /// 机架总长,脉冲 /// </summary> public int PosLength { get; set; } = 8000; /// <summary> /// 1个grid = N个pos /// </summary> public int PosOfGrid { get; set; } = 10; /// <summary> /// 比例 /// </summary> public double Mmpp { get; set; } = 0.1; #region /// <summary> /// 使能修正 /// </summary> public bool Enable { set; get; } /// <summary> /// 当前修正的组 /// </summary> public int CurrGroupIndex { get; set; } /// <summary> /// 当前进度 0 - 100 /// </summary> public int Progress { get; set; } /// <summary> /// 数据更新时间 /// </summary> public DateTime[] UpdateTimes { get; set; } /// <summary> /// 正在机架修正中 /// </summary> public bool IsRunning { get; set; } /// <summary> /// 来回次数 /// </summary> public int ScanCnt { get; set; } #endregion /// <summary> /// 当前被选择的组序号 /// </summary> public int SelectedGroupIndex { get; set; } #region 平滑方式生成修正曲线 /// <summary> /// 平均滤波平滑数,单位grid /// </summary> public int SmoothFactor { get; set; } = 50; #endregion #region 关键点方式生成修正曲线 public KeyPointsSelectMode KpSelectMode { get; set; } = KeyPointsSelectMode.Null; #endregion /// <summary> /// 数据好了!!!! /// 当 flyad7 的poslen, posOfGrid 发生变化时,DataOK = false, 需要重新记录。 /// </summary> public bool IsDataOK { get; set; } = false; public double AvgAd { get; set; } #region CMD public RelayCommand SelectedGroup0Cmd { get; } public RelayCommand SelectedGroup1Cmd { get; } public RelayCommand StopCmd { get; } public RelayCommand StartCmd { get; } public RelayCommand ClearCmd { get; } public RelayCommand CreateCorrBySmoothCmd { get; } public RelayCommand CreateCorrByKeyPointCmd { get; } /// <summary> /// 添加关键点模式 /// </summary> public RelayCommand AddKeyPointModeCmd { get; } public RelayCommand RemoveKeyPointModeCmd { get; } public RelayCommand MoveKeyPointModeCmd { get; } public RelayCommand CancelKeyPointModeCmd { get; } public RelayCommand SetCorrDataCmd { get; } public RelayCommand SaveXlsxCmd { get; } public RelayCommand LoadXlsxCmd { get; } #endregion IScanCorrService scanCorrService; IInitParamService initParamService; ITDGageService gageService; KeyPointLine keyPointLine; public PgScanCorrVm() { #region 与数据无关界面参数 XFormatter = (x) => { //x 是 grid int pos = (int)(x * PosOfGrid); int mm = (int)(pos * Mmpp); return $"{pos}\n{mm}mm"; }; YFormatter = (y) => { return $"{ y:F0}"; }; Mapper = Mappers.Xy<XY>() .X((value, index) => value.X) .Y(value => value.Y); SeriesInfos = new SeriesInfo[] { new SeriesInfo(){Name = "正向原始", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(0)}, new SeriesInfo(){Name = "反向原始", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(1)}, new SeriesInfo(){Name = "正向滤波", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(0,true)}, new SeriesInfo(){Name = "反向滤波", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(1,true)}, }; for (int i = 0; i < SeriesInfos.Count(); i++) { var seriesInfo = SeriesInfos[i]; var lineSeries = new LineSeries { Values = seriesInfo.Datas, StrokeThickness = 2, Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent), Stroke = seriesInfo.Color, PointGeometry = null }; Series.Add(lineSeries); } keyPointLine = new KeyPointLine(); Misc.BindingOperations.SetBinding(this, nameof(SmoothFactor), keyPointLine, nameof(keyPointLine.SmoothFactor)); { var lineSeries = new LineSeries { Values = KeyPointDatas, StrokeThickness = 6, Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent), Stroke = FLY.ControlLibrary.Themes.Styles.GetForeground(2), Configuration = Mapper, //PointGeometry = Geometry. PointGeometrySize = 15, //PointForeground = "#222E31" }; Series.Add(lineSeries); } keyPointLine.KeyPoints.CollectionChanged += (s, e) => { UpdateKeyPointDatas(); }; #endregion StopCmd = new RelayCommand(Stop); StartCmd = new RelayCommand(Start); ClearCmd = new RelayCommand(Clear); CreateCorrBySmoothCmd = new RelayCommand(CreateCorrBySmooth); CreateCorrByKeyPointCmd = new RelayCommand(CreateCorrByKeyPoint); AddKeyPointModeCmd = new RelayCommand(AddKeyPointMode); RemoveKeyPointModeCmd = new RelayCommand(RemoveKeyPointMode); MoveKeyPointModeCmd = new RelayCommand(MoveKeyPointMode); CancelKeyPointModeCmd = new RelayCommand(CancelKeyPointMode); SetCorrDataCmd = new RelayCommand(SetCorrData); SelectedGroup0Cmd = new RelayCommand(SelectedGroup0); SelectedGroup1Cmd = new RelayCommand(SelectedGroup1); SaveXlsxCmd = new RelayCommand(SaveXlsx); LoadXlsxCmd = new RelayCommand(LoadXlsx); } public void Init( IScanCorrService scanCorrService, IInitParamService initParamService, ITDGageService gageService ) { this.scanCorrService = scanCorrService; this.initParamService = initParamService; this.gageService = gageService; Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.Enable), this, nameof(Enable), BindingOperations.BindingMode.TwoWay); Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.ScanCnt), this, nameof(ScanCnt)); Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.CurrGroupIndex), this, nameof(CurrGroupIndex)); Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.Progress), this, nameof(Progress)); Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.IsRunning), this, nameof(IsRunning)); Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.SmoothFactor), this, nameof(SmoothFactor)); Misc.BindingOperations.SetBinding(this.initParamService, nameof(initParamService.Encoder1_mmpp), this, nameof(Mmpp)); Misc.BindingOperations.SetBinding(this.initParamService, nameof(initParamService.PosLength), this, nameof(PosLength)); Misc.BindingOperations.SetBinding(this.initParamService, nameof(initParamService.PosOfGrid), this, nameof(PosOfGrid)); UpdateTimes = new DateTime[2]; downloadAll(); this.PropertyChanged += PgScanCorrVm_PropertyChanged; this.scanCorrService.PropertyChanged += ScanCorrService_PropertyChanged; } private void ScanCorrService_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(FObjBase.FObjServiceClient.IsConnected)) { var client = scanCorrService as FObjBase.FObjServiceClient; if (client.IsConnected) { downloadAll(); } } else if (e.PropertyName == nameof(scanCorrService.UpdateTimes)) { if (UpdateTimes[SelectedGroupIndex] != scanCorrService.UpdateTimes[SelectedGroupIndex]) { //当前显示的,发生变化了,下载 downloadAll(); } } } private void Clear() { scanCorrService.Clear(SelectedGroupIndex); FLY.ControlLibrary.Window_Tip.Show("成功", $"清除{SelectedGroupIndex}组数据成功", TimeSpan.FromSeconds(2)); } private void Start() { scanCorrService.Start(SelectedGroupIndex, ScanCnt, SmoothFactor); FLY.ControlLibrary.Window_Tip.Show("准备开始", null, TimeSpan.FromSeconds(2)); } private void Stop() { gageService.StartP2(Base.Common.STARTP2_MODE.STOP); FLY.ControlLibrary.Window_Tip.Show("停止", null, TimeSpan.FromSeconds(2)); } private void SelectedGroup1() { SelectedGroupIndex = 1; } private void SelectedGroup0() { SelectedGroupIndex = 0; } private void SetCorrData() { //TODO //判断是否有数据 for (int i = 2; i <= 3; i++) { } int[][] corrDatas = new int[2][]; for(int i=0;i<2;i++) corrDatas[i] = SeriesInfos[2+i].Datas.Select(d => double.IsNaN(d) ? Misc.MyBase.NULL_VALUE : (int)d).ToArray(); //发送出去 scanCorrService.SetCorrData(SelectedGroupIndex, corrDatas, (int)AvgAd); FLY.ControlLibrary.Window_Tip.Show("应用成功", null, TimeSpan.FromSeconds(2)); } private void MoveKeyPointMode() { KpSelectMode = KeyPointsSelectMode.Move; } private void RemoveKeyPointMode() { KpSelectMode = KeyPointsSelectMode.Remove; } private void AddKeyPointMode() { KpSelectMode = KeyPointsSelectMode.Add; } private void CancelKeyPointMode() { KpSelectMode = KeyPointsSelectMode.Null; } private void CreateCorrByKeyPoint() { CancelKeyPointMode(); if (keyPointLine.KeyPoints.Count() < 3) { //少于3个点 FLY.ControlLibrary.Window_WarningTip.Show("异常", $"关键点必须≥3个", TimeSpan.FromSeconds(2)); return; } for (int i = 0; i < 2; i++) { var filterDatas = keyPointLine.CalFilterDatas(SeriesInfos[i].Datas); SeriesInfos[i + 2].Datas.Clear(); SeriesInfos[i + 2].Datas.AddRange(filterDatas); } //计算平均值 double avg0 = SeriesInfos[2].Datas.AverageNoNull(); double avg1 = SeriesInfos[3].Datas.AverageNoNull(); AvgAd = (int)((avg0 + avg1) / 2); FLY.ControlLibrary.Window_Tip.Show("生成成功", null, TimeSpan.FromSeconds(2)); } void Smooth(ChartValues<double> orgDatas, ChartValues<double> filterDatas) { var filters = new double[orgDatas.Count()]; for (int i = 0; i < orgDatas.Count(); i++) { double sum = 0; int cnt = 0; for (int j = 0; j < SmoothFactor; j++) { int index = i - SmoothFactor / 2 + j; if (index < 0) continue; else if (index >= orgDatas.Count()) break; if (!double.IsNaN(orgDatas[index])) { sum += orgDatas[index]; cnt++; } } if (cnt > 0) filters[i] = sum / cnt; else filters[i] = double.NaN; } filterDatas.Clear(); filterDatas.AddRange(filters); } private void CreateCorrBySmooth() { for(int i=0;i<2;i++) Smooth(SeriesInfos[i].Datas, SeriesInfos[2+i].Datas); //计算平均值 double avg0 = SeriesInfos[2].Datas.AverageNoNull(); double avg1 = SeriesInfos[3].Datas.AverageNoNull(); AvgAd = (int)((avg0 + avg1) / 2); FLY.ControlLibrary.Window_Tip.Show("生成成功", null, TimeSpan.FromSeconds(2)); } void downloadAll() { var client = scanCorrService as FObjBase.FObjServiceClient; if (!client.IsConnected) return; //下载数据 scanCorrService.GetScanCorrGroup(SelectedGroupIndex, scanCorrService_GetScanCorrGroup, null); keyPointLine.Clear(); } void scanCorrService_GetScanCorrGroup(object asyncContext, object retData) { var reponse = retData as GetScanCorrGroupResponse; int groupIndex = reponse.GroupIndex; UpdateTimes[groupIndex] = reponse.UpdateTime; AvgAd = Misc.MyBase.ISVALIDATA(reponse.Avg) ? reponse.Avg : double.NaN; IsDataOK = reponse.IsDataOK; SeriesInfos[0].Datas.Clear(); SeriesInfos[1].Datas.Clear(); SeriesInfos[2].Datas.Clear(); SeriesInfos[3].Datas.Clear(); if (reponse.OrgDatas == null || reponse.OrgDatas.Count() != 2) return; if (reponse.OrgDatas[0] == null) return; SeriesInfos[0].Datas.AddRange(reponse.OrgDatas[0].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN)); var list = SeriesInfos[0].Datas.Where((d) => { if (double.IsNaN(d)) return false; else return true; }); //设置Y轴范围 int max = (int)list.Max(); int min = (int)list.Min(); //扩大3倍显示 int range = max - min; if (range == 0) { //异常 } else { ymax = max + range / 4; ymin = min - range / 4; YRangeSliderMin = min - range; YRangeSliderMax = max + range; } NotifyPropertyChanged(nameof(YMax)); NotifyPropertyChanged(nameof(YMin)); if (reponse.OrgDatas[1] == null) return; SeriesInfos[1].Datas.AddRange(reponse.OrgDatas[1].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN)); if (reponse.CorrDatas == null || reponse.CorrDatas.Count() != 2 || reponse.CorrDatas[0] == null || reponse.CorrDatas[1] == null) return; SeriesInfos[2].Datas.AddRange(reponse.CorrDatas[0].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN)); SeriesInfos[3].Datas.AddRange(reponse.CorrDatas[1].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN)); } private void PgScanCorrVm_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(SelectedGroupIndex)) { downloadAll(); } } public enum SelectType { Click, Move, Cancel } public void Select(SelectType selectType, System.Windows.Point cpBegin, System.Windows.Point cpEnd) { if (KpSelectMode == KeyPointsSelectMode.Null) return; switch (KpSelectMode) { case KeyPointsSelectMode.Add: { if (selectType != SelectType.Click) return; keyPointLine.Add(cpBegin.X, SeriesInfos[0].Datas); return; } break; case KeyPointsSelectMode.Remove: { if (selectType != SelectType.Click) return; keyPointLine.Remove(cpBegin.X); return; } break; } } void UpdateKeyPointDatas() { KeyPointDatas.Clear(); KeyPointDatas.AddRange(keyPointLine.KeyPoints); } private async void LoadXlsx() { //加载数据 string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); OpenFileDialog sfd = new OpenFileDialog(); sfd.Filter = "xlsx files (*.xlsx)|*.xlsx"; sfd.InitialDirectory = strDesktopPath; if (sfd.ShowDialog() == true) { string filename = sfd.FileName; Exception error = null; var ret = await Task.Factory.StartNew(() => { try { LoadXlsx(filename); } catch (Exception e) { error = e; return false; } return true; }); if (ret) { //画图 FLY.ControlLibrary.Window_Tip.Show("成功", $"加载{filename}", TimeSpan.FromSeconds(2)); } else { MessageBox.Show($"{error}", $"加载{filename}失败", MessageBoxButton.OK, MessageBoxImage.Error); } } } private void LoadXlsx(string filepath) { //检测标题 DataTable dataTable = new DataTable("table_scancorr"); dataTable.Columns.Add(new DataColumn() { ColumnName = "序号", DataType = typeof(double), Caption = "0" }); dataTable.Columns.Add(new DataColumn() { ColumnName = "脉冲", DataType = typeof(double), Caption = "0" }); dataTable.Columns.Add(new DataColumn() { ColumnName = "位置mm", DataType = typeof(double), Caption = "0" }); for (int j = 0; j < SeriesInfos.Count(); j++) { dataTable.Columns.Add(new DataColumn() { ColumnName = SeriesInfos[j].Name, DataType = typeof(double), Caption = "0" }); } using (ExcelPackage p = new ExcelPackage(new FileInfo(filepath))) { ExcelWorksheet sheet = p.Workbook.Worksheets["机架修正"]; FromSheet(sheet, dataTable); } App.Current.Dispatcher.Invoke(() => { for (int j = 2; j <= 3; j++) { List<double> datas = new List<double>(); for (int i = 0; i < dataTable.Rows.Count; i++) { var dataRow = dataTable.Rows[i]; double ad = System.Convert.ToDouble(dataRow[SeriesInfos[j].Name]); datas.Add(ad); } SeriesInfos[j].Datas.Clear(); SeriesInfos[j].Datas.AddRange(datas); datas.AverageNoNull(); } double avg0 = SeriesInfos[2].Datas.AverageNoNull(); double avg1 = SeriesInfos[3].Datas.AverageNoNull(); if (!double.IsNaN(avg0) && !double.IsNaN(avg1)) { //异常 AvgAd = (avg0 + avg1) / 2; } }); } void FromSheet(ExcelWorksheet sheet, DataTable dataTable) { int from_row = 1; int row = from_row; for (int i = 0; i < dataTable.Columns.Count; i++) { int col = i + 1; if ((string)(sheet.Cells[row, col].Value) != dataTable.Columns[i].ColumnName) { throw new Exception($"格式出错, 第{i}列 不是{dataTable.Columns[i].ColumnName}"); } } row++; while (true) { var newRow = dataTable.NewRow(); //当一行都是空的,认为没有数据 int nullCnt = 0; for (int j = 0; j < dataTable.Columns.Count; j++) { int col = j + 1; object value = sheet.Cells[row, col].Value; if (value is string || value == null) { if (string.IsNullOrEmpty((string)(value))) { //空的 nullCnt++; //continue; return; } } try { newRow[j] = sheet.Cells[row, col].Value; } catch (Exception e) { throw e; } } if (nullCnt == dataTable.Columns.Count) { //没有数据了 break; } dataTable.Rows.Add(newRow); row++; } } private void SaveXlsx() { //下载数据 string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); string filename = $"机架修正_组{SelectedGroupIndex}_{DateTime.Now:yyyyMMddHHmmss}.xlsx"; SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "xlsx files (*.xlsx)|*.xlsx"; sfd.InitialDirectory = strDesktopPath; sfd.FileName = filename; if (sfd.ShowDialog() == true) { filename = sfd.FileName; Task.Factory.StartNew(() => { SaveToXlsx(filename); App.Current.Dispatcher.Invoke(() => { FLY.ControlLibrary.Window_Tip.Show("成功", $"导出到{filename}", TimeSpan.FromSeconds(2)); }); }); } } private void SaveToXlsx(string filepath) { if (File.Exists(filepath)) { File.Delete(filepath); } using (ExcelPackage p = new ExcelPackage(new FileInfo(filepath))) { ExcelWorksheet sheet = p.Workbook.Worksheets.Add("机架修正"); DataTable dataTable = new DataTable("table_scancorr"); dataTable.Columns.Add(new DataColumn() { ColumnName = "序号", DataType = typeof(double), Caption = "0" }); dataTable.Columns.Add(new DataColumn() { ColumnName = "脉冲", DataType = typeof(double), Caption = "0" }); dataTable.Columns.Add(new DataColumn() { ColumnName = "位置mm", DataType = typeof(double), Caption = "0" }); for (int j = 0; j < SeriesInfos.Count(); j++) { dataTable.Columns.Add(new DataColumn() { ColumnName = SeriesInfos[j].Name, DataType = typeof(double), Caption = "0" }); } int count = SeriesInfos.Max(cv => cv.Datas.Count()); for (int i = 0; i < count; i++) { var dataRow = dataTable.NewRow(); dataRow["序号"] = i; dataRow["脉冲"] = i * PosOfGrid; dataRow["位置mm"] = (int)(i * PosOfGrid * Mmpp); for (int j = 0; j < SeriesInfos.Count(); j++) { if (i < SeriesInfos[j].Datas.Count()) { //if(!double.IsNaN(SeriesInfos[j].Datas[i])) dataRow[SeriesInfos[j].Name] = SeriesInfos[j].Datas[i]; } } dataTable.Rows.Add(dataRow); } ToSheet(sheet, dataTable); p.Save(); } void ToSheet(ExcelWorksheet sheet, DataTable dataTable) { int from_row = 1; int row = from_row; //添加标题 for (int i = 0; i < dataTable.Columns.Count; i++) { int col = i + 1; sheet.Cells[row, col].Value = dataTable.Columns[i].ColumnName; //格式 sheet.Column(col).Style.Numberformat.Format = dataTable.Columns[i].Caption; } row++; for (int i = 0; i < dataTable.Rows.Count; i++) { for (int j = 0; j < dataTable.Columns.Count; j++) { int col = j + 1; sheet.Cells[row, col].Value = dataTable.Rows[i][j]; } row++; } int colcnt = dataTable.Columns.Count; int rowcnt = dataTable.Rows.Count; var range = sheet.Cells[from_row, 1, from_row + rowcnt, colcnt]; var tbl = sheet.Tables.Add(range, dataTable.TableName); sheet.Cells[sheet.Dimension.Address].AutoFitColumns(); } } #region INotifyPropertyChanged 成员 public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #endregion } /// <summary> /// 生成关键点曲线 /// </summary> public class KeyPointLine:INotifyPropertyChanged { public ObservableCollection<XY> KeyPoints { get; } = new ObservableCollection<XY>(); public int SmoothFactor { get; set; } = 20; public event PropertyChangedEventHandler PropertyChanged; public void Add(double x, IEnumerable<double> orgDatas) { if (x < 0 || x >= orgDatas.Count()) return;//超出范围 int index = FindIndex(x); if (index != -1) return; double y = CalY(x, orgDatas); //没有,可以添加 Insert(x, y); } public void Remove(double x) { int index = FindIndex(x); if (index != -1) return; KeyPoints.RemoveAt(index); } public void Clear() { KeyPoints.Clear(); } /// <summary> /// 计算 位置为x 在 orgDatas中的y /// </summary> /// <param name="x"></param> /// <param name="orgDatas"></param> /// <returns></returns> double CalY(double x, IEnumerable<double> orgDatas) { double sum = 0; int cnt = 0; for (int j = 0; j < SmoothFactor; j++) { int index = (int)(x - SmoothFactor / 2 + j); if (index < 0) continue; else if (index >= orgDatas.Count()) break; var value = orgDatas.ElementAt(index); if (!double.IsNaN(value)) { sum += value; cnt++; } } if (cnt > 0) return sum / cnt; else return double.NaN; } int FindIndex(double x) { for (int i = 0; i < KeyPoints.Count(); i++) { if (Math.Abs(KeyPoints[i].X - x) < SmoothFactor / 2) { return i; } } return -1; } int Insert(double x, double y) { for (int i = 0; i < KeyPoints.Count(); i++) { if (x < KeyPoints[i].X) { //就在它前面插入 KeyPoints.Insert(i, new XY() { X = x, Y = y }); return i; } } //在最后添加 KeyPoints.Add(new XY() { X = x, Y = y }); return KeyPoints.Count() - 1; } List<XY> GetKeyValues(IEnumerable<double> orgDatas) { List<XY> keyValues = new List<XY>(); for (int i = 0; i < KeyPoints.Count(); i++) { int x = (int)KeyPoints[i].X; double sum = 0; int cnt = 0; for (int j = 0; j < SmoothFactor; j++) { int index = x - SmoothFactor / 2 + j; if (index < 0) continue; else if (index >= orgDatas.Count()) break; var value = orgDatas.ElementAt(index); if (!double.IsNaN(value)) { sum += value; cnt++; } } double y; if (cnt > 0) y = sum / cnt; else y = double.NaN; keyValues.Add(new XY() { X = x, Y = y }); } return keyValues; } DenseVector Solve(List<XY> keyValues, int i) { DenseMatrix X = new DenseMatrix(3, 3); DenseVector Y = new DenseVector(3); var kv0 = keyValues[i - 1]; var kv1 = keyValues[i]; var kv2 = keyValues[i + 1]; X.SetRow(0, new double[] { kv0.X * kv0.X, kv0.X, 1 }); X.SetRow(1, new double[] { kv1.X * kv1.X, kv1.X, 1 }); X.SetRow(2, new double[] { kv2.X * kv2.X, kv2.X, 1 }); Y.SetValues(new double[] { kv0.Y, kv1.Y, kv2.Y }); DenseVector abc = (DenseVector)X.Solve(Y); return abc; } public double[] CalFilterDatas(IEnumerable<double> orgDatas) { List<XY> keyValues = GetKeyValues(orgDatas); //计算每个点的2次方程 a,b,c List<DenseVector> abcList = new List<DenseVector>(); for (int i = 0; i < keyValues.Count(); i++) { if (i == 0) { DenseVector abc = Solve(keyValues, 1); abcList.Add(abc); } else if (i == 1) { abcList.Add(abcList.Last()); } else if (i == keyValues.Count() - 1) { abcList.Add(abcList.Last()); } else { DenseVector abc = Solve(keyValues, i); abcList.Add(abc); } } double[] filterDatas = new double[orgDatas.Count()]; int begin = -1; for (int i = 0; i < keyValues.Count(); i++) { int j = (int)keyValues[i].X; filterDatas[j] = keyValues[i].Y; for (j=j-1; j > begin; j--) { double y = abcList[i][0] * j * j + abcList[i][1] * j + abcList[i][2]; if (i - 1 >= 0) { double y_pre = abcList[i - 1][0] * j * j + abcList[i - 1][1] * j + abcList[i - 1][2]; //权重 double t = (j - keyValues[i - 1].X) / (keyValues[i].X - keyValues[i - 1].X); y = t*y +(1 - t) * y_pre; } filterDatas[j] = y; } begin = (int)keyValues[i].X; } //计算最后的 { int i = keyValues.Count() - 1; for (int j = begin + 1; j < filterDatas.Count(); j++) { double y = abcList[i][0] * j * j + abcList[i][1] * j + abcList[i][2]; filterDatas[j] = y; } } return filterDatas; } } public enum KeyPointsSelectMode { Null, Add, Remove, Move } }