duheng
2025-03-28 b825d70578b0ddf6d479569887c194f919795dad
WinFrmUI/PBS.WinFrmUI.Hydro/99-map-view/MapViewer.cs
@@ -1,42 +1,25 @@

using Newtonsoft.Json;
using System.ComponentModel;
using Newtonsoft.Json;
using PBS.WinFrmUI.Hydro;
using System.Data;
using System.IO;
using System.Numerics;
using System.Text;
using static Hydro.MapViewEnum;
using Cursor = System.Windows.Forms.Cursor;
namespace Hydro
namespace HydroUI
{
    public partial class MapViewer : UserControl
    public partial class MapViewer : XtraUserControl
    {
        #region 一、全局
        #region View
        #region 一、全局
        #region 初始化
        //public MapViewer()
        //{
        //    InitializeComponent();
        //    //_Template = new Template();
        //    //toolStrip1.Visible = false;
        //    MapCenter = PointF.Empty;
        //    zoom = 1.0f;
        //    DoubleBuffered = true;
        //    SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        //    BackColor = Color.Transparent;
        //}
        #region 初始化
        private bool _showToolBar = true;
        [DisplayName("显示工具栏")]
        public bool showToolBar
        {
            get
            {
                return _showToolBar;// this.panel1==null?true:Visible;
                return _showToolBar;
            }
            set
            {
@@ -60,36 +43,23 @@
                if (this.statusStrip1 != null) this.statusStrip1.Visible = value;
            }
        }
        //public MapViewer()
        //{
        //    this.showToolBar = false;
        //    //this.showToolBar = false;
        //    InitializeComponent();
        //    MapCenter = PointF.Empty;
        //    zoom = 1.0f;
        //    DoubleBuffered = true;
        //    SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        //    BackColor = Color.Transparent;
        //}
        public MapViewer(bool showToolBar = false)
        private bool _showPropertyForm = true;
        [DisplayName("显示属性栏")]
        public bool ShowPropertyForm
        {
            this.showToolBar = showToolBar;
            InitializeComponent();
            //var propertyForm = new PropertyForm();
            //propertyForm.Dock = DockStyle.Right;
            //this.Controls.Add(propertyForm);
            MapCenter = PointF.Empty;
            zoom = 1.0f;
            DoubleBuffered = true;
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            BackColor = Color.Transparent;
            //if (!showToolBar) this.panel1.Visible = false;
            get
            {
                return _showPropertyForm;// this.panel1==null?true:Visible;
            }
            set
            {
                _showPropertyForm = value;
                if (this.propertyForm != null) this.propertyForm.Visible = value;
            }
        }
        public MapViewer()
        {
@@ -103,10 +73,7 @@
            BackColor = Color.Transparent;
        }
        public void SetEditMode(bool isEditMode)
        {
            _IsEditMode = isEditMode;
        }
        private void MapViewer_Load(object sender, EventArgs e)
        {
            GlobalObject.PropertyForm = this.propertyForm;
@@ -150,7 +117,7 @@
                AreaViewModel area = new AreaViewModel();
                area.ID = item.Key.ToString();
                area.InnerNodes = new List<NodeViewModel>();
                //area.Name= item.Key.ToString();
                area.InnerNodes = new List<NodeViewModel>();
                //6个面设置不一样的颜色
                switch (item.Key)
@@ -255,7 +222,6 @@
            {
                GlobalObject.PropertyForm.SetEnabled(_IsEditMode);
                GlobalObject.PropertyForm.SetNet(_Template.network);
                //_Template.network.MapObjects.AddUndoRedoSupport(GlobalObject.PropertyForm.propertyGrid);
            }
@@ -266,7 +232,7 @@
        }
        Dictionary<TemplateType, bool> _ViewModel = null;
        Dictionary<PBS.eModelTemplateType, bool> _ViewModel = null;
        public void Clear()
        {
            _Template = null;
@@ -276,7 +242,7 @@
            RotationF = 90;
            SetMapInvalidate();
        }
        public void SetData(Template template, dict<string, dynamic> param = null, Dictionary<TemplateType, bool> viewMode = null)
        public void SetData(Template template, dict<string, dynamic> param = null, Dictionary<PBS.eModelTemplateType, bool> viewMode = null)
        {
            this.param = param;
            bool reLoad = _Template == template;
@@ -284,43 +250,14 @@
            this._ViewModel = viewMode;
            //if ( _Template?.Floors!=null)
            //{
            //    _Template.Floors.ForEach(f=>
            //    {
            //        var btn = new ToolStripButton("  "+f.Name+"  ");
            //        btn.Click += (s, e) =>
            //        {
            //            this.mapOption.ShowFloor = f.FloorIndex;
            //            if (f.MapView != null )
            //            {
            //                this.mapOption= f.MapView.Copy();
            //            }
            //            this.SetInvalidated();
            //        };
            //        ToolStripMenuItem_Floor.DropDownItems.Add(btn);
            //    });
            //}
            //_Template.network = null;
            if (!LoadData()) return;
            //LoadData();
            //    temp.network.LoadRepeaters();
            //    map.SetStartEndPoint(temp.Node1, temp.Node2);
            if (!reLoad)
            {
                if (_Template.view == null)
                    InitCenter();
                else
                {
                    //MapCenter = _Template.view.Center;
                    //zoom = _Template.view.zoom;
                    //Rotation = _Template.view.rotation;
                    //RotationF = _Template.view.rotationF;
                    this.mapOption = _Template.view.Copy();
                    SetMapInvalidate();
                }
@@ -330,7 +267,7 @@
            label_center.Text = $"center:({MapCenter.X.ToString("0.00")} ,{MapCenter.Y.ToString("0.00")})";
            label_zoom.Text = $"Zoom:{zoom.ToString("0.000")}";
            toolStripStatusLabel_rotation.Text = $"Rotation:({Rotation.ToString("0")},{RotationF.ToString("0")})";
            //SetInvalidated();
        }
        private void InitCenter()
@@ -373,9 +310,7 @@
            _needPaintAll = true;
            PMin_Show = ScreenToMap(new PointF(0, this.map.Height));
            PMax_Show = ScreenToMap(new PointF(this.map.Width, 0));
            _timerDraw = true;
            _timerDraw = true;
        }
        public void SetStartEndPoint(String node1, string node2)
@@ -454,11 +389,9 @@
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            base.OnResize(e);
            // 当控件尺寸改变时,触发重绘
            this.SetMapInvalidate();
            this.SetMapInvalidate();
        }
        #endregion
@@ -601,116 +534,109 @@
            _isInsertingObject = true;
            _newTemplate = temp;
            _undoOldValue = new PointF3D(0, 0, 0);
            _OperaNode = temp.network.Nodes.Find(node => node.ID == temp.Node1) as NodeViewModel;
            //if (_OperaNode == null) _OperaNode=_NewNet.Nodes[0];
            _OperaNode = temp.network.Nodes.Find(node => node.ID == temp.Node1);
        }
        bool controlDown = false;
        PointF _MousePosition = new PointF(0, 0);
        void mapMouseMove(MouseEventArgs e)
        {
            //base.OnMouseMove(e);
            bool needInvalidate = false;
            bool flag = false;
            _MousePosition = ScreenToMap(new PointF(e.X, e.Y));
            if (_isMovingObject)
            {
                var p = MapPointToWorldPoint(_MousePosition, _OperaNode.Elev);
                var oldP = (PointF3D)_undoOldValue;
                if (!float.IsInfinity(p.X) && !float.IsInfinity(p.Y)) _newTemplate.OffSet = new PointF3D(p.X - oldP.X, p.Y - oldP.Y, 0); //_OperaNode.Position = p;
                PointF pointF = MapPointToWorldPoint(_MousePosition, _OperaNode.Elev);
                PointF3D pointF3D = (PointF3D)_undoOldValue;
                if (!float.IsInfinity(pointF.X) && !float.IsInfinity(pointF.Y))
                {
                    _newTemplate.OffSet = new PointF3D(pointF.X - pointF3D.X, pointF.Y - pointF3D.Y, 0f);
                }
                GlobalObject.PropertyForm.propertyGrid.Refresh();
                needInvalidate = true;
                GlobalObject.PropertyForm.propertyGridControl1.Refresh();
                flag = true;
            }
            else if (_isPainting)
            {
                needInvalidate = true;
                flag = true;
            }
            else if (_isPanning)
            {
                var vector = GetRotateVector(new PointF(e.X, e.Y), new PointF(_lastMouseX, _lastMouseY));
                MapCenter = new PointF(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")})";
                needInvalidate = true;
                PointF rotateVector = GetRotateVector(new PointF(e.X, e.Y), new PointF(_lastMouseX, _lastMouseY));
                MapCenter = new PointF(MapCenter.X - rotateVector.X / Zoom.X, MapCenter.Y - rotateVector.Y / Zoom.Y);
                label_center.Text = "center:(" + MapCenter.X.ToString("0.000") + " ," + MapCenter.Y.ToString("0.000") + ")";
                flag = true;
            }
            else if (_isDragging || _isDrawingPolygon || _isSettingBackGroundPictur)
            {
                mousePosition = _MousePosition;
                // 绘制选择框
                // 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);
                needInvalidate = true;
                flag = true;
            }
            else if (_isRotating)
            {
                mousePosition = _MousePosition;
                bool is下半屏幕 = RotaStartPos.Y >= this.map.Height / 2;
                if (ModifierKeys != Keys.Alt) Rotation = Rotation0 + ((float)e.X - (float)RotaStartPos.X) * 180 * 2.5 / (float)this.map.Width * (is下半屏幕 ? 1 : 1);
                if (ModifierKeys != Keys.Shift) RotationF = 俯视角度_start + ((float)e.Y - (float)RotaStartPos.Y) * 180 * 2.5 / (float)this.map.Height;
                if (RotationF > 90) RotationF = 90;
                if (RotationF < 0) RotationF = 0;
                needInvalidate = true;
                bool flag2 = RotaStartPos.Y >= (float)(map.Height / 2);
                if (Control.ModifierKeys != Keys.Alt)
                {
                    Rotation = Rotation0 + (double)(((float)e.X - RotaStartPos.X) * 180f) * 2.5 / (double)(float)map.Width * (double)(flag2 ? 1 : 1);
                }
                if (Control.ModifierKeys != Keys.Shift)
                {
                    RotationF = 俯视角度_start + (double)(((float)e.Y - RotaStartPos.Y) * 180f) * 2.5 / (double)(float)map.Height;
                }
                if (RotationF > 90.0)
                {
                    RotationF = 90.0;
                }
                if (RotationF < 0.0)
                {
                    RotationF = 0.0;
                }
                flag = true;
            }
            else
            if (_isInsertingObject)
            else if (_isInsertingObject)
            {
                var p = MapPointToWorldPoint(_MousePosition, _OperaNode.Elev);
                var oldP = (PointF3D)_undoOldValue;
                if (!float.IsInfinity(p.X) && !float.IsInfinity(p.Y)) _newTemplate.OffSet = new PointF3D(p.X - oldP.X, p.Y - oldP.Y, 0); //_OperaNode.Position = p;
                PointF pointF2 = MapPointToWorldPoint(_MousePosition, _OperaNode.Elev);
                PointF3D pointF3D2 = (PointF3D)_undoOldValue;
                if (!float.IsInfinity(pointF2.X) && !float.IsInfinity(pointF2.Y))
                {
                    _newTemplate.OffSet = new PointF3D(pointF2.X - pointF3D2.X, pointF2.Y - pointF3D2.Y, 0f);
                }
                GlobalObject.PropertyForm.propertyGrid.Refresh();
                needInvalidate = true;
                GlobalObject.PropertyForm.propertyGridControl1.Refresh();
                flag = true;
            }
            //else
            /*判断是否触碰到对象*/
            if (_mouseHoverCheckFlag)
            {
                _mouseHoverCheckFlag = false;
                // 遍历所有对象,找出范围内的对象
                PointF clickedPoint = new PointF(e.X, e.Y);  //ScreenToMap(new PointF(e.X, e.Y));
                var obj = GetObj_by_ScreenPoint(clickedPoint);
                if (hoveredObjs.Count > 0 && hoveredObjs[0] == obj || hoveredObjs.Count == 0 && obj == null)
                PointF clickedPoint = new PointF(e.X, e.Y);
                IBaseViewModel obj_by_ScreenPoint = GetObj_by_ScreenPoint(clickedPoint);
                if ((hoveredObjs.Count <= 0 || hoveredObjs[0] != obj_by_ScreenPoint) && (hoveredObjs.Count != 0 || obj_by_ScreenPoint != null))
                {
                    //needInvalidate = false;
                }
                else
                {
                    needInvalidate = true;
                    hoveredObjs.ForEach(o => o.Hovered = false);
                    hoveredObjs.Clear();
                    if (obj != null)
                    flag = true;
                    hoveredObjs.ForEach(delegate (IBaseViewModel o)
                    {
                        obj.Hovered = true;
                        hoveredObjs.Add(obj);
                        o.Hovered = false;
                    });
                    hoveredObjs.Clear();
                    if (obj_by_ScreenPoint != null)
                    {
                        obj_by_ScreenPoint.Hovered = true;
                        hoveredObjs.Add(obj_by_ScreenPoint);
                    }
                }
            }
            if (flag)
            {
                SetMapInvalidate();
            }
            if (needInvalidate) this.SetMapInvalidate();
            label_mouse.Text = $"X:{e.X.ToString("0")} Y:{e.Y.ToString("0")} [Map]X:{_MousePosition.X.ToString("0.00")} Y:{_MousePosition.Y.ToString("0.00")}";
            label_mouse.Text = "X:" + e.X.ToString("0") + " Y:" + e.Y.ToString("0") + " [Map]X:" + _MousePosition.X.ToString("0.00") + " Y:" + _MousePosition.Y.ToString("0.00");
            _lastMouseX = e.X;
            _lastMouseY = e.Y;
        }
@@ -788,10 +714,8 @@
                {
                    var node = _Nodes[i] as NodeViewModel;
                    PointF p = WorldPointToMapPoint(node);
                    if (rectangle0.Contains(p.ToPoint()))
                    if (rectangle0.Contains((int)p.X, (int)p.Y))
                    {
                        //_Nodes[i].Selected = true;
                        //selectedObjs.Add(_Nodes[i]);
                        objs.Add(node);
                    }
                }
@@ -799,11 +723,8 @@
                for (int i = 0; i < _Links.Count; i++)
                {
                    var link = _Links[i] as LinkViewModel;
                    //PointF p = GetPointF(Pipes[i]);
                    if (rectangle0.Contains(link.Position.ToPoint()))
                    if (rectangle0.Contains(new Point((int)link.Position.X, (int)link.Position.Y)))
                    {
                        //_Links[i].Selected = true;
                        //selectedObjs.Add(_Links[i]);
                        objs.Add(link);
                    }
                }
@@ -833,20 +754,19 @@
            if  /*插入结构*/(e.Button == MouseButtons.Left && _isInsertingObject)
            {
                var net = _newTemplate.network;//clone
                var net0 = _newTemplate.network.DeepCopyByBin<MapViewNetWork>();
                var net0 = _newTemplate.network.DeepCopy<MapViewNetWork>();
                List<NodeViewModel> nodes = _Nodes.Select(node => (NodeViewModel)node).ToList();
                float minDist = 100f;
                NodeViewModel minNode = null;
                foreach (NodeViewModel node in nodes)
                {
                    PointF mapPos = WorldPointToMapPoint(node);
                    PointF currentPoint = MapToScreen(mapPos);
                    float currentDist = 0;
                    if (node != _OperaNode && (currentDist = Get_dist(new PointF(e.X, e.Y), currentPoint)) < 15f)
                    {
                        if (currentDist < minDist) currentDist = minDist;
                        if (currentDist < minDist)
                            currentDist = minDist;
                        minNode = node;
                        break;
                    }
@@ -858,12 +778,9 @@
                    var p1 = (PointF3D)_undoOldValue;
                    var p2 = minNode.Position3D;
                    //var dd = _newTemplate.OffSet;
                    dx = p2.X - p1.X;
                    dy = p2.Y - p1.Y;
                    dz = p2.Z - p1.Z;
                }
                else
@@ -873,14 +790,6 @@
                    _isInsertingObject = false;
                    this.SetMapInvalidate();
                    return;
                    var p1 = (PointF3D)_undoOldValue;
                    var p2 = _OperaNode.Position3D;
                    var dd = _newTemplate.OffSet;
                    dx = dd.X; //p2.X - p1.X;
                    dy = dd.Y;// p2.Y - p1.Y;
                    dz = dd.Z;// p2.Z - p1.Z;
                }
@@ -889,7 +798,6 @@
                var list = _Network.Add(net);
                var j = _Network.AddPipe(minNode, _OperaNode);
                //j.Length = 0.0001f;
                list.Add(j);
                _OperaNode = null;
                _NewNet.Clear();
@@ -903,7 +811,6 @@
                selectedObjs.AddRange(list);
                MapObjectExtensions.AddCommand(_Network, "Add", null, list);
                _isInsertingObject = false;
@@ -1013,13 +920,9 @@
                var p1 = (PointF3D)_undoOldValue;
                var p2 = _OperaNode.Position3D;
                var dd = _newTemplate.OffSet;
                var dx = dd.X; //p2.X - p1.X;
                var dy = dd.Y;// p2.Y - p1.Y;
                var dz = dd.Z;// p2.Z - p1.Z;
                              //selectedNodes.ForEach(n => { n.Position3D = new PointF3D(n.X + dx, n.Y + dy, n.Elev + dz); });
                              //List<PointF3D> newPositons = selectedNodes.Select(n => n.Position3D).ToList();
                              //List<PointF3D> oldPositons = newPositons.Select(n => new PointF3D(n.X - dx, n.Y - dy, n.Z - dz)).ToList();
                              //MapObjectExtensions.AddCommand(selectedNodes, "Position3D", oldPositons, newPositons);
                var dx = dd.X;
                var dy = dd.Y;
                var dz = dd.Z;
                _OperaNode = null;
                _NewNet.Clear();
                return;
@@ -1080,8 +983,7 @@
                                //需要把鼠标位置转换为立管的位置,获取鼠标位置的高程
                                var wPos = GetZZWorldPoint(_select_junction1.Position3D, _MousePosition, new Vector3(0, 0, 1));
                                //var m = ScreenToVMap(new PointF(e.X, e.Y));
                                //z = m.Y;
                                p = new PointF(wPos.X, wPos.Y);
                                var l = AddLink(new PointF(e.X, e.Y), isdoubleClick, p, wPos.Z);
                                if (l.Count > 0) MapObjectExtensions.AddCommand(_Network, "Add", null, l);
@@ -1091,7 +993,6 @@
                                if (mapOption.IsOrtho)
                                {
                                    var wPos = GetZZWorldPoint(_select_junction1.Position3D, _MousePosition, new Vector3(1, 1, 0));
                                    //getPointAndHeight(e, _select_junction1, out p, out z);
                                    p = new PointF(wPos.X, wPos.Y);
                                    var l = AddLink(new PointF(e.X, e.Y), isdoubleClick, p, wPos.Z);
                                    if (l.Count > 0) MapObjectExtensions.AddCommand(_Network, "Add", null, l);
@@ -1118,7 +1019,7 @@
                _isDragging = false;
                // 遍历所有点,找出最近的点
                PointF clickedPoint = new PointF(e.X, e.Y);  //ScreenToMap(new PointF(e.X, e.Y));
                PointF clickedPoint = new PointF(e.X, e.Y);
                var obj = GetObj_by_ScreenPoint(clickedPoint);
                bool isJunction = obj is NodeViewModel;
@@ -1314,8 +1215,7 @@
                }
                else
                {
                    //GlobalObject.LockSelect
                {
                    selectedObjs.ForEach(o => o.Selected = false);
                    selectedObjs.Clear();
@@ -1441,16 +1341,12 @@
            {
                var link = _Links[i] as LinkViewModel;
                if (!link.Visible) continue;
                //float dist = (clickedPoint.X - Pipes[i].X) * (clickedPoint.X - Pipes[i].X) +
                //    (clickedPoint.Y - Pipes[i].Y) * (clickedPoint.Y - Pipes[i].Y);
                PointF mapPos1 = WorldPointToMapPoint(link.StartNode.Position, link.StartNode.Elev);
                PointF currentPoint1 = MapToScreen(mapPos1);
                PointF mapPos2 = WorldPointToMapPoint(link.EndNode.Position, link.EndNode.Elev);
                PointF currentPoint2 = MapToScreen(mapPos2);
                //根据currentPoint1和currentPoint2,判断clickedPoint离线段currentPoint1和currentPoint2的距离
                float dist = Get_dist(clickedPoint, currentPoint1, currentPoint2, DistLimit);
                //float dist = Get_dist(clickedPoint, currentPoint);
                if (dist < minDist && dist <= DistLimit)
                {
                    minDist = dist;
@@ -1462,50 +1358,7 @@
            return obj;
        }
        List<IBaseViewModel> GetObjs_by_ScreenPoint(PointF clickedPoint, float DistLimit = 15f)
        {
            float minDist = float.MaxValue;
            int minIndex = -1;
            IBaseViewModel obj = null;
            List<IBaseViewModel> objs = new List<IBaseViewModel>();
            for (int i = 0; i < _Nodes.Count; i++)
            {
                var node = _Nodes[i] as NodeViewModel;
                if (!node.Visible) continue;
                PointF mapPos = WorldPointToMapPoint(node);
                PointF currentPoint = MapToScreen(mapPos);
                float dist = Get_dist(clickedPoint, currentPoint);
                if (dist <= DistLimit)
                {
                    objs.Add(node);
                }
            }
            for (int i = 0; i < _Links.Count; i++)
            {
                var link = _Links[i] as LinkViewModel;
                if (!link.Visible) continue;
                //float dist = (clickedPoint.X - Pipes[i].X) * (clickedPoint.X - Pipes[i].X) +
                //    (clickedPoint.Y - Pipes[i].Y) * (clickedPoint.Y - Pipes[i].Y);
                PointF mapPos1 = WorldPointToMapPoint(link.StartNode.Position, link.StartNode.Elev);
                PointF currentPoint1 = MapToScreen(mapPos1);
                PointF mapPos2 = WorldPointToMapPoint(link.EndNode.Position, link.EndNode.Elev);
                PointF currentPoint2 = MapToScreen(mapPos2);
                //根据currentPoint1和currentPoint2,判断clickedPoint离线段currentPoint1和currentPoint2的距离
                float dist = Get_dist(clickedPoint, currentPoint1, currentPoint2, DistLimit);
                //float dist = Get_dist(clickedPoint, currentPoint);
                if (dist < minDist && dist <= DistLimit)
                {
                    objs.Add(link);
                }
            }
            return objs;
        }
        /// <summary>
        /// 鼠标滚轮事件
        /// </summary>
@@ -1580,7 +1433,7 @@
            }
            //else
            /*判断是否触碰到对象*/
            if (_mouseHoverCheckFlag)
            {
@@ -1616,6 +1469,7 @@
            _lastMouseX = e.X;
            _lastMouseY = e.Y;
        }
        void CubeViewMouseUp(MouseEventArgs e)
        {
            //BookMark    :鼠标抬起事件
@@ -1722,20 +1576,12 @@
        #region 视角工具
        // 显示点属性
        private void ShowProperties(IBaseViewModel obj)
        {
            string str = "point";
            if (obj is LinkViewModel) 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);
        }
        private void label_center_Click(object sender, EventArgs e)
        {
            GlobalObject.PropertyForm.SetObj(MapCenter);
        }
        double 俯视角度_bak = 45;
        private void tool视角_ButtonClick(object sender, EventArgs e)
        {
@@ -1821,13 +1667,14 @@
        }
        private void 设置长度ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            double length = 0;
            DialogResult result;
            InputBox input = new InputBox("输入长度");
            if (selectedObjs.Count == 1 && selectedObjs[0] is LinkViewModel l)
            {
                if (double.TryParse(input.ShowDialog(), out length))
                var dlg = new InputDlg();
                dlg.SetBindingData("输入口径");
                dlg.ReloadDataEvent += (length) =>
                {
                    var count1 = _Links.FindAll(ll => ll.Node1 == l.StartNode.ID || ll.Node2 == l.StartNode.ID).Count;
                    var count2 = _Links.FindAll(ll => ll.Node1 == l.EndNode.ID || ll.Node2 == l.EndNode.ID).Count;
@@ -1846,15 +1693,10 @@
                    SetMapInvalidate();
                }
            }
                };
            this.SetMapInvalidate();
            }
        }
        void MovePointbyLength(NodeViewModel node1, NodeViewModel node2, float Length)
        {
@@ -1879,9 +1721,7 @@
                node2.X += moveVector.X;
                node2.Y += moveVector.Y;
                node2.Elev += moveVector.Z;
                // 输出移动后的点 B 坐标
                //Console.WriteLine("移动后点 B 的坐标为 ({0}, {1}, {2})", x2, y2, z2);
            }
        }
        private void 全部显示ToolStripMenuItem_Click(object sender, EventArgs e)
