using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; using System.Numerics; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Yw.WinFrmUI.Q3d { partial class empty { } //定义一个委托,用来存储void Draw(Graphics bufferG, Template template) public delegate void DrawDelegate(Graphics bufferG, Settings template); public delegate void MouseDelegate(MouseEventArgs e); partial class Drawer { #region 定义事件 //定义一个event,将label_center.Text传出 public event EventHandler CenterChanged; public event EventHandler ZoomChanged; public event EventHandler RotationChanged; public event EventHandler RotationFChanged; public event EventHandler MousePositionChanged; //定义一个event,将选中的对象集传出 public event EventHandler> SelectedObjectsChanged; #endregion #region 绘制的相关属性,不允许外部访问 private DrawDelegate DrawNet = null; private DrawDelegate DrawNetNew = null; private DrawDelegate DrawBackGroundPic = null; //绘制辅助线 private DrawDelegate DrawAuxiliary = null; private MouseDelegate onMouseDown = null; private MouseDelegate onMouseMove = null; private MouseDelegate onMouseUp = null; private MouseDelegate onMouseWheel = null; private DrawingMode drawingMode = DrawingMode.All; private bool Inited = false; private DrawingStatus Status = DrawingStatus.Ready; #endregion #region 重写控件绘制事件 protected override void OnPaint(PaintEventArgs e) { this.Status = DrawingStatus.drawingBase; base.OnPaint(e); if (!Inited) { this.Status = DrawingStatus.Ready; return; } if (float.IsInfinity(mapOption.zoom)) { this.Status = DrawingStatus.Ready; return; } _needPaintAll = false; if (buffer == null || buffer.Width != Width || buffer.Height != Height) { buffer?.Dispose(); buffer = new Bitmap(Width, Height); } // 使用缓存绘制,避免在每次重绘时重新计算所有要绘制的元素 var bufferG = Graphics.FromImage(buffer); // 先将控件的背景填充为白色 bufferG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; bufferG.Clear(Color.Transparent); bufferG.TranslateTransform(Width / 2, Height / 2); bufferG.ScaleTransform(mapOption.zoom, -mapOption.zoom); bufferG.TranslateTransform(-mapOption.Center.X, -mapOption.Center.Y); this.Status = DrawingStatus.drawingPic; DrawBackGroundPic(bufferG, _Template); this.Status = DrawingStatus.drawingNet; //BookMark :绘制地图事件 DrawNet(bufferG, _Template); this.Status = DrawingStatus.drawingNetNew; //if (_newTemplate?.network != null) DrawNetNew(bufferG, _newTemplate); this.Status = DrawingStatus.drawingOthers; DrawAuxiliary(bufferG, null); // 将生成的画布绘制到控件上 e.Graphics.DrawImage(buffer, 0, 0); bufferG.Dispose(); this.Status = DrawingStatus.Ready; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (Inited) onMouseDown(e); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (Inited) onMouseMove(e); } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (Inited) onMouseUp(e); } protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); if (Inited) onMouseWheel(e); } #endregion #region 绘制的方法实现 void DrawBackGroud(Graphics bufferG, Settings template) { if (template == null) return; var _Nodes = _network.Nodes.ViewNodes; var _Links = _network.Links.ViewLinks; var Cpoints = getCurclePoints(64).ToList(); var r = 1.73f / zoom; var rt = r; float minElve = float.MinValue; float maxElve = float.MaxValue; r = r * Link_multiply; List diametersZoom = new List() { new PointF(0, 0.08f), new PointF(150, 0.03f), new PointF(300, 0.001f), new PointF(800, 0.0001f) }; Pen penN = new Pen(Color.FromArgb(0, 0, 255), 1 * r); //背景图绘制 if (this.mapOption.isShowPic && template != null && File.Exists(template.BackGroundImg_FullPath)) { //var gs = bufferG.Save(); // 应用矩阵变换以抵消之前的翻转效果 //bufferG.ScaleTransform(1 / Zoom.X, 1 / Zoom.Y); List p = new List(); if (!this.mapOption.isAutoBackgroundImage) { var Cps = new List { template.BackGroundPoint1, new PointF(template.BackGroundPoint2.X,template.BackGroundPoint1.Y), new PointF(template.BackGroundPoint1.X,template.BackGroundPoint2.Y), //template.BackGroundPoint2, }; Cps.ForEach(cp => p.Add(WorldPointToMapPoint(cp, template.BackGroundElev, template.OffSet))); } else { var p1 = new PointF(template.BackGroundImgX, template.BackGroundImgY); var p2 = new PointF(template.BackGroundImgX + template.BackGroundImgWidth, template.BackGroundImgY + template.BackGroundImgHeight); var f = template.BackGroundImgRotaAngle / 180 * Math.PI; var djx = Math.Sqrt((Math.Pow(template.BackGroundImgWidth, 2) + Math.Pow(template.BackGroundImgHeight, 2))); var p3 = new PointF(p1.X + (float)(Math.Cos(f) * template.BackGroundImgWidth), p1.Y + (float)(Math.Sin(f) * template.BackGroundImgWidth)); var p4 = new PointF(p1.X - (float)(Math.Sin(f) * template.BackGroundImgHeight), p1.Y + (float)(Math.Cos(f) * template.BackGroundImgHeight)); p3.Y = p4.Y; //p4.Y = -p4.Y; var Cps = new List { p4, p3,p1 }; template.BackGroundPoint1 = p4; template.BackGroundPoint2 = new PointF(p3.X, p1.Y); Cps.ForEach(cp => p.Add(WorldPointToMapPoint(cp, template.BackGroundElev, template.OffSet))); } try { var img = System.Drawing.Image.FromFile(template.BackGroundImg_FullPath); if (img != null) { bufferG.FillPolygon(penN.Brush, p.ToArray()); bufferG.DrawImage(img, p.ToArray()); } } catch { } //bufferG.Restore(gs); } } void Draw(Graphics bufferG, Settings template) { if (template == null) return; var _Nodes = _network.Nodes.ViewNodes; var _Links = _network.Links.ViewLinks; var _Areas = _network.Areas; var Cpoints = getCurclePoints(64).ToList(); var r = 1.73f / zoom; var rt = r; float minElve = float.MinValue; float maxElve = float.MaxValue; r = r * Link_multiply; List diametersZoom = new List() { new PointF(0, 0.08f), new PointF(150, 0.03f), new PointF(300, 0.001f), new PointF(800, 0.0001f) }; Pen penN = new Pen(Color.FromArgb(0, 0, 255), 1 * r); Pen penChoosed = new Pen(Color.Purple, 5 * r); Pen pen_valveChoosed = new Pen(Color.Red, 5 * r); Pen penClosed = new Pen(Color.OrangeRed, 2 * r); Pen penHovered = new Pen(Color.DeepSkyBlue, 5 * r); //绘制面 using (Pen pen0 = new Pen(Color.FromArgb(0, 0, 255), 2 * r)) { foreach (var area in _Areas) { if (!area.Visible) continue; if (!IsFaceVisibleToCamera(area, GetCameraPosition())) continue; if (area.Z < minElve || area.Z >= maxElve) continue; var p = new List(); foreach (var node in area.InnerNodes) { p.Add(CubeWorldPointToMapPoint(node, template.OffSet)); } if (p.Count < 3) continue; pen0.Color = penClosed.Color = area.color; Pen pen = pen0; if (area.Hovered) pen = penHovered; bufferG.FillPolygon(pen.Brush, p.ToArray()); bufferG.DrawPolygon(pen, p.ToArray()); //显示area的名称 var c = new PointF(p.Average(p0 => p0.X), p.Average(p0 => p0.Y)); var brush = new SolidBrush(Color.White); var gs = bufferG.Save(); // 应用矩阵变换以抵消之前的翻转效果 bufferG.ScaleTransform(1 / Zoom.X, 1 / Zoom.Y); Font font = new Font(FontFamily.GenericSansSerif, 10 * 10 * zoom); SizeF textSize = bufferG.MeasureString(area.Name, font); var center = new PointF(c.X * Zoom.X, c.Y * Zoom.Y); float textLeft = center.X - textSize.Width / 2; float textTop = center.Y - textSize.Height / 2; PointF pd = new PointF(textLeft, textTop); bufferG.DrawString(area.Name, font, brush, pd); // 恢复之前保存的绘图状态 bufferG.Restore(gs); } } // 绘制线 HashSet dict_flow_direction = new HashSet(); using (Pen pen0 = new Pen(Color.FromArgb(0, 0, 255), 2 * r)) { foreach (var link in _Links) { if (!link.Visible) continue; if (link.Z < minElve || link.Z >= maxElve) continue; //if (_isMovingObject && (link.StartNode == _OperaNode || link.EndNode == _OperaNode)) continue; var p1 = WorldPointToMapPoint(link.StartNode, template.OffSet); var p2 = WorldPointToMapPoint(link.EndNode, template.OffSet); if (!isVisible(p1) && !isVisible(p2)) continue; if (LinkColour != null) { pen0.Color = penClosed.Color = GraphHelper.getLinkColor(LinkColour, link); } Pen pen = pen0; //#if DEBUG //#else if (_Template != null && _Template.mapOption._ShowStatus && link.Status == Yw.WinFrmUI.Q3d.ObjectEnum.StatusType.CLOSED) pen = penClosed; //#endif if (link.Hovered) pen = penHovered; 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; if (link is HeatExchanger) { if (link.Selected || IsShowValve) { var c = new PointF((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2); bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 }); var valveShapeHeight = link.Selected ? 10 : 5; PointF[] points = new PointF[] { GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), }; bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, points); } } else if (link is AirCompressor) { if (link.Selected || IsShowValve) { var c = new PointF((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2); bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 }); var valveShapeHeight = link.Selected ? 10 : 5; PointF[] points = new PointF[] { GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X , c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), }; bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, points); } } else if (link is ValveViewModel) { if (link.Selected || IsShowValve) { var c = new PointF((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2); bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 }); var valveShapeHeight = link.Selected ? 10 : 5; PointF[] points = new PointF[] { GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight * r, c.Y - valveShapeHeight * r,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight * r, c.Y + valveShapeHeight * r,c,p1,p2), }; bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, points); } } else if (link is PumpViewModel) { if (link.Selected || IsShowValve) { var c = new PointF((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2); //bufferG.DrawLine(link.Selected ? pen_valveChoosed : pen, p1, p2); bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 }); // 绘制圆形部分(水泵的泵体) float radius = 5 * r; float diameter = radius * 2; #region 圆形拆分 List p = new List(); Cpoints.ForEach(cp => p.Add(GraphHelper.getRotatePoint(c.X + cp.X * radius - radius, c.Y + cp.Y * radius, c, p1, p2))); #endregion bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, p.ToArray()); // 绘制连接线 var valveShapeHeight = link.Selected ? radius * 2 : radius; PointF[] points = new PointF[] { GraphHelper.getRotatePoint(c.X - valveShapeHeight , c.Y + valveShapeHeight ,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight , c.Y ,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight , c.Y ,c,p1,p2), GraphHelper.getRotatePoint(c.X + valveShapeHeight , c.Y + valveShapeHeight ,c,p1,p2), GraphHelper.getRotatePoint(c.X - valveShapeHeight , c.Y + valveShapeHeight ,c,p1,p2), }; bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, points); } } else { if (link.StartNode == null || link.EndNode == null) continue; try { bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 }); } catch (Exception ex) { } if (_Template.mapOption._ShowFlowDirection) { var c = new PointF((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2); var ps = MapToScreen(c); //将ps转换为ulong,精度为20,并加入到dict_flow_direction中 var ps_20 = GraphHelper.GetUlongByPoint(ps, 5); if (!dict_flow_direction.Contains(ps_20)) { dict_flow_direction.Add(ps_20); bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 }); // 绘制圆形部分(水泵的泵体) float radius = 5 * r; float diameter = radius * 2; #region 圆形拆分 float activeD = 1; if (link.EN_FLOW < 0) activeD = -1; List p = new List(); Cpoints.ForEach(cp => p.Add(GraphHelper.getRotatePoint(c.X - activeD * cp.X * radius + activeD * radius, c.Y + cp.Y * radius, c, p1, p2))); #endregion bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, p.ToArray()); var valveShapeHeight = link.Selected ? radius * 2 : radius; PointF[] points = new PointF[] { GraphHelper.getRotatePoint(c.X -activeD* valveShapeHeight , c.Y + valveShapeHeight ,c,p1,p2), GraphHelper.getRotatePoint(c.X , c.Y ,c,p1,p2), GraphHelper.getRotatePoint(c.X - activeD*valveShapeHeight , c.Y - valveShapeHeight,c,p1,p2), GraphHelper.getRotatePoint(c.X + activeD*valveShapeHeight , c.Y - valveShapeHeight,c,p1,p2), GraphHelper.getRotatePoint(c.X + activeD*valveShapeHeight , c.Y + valveShapeHeight ,c,p1,p2), GraphHelper.getRotatePoint(c.X - activeD*valveShapeHeight , c.Y + valveShapeHeight ,c,p1,p2), }; bufferG.FillPolygon(link.Selected ? pen_valveChoosed.Brush : pen.Brush, points); } } } } } r = rt; HashSet dict_point = new HashSet(); //绘制点 penChoosed = new Pen(Color.Green, 1f * r); Brush brushChoosed = penChoosed.Brush; SolidBrush whiteBrush = new SolidBrush(Color.White); using (Pen pen0 = new Pen(Color.FromArgb(255, 0, 0), 1 * r)) { foreach (NodeViewModel node in _Nodes) { if (!node.Visible) continue; if (node.Z < minElve || node.Z >= maxElve) continue; Pen pen = pen0; Brush brush = pen.Brush; float pr = (float)(r * 0.5); pr = pr * Junction_multiply; PointF p = WorldPointToMapPoint(node, template.OffSet); if (!isVisible(p)) continue; var ps_20 = GraphHelper.GetUlongByPoint(p, 0.1f); if (dict_point.Contains(ps_20)) continue; dict_point.Add(ps_20); //var x = junction.Position.X * zoom + PanningOffset.X - radius / 2.0f; //var y = junction.Position.Y * zoom + PanningOffset.Y - radius / 2.0f; if (NodeColour != null) { pen.Color = penChoosed.Color = GraphHelper.getNodeColor(NodeColour, node); brush = pen.Brush; brushChoosed = penChoosed.Brush; } if (node.Hovered) { pen = penHovered; brush = pen.Brush; pr = pr * 2; } var rectangle = new RectangleF((float)p.X - 5 * pr, (float)p.Y - 5 * pr, 10 * pr, 10 * pr); float zoomAtMin = 0; for (int i = 0; i < diametersZoom.Count; i++) { PointF point = diametersZoom[i]; if (node.MaxDiameter >= point.X) continue; zoomAtMin = diametersZoom[i - 1].Y; break; } if (zoomAtMin >= zoom) continue; if (node == _OperaNode) { //bufferG.DrawEllipse(junction.Choosed ? penChoosed : pen, rectangle); var whiteRect = new RectangleF(rectangle.X - 4 * pr, rectangle.Y - 4 * pr, rectangle.Width + 8 * pr, rectangle.Height + 8 * pr); bufferG.FillEllipse(whiteBrush, whiteRect); whiteRect = new RectangleF(rectangle.X + 2 * pr, rectangle.Y + 2 * pr, rectangle.Width - 4 * pr, rectangle.Height - 4 * pr); bufferG.DrawEllipse(node.Selected ? penChoosed : pen, whiteRect); whiteRect = new RectangleF(rectangle.X - 4 * pr, rectangle.Y - 4 * pr, rectangle.Width + 8 * pr, rectangle.Height + 8 * pr); bufferG.DrawEllipse(node.Selected ? penChoosed : pen, whiteRect); //bufferG.DrawEllipse(junction.Choosed ? penChoosed : pen, rectangle); } else if (node.ID == _EndPoint) { var whiteRect = new RectangleF(rectangle.X - 4 * pr, rectangle.Y - 4 * pr, rectangle.Width + 8 * pr, rectangle.Height + 8 * pr); bufferG.FillEllipse(whiteBrush, whiteRect); whiteRect = new RectangleF(rectangle.X - 4 * pr, rectangle.Y - 4 * pr, rectangle.Width + 8 * pr, rectangle.Height + 8 * pr); bufferG.DrawEllipse(node.Selected ? penChoosed : pen, whiteRect); //whiteRect = new RectangleF(rectangle.X + 2 * pr, rectangle.Y + 2 * pr, rectangle.Width - 4 * pr, rectangle.Height - 4 * pr); //bufferG.DrawEllipse(junction.Choosed ? penChoosed : pen, whiteRect); var p1 = new PointF(rectangle.X + 2 * pr, rectangle.Y + 2 * pr); var p2 = new PointF(p1.X + 6 * pr, p1.Y + 6 * pr); bufferG.DrawLine(node.Selected ? penChoosed : pen, p1, p2); p1 = new PointF(rectangle.X + 2 * pr, rectangle.Y + 8 * pr); p2 = new PointF(p1.X + 6 * pr, p1.Y - 6 * pr); bufferG.DrawLine(node.Selected ? penChoosed : pen, p1, p2); } else if (node is TankViewModel) { pr *= 2; rectangle = new RectangleF((float)p.X - 5 * pr, (float)p.Y - 5 * pr, 10 * pr, 10 * pr); RectangleF r0 = new RectangleF(rectangle.X, rectangle.Y + 5 * pr, 10 * pr, 5 * pr); RectangleF r1 = new RectangleF(rectangle.X + 2 * pr, rectangle.Y, 6 * pr, 5 * pr); bufferG.FillRectangle(node.Selected ? brushChoosed : brush, r0); bufferG.FillRectangle(node.Selected ? brushChoosed : brush, r1); } else if (node is ReservoirViewModel) { pr *= 2; rectangle = new RectangleF((float)p.X - 5 * pr, (float)p.Y - 5 * pr, 10 * pr, 10 * pr); RectangleF r0 = new RectangleF(rectangle.X, rectangle.Y + 2 * pr, rectangle.Width, 6 * pr); RectangleF r1 = new RectangleF(rectangle.X, rectangle.Y + 8 * pr, 1 * pr, 2 * pr); RectangleF r2 = new RectangleF(rectangle.X + 9 * pr, rectangle.Y + 8 * pr, 1 * pr, 2 * pr); bufferG.FillRectangle(node.Selected ? brushChoosed : brush, r0); bufferG.FillRectangle(node.Selected ? brushChoosed : brush, r1); bufferG.FillRectangle(node.Selected ? brushChoosed : brush, r2); } else if (node is MeterViewModel) { //bufferG.DrawEllipse(junction.Choosed ? penChoosed : pen, rectangle); bufferG.FillEllipse(node.Selected ? brushChoosed : brush, rectangle); var whiteRect = new RectangleF(rectangle.X + 1 * pr, rectangle.Y + 1 * pr, rectangle.Width - 2 * pr, rectangle.Height - 2 * pr); bufferG.FillEllipse(whiteBrush, whiteRect); var p1 = new PointF(rectangle.X + 5 * pr, rectangle.Y); var p2 = new PointF(rectangle.X + 5 * pr, rectangle.Y + 10 * pr); bufferG.DrawLine(node.Selected ? penChoosed : pen, p1, p2); } else { rectangle = new RectangleF((float)p.X - 3 * pr, (float)p.Y - 3 * pr, 6 * pr, 6 * pr); if (node.Selected || IsShowJunction) bufferG.FillEllipse(node.Selected ? brushChoosed : brush, rectangle); } } } using (Pen pen = new Pen(Color.FromArgb(255, 0, 0), 1 * r)) { Brush brush = pen.Brush; //获取_Nodes中自由水压最小的节点 var node = _Nodes.Where(n => n is JunctionViewModel || n is MeterViewModel && n.EN_PRESSURE != float.NaN).OrderBy(n => n.EN_PRESSURE).FirstOrDefault(); //判断node.EN_PRESSURE不是float.NaN if (node != null && !float.IsNaN(node.EN_PRESSURE)) { //if (node.Elev < minElve || node.Elev >= maxElve) continue; float pr = (float)(r * 0.5); pr = pr * Junction_multiply; PointF p = WorldPointToMapPoint(node, template.OffSet); var ps_20 = GraphHelper.GetUlongByPoint(p, 0.1f); dict_point.Add(ps_20); //var x = junction.Position.X * zoom + PanningOffset.X - radius / 2.0f; //var y = junction.Position.Y * zoom + PanningOffset.Y - radius / 2.0f; if (NodeColour != null) { pen.Color = GraphHelper.getNodeColor(NodeColour, node); brush = pen.Brush; brushChoosed = penChoosed.Brush; } var rectangle = new RectangleF((float)p.X - 5 * pr, (float)p.Y - 5 * pr, 10 * pr, 10 * pr); float zoomAtMin = 0; for (int i = 0; i < diametersZoom.Count; i++) { PointF point = diametersZoom[i]; if (node.MaxDiameter >= point.X) continue; zoomAtMin = diametersZoom[i - 1].Y; break; } var p1 = new PointF((float)p.X - 4 * pr, (float)p.Y - 2 * pr); var p2 = new PointF((float)p.X + 4 * pr, (float)p.Y - 2 * pr); var p3 = new PointF((float)p.X, (float)p.Y - 4 * pr); bufferG.DrawPolygon(node.Selected ? penChoosed : pen, new PointF[] { p1, p2, p3 }); var whiteRect = new RectangleF(rectangle.X - 4 * pr, rectangle.Y - 4 * pr, rectangle.Width + 8 * pr, rectangle.Height + 8 * pr); bufferG.FillEllipse(whiteBrush, whiteRect); whiteRect = new RectangleF(rectangle.X - 4 * pr, rectangle.Y - 4 * pr, rectangle.Width + 8 * pr, rectangle.Height + 8 * pr); bufferG.DrawEllipse(node.Selected ? penChoosed : pen, whiteRect); } } } //绘制辅助线 void DrawH(Graphics bufferG, Settings template) { var r = 2f / zoom; if (_isDragging && DragStartPos != new PointF(0, 0) && mousePosition != new PointF(0, 0)) { //将$"{MapCenter.X.ToString("0.00")},{MapCenter.Y.ToString("0.00")}"通过CenterChanged传出 CenterChanged?.Invoke(this, $"{MapCenter.X.ToString("0.00")},{MapCenter.Y.ToString("0.00")}"); var _lastMousePosition = DragStartPos; // 绘制矩形 var start = new PointF((float)Math.Min(mousePosition.X, _lastMousePosition.X), (float)Math.Min(mousePosition.Y, _lastMousePosition.Y)); var size = new SizeF((float)Math.Abs(_lastMousePosition.X - mousePosition.X), (float)Math.Abs(_lastMousePosition.Y - mousePosition.Y)); if (size.Width == 0) size.Width = 0.01f; if (size.Height == 0) size.Height = 0.01f; var rectangle0 = new RectangleF(start, size); using (var pen = new Pen(Color.Black, 0.5f * r)) { bufferG.DrawRectangles(pen, new RectangleF[] { rectangle0 }); } } if (_isPainting) { if (_mouseState == MapViewEnum.MouseState.新增立管) { var wPos = GetZZWorldPoint(_select_junction1.Position3D, _MousePosition, new Vector3(0, 0, 1)); using (var pen = new Pen(Color.Black, 1 * r)) { pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; bufferG.DrawLine(pen, WorldPointToMapPoint(_select_junction1), WorldPointToMapPoint(wPos)); } } else { using (var pen = new Pen(Color.Black, 1 * r)) { pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; if (mapOption.IsOrtho) { var wPos = GetZZWorldPoint(_select_junction1.Position3D, _MousePosition, new Vector3(1, 1, 0)); //getPointAndHeight(e, _select_junction1, out p, out z); var mapPos = WorldPointToMapPoint(wPos); bufferG.DrawLine(pen, WorldPointToMapPoint(_select_junction1), mapPos); } else { bufferG.DrawLine(pen, WorldPointToMapPoint(_select_junction1), _MousePosition); } } } } if (_isDrawingPolygon && polygonPoints.Count > 0) { List pf = polygonPoints.ToList(); pf.Add(new PointF(mousePosition.X, mousePosition.Y)); using (var pen = new Pen(Color.Black, 1 * r)) { // 绘制多边形虚线边框 pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; bufferG.DrawLines(pen, pf.ToArray()); } } if (_isSettingBackGroundPictur) { var _lastMousePosition = DragStartPos; // 绘制矩形 var start = new PointF((float)Math.Min(mousePosition.X, _lastMousePosition.X), (float)Math.Min(mousePosition.Y, _lastMousePosition.Y)); var size = new SizeF((float)Math.Abs(_lastMousePosition.X - mousePosition.X), (float)Math.Abs(_lastMousePosition.Y - mousePosition.Y)); var rectangle0 = new RectangleF(start, size); using (var pen = new Pen(Color.Black, 1 * r)) { bufferG.DrawRectangles(pen, new RectangleF[] { rectangle0 }); } } if (_isMovingObject) { var newP = _MousePosition; //var p = MapPointToWorldPoint(, _OperaNode.Elev); var oldP3D = (PointF3D)_undoOldValue; var oldP = WorldPointToMapPoint(new PointF(oldP3D.X, oldP3D.Y), oldP3D.Z); List pf = new List { oldP, newP }; using (var pen = new Pen(Color.Black, 1 * r)) { // 绘制多边形虚线边框 pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; bufferG.DrawLines(pen, pf.ToArray()); } } } #endregion #region 基础坐标转换方法 /// /// 将屏幕坐标转换为世界坐标。 (x,y),返回输入屏幕坐标世界坐标 (wx, wy)。 /// /// /// private PointF ScreenToVMap(PointF screenPos, float z = 0) { var centerX = this.Width / 2; var centerY = this.Height / 2; var worldX = (screenPos.X - centerX - Z(z).X) / Zoom.X + MapCenter.X; var worldY = (screenPos.Y - centerY) / Zoom.Y + 0; //if (is3Dview) worldY = -(screenPos.Y - centerY + 2 * z) / (0.5f* zoom) + center.Y; return new PointF(worldX, worldY); } /// /// 将屏幕坐标转换为世界坐标。输入屏幕坐标 (x,y),返回世界坐标 (wx, wy)。 /// /// /// private PointF ScreenToMap(PointF screenPos, float z = 0) { var centerX = this.Width / 2; var centerY = this.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); } /// /// 世界投影坐标转换为屏幕坐标 /// /// /// /// private PointF MapToScreen(PointF mapPos, float z = 0) { var centerX = this.Width / 2; var centerY = this.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); } // 根据旋转角度计算旋转后的坐标 /// /// 计算围绕竖直线Z轴旋转后的坐标 /// /// 世界坐标 /// /// private PointF3D Get平面旋转Point(PointF3D p, PointF3D MapC) { PointF3D center = MapC; 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); float z = p.Z; return new PointF3D(x, y, z); } /// /// 计算围绕竖直线Z轴旋转前的坐标 /// /// p是世界坐标 /// /// private PointF3D Get平面还原Point(PointF3D p, PointF3D MapC) { PointF3D center = MapC; 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); float z = p.Z; return new PointF3D(x, y, z); } /// /// 将世界坐标投影到xy平面,建立投影坐标系,法向量为Z轴 /// /// /// /// /// private PointF Get俯视角投影Point(PointF3D p, PointF3D MapC) { PointF3D center = MapC; double radian_fushi = 俯视弧度; float sin = (float)Math.Sin(radian_fushi); float cos = (float)Math.Cos(radian_fushi); float x = (float)p.X; float y = (float)(sin * (p.Y - center.Y) + center.Y) + cos * p.Z - center.Z; float z = p.Z; return new PointF(x, y); } /// /// 计算围绕X轴旋转前的坐标 /// /// /// /// /// private PointF3D Get俯视角投影还原Point(PointF3D p, PointF3D MapC) { PointF3D center = MapC; double radian_fushi = 俯视弧度; float sin = (float)Math.Sin(radian_fushi); float cos = (float)Math.Cos(radian_fushi); float x = p.X; float y = (p.Y - center.Y - cos * p.Z + center.Z) / sin + center.Y; float z = p.Z; return new PointF3D(x, y, z); } /// /// 输入两个<屏幕坐标>的点,返回连接两点<世界坐标>的向量 /// 该方法用来计算鼠标拖动/双击定位/鼠标位置缩放时,视角中心点的移动 /// /// 屏幕坐标 /// 屏幕坐标 /// private PointF3D GetWorldVectorByScreenPoints(PointF p, PointF p0) { double radian = Rotation * Math.PI / 180; // 角度转弧度 double radian_fushi = 俯视弧度; var wp0 = ScreenToMap(p0); var wp = ScreenToMap(p); //通过p0到p的点,构造一个二维向量 var vector = new Vector2(wp.X - wp0.X, wp.Y - wp0.Y); //通过俯视角度,根据vector的Y分量,计算新的y和z分量 float y = (float)(vector.Y * Math.Sin(radian_fushi)); float z = (float)(vector.Y * Math.Cos(radian_fushi)); //构造一个新的向量 var vector3 = new Vector3(vector.X, y, z); //通过平面旋转,将vector3,还原到世界坐标 var vector3_ = Get平面还原Point(new PointF3D(vector3.X, vector3.Y, vector3.Z), new PointF3D(0, 0, 0)); //对vector3_取反向量 //,得到最终的向量 return new PointF3D(vector3_.X, vector3_.Y, vector3_.Z); //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)); //float z = (float)(Math.Sin(radian_fushi) * (p.Y - p0.Y)); // 添加俯视角度的影响 //return new PointF3D(x / Zoom.X, y / Zoom.Y, z/Zoom.Z); } /// /// 获取世界投影坐标 /// /// /// /// private PointF WorldPointToMapPoint(PointF point, float z, PointF3D offset = null) { if (offset == null) offset = new PointF3D(0, 0, 0); PointF3D point3d = new PointF3D(point.X + offset.X, point.Y + offset.Y, z + offset.Z); var pointR = Get平面旋转Point(point3d, MapCenter); var pointT = Get俯视角投影Point(pointR, MapCenter); //var n=new PointF((float)pointR.X - Z(z).X, (float)(pointR.Y - Z(z).Y)); return pointT; } private PointF WorldPointToMapPoint(PointF3D point, PointF3D offset = null) { return WorldPointToMapPoint(new PointF(point.X, point.Y), point.Z, offset); } private PointF WorldPointToMapPoint(NodeViewModel junction, PointF3D offset = null) { if (junction == null) return new PointF(0, 0); var p = WorldPointToMapPoint(junction.Position, junction.Z, offset); return p; } private PointF CubeWorldPointToMapPoint(NodeViewModel junction, PointF3D offset = null) { if (junction == null) return new PointF(0, 0); var point = junction.Position3D; var z = junction.Z; if (offset == null) offset = new PointF3D(0, 0, 0); point = new PointF3D(point.X + offset.X, point.Y + offset.Y, point.Z + offset.Z); var pointR = Get平面旋转Point(point, new PointF3D(0, 0, 0)); var pointT = Get俯视角投影Point(pointR, new PointF3D(0, 0, 0)); //var n=new PointF((float)pointR.X - Z(z).X, (float)(pointR.Y - Z(z).Y)); return pointT; } private List WorldPointToMapPoint(LinkViewModel pipe, PointF3D offset = null) { List list = new List(); PointF p; p = WorldPointToMapPoint(pipe.StartNode, offset); list.Add(p); p = WorldPointToMapPoint(pipe.EndNode, offset); list.Add(p); return list; } /// /// 获取正交投影坐标,返回的是世界坐标 /// /// 世界坐标 /// 地图坐标 /// 投影向量 /// /// private PointF3D GetZZWorldPoint(PointF3D position3D, PointF mousePosition, Vector3 vector3) { //做一条通过position3D的平行于vector3的直线, if (vector3 == new Vector3(0, 0, 1)) { return GetLGWorldPoint(position3D, mousePosition); } else { var p2 = MapPointToWorldPoint(mousePosition, position3D.Z); var vector = new Vector2(p2.X - position3D.X, p2.Y - position3D.Y); //判断二维向量vector在第几象限,距离哪个轴最近 var x = vector.X; var y = vector.Y; var x1 = Math.Abs(x); var y1 = Math.Abs(y); if (x1 > y1) { if (x > 0) { //第一象限 return new PointF3D(position3D.X + Math.Abs(x), position3D.Y, position3D.Z); } else { //第三象限 return new PointF3D(position3D.X - Math.Abs(x), position3D.Y, position3D.Z); } } else { if (y > 0) { //第二象限 return new PointF3D(position3D.X, position3D.Y + Math.Abs(y), position3D.Z); } else { //第四象限 return new PointF3D(position3D.X, position3D.Y - Math.Abs(y), position3D.Z); } } } } /// /// 获取正交投影坐标,返回的是世界坐标 /// /// 世界坐标 /// 地图坐标 /// 投影向量 /// /// private PointF3D GetLGWorldPoint(PointF3D position3D, PointF p2) { double radian_fushi = 俯视弧度; float sin = (float)Math.Sin(radian_fushi); float cos = (float)Math.Cos(radian_fushi); var p1 = WorldPointToMapPoint(position3D); var dy = p2.Y - p1.Y; float dz = dy / cos; return new PointF3D(position3D.X, position3D.Y, position3D.Z + dz); } /// /// 获取地图投影坐标 /// /// /// /// public PointF3D MapPointToWorldPoint(PointF point, float z = 0) { var pointT = Get俯视角投影还原Point(new PointF3D(point.X, point.Y, z), MapCenter); pointT = Get平面还原Point(pointT, MapCenter); //var n=new PointF((float)pointR.X - Z(z).X, (float)(pointR.Y - Z(z).Y)); return pointT; } #endregion #region 计算三维相机 public PointF3D GetCameraPosition(float distance = 1) { // 将角度转换为弧度 float rotationRadians = (float)Rotation * (float)Math.PI / 180; float rotationFRadians = (float)RotationF * (float)Math.PI / 180; // 计算相机的球坐标系位置 float x = distance * (float)Math.Sin(rotationFRadians) * (float)Math.Cos(rotationRadians); float y = distance * (float)Math.Sin(rotationFRadians) * (float)Math.Sin(rotationRadians); float z = distance * (float)Math.Cos(rotationFRadians); return new PointF3D(x, y, z); } public bool IsFaceVisibleToCamera(AreaViewModel area, PointF3D cameraPosition) { //正面是2,右面是1,左面是3,背面是4 //Rotation为0时,只显示正面,Rotation为90时,只显示右面,Rotation为180时,只显示背面,Rotation为270时,只显示左面 int delta = 5; bool flag = false; switch (area.Name) { case "左": //Rotation为0~180时显示 if (RotationF <= 90 - delta && Rotation >= 0 + delta && Rotation <= 180 - delta) flag = true; break; case "前": //Rotation为0~180时显示 if (RotationF <= 90 - delta && Rotation >= -90 + delta && Rotation <= 90 - delta) flag = true; break; case "右": //Rotation为0~180时显示 if (RotationF <= 90 - delta && Rotation >= -180 + delta && Rotation <= 0 - delta) flag = true; break; case "后": //Rotation为0~180时显示 if (RotationF <= 90 - delta && ((Rotation >= 90 + delta && Rotation <= 180) || (Rotation >= -180 && Rotation <= -90 - delta))) flag = true; break; case "上": if (RotationF >= 0 + delta) flag = true; break; case "下": if (RotationF <= 0 - delta) flag = true; break; } return flag; var faceVertices = area.InnerNodes; if (faceVertices.Count < 3) return false; PointF3D faceVertexA = faceVertices[0].Position3D; PointF3D faceVertexB = faceVertices[1].Position3D; PointF3D faceVertexC = faceVertices[2].Position3D; // 计算法向量 PointF3D AB = faceVertexB - faceVertexA; PointF3D AC = faceVertexC - faceVertexA; PointF3D normal = AB ^ AC; // 计算从相机位置到面的向量 PointF3D PD = faceVertexA - cameraPosition; // 计算点积 float dotProduct = normal * PD; // 判断面是否朝向相机 bool isFacingCamera = dotProduct > 0; return isFacingCamera; } #endregion #region 判断可见性 private 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; } //判断A距离线段B和C的距离,如果超出了线段的范围,则返回到最近的端点的距离;距离线段中心点越远,返回的距离越大; private float Get_dist(PointF A, PointF B, PointF C, float MaxOff) { //PointF A, PointF B,PointF C,求点A到B、C构成线段的中心点的距离 float dist_off = GetDistanceFromPointAToMidpointOfLineSegmentBC(A, B, C); //使用dist_off 跟 线段A、B的长度比较,如果大于1/2,则返回MaxOff,否则按照比例返回 float dist_len = Get_dist(B, C); if (dist_len < 5) dist_len = 5; float dist_add = (dist_off / dist_len > 0.5 ? MaxOff : dist_off / dist_len * 2 * MaxOff); float dx = C.X - B.X; float dy = C.Y - B.Y; float dist = (float)Math.Sqrt(dx * dx + dy * dy); if (dist == 0) return Get_dist(A, B) + dist_add; float t = ((A.X - B.X) * dx + (A.Y - B.Y) * dy) / (dist * dist); if (t < 0) return Get_dist(A, B) + dist_add; if (t > 1) return Get_dist(A, C) + dist_add; float x = B.X + t * dx; float y = B.Y + t * dy; return Get_dist(A, new PointF(x, y)) + dist_add; } private float GetDistanceFromPointAToMidpointOfLineSegmentBC(PointF A, PointF B, PointF C) { // Calculate the midpoint of the line segment BC PointF midpoint = new PointF((B.X + C.X) / 2, (B.Y + C.Y) / 2); // Calculate the distance from point A to the midpoint float dx = midpoint.X - A.X; float dy = midpoint.Y - A.Y; float distance = (float)Math.Sqrt(dx * dx + dy * dy); return distance; } 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; } /// /// 判断是否在屏幕坐标内 /// /// /// public bool isVisible(List list_MapPos) { bool visible = false; foreach (var MapPos in list_MapPos) { if (MapPos.X < PMin_Show.X || MapPos.X > PMax_Show.X || MapPos.Y < PMin_Show.Y || MapPos.Y > PMax_Show.Y) { } else { visible = true; return true; } } return visible; } #endregion } }