using DevExpress.Utils; using DevExpress.XtraCharts; using DevExpress.XtraEditors; using System.Text; namespace Yw.WinFrmUI.Phart { /// /// /// public partial class ValvePerform2dChart : DevExpress.XtraEditors.XtraUserControl { public ValvePerform2dChart() { InitializeComponent(); InitialChart(); this.chartControl1.RuntimeHitTesting = true; } #region Private Variable private XYDiagram _main_chart_diagram; private AxisX _axis_x_q; private AxisY _axis_y_ql; private ConstantLine _work_pt_line; private ConstantLine _work_l_line; private Series _series_cubic_spline_ql; private Series _series_pt_ql; private List _series_custom_list; private TextAnnotation _work_pt_txt_annot; private Yw.Geometry.CubicSpline2d _cubic_spline_ql; private List _pt_ql_list; private List _def_pt_ql_list; private ValveGroupPt _work_point = new(0, 0, 0, 0, 0); private ValveCoordinate _coordinate_paras; private bool _initial_data = false; #endregion #region Public Variable /// /// 定义点是否可见 /// public bool DefinePointVisible { get => _definePointVisible; set { _definePointVisible = value; this.barCekDefinePointVisible.Checked = _definePointVisible; } } private bool _definePointVisible = false; /// /// 工作线是否可见 /// public bool LineVisible { get => _lineVisible; set { _lineVisible = value; this.barCekLineVisible.Checked = _lineVisible; } } private bool _lineVisible = false; #endregion #region Public Evnet public event Action OnCurveCoordinateChanged; #endregion #region Initial /// /// 初始化图表 /// private void InitialChart() { this.chartControl1.SetChartDisplay(); this.chartControl1.Legend.Visibility = DevExpress.Utils.DefaultBoolean.False; _main_chart_diagram = (XYDiagram)chartControl1.Diagram; _axis_x_q = _main_chart_diagram.AxisX; _axis_x_q.SetAxisXQDisplay(); _axis_y_ql = _main_chart_diagram.AxisY; _axis_y_ql.SetAxisYQLDisplay(); _work_pt_line = (ConstantLine)_main_chart_diagram.AxisX.ConstantLines.GetElementByName("WorkPointLine"); _work_pt_line.SetWorkPointLineDisplay(); _work_l_line = (ConstantLine)_main_chart_diagram.AxisY.ConstantLines.GetElementByName("WorkLLine"); _work_l_line.SetWorkHLineDisplay(); _series_cubic_spline_ql = this.chartControl1.GetSeriesByName("SeriesCurveQL"); _series_cubic_spline_ql.SetCurveQLDisplay(); _series_pt_ql = this.chartControl1.GetSeriesByName("SeriesPointQL"); _series_pt_ql.SetPointQLDisplay(); _work_pt_txt_annot = this.chartControl1.AnnotationRepository[0] as TextAnnotation; _work_pt_txt_annot.SetTextAnnoWorkPointDisplay(); _axis_x_q.Visibility = DefaultBoolean.False; _axis_x_q.GridLines.Visible = false; _axis_y_ql.Visibility = DefaultBoolean.False; _axis_y_ql.GridLines.Visible = false; _work_pt_line.Visible = false; _work_l_line.Visible = false; _work_pt_txt_annot.Visible = false; _series_pt_ql.Visible = false; this.chartControl1.ObjectHotTracked += new DevExpress.XtraCharts.HotTrackEventHandler(this.chartControl1_ObjectHotTracked); this.chartControl1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.chartControl1_MouseMove); this.chartControl1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.chartControl1_MouseUp); this.chartControl1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.chartControl1_MouseDown); this.chartControl1.Resize += new System.EventHandler(this.chartControl1_Resize); } /// /// 初始化图表数据 /// public void InitialChartData() { _initial_data = false; _cubic_spline_ql = null; _pt_ql_list = null; _def_pt_ql_list = null; _coordinate_paras = null; _work_point = new(0, 0, 0, 0, 0); UpdateChart(false); } #endregion /// /// 绑定数据 /// /// /// public void SetBindingData( Yw.Geometry.CubicSpline2d cubic_spline_ql, string coordinate_paras = "", bool calc_coordinate = false) { if (cubic_spline_ql == null) { InitialChartData(); return; } List pt_ql_list; pt_ql_list = cubic_spline_ql.GetPointList(12); SetBindingData(cubic_spline_ql, pt_ql_list, null, coordinate_paras, calc_coordinate); } /// /// 绑定数据 /// /// /// /// /// /// public void SetBindingData( Yw.Geometry.CubicSpline2d cubic_spline_ql, List pt_ql_list, List def_pt_ql_list, string coordinate_paras = null, bool calc_coordinate = false) { if (pt_ql_list == null || !pt_ql_list.Any()) { InitialChartData(); return; } _initial_data = true; _cubic_spline_ql = cubic_spline_ql; _pt_ql_list = pt_ql_list; _def_pt_ql_list = def_pt_ql_list; _coordinate_paras = ValveCoordinate.ToModel(coordinate_paras); UpdateChart(calc_coordinate); } /// /// 更新图表 /// /// 计算坐标 public void UpdateChart(bool calc_coordinate = false) { if (calc_coordinate || _coordinate_paras == null) { //不强迫计算,就用上次更新的坐标系 CalcCoordinate(); } CalcChartAxis(); CalcSeries(); CalcWorkPointByQ(); CalcActionSpotByQ(); CalcTextAnchorPoint(); } #region Calc /// /// 计算坐标 /// public void CalcCoordinate() { if (_pt_ql_list == null || _pt_ql_list.Count < 4) { //设置成白板坐标 _coordinate_paras = new ValveCoordinate(); _coordinate_paras.GridNumberX = 30; _coordinate_paras.GridNumberY = 16; //显示的坐标线号 _coordinate_paras.StartLineNoL = 10; _coordinate_paras.EndLineNoL = 15; //坐标最小值和间隔 _coordinate_paras.CoordMinQ = 0; _coordinate_paras.CoordSpaceQ = 1000; _coordinate_paras.CoordMinL = 10; _coordinate_paras.CoordSpaceL = 100; return; } _coordinate_paras = ValveCoordinate.CalcCoordinate(_pt_ql_list); } /// /// 计算图表轴 /// public void CalcChartAxis() { if (_coordinate_paras == null) { _axis_x_q.Visibility = DefaultBoolean.False; _axis_x_q.GridLines.Visible = false; _axis_y_ql.Visibility = DefaultBoolean.False; _axis_y_ql.GridLines.Visible = false; _work_pt_line.Visible = false; _work_l_line.Visible = false; _work_pt_txt_annot.Visible = false; return; } //流量 if (_pt_ql_list != null) { //计算刻度 var labels = new List(); var disQ = _coordinate_paras.CoordMinQ; for (int i = 0; i < _coordinate_paras.GridNumberX + 1; i++) { labels.Add(new CustomAxisLabel(disQ.ToString("N0"), disQ)); disQ = disQ + _coordinate_paras.CoordSpaceQ; } //坐标刻度 _axis_x_q.CustomLabels.Clear(); _axis_x_q.CustomLabels.AddRange(labels.ToArray()); _axis_x_q.Visibility = DefaultBoolean.True; _axis_x_q.GridLines.Visible = true; _axis_x_q.SetAxisRange(_coordinate_paras.CoordMinQ, _coordinate_paras.CoordMinQ + _coordinate_paras.GridNumberX * _coordinate_paras.CoordSpaceQ); _work_pt_txt_annot.Visible = _lineVisible; } //水损 if (_pt_ql_list != null) { //计算刻度 var labels = new List(); var disL = _coordinate_paras.CoordMinL + _coordinate_paras.CoordSpaceL * _coordinate_paras.StartLineNoL; for (int i = _coordinate_paras.StartLineNoL; i < _coordinate_paras.EndLineNoL + 1; i++) { labels.Add(new CustomAxisLabel(disL.ToString(), disL)); disL = disL + _coordinate_paras.CoordSpaceL; } _axis_y_ql.CustomLabels.Clear(); _axis_y_ql.CustomLabels.AddRange(labels.ToArray()); _axis_y_ql.Visibility = DefaultBoolean.True; _axis_y_ql.GridLines.Visible = true; } _axis_x_q.SetAxisRange(_coordinate_paras.CoordMinQ, _coordinate_paras.CoordMinQ + _coordinate_paras.GridNumberX * _coordinate_paras.CoordSpaceQ); //_axis_y_ql.SetAxisRange(_coordinate_paras.CoordMinL, _coordinate_paras.CoordMinL + _coordinate_paras.GridNumberY * _coordinate_paras.CoordSpaceL); _axis_y_ql.SetAxisRange(_coordinate_paras.DispMinL(), _coordinate_paras.DispMaxL()); } /// /// 计算系列 /// public void CalcSeries() { if (_pt_ql_list != null && _pt_ql_list.Count > 3) { _series_cubic_spline_ql.Visible = true; _series_cubic_spline_ql.Points.Clear(); foreach (var curvePoint in _pt_ql_list) { var seriesPoint = new SeriesPoint(curvePoint.X, curvePoint.Y); _series_cubic_spline_ql.Points.Add(seriesPoint); } } else { _series_cubic_spline_ql.Points.Clear(); _series_cubic_spline_ql.Visible = false; _work_pt_line.Visible = false; _work_l_line.Visible = false; _work_pt_txt_annot.Visible = false; } if (_def_pt_ql_list != null && _def_pt_ql_list.Any()) { _series_pt_ql.Points.Clear(); foreach (var definePoint in _def_pt_ql_list) { var seriesPoint = new SeriesPoint(definePoint.X, definePoint.Y); _series_pt_ql.Points.Add(seriesPoint); } } } /// /// 计算注释定位 /// private void CalcTextAnchorPoint() { var x = this.chartControl1.Location.X + this.chartControl1.Width - (100); var y = this.chartControl1.Height - (200); (_work_pt_txt_annot.AnchorPoint as ChartAnchorPoint).X = x; (_work_pt_txt_annot.AnchorPoint as ChartAnchorPoint).Y = y; } /// /// 计算工作点 /// /// public void CalcWorkPointByQ(double? workQ = null) { if (!_lineVisible) { _work_pt_line.Visible = false; _work_pt_line.Title.Visible = false; _work_l_line.Visible = false; _work_l_line.Title.Visible = false; _work_pt_txt_annot.Visible = false; return; } else { _work_pt_line.Visible = true; _work_pt_line.Title.Visible = true; _work_l_line.Visible = true; _work_l_line.Title.Visible = true; _work_pt_txt_annot.Visible = true; } if (_pt_ql_list == null || !_pt_ql_list.Any()) return; var minQ = _pt_ql_list.Min(x => x.X); var maxQ = _pt_ql_list.Max(x => x.X); if (workQ == null) { workQ = (minQ + maxQ) / 2; } else { if (workQ < minQ || workQ > maxQ) return; } _work_point.Q = workQ.Value; _work_point.L = _cubic_spline_ql.GetPointY(_work_point.Q); var workInfoStringBuilder = new StringBuilder(); workInfoStringBuilder.AppendLine($"流量:{_work_point.Q.ToString("N1")} "); workInfoStringBuilder.AppendLine($"水损:{_work_point.L.ToString("N1")} "); _work_pt_line.AxisValue = _work_point.Q; _work_pt_line.Title.Text = _work_point.Q.ToString("N1"); _work_l_line.AxisValue = _work_point.L; _work_l_line.Title.Text = _work_point.L.ToString("N1"); //测试展示效果 _work_pt_txt_annot.Text = workInfoStringBuilder.ToString(); _work_pt_txt_annot.AutoSize = true; } /// /// 根据水损计算工作点 /// /// public void CalcWorkPointByH(double workH) { if (!_lineVisible) return; if (_cubic_spline_ql == null) return; var minH = _pt_ql_list.Min(x => x.Y); var maxH = _pt_ql_list.Max(x => x.Y); if (workH < minH || workH > maxH) return; var workQ = _cubic_spline_ql.GetPointY(workH); CalcWorkPointByQ(workQ); } /// /// 根据最大流量计算工作点 /// public void CalcWorkPointByMaxQ() { if (!_lineVisible) return; if (_cubic_spline_ql == null) return; var workQ = _cubic_spline_ql.MaxX; CalcWorkPointByQ(workQ); } /// /// 计算作用点 /// /// public void CalcActionSpotByQ(double? action_spot_q = null) { if (_series_custom_list != null && _series_custom_list.Any()) { this.chartControl1.BeginInit(); foreach (var series in _series_custom_list) { var exist_series = this.chartControl1.GetSeriesByName(series.Name); if (exist_series != null) { this.chartControl1.Series.Remove(exist_series); } } this.chartControl1.EndInit(); } _series_custom_list = new List(); if (_pt_ql_list == null || !_pt_ql_list.Any()) return; if (action_spot_q == null) return; var minQ = _pt_ql_list.Min(x => x.X); var maxQ = _pt_ql_list.Max(x => x.X); if (action_spot_q < minQ || action_spot_q > maxQ) return; var Q = action_spot_q.Value; var L = _cubic_spline_ql.GetPointY(Q); var action_spot_qh = CreatePointSeries(Q, L); _series_custom_list.Add(action_spot_qh); this.chartControl1.Series.Add(action_spot_qh); } /// /// 创建点系列 /// private Series CreatePointSeries(double x, double y) { string name = "流量水损作用点"; Color color = Perform2dChartDisplay.PointColorQL; AxisYBase axis_y = _axis_y_ql; var series_point_view = new DevExpress.XtraCharts.PointSeriesView(); series_point_view.Color = color; series_point_view.PointMarkerOptions.BorderColor = color; series_point_view.PointMarkerOptions.Size = 12; series_point_view.PointMarkerOptions.Kind = MarkerKind.Cross; series_point_view.AxisY = axis_y; var series_point = new DevExpress.XtraCharts.Series(); series_point.View = series_point_view; series_point.ArgumentScaleType = DevExpress.XtraCharts.ScaleType.Numerical; series_point.LabelsVisibility = DevExpress.Utils.DefaultBoolean.False; series_point.Name = name; series_point.ShowInLegend = false; series_point.CrosshairEnabled = DefaultBoolean.False; series_point.ShowInLegend = true; series_point.SeriesPointsSorting = SortingMode.None; series_point.SeriesPointsSortingKey = SeriesPointKey.Value_1; series_point.Visible = true; series_point.LabelsVisibility = DefaultBoolean.True; series_point.Label.TextPattern = "{V:N1}"; series_point.Points.Add(new SeriesPoint(x, new double[] { y })); return series_point; } #endregion #region ChartEvent ToolTipController toolTip = new ToolTipController(); private void chartControl1_ObjectHotTracked(object sender, HotTrackEventArgs e) { if (!_initial_data) return; if (e.AdditionalObject is SeriesPoint seriesPoint) { var tip = string.Format("X:{0:N1} Y:{1:N1}", seriesPoint.Argument, seriesPoint.Values[0]); toolTip.ShowHint(tip); } else { toolTip.HideHint(); } } // 右键对象 private object _rightClickObj = null; private bool _onMoveWorkPointLine = false; private bool _onMoveWorkHLine = false; private void chartControl1_MouseDown(object sender, MouseEventArgs e) { if (!_initial_data) return; var hitInfo = chartControl1.CalcHitInfo(e.Location); if (e.Button == MouseButtons.Left) { if (hitInfo.InSeries) { _rightClickObj = hitInfo.Series; } else if (hitInfo.InAxis) { _rightClickObj = hitInfo.Axis; } else if (hitInfo.InConstantLine) { if (hitInfo.ConstantLine == _work_pt_line) { _onMoveWorkPointLine = true; } else if (hitInfo.ConstantLine == _work_l_line) { _onMoveWorkHLine = true; } } else if (hitInfo.InAnnotation) { _rightClickObj = hitInfo.Annotation; } else { _rightClickObj = null; } } else if (e.Button == MouseButtons.Right) { if (hitInfo.InConstantLine) { this.popMenuLine.ShowPopup(MousePosition); } else { this.popMenuChart.ShowPopup(MousePosition); } } } private void chartControl1_MouseMove(object sender, MouseEventArgs e) { if (!_initial_data) return; if (_onMoveWorkPointLine) { var diagramCoordinates = _main_chart_diagram.PointToDiagram(e.Location); var axisValue = diagramCoordinates.GetAxisValue(_axis_x_q); if (axisValue == null) return; double chartQ = axisValue.NumericalValue; CalcWorkPointByQ(chartQ); } else if (_onMoveWorkHLine) { var diagramCoordinates = _main_chart_diagram.PointToDiagram(e.Location); var axisValue = diagramCoordinates.GetAxisValue(_axis_y_ql); if (axisValue == null) return; double chartH = axisValue.NumericalValue; CalcWorkPointByH(chartH); } } private void chartControl1_MouseUp(object sender, MouseEventArgs e) { if (!_initial_data) return; _onMoveWorkPointLine = false; _onMoveWorkHLine = false; } private void chartControl1_Resize(object sender, EventArgs e) { CalcTextAnchorPoint(); } #endregion #region Right Click Menu #region Event private void barBtnSetAxisQValue_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { if (_pt_ql_list == null || !_pt_ql_list.Any()) return; var dlg = new PumpAxisValueDlg(); dlg.SetBindingData(); dlg.VerifyValueChanged += (value) => { var min = _pt_ql_list.Min(x => x.X); var max = _pt_ql_list.Max(x => x.X); if (value < min || value > max) return false; CalcWorkPointByQ(value); return true; }; dlg.ShowDialog(); } private void barBtnSetAxisHValue_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { if (_pt_ql_list == null || !_pt_ql_list.Any()) return; var dlg = new PumpAxisValueDlg(); dlg.SetBindingData(); dlg.VerifyValueChanged += (value) => { var min = _pt_ql_list.Min(x => x.Y); var max = _pt_ql_list.Max(x => x.Y); if (value < min || value > max) return false; CalcWorkPointByH(value); return true; }; dlg.ShowDialog(); } private void barBtnPositioningMaxQ_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { CalcWorkPointByMaxQ(); } private void barCekDefinePointVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetDefinePointVisible(this.barCekDefinePointVisible.Checked); } private void barCekLineVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetLineVisible(this.barCekLineVisible.Checked); } private void barCekLegendVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetLegendVisible(this.barCekLegendVisible.Checked); } private void barCekSetAxisNameVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetAxisNameVisible(this.barCekSetAxisNameVisible.Checked); } private void barCekSetSplitPanel_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { CalcChartAxis(); } private void barCekChartDisplay_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetChartDisplay(this.barCekChartDisplay.Checked); } private void barBtnSetChartAxis_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetChartAxis(); } private void barBtnSetChartEquation_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { XtraMessageBox.Show("待补充"); //SetChartEquation(); } private void barBtnExportXls_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { ExportXls(); } private void barBtnExportImage_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { ExportImage(); } #endregion /// /// 设置定义点显示 /// public void SetDefinePointVisible(bool visible) { _series_pt_ql.Visible = visible; } /// /// 设置工作点显示 /// public void SetLineVisible(bool visible) { if (!_initial_data) return; _lineVisible = visible; CalcWorkPointByQ(); } /// /// 设置图例显示 /// public void SetLegendVisible(bool visible) { this.chartControl1.Legend.Visibility = visible ? DefaultBoolean.True : DefaultBoolean.False; } /// /// 设置轴名称显示 /// public void SetAxisNameVisible(bool visible) { _axis_x_q.Title.Visibility = visible ? DefaultBoolean.True : DefaultBoolean.False; _axis_y_ql.Title.Visibility = visible ? DefaultBoolean.True : DefaultBoolean.False; } /// /// 设置图表显示 /// public void SetChartDisplay(bool monoColor) { if (monoColor) { this.chartControl1.SetChartMonoColorDisplay(); } else { this.chartControl1.SetChartBackColor(); _axis_x_q.SetAxisYQColorDisplay(); _axis_y_ql.SetAxisYQLColorDisplay(_series_cubic_spline_ql, _series_pt_ql, true); } } /// /// 设置坐标轴 /// public void SetChartAxis() { var dlg = new ValveChartCoordinateDlg(); dlg.SetBindingData(_coordinate_paras); dlg.OnChangedCoord += (rhs) => { _coordinate_paras = rhs; CalcChartAxis(); this.OnCurveCoordinateChanged?.Invoke(_coordinate_paras); }; dlg.ShowDialog(); } ///// ///// 曲线方程 ///// //public void SetChartEquation() //{ // var dlg = new CurveEquationDlg(); // dlg.SetBindingData(_cubic_spline_qh, _cubic_spline_qe, _cubic_spline_qp); // dlg.ShowDialog(); //} /// /// 导出Excel /// public void ExportXls() { //Perform2dExportHelper.ExportXLS(_cubic_spline_ql, _cubic_spline_qe, _cubic_spline_qp, 12); } /// /// 导出图片 /// public void ExportImage() { var dlg = new SaveFileDialog(); dlg.Filter = "jpg 图片(*.jpg)|*.jpg"; if (dlg.ShowDialog() != DialogResult.OK) return; this.chartControl1.ExportToImage(dlg.FileName, System.Drawing.Imaging.ImageFormat.Jpeg); } #endregion } }