using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using static System.Windows.Forms.AxHost; using static System.Windows.Forms.LinkLabel; using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrackBar; namespace CloudWaterNetwork { public partial class PumpViewer : UserControl { private List _junctions=new List(); private List _links=new List(); private List _areas=new List(); private Bitmap buffer; private Point offset=new Point(0,0); private float zoom = 1.0f; private double Rotation = 0; private double Rotation0 = 0; private StatusStrip statusStrip1; private ToolStripStatusLabel label_center; private ToolStripStatusLabel label_zoom; private ToolStripStatusLabel label_mouse; private PointF MapCenter; private PointF MapCenter0; private Label label_choose; private bool is3Dview = false; public List junctions { get { return _junctions; } set { _junctions = value; } } public List Pipes { get { return _links; } set { _links = value; } } public List Areas { get { return _areas; } set { _areas = value; } } //public float Scale //{ // get { return zoom; } // set { zoom = value; } //} public Point PanningOffset { get { return offset; } set { offset = value; } } public PumpViewer() { InitializeComponent(); junctions = new List(); Pipes = new List(); Areas = new List(); MapCenter = PointF.Empty; zoom = 0.1f; DoubleBuffered = true; SetStyle(ControlStyles.SupportsTransparentBackColor, true); BackColor = Color.Transparent; } public void SetData(Network network) { SetData(network.Nodes, network.Links, network.areas); } public void Set3DView(bool is3Dview) { this.is3Dview = is3Dview; Invalidate(); } public void SetData(List Junctions, List Links, List Areas) { this.junctions = Junctions; this.Pipes = Links; this.Areas = Areas; MapCenter = PointF.Empty; float x0=99999999999f, y0= 99999999999f, x1=-99999999999f, y1=-99999999999f; foreach (Node junction in Junctions) { MapCenter.X +=(float) junction.X; MapCenter.Y +=(float) junction.Y; if (x0>junction.X) x0=junction.X; if (y0>junction.Y) y0=junction.Y; if (x1 0) { MapCenter.X /= Junctions.Count; MapCenter.Y /= Junctions.Count; } Invalidate(); } public void SetRotation(double d) { this.Rotation = d; Invalidate(); } /// /// 将屏幕坐标转换为世界坐标。输入屏幕坐标 (x,y),返回世界坐标 (wx, wy)。 /// /// /// public PointF ScreenToMap(PointF screenPos,float z=0) { var centerX = Width / 2; var centerY = Height / 2; var worldX = (screenPos.X - centerX - Z(z).X) / Zoom.X + MapCenter.X; var worldY = (screenPos.Y - centerY - Z(z).Y) / Zoom.Y + MapCenter.Y; //if (is3Dview) worldY = -(screenPos.Y - centerY + 2 * z) / (0.5f* zoom) + center.Y; return new PointF(worldX, worldY); } public PointF MapToScreen(PointF mapPos, float z=0) { var centerX = Width / 2; var centerY = Height / 2; var screenX = (mapPos.X - MapCenter.X) * Zoom.X + centerX + Z(z).X; var screenY = (mapPos.Y - MapCenter.Y) * Zoom.Y + centerY + Z(z).Y; //if (is3Dview) screenY = -(mapPos.Y - center.Y) * (0.5f * zoom) + centerY - 2 * z; return new PointF(screenX, screenY); } public float Get_dist(PointF A, PointF B) { float dx = A.X - B.X; float dy = A.Y - B.Y; float dist = (float)Math.Sqrt(dx * dx + dy * dy); return dist; } PointF PMin_Show, PMax_Show; /// /// 判断是否在屏幕坐标内 /// /// /// public bool isVisible( PointF MapPos) { if (MapPos.X < PMin_Show.X || MapPos.X > PMax_Show.X || MapPos.Y < PMin_Show.Y || MapPos.Y > PMax_Show.Y) return false; else return true; } ///// ///// 获取屏幕距离 ///// //private double GetDistOnScreen(double dist) //{ //} //Graphics bufferG=null; // 根据旋转角度计算旋转后的坐标 private PointF GetRotatePoint(PointF p ) { PointF center = MapCenter; double radian = Rotation * Math.PI / 180; // 角度转弧度 float x = (float)(Math.Cos(radian) * (p.X - center.X) - Math.Sin(radian) * (p.Y - center.Y) + center.X); float y = (float)(Math.Sin(radian) * (p.X - center.X) + Math.Cos(radian) * (p.Y - center.Y) + center.Y); return new PointF(x, y); } private PointF GetRotateVector(PointF p,PointF p0) { double radian = Rotation * Math.PI / 180; // 角度转弧度 float x = (float)(Math.Cos(radian) * (p.X - p0.X) - Math.Sin(radian) * (p.Y - p0.Y)); float y = (float)(Math.Sin(radian) * (p.X - p0.X) + Math.Cos(radian) * (p.Y - p0.Y)); return new PointF(x, y); } private PointF TransformPoint(PointF point, float z = 0) { var pointR = GetRotatePoint(point); var n=new PointF((float)pointR.X - Z(z).X, (float)(pointR.Y - Z(z).Y)); return n; } private PointF GetMapPoint_3D(Node junction) { PointF p; p = TransformPoint(junction.Position, junction.Elev); return p; } //private Point3D pointoff() //{ //} public PointF Zoom { get { var p = new PointF(0, 0); p.X = zoom; p.Y = is3Dview ?-0.5f * zoom : -1f * zoom; return p; } } private PointF Z(float z) { if (is3Dview) { var point = new PointF(0, -2f * z); return point; } else { return new PointF(0, 0); } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); if (buffer == null || buffer.Width != Width || buffer.Height != Height) { buffer?.Dispose(); buffer = new Bitmap(Width, Height); } // 使用缓存绘制,避免在每次重绘时重新计算所有要绘制的元素 //if (bufferG == null) bufferG = Graphics.FromImage(buffer); using (var bufferG = Graphics.FromImage(buffer)) //using (var bufferG = e.Graphics) { // 先将控件的背景填充为白色 bufferG.Clear(Color.Transparent); //bufferG.TranslateTransform(Width/2, Height/2); //bufferG.ScaleTransform(Zoom.X, Zoom.Y); //if (!this.is3Dview) //{ // bufferG.ScaleTransform(zoom, -zoom); //} //else //{ // //bufferG.RotateTransform(30); // bufferG.ScaleTransform(zoom, (float)-0.5* zoom); //} //bufferG.TranslateTransform(-MapCenter.X, -MapCenter.Y); //List diametersZoom = new List() { new PointF(0, 0.8f), new PointF(150, 0.3f), new PointF(300, 0.001f), new PointF(800, 0.0001f) }; //using (SolidBrush brush = new SolidBrush(Color.FromArgb(0, 255, 0, 0))) //{ // foreach (Area area in _areas) // { // bufferG.FillPolygon(brush, area.Points.Select( // p => new PointF((float) (p.X), (float)(p.Y)) // ).ToArray()); // } //} //Pen penChoosed = new Pen(Color.Purple, 5); //// 绘制线 //using (Pen pen = new Pen(Color.FromArgb(0, 0, 255), 2)) //{ // foreach (var link in _links) // { // if (!isVisible(link.Position)) continue; // float zoomAtMin = 0; // for (int i = 0; i < diametersZoom.Count; i++) // { // PointF point = diametersZoom[i]; // if (link.Diameter >= point.X) continue; // zoomAtMin = diametersZoom[i - 1].Y; // break; // } // if (zoomAtMin >= zoom) continue; // bufferG.DrawLines(link.Choosed?penChoosed: pen, new PointF[] {GetMapPoint_3D(link.StartPoint),GetMapPoint_3D(link.EndPoint) }); // } //} //SolidBrush brushChoosed = new SolidBrush(Color.Green); //using (SolidBrush brush = new SolidBrush(Color.FromArgb(255, 0, 0))) //{ // foreach (Junction junction in _junctions) // { // if (!isVisible( junction.Position)) continue; // var radius = 4.0f * zoom; // //var x = junction.Position.X * zoom + PanningOffset.X - radius / 2.0f; // //var y = junction.Position.Y * zoom + PanningOffset.Y - radius / 2.0f; // PointF p = GetMapPoint_3D(junction); // var rectangle = new RectangleF((float)p.X - 5, (float)p.Y - 5, 10, 10); // float zoomAtMin = 0; // for (int i = 0; i < diametersZoom.Count; i++) // { // PointF point = diametersZoom[i]; // if (junction.MaxDiameter >= point.X) continue; // zoomAtMin = diametersZoom[i - 1].Y; // break; // } // if (zoomAtMin >= zoom) continue; // bufferG.FillEllipse(junction.Choosed ? brushChoosed : brush, rectangle); // } //} base.OnPaint(e); if (this.BackgroundImage != null) { float ratio = (float)this.BackgroundImage.Width / this.BackgroundImage.Height; RectangleF destRect = new RectangleF(0, 0, this.Width, this.Width / ratio * 2 / 3); e.Graphics.DrawImage(this.BackgroundImage, destRect); } if (isDragging) { var _lastMousePosition = DragStartPos; // 绘制矩形 var start = new Point((int)Math.Min(mousePosition.X, _lastMousePosition.X), (int)Math.Min(mousePosition.Y, _lastMousePosition.Y)); var size = new Size((int)Math.Abs(_lastMousePosition.X - mousePosition.X), (int)Math.Abs(_lastMousePosition.Y - mousePosition.Y)); var rectangle0 = new Rectangle(start, size); using (var pen = new Pen(Color.Black, 2)) { bufferG.DrawRectangle(pen, rectangle0); } } } // 将生成的画布绘制到控件上 e.Graphics.DrawImage(buffer, 0,0); //BackColor = Color.FromArgb(500, BackColor); } public new void Invalidate() { base.Invalidate(); PMin_Show = ScreenToMap(new PointF(0, Height)); PMax_Show = ScreenToMap(new PointF(Width, 0)); } protected override void OnResize(EventArgs e) { base.OnResize(e); // 当控件尺寸改变时,触发重绘 this.Invalidate(); } public PointF mouseXY=new PointF(0,0); PointF DragStartPos; PointF RotaStartPos; bool isPanning; bool isDragging; bool isRotating; List ChoosedObjs = new List(); PointF mousePosition; // control+鼠标中间按下缩放 protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Middle ) { if (ModifierKeys == Keys.Control) { DragStartPos = ScreenToMap( new PointF(e.X, e.Y)); isDragging = true; } else { this.Cursor = Cursors.SizeAll; MapCenter0 = MapCenter; isPanning = true; } } else if (e.Button==MouseButtons.Left) { DragStartPos = ScreenToMap(new PointF(e.X, e.Y)); isDragging = true; } else if (e.Button==MouseButtons.Right) { RotaStartPos = new PointF(e.X, e.Y); Rotation0 = Rotation; isRotating = true; } } bool controlDown = false; protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); var MP = ScreenToMap(new PointF(e.X, e.Y)); if (isPanning) { var vector = GetRotateVector(new PointF(e.X,e.Y), new PointF(_lastMouseX, _lastMouseY)); //center.X -= e.X / zoom - _lastMouseX / zoom; //center.Y += e.Y / zoom - _lastMouseY / zoom; //MapCenter.X -= (e.X - _lastMouseX) / Zoom.X; //MapCenter.Y -= (e.Y - _lastMouseY) / Zoom.Y; MapCenter.X -= vector.X / Zoom.X ; MapCenter.Y -= vector.Y / Zoom.Y ; label_center.Text = $"center:({MapCenter.X.ToString("0.000")} ,{MapCenter.Y.ToString("0.000")})"; Invalidate(); } else if (isDragging) { mousePosition = MP; // 绘制选择框 // Rectangle selectionRect = new Rectangle( // Math.Min((int)DragStartPos.X, (int)MP.X), // Math.Min((int)DragStartPos.Y, (int)MP.Y), // Math.Abs((int)DragStartPos.X - (int)MP.X), // Math.Abs((int)DragStartPos.Y - (int)MP.Y)); //DrawSelectionRect(selectionRect); Invalidate(); } else if (isRotating) { mousePosition = MP; Rotation= Rotation0 + ((float)e.X- (float)RotaStartPos.X) * 180 *2.5 / (float)this.Width; Invalidate(); } label_mouse.Text = $"X:{e.X.ToString("0.000")} Y:{e.Y.ToString("0.000")} [Map]X:{MP.X.ToString("0.000")} Y:{MP.Y.ToString("0.000")}"; _lastMouseX = e.X; _lastMouseY = e.Y; } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (e.Button == MouseButtons.Middle) { if (ModifierKeys == Keys.Control) { isDragging = false; Cursor = Cursors.Default; var _lastMousePosition = DragStartPos; // 绘制矩形 var start = new Point((int)Math.Min(mousePosition.X, _lastMousePosition.X), (int)Math.Min(mousePosition.Y, _lastMousePosition.Y)); var size = new Size((int)Math.Abs(_lastMousePosition.X - mousePosition.X), (int)Math.Abs(_lastMousePosition.Y - mousePosition.Y)); var rectangle0 = new Rectangle(start, size); ChoosedObjs.ForEach(obj => obj.Selected = false); ChoosedObjs.Clear(); for (int i = 0; i < junctions.Count; i++) { PointF p = GetMapPoint_3D(junctions[i]); if (rectangle0.Contains(p.ToPoint())) { junctions[i].Selected = true; ChoosedObjs.Add(junctions[i]); } } for (int i = 0; i < Pipes.Count; i++) { //PointF p = GetPointF(Pipes[i]); if (rectangle0.Contains(Pipes[i].Position.ToPoint())) { Pipes[i].Selected = true; ChoosedObjs.Add(Pipes[i]); } } Invalidate(); //zoom *= (float)Math.Pow(2, e.Delta / 120.0 / 10.0); //zoom = Math.Max(0.1f, Math.Min(10.0f, zoom)); //center.X += e.Location.X / oldZoom - e.Location.X / zoom; //center.Y += e.Location.Y / oldZoom - e.Location.Y / zoom; label_center.Text = $"center:({MapCenter.X.ToString("0.000")} ,{MapCenter.Y.ToString("0.000")})"; label_zoom.Text = $"Zoom:{zoom.ToString("0.000")}"; Invalidate(); } else { Cursor = Cursors.Default; isPanning = false; } }else if (e.Button==MouseButtons.Left) { if (mousePosition != new PointF(0, 0)) { isDragging = false; Cursor = Cursors.Default; var _lastMousePosition = DragStartPos; // 绘制矩形 var start = new Point((int)Math.Min(mousePosition.X, _lastMousePosition.X), (int)Math.Min(mousePosition.Y, _lastMousePosition.Y)); var size = new Size((int)Math.Abs(_lastMousePosition.X - mousePosition.X), (int)Math.Abs(_lastMousePosition.Y - mousePosition.Y)); var rectangle0 = new Rectangle(start, size); ChoosedObjs.ForEach(obj => obj.Selected = false); ChoosedObjs.Clear(); for (int i = 0; i < junctions.Count; i++) { PointF p = GetMapPoint_3D(junctions[i]); if (rectangle0.Contains(p.ToPoint())) { junctions[i].Selected = true; ChoosedObjs.Add(junctions[i]); } } for (int i = 0; i < Pipes.Count; i++) { if (rectangle0.Contains(Pipes[i].Position.ToPoint())) { Pipes[i].Selected = true; ChoosedObjs.Add(Pipes[i]); } } GlobalObject.PropertyForm.SetObjs(ChoosedObjs); Invalidate(); mousePosition = new PointF(0, 0); } else { isDragging = false; // 遍历所有点,找出最近的点 PointF clickedPoint = new PointF(e.X, e.Y); //ScreenToMap(new PointF(e.X, e.Y)); float minDist = float.MaxValue; float DistLimit = 5f; int minIndex = -1; bool isJunction = true; for (int i = 0; i < junctions.Count; i++) { PointF mapPos = GetRotatePoint(junctions[i].Position); PointF currentPoint =MapToScreen(mapPos, junctions[i].Elev); float dist = Get_dist(clickedPoint, currentPoint ); if (dist < minDist) { minDist = dist; minIndex = i; } } for (int i = 0; i < Pipes.Count; i++) { //float dist = (clickedPoint.X - Pipes[i].X) * (clickedPoint.X - Pipes[i].X) + // (clickedPoint.Y - Pipes[i].Y) * (clickedPoint.Y - Pipes[i].Y); PointF mapPos = GetRotatePoint(Pipes[i].Position); PointF currentPoint = MapToScreen(mapPos, Pipes[i].Elev); float dist = Get_dist(clickedPoint, currentPoint); if (dist < minDist) { minDist = dist; minIndex = i; isJunction = false; } } if (minIndex >= 0 && minDist<=DistLimit) { if (isJunction) { ChoosedObjs.ForEach(obj => obj.Selected = false); ChoosedObjs.Clear(); junctions[minIndex].Selected = true; ChoosedObjs.Add(junctions[minIndex]); Invalidate(); // 显示该点的属性 ShowProperties(junctions[minIndex]); } else { ChoosedObjs.ForEach(obj => obj.Selected = false); ChoosedObjs.Clear(); Pipes[minIndex].Selected = true; ChoosedObjs.Add(Pipes[minIndex]); Invalidate(); // 显示该点的属性 ShowProperties(Pipes[minIndex]); } } else { ChoosedObjs.ForEach(obj => obj.Selected = false); ChoosedObjs.Clear(); Invalidate(); } } //zoom *= (float)Math.Pow(2, e.Delta / 120.0 / 10.0); //zoom = Math.Max(0.1f, Math.Min(10.0f, zoom)); //center.X += e.Location.X / oldZoom - e.Location.X / zoom; //center.Y += e.Location.Y / oldZoom - e.Location.Y / zoom; label_center.Text = $"center:({MapCenter.X.ToString("0.000")} ,{MapCenter.Y.ToString("0.000")})"; label_zoom.Text = $"Zoom:{zoom.ToString("0.000")}"; Invalidate(); } else if(e.Button==MouseButtons.Right) { if (isRotating) { isRotating = false; } } } protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); float oldZoom = zoom; zoom *= (float)Math.Pow(2, e.Delta / 120.0 / 10.0); zoom = Math.Max(0.1f, Math.Min(10.0f, zoom)); //center.X += e.Location.X / oldZoom - e.Location.X / zoom; //center.Y += e.Location.Y / oldZoom - e.Location.Y / zoom; label_center.Text = $"center:({MapCenter.X.ToString("0.000")} ,{MapCenter.Y.ToString("0.000")})"; label_zoom.Text = $"Zoom:{zoom.ToString("0.000")}"; Invalidate(); } private int _lastMouseX; private int _lastMouseY; // 显示点属性 private void ShowProperties(MapObject obj) { string str = "point"; // TODO: 根据需要实现 if (obj is Link) str = "pipe"; //MessageBox.Show($"{str}:({obj.X.ToString("0.000")} ,{obj.Y.ToString("0.000")})"); label_choose.Text = $"{str}[{obj.ID}]({obj.X.ToString("0.000")},{obj.Y.ToString("0.000")})"; GlobalObject.PropertyForm.SetObj(obj); } //public void DrawLine(PointF start, PointF end) //{ // var pipeline = new List(); // pipeline.Add(start); // pipeline.Add(end); // pipeline.ForEach(pt => pt.Transform(x => (x - _viewCenter) / _scale)); // var nearbyPipes = GetNearbyPipes(pipeline); // // TODO: Display nearby pipes //} //private List> GetNearbyPipes(List pipeline) //{ // // TODO: Implement this method to retrieve nearby pipes based on the given pipeline // // This method should return a list of Tuple, where each tuple represents a pipe // throw new NotImplementedException(); //} private void InitializeComponent() { this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.label_center = new System.Windows.Forms.ToolStripStatusLabel(); this.label_zoom = new System.Windows.Forms.ToolStripStatusLabel(); this.label_mouse = new System.Windows.Forms.ToolStripStatusLabel(); this.label_choose = new System.Windows.Forms.Label(); this.statusStrip1.SuspendLayout(); this.SuspendLayout(); // // statusStrip1 // this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.label_center, this.label_zoom, this.label_mouse}); this.statusStrip1.Location = new System.Drawing.Point(0, 578); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Size = new System.Drawing.Size(719, 22); this.statusStrip1.TabIndex = 0; this.statusStrip1.Text = "statusStrip1"; // // label_center // this.label_center.Name = "label_center"; this.label_center.Size = new System.Drawing.Size(73, 17); this.label_center.Text = "center:0,0"; // // label_zoom // this.label_zoom.Name = "label_zoom"; this.label_zoom.Size = new System.Drawing.Size(61, 17); this.label_zoom.Text = "Zoom:1"; // // label_mouse // this.label_mouse.Name = "label_mouse"; this.label_mouse.Size = new System.Drawing.Size(65, 17); this.label_mouse.Text = "X:0 Y:0"; // // label_choose // this.label_choose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.label_choose.AutoSize = true; this.label_choose.Location = new System.Drawing.Point(444, 584); this.label_choose.Name = "label_choose"; this.label_choose.Size = new System.Drawing.Size(0, 12); this.label_choose.TabIndex = 1; // // MapViewer // this.Controls.Add(this.label_choose); this.Controls.Add(this.statusStrip1); this.Name = "MapViewer"; this.Size = new System.Drawing.Size(719, 600); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } } }