using DevExpress.XtraEditors; using System.Data; namespace Yw.WinFrmUI.Phart { public partial class PumpPictureBoxCtrl : DevExpress.XtraEditors.XtraUserControl { public PumpPictureBoxCtrl() { InitializeComponent(); this.gridView1.SetDefaultEditView(); this.colDelete.OptionsColumn.AllowEdit = false; this.colDelete.OptionsColumn.ReadOnly = false; this.gridView1.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder; } #region 页面类 protected class CurrentViewModel { public CurrentViewModel() { } public CurrentViewModel(Yw.Geometry.Point2d rhs) { this.X = rhs.X; this.Y = rhs.Y; } public CurrentViewModel(double x, double y, int xp, int yp) { this.X = x; this.Y = y; this.Xpic = xp; this.Ypic = yp; } public double X { get; set; } public double Y { get; set; } public int Xpic { get; set; } public int Ypic { get; set; } } #endregion #region 变量 /// /// 有效范围 /// private Rectangle? _rect;//矩形 /// /// 是否正在获取等效线 /// private bool _isCaptureEuqualCurve = false; /// /// 是否打点等效线 /// private bool _isCaputueEtaPtInEqupCurve = false; /// /// 是否打点等效线,产生流量扬程(原水,五号沟泵站金海支线 是这个情况) /// public bool IsCaputueEtaPtInEqupCurve { set { _isCaputueEtaPtInEqupCurve = value; } } /// /// 捕捉曲线类型 /// private Yw.Ahart.eCurveType _currentCaptureCruve = Yw.Ahart.eCurveType.QH; /// /// 当前捕捉曲线类型 /// public Yw.Ahart.eCurveType CurrentCaptureCruve { get => _currentCaptureCruve; set => _currentCaptureCruve = value; } /// /// 流量单位 (默认m3/h) /// private Unit.eUnitQ _unitQ = Unit.eUnitQ.M3H; /// /// 流量单位 (默认m3/h) /// public Unit.eUnitQ UnitQ { get => _unitQ; set => _unitQ = value; } private bool _isSelBoundary; /// /// 允许设置有效范围 /// public bool IsSelBoundary { get { return _isSelBoundary; } set { _isSelBoundary = value; } } private bool _isPickPoint; /// /// 允许获取点 /// public bool IsPickPoint { get { return _isPickPoint; } set { _isPickPoint = value; } } #endregion #region 事件 /// /// 刷新曲线点事件 /// public event Action> ReloadPickPointsEvent; /// /// 刷新框选范围事件 /// public event Action ReloadBoundaryEvent; #endregion // 曲线图表坐标 public double ChartMinX = 0, ChartMaxX = 0; public double ChartMinY = 0, ChartMaxY = 0; /// /// 绑定数据列表 /// private List _allClickPoints; // /// 绑定数据 /// public void SetBindingData(string path) { if (!System.IO.File.Exists(path)) return; this.pictureBox1.Image = Image.FromFile(path); _rect = null; _allClickPoints = new List(); this.bindingSource1.DataSource = _allClickPoints; } /// /// 设置图片显示方式 /// public void SetPictureSizeMode(PictureBoxSizeMode sizeMode) { this.pictureBox1.SizeMode = sizeMode; } #region 选取点并绘制曲线 /// /// 开始获取点 /// public void StartPickPoint() { _isPickPoint = true; } /// /// 结束选取点 /// public void EndPickPoint() { _isPickPoint = false; } /// /// 清除选取点 /// public void ClearPickPoint() { if (XtraMessageBox.Show("是否清空点?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK) return; _allClickPoints.Clear(); ReloadPickPoints(); DrawPictureShape(); } /// /// 刷新界面 /// public void RefreshPictureShape(List points = null, Rectangle? picRect = null) { _rect = picRect; if (_rect == null) { this.pictureBox1.Invalidate(); return; } var rect = _rect.Value; _allClickPoints.Clear(); if (points != null && points.Any()) foreach (var item in points) _allClickPoints.Add(new CurrentViewModel(item)); ReloadPickPoints(); foreach (var row in _allClickPoints) { double ptX = row.X; double ptY = row.Y; int rX = 0, rY = 0; if (Math.Abs(ptX - ChartMinX) < 0.01) { rX = rect.Left;//防止分母为0 } else { rX = (int)(rect.Left + (rect.Right - rect.Left) / (ChartMaxX - ChartMinX) * (ptX - ChartMinX)); } if (Math.Abs(ptY - ChartMaxY) < 0.01) { rY = rect.Top; //防止分母为0 } else { rY = (int)(rect.Top - (rect.Bottom - rect.Top) / (ChartMaxY - ChartMinY) * (ptY - ChartMaxY)); } row.Xpic = rX; row.Ypic = rY; }; DrawPictureShape(); } #endregion #region Draw Picture /// /// 绘图 /// public void DrawPictureShape() { try { var list = _allClickPoints; this.pictureBox1.Refresh(); var picPoints = new List(); using (Graphics g = this.pictureBox1.CreateGraphics()) { //绘制边框 if (_rect != null) { using (Pen pen = new Pen(Color.Red, 3)) { g.DrawRectangle(pen, _rect.Value); } } //绘制点 if (list != null && list.Any()) { var num = 5; foreach (var row in list) { g.DrawLine(new Pen(Color.Black, 3), row.Xpic - num, row.Ypic + num, row.Xpic + num, row.Ypic - num); g.DrawLine(new Pen(Color.Black, 3), row.Xpic - num, row.Ypic - num, row.Xpic + num, row.Ypic + num); picPoints.Add(new Yw.Geometry.Point2d(row.Xpic, row.Ypic)); } //绘制线 if (list.Count >= 4 && !_isCaptureEuqualCurve) { //if (_currentCaptureCruveFitType == AStation.Curve.eFitType.ThroughPoint) // DrawThrountPoint(picPoints, 0.5f); //else DrawFitPoint(picPoints, 0.5f); } } } } catch (Exception) { throw; } } /// /// 画点和线(拟合) /// /// 图片点 /// private void DrawFitPoint(List picPoints, float tension) { if (picPoints == null || picPoints.Count < 4) return; var fitPoints = picPoints.GetFitPointList(); if (fitPoints == null || fitPoints.Count < 4) return; var resultPts = new List(); fitPoints.ForEach(x => resultPts.Add(new Point((int)x.X, (int)x.Y))); using (Graphics g = this.pictureBox1.CreateGraphics()) using (Pen pen = new Pen(Color.Red, 3)) g.DrawCurve(pen, resultPts.ToArray(), tension); } /// /// 绘制通过点 /// /// 图片点 /// private void DrawThrountPoint(List picPoints, float tension) { if (picPoints == null || picPoints.Count < 4) return; var resultPts = new List(); picPoints.ForEach(x => resultPts.Add(new PointF((float)x.X, (float)x.Y))); using (Graphics g = this.pictureBox1.CreateGraphics()) using (Pen pen = new Pen(Color.Red, 3)) g.DrawCurve(pen, resultPts.ToArray(), tension); } #endregion #region 图片区域的鼠标操作 private Point? _startPoint;//开始点 /// /// 开始选取范围 /// public void StartSelectionRange() { this.pictureBox1.Invalidate(); _rect = null; _startPoint = null; _isSelBoundary = true; this.Cursor = System.Windows.Forms.Cursors.Cross; this.DoubleBuffered = true; if (_allClickPoints != null) { _allClickPoints.Clear(); this.ReloadBoundaryEvent?.Invoke(_rect); ReloadPickPoints(); } } /// /// 鼠标按下 /// private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (_isSelBoundary) { _startPoint = e.Location; } else if (_isPickPoint) { int rX = e.X; int rY = e.Y; //精度 int xJd = 1, yJd = 1; if (Math.Abs(ChartMaxX - ChartMinX) < 12) xJd = 2; if (Math.Abs(ChartMaxX - ChartMinX) < 1) xJd = 3; double ptX = 0; if (rX == _rect.Value.Left) { ptX = ChartMinX; } else { ptX = UtilsHelper.Regulate(ChartMinX + (ChartMaxX - ChartMinX) / (_rect.Value.Right - _rect.Value.Left) * (rX - _rect.Value.Left), xJd); } if (ptX < 0) ptX = 0; Yw.Geometry.Point2d picPoint = null; if (!_isCaputueEtaPtInEqupCurve) { if (Math.Abs(ChartMaxY - ChartMinY) < 12) yJd = 2; if (Math.Abs(ChartMaxY - ChartMinY) < 2) yJd = 3; double ptY = 0; if (rY <= _rect.Value.Top) { ptY = ChartMaxY; } else { ptY = UtilsHelper.Regulate(ChartMaxY + (ChartMinY - ChartMaxY) / (_rect.Value.Bottom - _rect.Value.Top) * (rY - _rect.Value.Top), yJd); } if (ptY < 0) ptY = 0; _allClickPoints.Add(new CurrentViewModel(ptX, ptY, rX, rY)); picPoint = new Yw.Geometry.Point2d(ptX, ptY); } else { var dlg = new PickEqupPointDlg(); dlg.ReloadDataEvent += (ptY) => { _allClickPoints.Add(new CurrentViewModel(ptX, ptY, rX, rY)); picPoint = new Yw.Geometry.Point2d(ptX, ptY); return true; }; dlg.ShowDialog(); } ReloadPickPoints(); DrawPictureShape(); } } } /// /// 鼠标移动 /// private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { if (_isSelBoundary && _startPoint != null) { _rect = GetRectangle(_startPoint.Value, e.Location); this.pictureBox1.Invalidate(); } } //获取矩形 private Rectangle GetRectangle(Point p1, Point p2) { int x = p1.X < p2.X ? p1.X : p2.X; int y = p1.Y < p2.Y ? p1.Y : p2.Y; int width = Math.Abs(p1.X - p2.X); int height = Math.Abs(p1.Y - p2.Y); return new Rectangle(x, y, width, height); } /// /// 鼠标松开 /// private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { if (_isSelBoundary) { _startPoint = null; _isSelBoundary = false; this.Cursor = System.Windows.Forms.Cursors.Arrow; this.DoubleBuffered = false; this.ReloadBoundaryEvent?.Invoke(_rect); } } /// /// 绘制 /// private void pictureBox1_Paint(object sender, PaintEventArgs e) { if (_rect != null && _isSelBoundary) { using (Pen pen = new Pen(Color.Red, 3)) { e.Graphics.DrawRectangle(pen, _rect.Value); } } } #endregion #region gridView //删除点 private void gridView1_RowCellClick(object sender, DevExpress.XtraGrid.Views.Grid.RowCellClickEventArgs e) { if (e.Column == this.colDelete) { if (this.gridView1.GetRow(e.RowHandle) is CurrentViewModel row) { _allClickPoints.Remove(row); ReloadPickPoints(); DrawPictureShape(); } } } #endregion /// /// 得到图片曲线上的点 /// /// 返回获取点 /// public bool GetPicPoints(out List clickPoints) { clickPoints = null; if (_allClickPoints == null) return false; clickPoints = _allClickPoints.Select(x => new Yw.Geometry.Point2d(x.X, x.Y)).ToList(); List curvePoints = null; if (!_isCaptureEuqualCurve) { curvePoints = clickPoints.GetFitPointList(12); curvePoints?.OrderBy(x => x.X); } else { /* * 暂时只用完全通过点的方法,因为有时垂直很厉害时DrawFitPoint现状不好看 * 有增减性 就可以用拟合的方法 * 只用通过点的方法 */ curvePoints = clickPoints; } _allClickPoints.Clear(); ReloadPickPoints(); DrawPictureShape(); return true; } //刷新抓起点列表 private void ReloadPickPoints() { this.bindingSource1.ResetBindings(false); var pickPoints = _allClickPoints?.Select(x => new Yw.Geometry.Point2d(x.X, x.Y)).ToList(); this.ReloadPickPointsEvent?.Invoke(pickPoints); } } }