@@ -1934,6 +1774,7 @@
                }
            }
        }
        private void 旧版打开ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // 创建打开文件对话框
@@ -1975,13 +1816,8 @@
        {
            if (!_IsEditMode)
            {
                if (message.show("提醒", "浏览模式无法保存,是否恢复为编辑模式"))
                {
                    _IsEditMode = true;
                }
                else
                    return;
                TipFormHelper.ShowWarn("浏览模式无法保存,请切换编辑模式");
                return;
            }
            else
                _Network.BuildToInp(_filePath);
@@ -2339,7 +2175,6 @@
        #region 右键菜单
        private void 转换ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var nodes = selectedNodes;
@@ -2446,72 +2281,26 @@
        #region 编辑模式/浏览模式切换工具
        private void toolStripComboBox_expandRepeater_ButtonClick(object sender, EventArgs e)
        {
            _IsEditMode = !_IsEditMode;
            //toolStripComboBox_浏览模式.Text = isEditMode ? "编辑模式" : "浏览模式";
            //LoadData(true);
        }
        private void 浏览模式ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var obj = sender as ToolStripItem;
            _IsEditMode = obj.Text == "编辑模式";
            //toolStripComboBox_浏览模式.Text = isEditMode ? "编辑模式" : "浏览模式";
            //LoadData(true);
        }
        #endregion
        /// <summary>
        /// 工具栏可用
        /// </summary>
        public void EnableToolBar()
        {
            this.toolStrip1.Enabled = true;
        }
        /// <summary>
        /// 工具栏不可用
        /// </summary>
        public void DisEnableToolBar()
        {
            this.toolStrip1.Enabled = false;
        }
        /// <summary>
        /// 隐藏工具栏
        /// </summary>
        public void HideToolBar()
        {
            this.toolStrip1.Visible = false;
        }
        /// <summary>
        /// 显示工具栏
        /// </summary>
        public void ShowToolBar()
        {
            this.toolStrip1.Visible = true;
        }
         
        #region 分析工具
        private void 水平旋转ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //if (!selectedObjs[0].isNode())
            //{
            //    MessageBox.Show("围绕第一个节点旋转,选择集中第一个对象必须是");
            //}
        {
            var objs = GlobalObject.PropertyForm.selectionSet.selectedObjects;
            var selectobjs = objs.FindAll(o => o is NodeViewModel); //GlobalObject.PropertyForm.listBox1.SelectedItems;
            if (selectobjs.Count <= 0) return;
            //if (selectobjs.Count != 1)
            //{
            //    MessageBox.Show("围绕选中节点进行平面旋转,请在属性列表中选中一个旋转中心对象");
            //    return;
            //}
            if (selectobjs.Count <= 0) return;
            if (!(selectobjs[0] as IBaseViewModel).isNode())
            {
                MessageBox.Show("围绕选中节点进行平面旋转,请在属性列表中选中一个节点类对象");
@@ -2581,9 +2370,9 @@
            float z = line.Z;
            float[,] rotationMatrix = new float[,]
            {
        {cos + x * x * (1 - cos), x * y * (1 - cos) - z * sin, x * z * (1 - cos) + y * sin},
        {y * x * (1 - cos) + z * sin, cos + y * y * (1 - cos), y * z * (1 - cos) - x * sin},
        {z * x * (1 - cos) - y * sin, z * y * (1 - cos) + x * sin, cos + z * z * (1 - cos)}
            {cos + x * x * (1 - cos), x * y * (1 - cos) - z * sin, x * z * (1 - cos) + y * sin},
            {y * x * (1 - cos) + z * sin, cos + y * y * (1 - cos), y * z * (1 - cos) - x * sin},
            {z * x * (1 - cos) - y * sin, z * y * (1 - cos) + x * sin, cos + z * z * (1 - cos)}
            };
            // 定义结果集合,并遍历输入点集合进行旋转处理
@@ -2615,15 +2404,8 @@
        {
            var objs = GlobalObject.PropertyForm.selectionSet.selectedObjects;
            var list = objs.FindAll(o => o is NodeViewModel); //GlobalObject.PropertyForm.listBox1.SelectedItems;
            var list = objs.FindAll(o => o is NodeViewModel);
            if (list.Count <= 0) return;
            //if (selectobjs.Count != 1)
            //{
            //    MessageBox.Show("围绕选中节点进行三维缩放,请在属性列表中选中一个缩放中心对象");
            //    return;
            //}
            //if (!(selectobjs[0] as IBaseViewModel).isNode())
            //var list = objs.FindAll(o => o.ID == selectobjs[0]);
            if (list.Count >= 1 && !list[0].isNode())
            {
                MessageBox.Show("围绕选中节点进行三维缩放,请在属性列表中选中一个[节点类]缩放中心对象");
@@ -2687,125 +2469,11 @@
        }
        private void btn_初分配_Click(object sender, EventArgs e)
        {
            //string result = null;
            //if ((result=_Template.network.CheckValidate())!=null)
            //{
            //    MessageBox.Show(result);
            //    return;
            //}
            List<WaterEquivalentSettings> settings = new List<WaterEquivalentSettings>();
            settings.Add(new WaterEquivalentSettings()
            {
                waterEquivalents = new WaterEquivalentTemplate()
                {
                    ID = "1",
                    Name = "1",
                    WaterEquivalentCollection = new WaterEquivalentCollection()
                    {
                        new WaterEquivalent()
                        {
                            ID=1008,
                            MinRatedFlow=0.48f,
                            RatedFlow=0.72f,
                            Count=1,
                        },
                        new WaterEquivalent()
                        {
                            ID=1009,
                            MinRatedFlow=0.88f,
                            RatedFlow=0.96f,
                            Count=2,
                        },
                        new WaterEquivalent()
                        {
                            ID=1010,
                            MinRatedFlow=1.08f,
                            RatedFlow=1.08f,
                            Count=3,
                        }
                    }
                },
                Meters = new List<string>()
                {
                    //按以下规律生成数组到M10,"M1","M2"
                        "M1","M2","M3","M4","M5","M6","M7","M8","M9","M10","M11","M12","M13","M14","M15","M16",
                    "M17","M18","M19","M20","M21","M22","M23","M24","M25","M26","M27","M28","M29","M30"
                },
            });
            settings.Add(new WaterEquivalentSettings()
            {
                waterEquivalents = new WaterEquivalentTemplate()
                {
                    ID = "1",
                    Name = "1",
                    WaterEquivalentCollection = new WaterEquivalentCollection()
                    {
                        new WaterEquivalent()
                        {
                            ID=2008,
                            MinRatedFlow=0.48f,
                            RatedFlow=0.48f,
                            Count=1,
                        },
                        new WaterEquivalent()
                        {
                            ID=2009,
                            MinRatedFlow=0.12f,
                            RatedFlow=0.12f,
                            Count=5,
                        },
                        new WaterEquivalent()
                        {
                            ID=2010,
                            MinRatedFlow=0.16f,
                            RatedFlow=0.18f,
                            Count=6,
                        }
                    }
                },
                Meters = new List<string>()
                {
                    //按以下规律生成数组到M10,"M1","M2"
                       "M31","M32","M33","M34","M35","M36","M37","M38","M39","M40","M41","M42","M43","M44","M45","M46","M47","M48","M49","M50","M51","M52","M53","M54","M55","M56","M57","M58","M59","M60","M61","M62","M63","M64","M65","M66","M67","M68","M69","M70","M71","M72","M73","M74","M75","M76","M77","M78","M79","M80"
                },
            });
            var head = _Network.reservoirs.FirstOrDefault().Head;
            // _Network.WaterDistribution(_Template.FullPath, GlobalPath.configPath + "config_waterDistri.wdb", settings, 40, head);
        }
        private void btn_拓扑检查_Click(object sender, EventArgs e)
        public void btn_拓扑检查_Click(object sender, EventArgs e)
        {
            Dictionary<string, List<string>> result = _Template.network.CheckValidate();
            if (result.Count > 0)
            {
                //ListBox listBox2 = new ListBox();
                //listBox2.Dock = DockStyle.Fill;
                ////listBox2的item为什么显示不出来?
                //listBox2.SelectedIndexChanged += (s, e) =>
                //{
                //    if (listBox2.SelectedIndex >= 0)
                //    {
                //        List<IBaseViewModel> Objs = new List<IBaseViewModel>();
                //        Objs.AddRange(_Nodes.Select(n=>(NodeViewModel)n));
                //        Objs.AddRange(_Links.Select(l => (LinkViewModel)l));
                //        var obj = Objs.Find(o => o.ID == listBox2.SelectedItem.ToString());
                //        if (obj != null)
                //        {
                //            obj.Selected = true;
                //            selectedObjs.Add((NodeViewModel)obj);
                //            SetMapInvalidate();
                //        }
                //    }
                //};
            {
                ListBox listBox1 = new ListBox();
                listBox1.Dock = DockStyle.Fill;
                listBox1.Width = 200;
@@ -2850,43 +2518,23 @@
                ResultForm.StartPosition = FormStartPosition.CenterScreen;
                ResultForm.Controls.Add(listBox1);
                ResultForm.MinimizeBox = false;
                ResultForm.MaximizeBox = false;
                //ResultForm.Controls.Add(listBox2);
                //listBox2.Invalidate();
                ResultForm.MaximizeBox = false;
                ResultForm.Show();
                //新建一个窗口,显示检查结果
                //CheckForm checkForm = new CheckForm();
                //checkForm.Show();
            }
            else
            {
                MessageBox.Show("拓扑检查通过");
            }
            //if (!string.IsNullOrEmpty(result))
            //{
            //    MessageBox.Show(result);
            //    return;
            //}
              
        }
        public void toolStripButton_计算_Click(object sender, EventArgs e)
        {
            //if (_Template == null)
            //{
            //    MessageBox.Show("请先打开文件");
            //    return;
            //}
            //_Network.Calc(_Template.FullPath, GlobalPath.configPath + "config_calc.wdb");
        }
        public void 关阀搜索ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedObjs.Count > 0)//&& selectedObjs[0] is Link l
            {
            if (selectedObjs.Count > 0)
            {
                var objs = selectedObjs.ToList();
                var selectsValve = objs.FindAll(o => o is ValveViewModel);
                selectedObjs.ForEach(o => o.Selected = false);
@@ -2897,6 +2545,7 @@
            }
        }
        /// <summary>
        /// 关阀分析(考虑水源的情况)
        /// </summary>
@@ -2977,14 +2626,11 @@
            queue.Enqueue(startLink);
            if (visitedNodes == null)
                visitedNodes = new HashSet<NodeViewModel>();
            //visitedNodes.Add(startLink.StartNode);
            //visitedNodes.Add(startLink.EndNode);
                visitedNodes = new HashSet<NodeViewModel>();
            while (queue.Count > 0)
            {
                LinkViewModel currentLink = queue.Dequeue();
                //Console.WriteLine("Traversing Link: " + currentLink.ID);
                LinkViewModel currentLink = queue.Dequeue();
                foreach (var node in new NodeViewModel[] { currentLink.StartNode, currentLink.EndNode })
                {
@@ -3097,18 +2743,15 @@
                if (!hasSource.ContainsKey(node))
                {
                    //hasSource.Add(node, false);
                    hasSource[node] = FindSouce(node, visitedLinks, visitedLinks2, hasSource);
                }
                if (hasSource[node] == true) return true;
            }
            return false;
        }
        private bool FindSouce(NodeViewModel startNode, HashSet<LinkViewModel> visitedLinks, HashSet<LinkViewModel> visitedLinks2, Dictionary<IBaseViewModel, bool> hasSource = null)
        {
@@ -3123,9 +2766,7 @@
                else
                    visitedLinks2.Add(link);
                if (!hasSource.ContainsKey(link))
                {
                    //hasSource.Add(link,false);
                {
                    hasSource[link] = FindSouce(link, visitedLinks, visitedLinks2, hasSource);
                }
@@ -3137,24 +2778,7 @@
        }
        //private void 联通性ToolStripMenuItem_Click(object sender, EventArgs e)
        //{
        //    if (selectedObjs.Count > 0)//&& selectedObjs[0] is Link l
        //    {
        //        var objs = selectedObjs.FindAll(o => o is LinkViewModel).Select(o => o as LinkViewModel).ToList();
        //        var visitedNodes = new HashSet<NodeViewModel>();
        //        objs.ForEach(o => TraversePipeNetworkALL(o, visitedNodes));
        //        this.SetInvalidated();
        //    }
        //}
        private void TraversePipeNetworkALL(LinkViewModel startLink, HashSet<NodeViewModel> visitedNodes = null, int direction = 0)
        {
@@ -3164,14 +2788,11 @@
            queue.Enqueue(startLink);
            if (visitedNodes == null)
                visitedNodes = new HashSet<NodeViewModel>();
            //visitedNodes.Add(startLink.StartNode);
            //visitedNodes.Add(startLink.EndNode);
                visitedNodes = new HashSet<NodeViewModel>();
            while (queue.Count > 0)
            {
                LinkViewModel currentLink = queue.Dequeue();
                //Console.WriteLine("Traversing Link: " + currentLink.ID);
                LinkViewModel currentLink = queue.Dequeue();
                foreach (var node in new NodeViewModel[] { currentLink.StartNode, currentLink.EndNode })
                {
@@ -3182,8 +2803,7 @@
                        visitedNodes.Add(node);
                        node.Selected = true;
                        selectedObjs.Add(node);
                        //Console.WriteLine("Visiting Node: " + node.ID);
                        foreach (var link in node.ViewLinks)
                        {
                            if (!visitedNodes.Contains(link.StartNode) || !visitedNodes.Contains(link.EndNode))
@@ -3500,24 +3120,23 @@
            var result = ofd.ShowDialog();
            if (result == DialogResult.OK)
            {
                _Template = new Template(new Guid().ToString(), "新建", "复制", TemplateType.其他);
                _Template = new Template(new Guid().ToString(), "新建", "复制", PBS.eModelTemplateType.Custom);
                string json = File.ReadAllText(ofd.FileName);
                _Template.network = JsonConvert.DeserializeObject<MapViewNetWork>(json);
                _Template.network.BuildRelation();
                TemplateList.AddTemp(_Template);
                LoadData();
            }
        }
        public void buttonUndo_Click(object sender, EventArgs e)
        {
            SetMapInvalidate();
            GlobalObject.PropertyForm.propertyGrid.Refresh();
            GlobalObject.PropertyForm.propertyGridControl1.Refresh();
            MapObjectExtensions.Undo();
        }
        public void buttonRedo_Click(object sender, EventArgs e)
        {
            SetMapInvalidate();
            GlobalObject.PropertyForm.propertyGrid.Refresh();
            GlobalObject.PropertyForm.propertyGridControl1.Refresh();
            MapObjectExtensions.Redo();
        }
        public void toolStripButton_save_ButtonClick(object sender, EventArgs e)
@@ -3526,12 +3145,8 @@
            if (!_IsEditMode)
            {
                if (message.show("提醒", "浏览模式无法保存,是否恢复为编辑模式"))
                {
                    _IsEditMode = true;
                }
                else
                    return;
                TipFormHelper.ShowWarn("浏览模式无法保存,请切换编辑模式");
                return;
            }
            else
            {
@@ -3782,12 +3397,8 @@
            if (!_IsEditMode)
            {
                if (message.show("提醒", "浏览模式无法保存,是否恢复为编辑模式"))
                {
                    _IsEditMode = true;
                }
                else
                    return;
                TipFormHelper.ShowWarn("浏览模式无法保存,请切换编辑模式");
                return;
            }
            else
            {
@@ -3898,17 +3509,17 @@
            // 计算点投影到直线上的坐标
            List<PointF3D> projectedPoints = new List<PointF3D>();
            List<PointF3D> modelTemplateedPoints = new List<PointF3D>();
            foreach (PointF3D point in points)
            {
                double projectedY = slope * point.X + interceptY;
                double projectedZ = slope * point.X + interceptZ;
                double modelTemplateedY = slope * point.X + interceptY;
                double modelTemplateedZ = slope * point.X + interceptZ;
                projectedPoints.Add(new PointF3D(point.X, (float)projectedY, (float)projectedZ));
                modelTemplateedPoints.Add(new PointF3D(point.X, (float)modelTemplateedY, (float)modelTemplateedZ));
            }
            return projectedPoints;
            return modelTemplateedPoints;
        }
        public void 添加底图ToolStripMenuItem_Click(object sender, EventArgs e)
@@ -3921,8 +3532,8 @@
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                var backgroundImagePath = openFileDialog.FileName;
                Global.ClearFileReadOnly(_Template.BackGroundImg_FullPath);
                FileCopy.Copy(backgroundImagePath, _Template.BackGroundImg_FullPath, true);
                HelperC.ClearFileReadOnly(_Template.BackGroundImg_FullPath);
                HelperC.Copy(backgroundImagePath, _Template.BackGroundImg_FullPath, true);
                设置底图ToolStripMenuItem_Click(1, new EventArgs());
            }
        }
@@ -3969,12 +3580,12 @@
        private void label_zoom_Click(object sender, EventArgs e)
        {
            string inputValue = Prompt.ShowDialog("请输入比例", "");
            if (float.TryParse(inputValue, out float result))
            {
                zoom = result;
            }
            TipFormHelper.ShowError("请输入比例,待补充");
            //string inputValue = Prompt.ShowDialog("请输入比例", "");
            //if (float.TryParse(inputValue, out float result))
            //{
            //    zoom = result;
            //}
        }
        private void label_file_DoubleClick(object sender, EventArgs e)
        {
@@ -4073,8 +3684,6 @@
            _Template.mapOption._ShowFlowDirection = true;
            this.SetMapInvalidate();
        }
        private void 隐藏流向ToolStripMenuItem_Click(object sender, EventArgs e)
        {
@@ -4143,29 +3752,14 @@
            _Network.MapObjects.ForEach(o => o.Visible = true);
            this.SetMapInvalidate();
        }
        public void toolStripButton_CalcMiniLoss_Click(object sender, EventArgs e)
        {
            //_Network.SetNodeDemand();
            //_Network.Calc(_Template.FullPath, GlobalPath.configPath + "config_calc.wdb");
            //_Network.CalcLinkMinorLoss();
        }
        private void toolStripButton_ClearMinor_Click(object sender, EventArgs e)
        {
            _Network.ClearMinorLoss();
        }
        private void 显示流向ToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            _isShowDirection = !_isShowDirection;
        }
        bool _isShowDirection = false;
        private void 颜色分级管理ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //Form_Colour form_Colour = new Form_Colour(_Template.Colours, _Template);
            //form_Colour.Show(this);
        }
        private bool __needpaintall = false;
        private void cb_Link_Colour_SelectedIndexChanged(object sender, EventArgs e)
@@ -4203,24 +3797,1416 @@
            _isOrtho = !_isOrtho;
        }
        private void 楼层管理ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //if (_Template.Regions == null)
            //{
            //    _Template.Regions = new List<TRegion>();
            //}
            //Form_EditFloors form_EditFloors = new Form_EditFloors(_Template.Regions);
            //form_EditFloors.Show(this);
        }
        #endregion
        private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        #endregion
        #region Model
        #region  核心属性
        TContainer TC = new TContainer();
        /// <summary>
        /// 地图选项
        /// </summary>
        public MapDimensions mapOption
        {
            get { return TC.mapOption; }
            set { TC.mapOption = value; }
        }
        /// <summary>
        /// 地图选项_起始操作时
        /// </summary>
        private MapDimensions mapOption0 = new MapDimensions();
        /// <summary>
        /// 临时管网层
        /// </summary>
        [Browsable(false)]
        public Template _newTemplate
        {
            get { return TC.newTemplate; }
            set { TC.newTemplate = value; }
        }
        [Browsable(false)]
        public Template _Template
        {
            get { return TC.template; }
            set
            {
                TC.template = value;
                label_file.Text = TC.template?.filePath;
            }
        }
        #endregion
        #region 交互属性
        /// <summary>
        /// 悬停对象
        /// </summary>
        private List<IBaseViewModel> hoveredObjs = new List<IBaseViewModel>();
        /// <summary>
        /// 选中对象
        /// </summary>
        public List<IBaseViewModel> selectedObjs = new List<IBaseViewModel>();
        private List<NodeViewModel> selectedNodes => selectedObjs.FindAll(o => o is NodeViewModel).Select(o => (NodeViewModel)o).ToList();
        private List<LinkViewModel> selectedLinks => selectedObjs.FindAll(o => o is LinkViewModel).Select(o => (LinkViewModel)o).ToList();
        MouseState _mouseState = MouseState.无;
        private NodeViewModel _OperaNode = null;
        public PointF mouseXY = new PointF(0, 0);
        PointF DragStartPos;
        PointF _ClickStartPos;
        PointF RotaStartPos;
        PointF BackGroudPicLeftPos;
        bool _isPanning;
        /// <summary>
        /// 拖拽选择
        /// </summary>
        bool _isDragging;
        bool _isRotating;
        bool _isPainting;
        PointF mousePosition;
        // control+鼠标中间按下缩放
        bool _isInsertingObject = false;
        bool _isMovingObject = false;
        bool _isPastingObject = false;
        Cursor _lastCursor;
        object _undoOldValue = null;
        private List<PointF> polygonPoints = new List<PointF>();
        private bool _isDrawingPolygon;
        #endregion
        #region 新增管网(辅助)
        MapViewNetWork _NewNet
        {
            get
            {
                if (_newTemplate == null) _newTemplate = new Template();
                if (_newTemplate.network == null) _newTemplate.network = new MapViewNetWork();
                return _newTemplate.network;
            }
        }
        #endregion
        #region 显示选项(辅助)
        private string _StartPoint = null;
        private string _EndPoint = null;
        //private bool __isEditMode = true;
        [Browsable(false)]
        public bool _IsEditMode
        {
            get { return this.mapOption?.isEditMode ?? true; }
            set
            {
                toolStripComboBox_浏览模式.Text = value ? "编辑模式" : "浏览模式";
                if (this.mapOption != null) this.mapOption.isEditMode = value;
                转换为ToolStripMenuItem.Visible = _IsEditMode;
                toolStripSeparator9.Visible = _IsEditMode;
                删除ToolStripMenuItem.Visible = _IsEditMode;
                删除ToolStripMenuItem1.Visible = _IsEditMode;
                复制ToolStripMenuItem.Visible = _IsEditMode;
                复制ToolStripMenuItem1.Visible = _IsEditMode;
                粘贴ToolStripMenuItem.Visible = _IsEditMode;
                粘贴ToolStripMenuItem1.Visible = _IsEditMode;
                设置长度ToolStripMenuItem.Visible = _IsEditMode;
                设为关闭ToolStripMenuItem.Visible = _IsEditMode;
                设为立管点ToolStripMenuItem.Visible = _IsEditMode;
                对齐ToolStripMenuItem.Visible = _IsEditMode;
                对齐ToolStripMenuItem1.Visible = _IsEditMode;
                toolStripButton_新建节点.Enabled = _IsEditMode;
                toolStripButton_新建管线.Enabled = _IsEditMode;
                toolStripButton_新建立管.Enabled = _IsEditMode;
                toolStripButton_添加水库.Enabled = _IsEditMode;
                toolStripButton_添加水池.Enabled = _IsEditMode;
                toolStripButton_添加水表.Enabled = _IsEditMode;
                toolStripButton_添加阀门.Enabled = _IsEditMode;
                toolStripButton_重复器.Enabled = _IsEditMode;
            }
        }
        [Browsable(false)]
        public float Link_multiply
        {
            get
            {
                if (_Template == null || _Template.mapOption == null) return 1.0f;
                return _Template.mapOption.Link_multiply;
            }
            set
            {
                if (_Template == null || _Template.mapOption == null) return; _Template.mapOption.Link_multiply = value;
            }
        }
        [Browsable(false)]
        public float junction_multiply
        {
            get
            {
                if (_Template == null || _Template.mapOption == null) return 1.0f;
                return _Template.mapOption.junction_multiply;
            }
            set
            {
                if (_Template == null || _Template.mapOption == null) return; _Template.mapOption.junction_multiply = value;
            }
        }
        [DisplayName("显示阀门")]
        public bool _ShowValve
        {
            get
            {
                if (_Template == null || _Template.mapOption == null) return true;
                return _Template.mapOption._ShowValve;
            }
            set
            {
                if (_Template == null || _Template.mapOption == null) return; _Template.mapOption._ShowValve = value;
            }
        }
        [DisplayName("显示节点")]
        public bool _ShowJunction
        {
            get
            {
                if (_Template == null || _Template.mapOption == null) return true;
                return _Template.mapOption._ShowJunction;
            }
            set
            {
                if (_Template == null || _Template.mapOption == null) return; _Template.mapOption._ShowJunction = value;
            }
        }
        string _filePath
        {
            get
            {
                if (string.IsNullOrEmpty(_Template?.filePath))
                    return null;
                string path = _Template.filePath.TrimStart('\\');
                return
                    Path.Combine(Directory.GetCurrentDirectory(), path);
            }
            //set
            //{
            //    _Template.路径 = value;
            //}
        }
        private MapViewNetWork _Network
        {
            get
            {
                return _Template?.network;
            }
        }
        #endregion
        #region 管网属性(辅助)
        [Browsable(false)]
        public List<NodeCalcModel> _Nodes
        {
            get { return _Network?.Nodes ?? new List<NodeCalcModel>(); }
        }
        [Browsable(false)]
        public List<LinkCalcModel> _Links
        {
            get { return _Network?.Links ?? new List<LinkCalcModel>(); }
        }
        #endregion
        #region 视角设置(辅助)
        private const float MinZoom = 0.1f;
        private const float MaxZoom = 1000.0f;
        [DisplayName("缩放系数")]
        public float zoom
        {
            get
            {
                return mapOption.zoom;
            }
            set
            {
                label_zoom.Text = $"Zoom:{zoom.ToString("0.000")}";
                mapOption.zoom = value;
            }
        }
        [DisplayName("旋转角度")]
        public double Rotation
        {
            get
            {
                return mapOption.rotation;
            }
            set
            {
                toolStripStatusLabel_rotation.Text = $"Rotation:({Rotation.ToString("0")},{RotationF.ToString("0")})";
                //将旋转角度转换为-180~180
                value = value % 360;
                if (value > 180)
                    value -= 360;
                else if (value < -180)
                    value += 360;
                mapOption.rotation = value;
            }
        }
        private double Rotation0 = 0;
        [Browsable(false)]
        public PointF MapCenter
        {
            get
            {
                return mapOption.Center;
            }
            set
            {
                label_center.Text = $"center:({MapCenter.X.ToString("0.00")} ,{MapCenter.Y.ToString("0.00")})";
                mapOption.Center = value;
            }
        }
        private PointF MapCenter0;
        private bool is3Dview = false;
        double 俯视角度_start = 90;
        public bool Lock2DView
        {
            get
            {
                return mapOption.Lock2DView;
            }
            set
            {
                mapOption.Lock2DView = value;
            }
        }
        /// <summary>
        /// 俯视线与底面的夹角,投影用sin
        /// </summary>
        [DisplayName("俯视角度")]
        public double RotationF
        {
            get
            {
                return mapOption.rotationF;
            }
            set
            {
                mapOption.rotationF = value;
            }
        }
        [Browsable(false)]
        public double 俯视弧度
        {
            get
            {
                return RotationF / 180 * Math.PI;
            }
        }
        [Browsable(false)]
        public PointF Zoom
        {
            get
            {
                return new PointF(zoom, -zoom);
            }
        }
        private PointF Z(float z)
        {
            return new PointF(0, 0);
        }
        #endregion
        #region 颜色分级(辅助)
        public Colour NodeColour
        {
            set
            {
                var type = value.Type;
                value.isChoosed = true;
                _Template?.Colours?.RemoveAll(cl => cl.Type == type);
                _Template.Colours.Add(value);
                mapOption.ColourNode = type;
            }
            private get
            {
                return _Template?.Colours?.FirstOrDefault(cl => cl.isChoosed && cl.Type == mapOption.ColourNode);
            }
        }
        public Colour LinkColour
        {
            set
            {
                var type = value.Type;
                value.isChoosed = true;
                _Template?.Colours?.RemoveAll(cl => cl.Type == type);
                _Template.Colours.Add(value);
                mapOption.ColourLink = type;
            }
            private get
            {
                return _Template?.Colours?.FirstOrDefault(cl => cl.isChoosed && cl.Type == mapOption.ColourLink);
            }
        }
        //private bool __isOrtho = true;
        #endregion
        #region 正交模式
        private bool _isOrtho
        {
            get
            {
                return mapOption.IsOrtho;
            }
            set
            {
                mapOption.IsOrtho = value;
                if (mapOption.IsOrtho)
                {
                    label_ZZ.Text = "正交模式:开";
                }
                else
                {
                    label_ZZ.Text = "正交模式:关";
                }
            }
        }
        #endregion
        #endregion
        #region Draw
        #region 事件
        //按帧数判断是否重绘,减少计算量(每帧最多重绘一次)
        bool _timerDraw = false;
        //按帧数判断鼠标悬停对象,减少计算量(每帧最多判断一次)
        bool _mouseHoverCheckFlag = false;
        #endregion
        void DrawBackGroud(Graphics bufferG, Template template)
        {
            if (template == null) return;
            var _Nodes = template.network.Nodes.ViewNodes;
            var _Links = template.network.Links.ViewLinks;
            var Cpoints = getCurclePoints(64).ToList();
            var r = 1.73f / zoom;
            var rt = r;
            r = r * Link_multiply;
            List<PointF> diametersZoom = new List<PointF>() { 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))
            { 
                List<PointF> p = new List<PointF>();
                if (!this.mapOption.isAutoBackgroundImage)
                {
                    var Cps = new List<PointF>
                    {
                        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;
                    var Cps = new List<PointF>
                {
                    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
                {
                }
            }
        }
        void Draw(Graphics bufferG, Template template)
        {
            if (template == null) return;
            var _Nodes = template.network.Nodes.ViewNodes;
            var _Links = template.network.Links.ViewLinks;
            var _Areas = template.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<PointF> diametersZoom = new List<PointF>() { 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.Elev < minElve || area.Elev >= maxElve) continue;
                    var p = new List<PointF>();
                    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<long> dict_flow_direction = new HashSet<long>();
            using (Pen pen0 = new Pen(Color.FromArgb(0, 0, 255), 2 * r))
            {
                foreach (var link in _Links)
                {
                    if (!link.Visible) continue;
                    if (link.Elev < minElve || link.Elev >= 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 == Hydro.Core.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 ValveViewModel)
                    {
                        if (link.Selected || _ShowValve)
                        {
                            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 || _ShowValve)
                        {
                            var c = new PointF((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
                            bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 });
                            // 绘制圆形部分(水泵的泵体)
                            float radius = 5 * r;
                            float diameter = radius * 2;
                            #region 圆形拆分
                            List<PointF> p = new List<PointF>();
                            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 is RepeaterViewModel re)
                    {
                        if (re.Status == RepeatStatus.收起 || _IsEditMode)
                        {
                            bufferG.DrawLines(link.Selected ? penChoosed : pen, new PointF[] { p1, p2 });
                            var listNode = GraphHelper.Get等分Nodes(p1, p2, Math.Max(re.RepeatTimes, 1));
                            for (int i = 0; i < listNode.Count; i++)
                            {
                                var c = listNode[i];
                                RectangleF[] rects = new RectangleF[] {
                                    new RectangleF(c.X-10*r,c.Y-8*r,20*r,16*r),
                                };
                                bufferG.FillRectangles(new SolidBrush(Color.White), rects);
                                // 保存当前绘图状态
                                var gs = bufferG.Save();
                                // 应用矩阵变换以抵消之前的翻转效果
                                bufferG.ScaleTransform(1 / Zoom.X, 1 / Zoom.Y);
                                int index = re.GetIndex(i);
                                string indexString = index == 0 ? "" : index.ToString();
                                Font font = new Font(FontFamily.GenericSansSerif, 10);
                                string text = $"{indexString}{re.NetworkShowName}";
                                SizeF textSize = bufferG.MeasureString(text, 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 p = new PointF(textLeft, textTop);
                                bufferG.DrawString(text, font, link.Selected ? penChoosed.Brush : pen.Brush, p);
                                // 恢复之前保存的绘图状态
                                bufferG.Restore(gs);
                                if (textSize.Width / Zoom.X > rects[0].Width)
                                {
                                    rects[0] = new RectangleF(c.X - textSize.Width / 2 / Zoom.X - 1 * r, c.Y - 8 * r, textSize.Width / Zoom.X + 2 * r, 16 * r);
                                }
                                try
                                {
                                    bufferG.DrawRectangles(penN, rects);
                                }
                                catch { }
                            }
                        }
                    }
                    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<PointF> p = new List<PointF>();
                                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<long> dict_point = new HashSet<long>();
            //绘制点
            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.Elev < minElve || node.Elev >= 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);
                    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)
                    {
                        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);
                    }
                    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);
                        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.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 || _ShowJunction)
                            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))
                {
                    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);
                    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, Template template)
        {
            var r = 2f / zoom;
            if (_isDragging && DragStartPos != new PointF(0, 0) && mousePosition != new PointF(0, 0))
            {
                label_center.Text = $"S:{DragStartPos.X}:{DragStartPos.Y} E:{mousePosition.X}:{mousePosition.Y}";
                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 == 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));
                            var mapPos = WorldPointToMapPoint(wPos);
                            if (double.IsNaN(mapPos.X) || double.IsNaN(mapPos.Y))
                            {
                                return;
                            }
                            bufferG.DrawLine(pen, WorldPointToMapPoint(_select_junction1), mapPos);
                        }
                        else
                        {
                            bufferG.DrawLine(pen, WorldPointToMapPoint(_select_junction1), _MousePosition);
                        }
                    }
                }
            }
            if (_isDrawingPolygon && polygonPoints.Count > 0)
            {
                List<PointF> 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<PointF> pf = new List<PointF> { oldP, newP };
                using (var pen = new Pen(Color.Black, 1 * r))
                {
                    // 绘制多边形虚线边框
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    bufferG.DrawLines(pen, pf.ToArray());
                }
            }
        }
        #region 基础坐标转换方法
        /// <summary>
        /// 将屏幕坐标转换为世界坐标。输入屏幕坐标 (x,y),返回世界坐标 (wx, wy)。
        /// </summary>
        /// <param name="screenPos"></param>
        /// <returns></returns>
        private PointF ScreenToVMap(PointF screenPos, float z = 0)
        {
            var centerX = this.map.Width / 2;
            var centerY = this.map.Height / 2;
            var worldX = (screenPos.X - centerX - Z(z).X) / Zoom.X + MapCenter.X;
            var worldY = (screenPos.Y - centerY) / Zoom.Y + 0;
            return new PointF(worldX, worldY);
        }
        /// <summary>
        /// 将屏幕坐标转换为世界坐标。输入屏幕坐标 (x,y),返回世界坐标 (wx, wy)。
        /// </summary>
        /// <param name="screenPos"></param>
        /// <returns></returns>
        private PointF ScreenToMap(PointF screenPos, float z = 0)
        {
            var centerX = this.map.Width / 2;
            var centerY = this.map.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);
        }
        /// <summary>
        /// 世界投影坐标转换为屏幕坐标
        /// </summary>
        /// <param name="mapPos"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        private PointF MapToScreen(PointF mapPos, float z = 0)
        {
            var centerX = this.map.Width / 2;
            var centerY = this.map.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);
        }
        // 根据旋转角度计算旋转后的坐标
        // 根据旋转角度计算旋转后的坐标
        private PointF Get平面旋转Point(PointF p, PointF MapC)
        {
            PointF 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);
            return new PointF(x, y);
        }
        private PointF Get平面还原Point(PointF p, PointF MapC)
        {
            PointF 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);
            return new PointF(x, y);
        }
        private PointF Get俯视角旋转Point(PointF p, float z, PointF MapC)
        {
            PointF 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 * z;
            return new PointF(x, y);
        }
        private PointF Get俯视角还原Point(PointF p, float z, PointF MapC)
        {
            PointF 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 = (p.Y - center.Y - cos * z) / sin + 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);
        }
        /// <summary>
        /// 获取世界投影坐标
        /// </summary>
        /// <param name="point"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        private PointF WorldPointToMapPoint(PointF point, float z, PointF3D offset = null)
        {
            if (offset == null) offset = new PointF3D(0, 0, 0);
            point = new PointF(point.X + offset.X, point.Y + offset.Y);
            var pointR = Get平面旋转Point(point, MapCenter);
            var pointT = Get俯视角旋转Point(pointR, z + offset.Z, MapCenter);
            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)
        {
            PointF p;
            if (junction == null) return new PointF(0, 0);
            p = WorldPointToMapPoint(junction.Position, junction.Elev, offset);
            return p;
        }
        private PointF CubeWorldPointToMapPoint(NodeViewModel junction, PointF3D offset = null)
        {
            PointF p;
            if (junction == null) return new PointF(0, 0);
            var point = junction.Position;
            var z = junction.Elev;
            if (offset == null) offset = new PointF3D(0, 0, 0);
            point = new PointF(point.X + offset.X, point.Y + offset.Y);
            var pointR = Get平面旋转Point(point, new PointF(0, 0));
            var pointT = Get俯视角旋转Point(pointR, z + offset.Z, new PointF(0, 0));
            return pointT;
        }
        /// <summary>
        /// 获取正交投影坐标,返回的是世界坐标
        /// </summary>
        /// <param name="position3D">世界坐标</param>
        /// <param name="mousePosition">地图坐标</param>
        /// <param name="vector3">投影向量</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        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);
                    }
                }
            }
        }
        /// <summary>
        /// 获取正交投影坐标,返回的是世界坐标
        /// </summary>
        /// <param name="position3D">世界坐标</param>
        /// <param name="mousePosition">地图坐标</param>
        /// <param name="vector3">投影向量</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        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);
        }
        /// <summary>
        /// 获取地图投影坐标
        /// </summary>
        /// <param name="point"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public PointF MapPointToWorldPoint(PointF point, float z = 0)
        {
            var pointT = Get俯视角还原Point(point, z, MapCenter);
            pointT = Get平面还原Point(pointT, MapCenter);
             
            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;
             
        }
        #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;
        /// <summary>
        /// 判断是否在屏幕坐标内
        /// </summary>
        /// <param name="screenPos"></param>
        /// <returns></returns>
        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;
        }
 
        #endregion
        #endregion
    }
}