b21146005583515244d7d66340766a8d2e767481..86fdd2266700b4f80a2d8b05b6a79d31802a7231
2025-04-18 lixiaojun
添加编辑功能
86fdd2 对比 | 目录
2025-04-18 lixiaojun
改变光源,管理器修改
28ba54 对比 | 目录
已删除1个文件
已修改25个文件
已添加10个文件
2206 ■■■■ 文件已修改
Yw.WpfUI.Hydro.L3d.Core/00-core/CameraL3dExtensions.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/00-core/CatalogL3d.cs 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/00-core/CatalogL3dExtensions.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/00-core/ConstParas.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/00-core/Vector3DExtensions.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d_Append.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d_Camera.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d_Method.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/01-network/04-link/00-core/LinkL3d.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/01-network/99-model/01-camera/CameraL3d.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/02-settings/01-paras/01-node/00-core/Paras_HydroL3d_Logical_Node.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/02-settings/01-paras/02-link/00-core/Paras_HydroL3d_Logical_Link.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/00-core/LogicalCreateHelper.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/00-core/LogicalLineTransformHelper.cs 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/00-core/LogicalVisual3D.cs 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/00-core/LogicalNode3D.cs 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/01-junction/00-core/LogicalJunction3D.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/02-source/00-core/LogicalSource3D.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/02-source/01-reservoir/LogicalReservoir3D.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/02-source/02-tank/LogicalTank3D.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/00-core/LogicalLink3D.cs 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/01-pipe/LogicalPipe3D.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/02-pump/LogicalPump3D.cs 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/03-valve/LogicalValve3D.cs 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/02-state/eLogicalState.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/05-camera/LogicalCamera3D.cs 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/05-camera/LogicalCameraHelper.cs 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/08-manager/LogicalEditManager.cs 746 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/08-manager/LogicalManager.cs 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/08-manager/eLogicalEditMode.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/03-logical/09-control/LogicalEditerL3d.xaml.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/Yw.WpfUI.Hydro.L3d.Core.csproj 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/paras_hydrol3d_settings.json 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Test.Core/MainWindow.xaml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Test.Core/MainWindow.xaml.cs 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Yw.WpfUI.Hydro.L3d.Core/00-core/CameraL3dExtensions.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// ç›¸æœºæ‹“展
    /// </summary>
    internal static class CameraL3dExtensions
    {
        /// <summary>
        ///
        /// </summary>
        public static LogicalCamera3D ToLogicalCamera3D(this CameraL3d camera)
        {
            if (camera == null)
            {
                return default;
            }
            return new LogicalCamera3D()
            {
                NearPlaneDistance = camera.NearPlaneDistance,
                FarPlaneDistance = camera.FarPlaneDistance,
                Position = camera.Position.ToPoint3D(),
                UpDirection = camera.UpDirection.ToVector3D(),
                LookDirection = camera.LookDirection.ToVector3D(),
                FieldOfView = camera.FieldOfView,
                Width = camera.Width
            };
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/00-core/CatalogL3d.cs
@@ -8,157 +8,162 @@
        /// <summary>
        /// ç»„ä»¶
        /// </summary>
        public const string Parter = "Parter";
        public const string Parter = "parter";
        /// <summary>
        /// å¯è§ç»„ä»¶
        /// </summary>
        public const string Visual = "Visual";
        public const string Visual = "visual";
        /// <summary>
        /// èŠ‚ç‚¹
        /// </summary>
        public const string Node = "Node";
        public const string Node = "node";
        /// <summary>
        /// æ°´æº
        /// </summary>
        public const string Source = "source";
        /// <summary>
        /// æ°´åº“
        /// </summary>
        public const string Reservoir = "Reservoir";
        public const string Reservoir = "reservoir";
        /// <summary>
        /// æ°´æ± 
        /// </summary>
        public const string Tank = "Tank";
        public const string Tank = "tank";
        /// <summary>
        /// æ°´ç®±
        /// </summary>
        public const string Waterbox = "Waterbox";
        public const string Waterbox = "waterbox";
        /// <summary>
        /// è¿žæŽ¥èŠ‚ç‚¹
        /// </summary>
        public const string Junction = "Junction";
        public const string Junction = "junction";
        /// <summary>
        /// è¿žæŽ¥ä»¶
        /// </summary>
        public const string Coupling = "Coupling";
        public const string Coupling = "coupling";
        /// <summary>
        /// é—·å¤´
        /// </summary>
        public const string Blunthead = "Blunthead";
        public const string Blunthead = "blunthead";
        /// <summary>
        /// å¼¯å¤´
        /// </summary>
        public const string Elbow = "Elbow";
        public const string Elbow = "elbow";
        /// <summary>
        /// ä¸‰é€š
        /// </summary>
        public const string Threelink = "Threelink";
        public const string Threelink = "threelink";
        /// <summary>
        /// å››é€š
        /// </summary>
        public const string Fourlink = "Fourlink";
        public const string Fourlink = "fourlink";
        /// <summary>
        /// æ‰©æ•£å™¨
        /// </summary>
        public const string Emitter = "Emitter";
        public const string Emitter = "emitter";
        /// <summary>
        /// å–·å¤´
        /// </summary>
        public const string Nozzle = "Nozzle";
        public const string Nozzle = "zozzle";
        /// <summary>
        /// æ¶ˆç«æ “
        /// </summary>
        public const string Hydrant = "Hydrant";
        public const string Hydrant = "hydrant";
        /// <summary>
        /// æ°´è¡¨
        /// </summary>
        public const string Meter = "Meter";
        public const string Meter = "meter";
        /// <summary>
        /// ä»ªè¡¨
        /// </summary>
        public const string Instrument = "Instrument";
        public const string Instrument = "instrument";
        /// <summary>
        /// æµé‡è®¡
        /// </summary>
        public const string Flowmeter = "Flowmeter";
        public const string Flowmeter = "flowmeter";
        /// <summary>
        /// åŽ‹åŠ›è¡¨
        /// </summary>
        public const string Pressmeter = "Pressmeter";
        public const string Pressmeter = "pressmeter";
        /// <summary>
        /// ç®¡æ®µ
        /// </summary>
        public const string Link = "Link";
        public const string Link = "link";
        /// <summary>
        /// ç®¡é“
        /// </summary>
        public const string Pipe = "Pipe";
        public const string Pipe = "pipe";
        /// <summary>
        /// è¿‡æ¸¡ä»¶(变径管)
        /// </summary>
        public const string Translation = "Translation";
        public const string Translation = "translation";
        /// <summary>
        /// æ°´æ³µ
        /// </summary>
        public const string Pump = "Pump";
        public const string Pump = "pump";
        /// <summary>
        /// é˜€é—¨
        /// </summary>
        public const string Valve = "Valve";
        public const string Valve = "valve";
        /// <summary>
        /// æ°´åŠ›é˜»ä»¶
        /// </summary>
        public const string Resistance = "Resistance";
        public const string Resistance = "resistance";
        /// <summary>
        /// æ¢çƒ­å™¨
        /// </summary>
        public const string Exchanger = "Exchanger";
        public const string Exchanger = "exchanger";
        /// <summary>
        /// åŽ‹ç¼©æœº
        /// </summary>
        public const string Compressor = "Compressor";
        public const string Compressor = "compressor";
        /// <summary>
        /// æ“ä½œ
        /// </summary>
        public const string Operation = "Operation";
        public const string Operation = "operation";
        /// <summary>
        /// æ›²çº¿
        /// </summary>
        public const string Curve = "Curve";
        public const string Curve = "curve";
        /// <summary>
        /// æ¨¡å¼
        /// </summary>
        public const string Pattern = "Pattern";
        public const string Pattern = "pattern";
        /// <summary>
        /// è§„则
        /// </summary>
        public const string Rule = "Rule";
        public const string Rule = "rule";
    }
}
Yw.WpfUI.Hydro.L3d.Core/00-core/CatalogL3dExtensions.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// åˆ†ç±»æ‰©å±•
    /// </summary>
    internal static class CatalogL3dExtensions
    {
        /// <summary>
        /// èŽ·å–åˆ†ç±»åç§°
        /// </summary>
        public static string GetCatalogName(this string code)
        {
            var caser = code?.Trim()?.ToLower();
            if (string.IsNullOrEmpty(caser))
            {
                return string.Empty;
            }
            var name = string.Empty;
            switch (caser)
            {
                case CatalogL3d.Parter: name = "组件"; break;
                case CatalogL3d.Visual: name = "可视组件"; break;
                case CatalogL3d.Node: name = "节点"; break;
                case CatalogL3d.Junction: name = "连接节点"; break;
                case CatalogL3d.Coupling: name = "连接件"; break;
                case CatalogL3d.Blunthead: name = "闷头"; break;
                case CatalogL3d.Elbow: name = "弯头"; break;
                case CatalogL3d.Threelink: name = "三通"; break;
                case CatalogL3d.Fourlink: name = "四通"; break;
                case CatalogL3d.Meter: name = "水表"; break;
                case CatalogL3d.Instrument: name = "仪表"; break;
                case CatalogL3d.Flowmeter: name = "流量计"; break;
                case CatalogL3d.Pressmeter: name = "压力表"; break;
                case CatalogL3d.Emitter: name = "扩散器"; break;
                case CatalogL3d.Nozzle: name = "喷头"; break;
                case CatalogL3d.Hydrant: name = "消火栓"; break;
                case CatalogL3d.Source: name = "水源"; break;
                case CatalogL3d.Reservoir: name = "水库"; break;
                case CatalogL3d.Tank: name = "æ°´æ± "; break;
                case CatalogL3d.Waterbox: name = "æ°´ç®±"; break;
                case CatalogL3d.Link: name = "管段"; break;
                case CatalogL3d.Pipe: name = "管道"; break;
                case CatalogL3d.Translation: name = "过渡件"; break;
                case CatalogL3d.Pump: name = "æ°´æ³µ"; break;
                case CatalogL3d.Valve: name = "阀门"; break;
                case CatalogL3d.Resistance: name = "阻件"; break;
                case CatalogL3d.Exchanger: name = "换热器"; break;
                case CatalogL3d.Compressor: name = "压缩机"; break;
                case CatalogL3d.Curve: name = "曲线"; break;
                case CatalogL3d.Pattern: name = "模式"; break;
                case CatalogL3d.Rule: name = "规则"; break;
                default: break;
            }
            return name;
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/00-core/ConstParas.cs
ÎļþÒÑɾ³ý
Yw.WpfUI.Hydro.L3d.Core/00-core/Vector3DExtensions.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    ///
    /// </summary>
    internal static class Vector3DExtensions
    {
        /// <summary>
        ///
        /// </summary>
        public static PointL3d ToPointL3d(this Vector3D pt)
        {
            return new PointL3d(pt.X, pt.Y, pt.Z);
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d.cs
@@ -22,6 +22,7 @@
        }
        private List<ParterL3d> _parters = new();
        /// <summary>
        /// å¯è§åˆ—表
        /// </summary>
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d_Append.cs
@@ -5,6 +5,7 @@
    /// </summary>
    public partial class NetworkL3d
    {
        /// <summary>
        /// æ·»åŠ ç»„ä»¶
        /// </summary>
@@ -196,5 +197,7 @@
    }
}
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d_Camera.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    ///
    /// </summary>
    public partial class NetworkL3d
    {
        /// <summary>
        /// é»˜è®¤ç›¸æœº
        /// </summary>
        public CameraL3d DefaultCamera { get; set; }
    }
}
Yw.WpfUI.Hydro.L3d.Core/01-network/00-core/NetworkL3d_Method.cs
@@ -9,16 +9,51 @@
        /// <summary>
        /// åˆ¤æ–­æ˜¯å¦å­˜åœ¨
        /// </summary>
        public bool IsExist(string id)
        internal bool IsExist(string id)
        {
            return _parters.Exists(x => x.Id == id);
        }
        /// <summary>
        /// åˆ¤æ–­æ˜¯å¦å­˜åœ¨
        /// </summary>
        internal bool IsExist(ParterL3d parter)
        {
            if (parter == null)
            {
                return false;
            }
            return _parters.Contains(parter);
        }
        /// <summary>
        /// ç§»é™¤
        /// </summary>
        internal bool Remove(ParterL3d parter)
        {
            return _parters.Remove(parter);
        }
        /// <summary>
        /// åˆ›å»ºId
        /// </summary>
        internal string CreateId(string catalog)
        {
            var ids = this.Parters.Select(x => x.Id).Distinct().ToList();
            var id = Yw.Untity.UniqueHelper.CreateFromFirst(catalog, ids);
            return id;
        }
        /// <summary>
        /// åˆ›å»ºåç§°
        /// </summary>
        internal string CreateName(string catalog)
        {
            var names = this.Parters.Select(x => x.Name).Distinct().ToList();
            var catalogName = catalog.GetCatalogName();
            var name = Yw.Untity.UniqueHelper.CreateFromFirst(catalogName, names);
            return name;
        }
Yw.WpfUI.Hydro.L3d.Core/01-network/04-link/00-core/LinkL3d.cs
@@ -18,8 +18,8 @@
        /// </summary>
        public LinkL3d(LinkL3d rhs) : base(rhs)
        {
            this.StartPosition = new PointL3d(rhs.StartPosition);
            this.EndPosition = new PointL3d(rhs.EndPosition);
            this.StartNode = rhs.StartNode;
            this.EndNode = rhs.EndNode;
        }
        /// <summary>
@@ -32,23 +32,27 @@
        /// </summary>
        public NodeL3d EndNode { get; set; }
        /// <summary>
        /// ä¸Šæ¸¸ä½ç½®
        /// </summary>
        public PointL3d StartPosition { get; set; }
        /// <summary>
        /// ä¸‹æ¸¸ä½ç½®
        /// </summary>
        public PointL3d EndPosition { get; set; }
        /// <summary>
        /// èŽ·å–ä½ç½®
        /// </summary>
        public override List<PointL3d> GetPositions()
        {
            return new List<PointL3d>() { this.StartPosition, this.EndPosition };
            var list = new List<PointL3d>();
            if (this.StartNode != null)
            {
                if (this.StartNode.Position != null)
                {
                    list.Add(this.StartNode.Position);
                }
            }
            if (this.EndNode != null)
            {
                if (this.EndNode.Position != null)
                {
                    list.Add(this.EndNode.Position);
                }
            }
            return list;
        }
Yw.WpfUI.Hydro.L3d.Core/01-network/99-model/01-camera/CameraL3d.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// ç›¸æœº
    /// </summary>
    public class CameraL3d
    {
        /// <summary>
        /// ç›¸æœºä½ç½®
        /// </summary>
        public PointL3d Position { get; set; }
        /// <summary>
        /// ç›¸æœºæ–¹å‘
        /// </summary>
        public PointL3d UpDirection { get; set; }
        /// <summary>
        /// è§‚察方向
        /// </summary>
        public PointL3d LookDirection { get; set; }
        /// <summary>
        /// è§†é‡Ž
        /// </summary>
        public double FieldOfView { get; set; }
        /// <summary>
        /// è¿‘平面距离
        /// </summary>
        public double NearPlaneDistance { get; set; }
        /// <summary>
        /// è¿œå¹³é¢è·ç¦»
        /// </summary>
        public double FarPlaneDistance { get; set; }
        /// <summary>
        /// æ­£å°„相机的宽度
        /// </summary>
        public double Width { get; set; }
    }
}
Yw.WpfUI.Hydro.L3d.Core/02-settings/01-paras/01-node/00-core/Paras_HydroL3d_Logical_Node.cs
@@ -21,6 +21,16 @@
        public Paras_HydroL3d_Point Selection { get; set; }
        /// <summary>
        /// å¢žåŠ 
        /// </summary>
        public Paras_HydroL3d_Point Adding { get; set; }
        /// <summary>
        /// ç¼–辑
        /// </summary>
        public Paras_HydroL3d_Point Editing { get; set; }
        /// <summary>
        /// å¸é™„距离
        /// </summary>
        public double SnapDistance { get; set; }
Yw.WpfUI.Hydro.L3d.Core/02-settings/01-paras/02-link/00-core/Paras_HydroL3d_Logical_Link.cs
@@ -20,7 +20,15 @@
        /// </summary>
        public Paras_HydroL3d_Line Selection { get; set; }
        /// <summary>
        /// å¢žåŠ 
        /// </summary>
        public Paras_HydroL3d_Line Adding { get; set; }
        /// <summary>
        /// ç¼–辑
        /// </summary>
        public Paras_HydroL3d_Line Editing { get; set; }
Yw.WpfUI.Hydro.L3d.Core/03-logical/00-core/LogicalCreateHelper.cs
@@ -8,7 +8,7 @@
        /// <summary>
        /// åˆ›å»º
        /// </summary>
        /// <param name="visuall3d"></param>
        /// <param name="visual"></param>
        /// <param name="stateHelper"></param>
        /// <param name="materialHelper"></param>
        /// <param name="overrideColorHelper"></param>
@@ -17,7 +17,7 @@
        /// <returns></returns>
        public static LogicalVisual3D Create
            (
                VisualL3d visuall3d,
                VisualL3d visual,
                LogicalStateHelper stateHelper,
                LogicalMaterialHelper materialHelper,
                LogicalOverrideColorHelper overrideColorHelper,
@@ -25,27 +25,31 @@
                LogicalOverrideVisibleHelper overrideVisibleHelper
            )
        {
            if (visuall3d == null)
            if (visual == null)
            {
                return default;
            }
            if (visuall3d is JunctionL3d junctionl3d)
            if (visual is JunctionL3d junction)
            {
                return new LogicalJunction3D(junctionl3d, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
                return new LogicalJunction3D(junction, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
            }
            if (visuall3d is SourceL3d sourcel3d)
            if (visual is ReservoirL3d reservoir)
            {
                return new LogicalSource3D(sourcel3d, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
                return new LogicalReservoir3D(reservoir, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
            }
            if (visuall3d is PipeL3d pipel3d)
            if (visual is TankL3d tank)
            {
                return new LogicalTank3D(tank, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
            }
            if (visual is PipeL3d pipel3d)
            {
                return new LogicalPipe3D(pipel3d, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
            }
            if (visuall3d is PumpL3d pumpl3d)
            if (visual is PumpL3d pumpl3d)
            {
                return new LogicalPump3D(pumpl3d, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
            }
            if (visuall3d is ValveL3d valvel3d)
            if (visual is ValveL3d valvel3d)
            {
                return new LogicalValve3D(valvel3d, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper);
            }
Yw.WpfUI.Hydro.L3d.Core/03-logical/00-core/LogicalLineTransformHelper.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,121 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// æŠ½è±¡çº¿è½¬æ¢è¾…助类
    /// </summary>
    internal class LogicalLineTransformHelper
    {
        /// <summary>
        ///
        /// </summary>
        public LogicalLineTransformHelper(Point3D start, Point3D end)
        {
            this.StartPoint = start;
            this.EndPoint = end;
            this.Direction = end - start;
            this.Length = this.Direction.Length;
            this.Direction.Normalize();
            this.Perpendicular = Math.Abs(Direction.X) > 0.9
                ? Vector3D.CrossProduct(Direction, new Vector3D(0, 1, 0))
                : Vector3D.CrossProduct(Direction, new Vector3D(1, 0, 0));
            this.Perpendicular.Normalize();
        }
        /// <summary>
        /// å¼€å§‹ç‚¹
        /// </summary>
        public Point3D StartPoint { get; }
        /// <summary>
        /// ç»“束点
        /// </summary>
        public Point3D EndPoint { get; }
        /// <summary>
        /// é•¿åº¦
        /// </summary>
        public double Length { get; }
        /// <summary>
        /// æ–¹å‘
        /// </summary>
        public Vector3D Direction { get; }
        /// <summary>
        /// åž‚线
        /// </summary>
        public Vector3D Perpendicular { get; }
        /// <summary>
        /// å°†å±€éƒ¨åæ ‡è½¬æ¢ä¸ºä¸–界坐标
        /// </summary>
        public Point3D LocalToWorld(Point3D local)
        {
            // è®¡ç®—中点作为局部坐标系原点
            Point3D center = this.StartPoint + (this.EndPoint - this.StartPoint) * 0.5;
            // åˆ›å»ºå˜æ¢çŸ©é˜µ
            var transform = new Matrix3D();
            // æ—‹è½¬ä»ŽZ轴到实际方向
            var axis = Vector3D.CrossProduct(new Vector3D(0, 0, 1), this.Direction);
            double angle = Vector3D.AngleBetween(new Vector3D(0, 0, 1), this.Direction);
            transform.Rotate(new Quaternion(axis, angle));
            // å¹³ç§»
            transform.Translate(new Vector3D(center.X, center.Y, center.Z));
            return transform.Transform(local);
        }
        /// <summary>
        /// å°†ä¸–界坐标转换为局部坐标
        /// </summary>
        public Point3D WorldToLocal(Point3D world)
        {
            Point3D center = this.StartPoint + (this.EndPoint - this.StartPoint) * 0.5;
            var transform = new Matrix3D();
            transform.Translate(new Vector3D(-center.X, -center.Y, -center.Z));
            var axis = Vector3D.CrossProduct(new Vector3D(0, 0, 1), Direction);
            double angle = -Vector3D.AngleBetween(new Vector3D(0, 0, 1), Direction);
            transform.Rotate(new Quaternion(axis, angle));
            return transform.Transform(world);
        }
        /// <summary>
        /// è®¡ç®—矩阵
        /// </summary>
        public Matrix3D CalculateMatrix()
        {
            // è®¡ç®—中点
            var center = this.StartPoint + (this.EndPoint - this.StartPoint) * 0.5;
            // è®¡ç®—旋转轴和角度(从Z轴旋转到实际方向)
            var axis = Vector3D.CrossProduct(new Vector3D(0, 0, 1), this.Direction);
            double angle = Vector3D.AngleBetween(new Vector3D(0, 0, 1), this.Direction);
            // åˆ›å»ºå˜æ¢çŸ©é˜µ
            var transform = new Matrix3D();
            transform.Rotate(new Quaternion(axis, angle));
            transform.Translate(new Vector3D(center.X, center.Y, center.Z));
            return transform;
        }
        /// <summary>
        /// è®¡ç®—转换
        /// </summary>
        public Transform3D CalculateTransform()
        {
            var matrix = CalculateMatrix();
            return new MatrixTransform3D(matrix);
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/00-core/LogicalVisual3D.cs
@@ -38,7 +38,6 @@
        protected readonly LogicalOverrideOpacityHelper _overrideOpacityHelper = null;//覆盖透明度辅助类
        protected readonly LogicalOverrideVisibleHelper _overrideVisibleHelper = null;//覆盖可见性辅助类
        /// <summary>
        /// Vmo
        /// </summary>
@@ -51,22 +50,62 @@
        /// <summary>
        /// æ›´æ–°å¯è§å…ƒç´ 
        /// åŒ…含位置、材质、几何图形和可见性
        /// </summary>
        public virtual void UpdateVisual()
        {
            this.UpdatePositions();
            this.UpdateGeometry();
            this.UpdateMaterial();
            this.UpdateTransform();
            this.UpdateVisibility();
        }
        //更新材质
        protected abstract void UpdateMaterial();
        /// <summary>
        /// æ›´æ–°ä½ç½®
        /// </summary>
        public abstract void UpdatePositions();
        //更新几何图形
        protected abstract void UpdateGeometry();
        /// <summary>
        /// æ›´æ–°æè´¨
        /// </summary>
        public abstract void UpdateMaterial();
        //更新可见性
        protected abstract void UpdateVisibility();
        /// <summary>
        /// æ›´æ–°å‡ ä½•图形
        /// </summary>
        public abstract void UpdateGeometry();
        /// <summary>
        /// æ›´æ–°å˜æ¢çŸ©é˜µ
        /// </summary>
        public abstract void UpdateTransform();
        /// <summary>
        /// æ›´æ–°å¯è§æ€§
        /// </summary>
        public virtual void UpdateVisibility()
        {
            bool visible = true;
            var state = _stateHelper.GetState(this.Vmo);
            if (state.HasFlag(eLogicalState.Visible))
            {
                visible = _overrideVisibleHelper.GetVisible(this.Vmo);
            }
            this.Visible = visible;
        }
        /// <summary>
        /// éªŒè¯
        /// </summary>
        public virtual bool Verify()
        {
            if (this.Vmo == null)
            {
                return false;
            }
            return true;
        }
    }
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/00-core/LogicalNode3D.cs
@@ -23,7 +23,7 @@
                LogicalOverrideVisibleHelper overrideVisibleHelper
            ) : base(vmo, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper)
        {
            this.Position = vmo.Position.ToPoint3D();
        }
        /// <summary>
@@ -41,12 +41,33 @@
        public Point3D Position { get; set; }
        /// <summary>
        /// æ›´æ–°å¯è§†åŒ–元素
        /// æ›´æ–°ä½ç½®
        /// </summary>
        public override void UpdateVisual()
        public override void UpdatePositions()
        {
            this.Position = this.Vmo.Position.ToPoint3D();
            base.UpdateVisual();
            if (this.Vmo != null)
            {
                if (this.Vmo.Position != null)
                {
                    this.Position = this.Vmo.Position.ToPoint3D();
                }
            }
        }
        /// <summary>
        /// éªŒè¯
        /// </summary>
        public override bool Verify()
        {
            if (this.Vmo == null)
            {
                return false;
            }
            if (this.Vmo.Position == null)
            {
                return false;
            }
            return base.Verify();
        }
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/01-junction/00-core/LogicalJunction3D.cs
@@ -35,8 +35,10 @@
            set { _vmo = value; }
        }
        //更新材质
        protected override void UpdateMaterial()
        /// <summary>
        /// æ›´æ–°æè´¨
        /// </summary>
        public override void UpdateMaterial()
        {
            var htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Normal.HtmlColor;
            var opacity = 1.0d;
@@ -57,24 +59,22 @@
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Highlight.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Adding))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Adding.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Editing))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Editing.HtmlColor;
            }
            var material = _materialHelper.GetMaterial(htmlColor, opacity);
            this.Material = material;
        }
        //更新可见性
        protected override void UpdateVisibility()
        {
            bool visible = true;
            var state = _stateHelper.GetState(this.Vmo);
            if (state.HasFlag(eLogicalState.Visible))
            {
                visible = _overrideVisibleHelper.GetVisible(this.Vmo);
            }
            this.Visible = visible;
        }
        //更新几何图形
        protected override void UpdateGeometry()
        /// <summary>
        /// æ›´æ–°å‡ ä½•图形
        /// </summary>
        public override void UpdateGeometry()
        {
            var radius = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Normal.Radius;
            var state = _stateHelper.GetState(this.Vmo);
@@ -94,6 +94,14 @@
            this.MeshGeometry = geometry;
        }
        /// <summary>
        /// æ›´æ–°å˜æ¢çŸ©é˜µ
        /// </summary>
        public override void UpdateTransform()
        {
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/02-source/00-core/LogicalSource3D.cs
@@ -35,8 +35,10 @@
            set { _vmo = value; }
        }
        //更新材质
        protected override void UpdateMaterial()
        /// <summary>
        /// æ›´æ–°æè´¨
        /// </summary>
        public override void UpdateMaterial()
        {
            var htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Normal.HtmlColor;
            var opacity = 1.0d;
@@ -57,24 +59,22 @@
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Highlight.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Adding))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Adding.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Editing))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Editing.HtmlColor;
            }
            var material = _materialHelper.GetMaterial(htmlColor, opacity);
            this.Material = material;
        }
        //更新可见性
        protected override void UpdateVisibility()
        {
            bool visible = true;
            var state = _stateHelper.GetState(this.Vmo);
            if (state.HasFlag(eLogicalState.Visible))
            {
                visible = _overrideVisibleHelper.GetVisible(this.Vmo);
            }
            this.Visible = visible;
        }
        //更新几何图形
        protected override void UpdateGeometry()
        /// <summary>
        /// æ›´æ–°å‡ ä½•图形
        /// </summary>
        public override void UpdateGeometry()
        {
            var radius = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Normal.Radius;
            var state = _stateHelper.GetState(this.Vmo);
@@ -97,6 +97,13 @@
            this.MeshGeometry = geometry;
        }
        /// <summary>
        /// æ›´æ–°å˜æ¢çŸ©é˜µ
        /// </summary>
        public override void UpdateTransform()
        {
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/02-source/01-reservoir/LogicalReservoir3D.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
namespace Yw.WpfUI.Hydro
{
    internal class LogicalReservoir3D : LogicalSource3D
    {
        /// <summary>
        ///
        /// </summary>
        public LogicalReservoir3D() { }
        /// <summary>
        ///
        /// </summary>
        public LogicalReservoir3D
            (
                ReservoirL3d vmo,
                LogicalStateHelper stateHelper,
                LogicalMaterialHelper materialHelper,
                LogicalOverrideColorHelper overrideColorHelper,
                LogicalOverrideOpacityHelper overrideOpacityHelper,
                LogicalOverrideVisibleHelper overrideVisibleHelper
            ) : base(vmo, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper)
        {
        }
        /// <summary>
        /// Vmo
        /// </summary>
        public new ReservoirL3d Vmo
        {
            get { return _vmo as ReservoirL3d; }
            set { _vmo = value; }
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/01-node/02-source/02-tank/LogicalTank3D.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    ///
    /// </summary>
    internal class LogicalTank3D : LogicalSource3D
    {
        /// <summary>
        ///
        /// </summary>
        public LogicalTank3D() { }
        /// <summary>
        ///
        /// </summary>
        public LogicalTank3D
            (
                TankL3d vmo,
                LogicalStateHelper stateHelper,
                LogicalMaterialHelper materialHelper,
                LogicalOverrideColorHelper overrideColorHelper,
                LogicalOverrideOpacityHelper overrideOpacityHelper,
                LogicalOverrideVisibleHelper overrideVisibleHelper
            ) : base(vmo, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper)
        {
        }
        /// <summary>
        /// Vmo
        /// </summary>
        public new TankL3d Vmo
        {
            get { return _vmo as TankL3d; }
            set { _vmo = value; }
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/00-core/LogicalLink3D.cs
@@ -23,8 +23,7 @@
                LogicalOverrideVisibleHelper overrideVisibleHelper
            ) : base(vmo, stateHelper, materialHelper, overrideColorHelper, overrideOpacityHelper, overrideVisibleHelper)
        {
            this.StartPosition = vmo.StartPosition.ToPoint3D();
            this.EndPosition = vmo.EndPosition.ToPoint3D();
        }
        /// <summary>
@@ -47,14 +46,66 @@
        public Point3D EndPosition { get; set; }
        /// <summary>
        /// æ›´æ–°å¯è§†åŒ–元素
        /// æ›´æ–°ä½ç½®ä¿¡æ¯
        /// </summary>
        public override void UpdateVisual()
        public override void UpdatePositions()
        {
            this.StartPosition = this.Vmo.StartPosition.ToPoint3D();
            this.EndPosition = this.Vmo.EndPosition.ToPoint3D();
            base.UpdateVisual();
            if (this.Vmo != null)
            {
                if (this.Vmo.StartNode != null)
                {
                    if (this.Vmo.StartNode.Position != null)
                    {
                        this.StartPosition = this.Vmo.StartNode.Position.ToPoint3D();
                    }
                }
                if (this.Vmo.EndNode != null)
                {
                    if (this.Vmo.EndNode.Position != null)
                    {
                        this.EndPosition = this.Vmo.EndNode.Position.ToPoint3D();
                    }
                }
            }
        }
        /// <summary>
        /// éªŒè¯
        /// </summary>
        public override bool Verify()
        {
            if (this.Vmo == null)
            {
                return false;
            }
            if (this.Vmo.StartNode == null)
            {
                return false;
            }
            if (this.Vmo.StartNode.Position == null)
            {
                return false;
            }
            if (this.Vmo.EndNode == null)
            {
                return false;
            }
            if (this.Vmo.EndNode.Position == null)
            {
                return false;
            }
            var snapDistance = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.SnapDistance;
            var length = (this.EndPosition - this.StartPosition).Length;
            if (length < snapDistance)
            {
                return false;
            }
            return base.Verify();
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/01-pipe/LogicalPipe3D.cs
@@ -35,8 +35,10 @@
            set { _vmo = value; }
        }
        //更新材质
        protected override void UpdateMaterial()
        /// <summary>
        /// æ›´æ–°æè´¨
        /// </summary>
        public override void UpdateMaterial()
        {
            var htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.HtmlColor;
            var opacity = 1.0d;
@@ -57,24 +59,22 @@
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Highlight.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Adding))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Adding.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Editing))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Editing.HtmlColor;
            }
            var material = _materialHelper.GetMaterial(htmlColor, opacity);
            this.Material = material;
        }
        //更新可见性
        protected override void UpdateVisibility()
        {
            bool visible = true;
            var state = _stateHelper.GetState(this.Vmo);
            if (state.HasFlag(eLogicalState.Visible))
            {
                visible = _overrideVisibleHelper.GetVisible(this.Vmo);
            }
            this.Visible = visible;
        }
        //更新几何图形
        protected override void UpdateGeometry()
        /// <summary>
        /// æ›´æ–°å‡ ä½•图形
        /// </summary>
        public override void UpdateGeometry()
        {
            var diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
            var state = _stateHelper.GetState(this.Vmo);
@@ -87,12 +87,9 @@
                diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Highlight.Diameter;
            }
            var start = this.Vmo.StartPosition.ToPoint3D();
            var end = this.Vmo.EndPosition.ToPoint3D();
            MeshBuilder builder = new MeshBuilder();
            builder.AddTube(
                path: new[] { start, end },
                path: new[] { this.StartPosition, this.EndPosition },
                diameter: diameter,  // çº¿ç›´å¾„(3D空间单位)
                thetaDiv: 8,    // æ¨ªæˆªé¢ç»†åˆ†åº¦
                false
@@ -101,6 +98,15 @@
            this.MeshGeometry = geometry;
        }
        /// <summary>
        /// æ›´æ–°å˜æ¢çŸ©é˜µ
        /// </summary>
        public override void UpdateTransform()
        {
        }
    }
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/02-pump/LogicalPump3D.cs
@@ -1,4 +1,6 @@
namespace Yw.WpfUI.Hydro
using System.Net;
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// æŠ½è±¡3Dæ°´æ³µ
@@ -35,8 +37,10 @@
            set { _vmo = value; }
        }
        //更新材质
        protected override void UpdateMaterial()
        /// <summary>
        /// æ›´æ–°æè´¨
        /// </summary>
        public override void UpdateMaterial()
        {
            var htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.HtmlColor;
            var opacity = 1.0d;
@@ -57,24 +61,22 @@
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Highlight.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Adding))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Adding.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Editing))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Editing.HtmlColor;
            }
            var material = _materialHelper.GetMaterial(htmlColor, opacity);
            this.Material = material;
        }
        //更新可见性
        protected override void UpdateVisibility()
        {
            bool visible = true;
            var state = _stateHelper.GetState(this.Vmo);
            if (state.HasFlag(eLogicalState.Visible))
            {
                visible = _overrideVisibleHelper.GetVisible(this.Vmo);
            }
            this.Visible = visible;
        }
        //更新几何图形
        protected override void UpdateGeometry()
        /// <summary>
        /// æ›´æ–°å‡ ä½•图形
        /// </summary>
        public override void UpdateGeometry()
        {
            var diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
            var state = _stateHelper.GetState(this.Vmo);
@@ -87,8 +89,8 @@
                diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Highlight.Diameter;
            }
            var sv = this.Vmo.StartPosition.ToVector3D();
            var ev = this.Vmo.EndPosition.ToVector3D();
            var sv = this.StartPosition.ToVector3D();
            var ev = this.EndPosition.ToVector3D();
            var center = (sv + ev) * 0.5d;
            var direction = (ev - sv);
            var length = direction.Length;
@@ -97,7 +99,7 @@
            //水泵管道
            MeshBuilder builder = new MeshBuilder();
            builder.AddTube(
                path: new[] { this.Vmo.StartPosition.ToPoint3D(), this.Vmo.EndPosition.ToPoint3D() },
                path: new[] { this.StartPosition, this.EndPosition },
                diameter: diameter,  // çº¿ç›´å¾„(3D空间单位)
                thetaDiv: 8,    // æ¨ªæˆªé¢ç»†åˆ†åº¦
                false
@@ -111,28 +113,21 @@
                diameter: diameter * 1.5d,
                thetaDiv: 16);
            double bladeWidth = diameter * 2d;
            double bladeLength = length * 0.6d;
            // åˆ›å»ºä¸¤ä¸ªäº¤å‰çš„叶片
            for (int i = 0; i < 2; i++)
            {
                var rotation = new RotateTransform3D(
                    new AxisAngleRotation3D(direction, i * 90));
                // å¶ç‰‡1
                var p1 = center + rotation.Transform(new Vector3D(-bladeLength / 2, -bladeWidth / 2, 0));
                var p2 = center + rotation.Transform(new Vector3D(bladeLength / 2, -bladeWidth / 2, 0));
                var p3 = center + rotation.Transform(new Vector3D(bladeLength / 2, bladeWidth / 2, 0));
                var p4 = center + rotation.Transform(new Vector3D(-bladeLength / 2, bladeWidth / 2, 0));
                builder.AddQuad(p1.ToPoint3D(), p2.ToPoint3D(), p3.ToPoint3D(), p4.ToPoint3D());
            }
            var geometry = builder.ToMesh();
            this.MeshGeometry = geometry;
        }
        /// <summary>
        /// æ›´æ–°å˜æ¢çŸ©é˜µ
        /// </summary>
        public override void UpdateTransform()
        {
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/01-visual/02-link/03-valve/LogicalValve3D.cs
@@ -1,11 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yw.WpfUI.Hydro
namespace Yw.WpfUI.Hydro
{
    internal class LogicalValve3D : LogicalLink3D
    {
@@ -39,8 +32,10 @@
            set { _vmo = value; }
        }
        //更新材质
        protected override void UpdateMaterial()
        /// <summary>
        /// æ›´æ–°æè´¨
        /// </summary>
        public override void UpdateMaterial()
        {
            var htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.HtmlColor;
            var opacity = 1.0d;
@@ -61,24 +56,22 @@
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Highlight.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Adding))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Adding.HtmlColor;
            }
            if (state.HasFlag(eLogicalState.Editing))
            {
                htmlColor = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Node.Editing.HtmlColor;
            }
            var material = _materialHelper.GetMaterial(htmlColor, opacity);
            this.Material = material;
        }
        //更新可见性
        protected override void UpdateVisibility()
        {
            bool visible = true;
            var state = _stateHelper.GetState(this.Vmo);
            if (state.HasFlag(eLogicalState.Visible))
            {
                visible = _overrideVisibleHelper.GetVisible(this.Vmo);
            }
            this.Visible = visible;
        }
        //更新几何图形
        protected override void UpdateGeometry()
        /// <summary>
        /// æ›´æ–°å‡ ä½•图形
        /// </summary>
        public override void UpdateGeometry()
        {
            var diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
            var state = _stateHelper.GetState(this.Vmo);
@@ -91,25 +84,22 @@
                diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Highlight.Diameter;
            }
            var start = this.Vmo.StartPosition.ToPoint3D();
            var end = this.Vmo.EndPosition.ToPoint3D();
            var sv = start.ToVector3D();
            var ev = end.ToVector3D();
            var sv = this.StartPosition.ToVector3D();
            var ev = this.EndPosition.ToVector3D();
            var center = (sv + ev) * 0.5d;
            var direction = (ev - sv);
            var length = direction.Length;
            direction.Normalize();
            var size = diameter * 2d;
            if (size > length)
            var size = diameter * 1.5d;
            if (size > length * 0.7)
            {
                size = length;
                size = length * 0.7;
            }
            //阀门管道
            var builder = new MeshBuilder();
            builder.AddTube(
                path: new[] { start, end },
                path: new[] { this.StartPosition, this.EndPosition },
                diameter: diameter,  // çº¿ç›´å¾„(3D空间单位)
                thetaDiv: 8,    // æ¨ªæˆªé¢ç»†åˆ†åº¦
                false
@@ -121,6 +111,16 @@
            this.MeshGeometry = geometry;
        }
        /// <summary>
        /// æ›´æ–°å˜æ¢çŸ©é˜µ
        /// </summary>
        public override void UpdateTransform()
        {
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/02-state/eLogicalState.cs
@@ -39,7 +39,19 @@
        /// <summary>
        /// å¯è§æ€§
        /// </summary>
        Visible = 0x32
        Visible = 0x32,
        /// <summary>
        /// å¢žåŠ 
        /// </summary>
        Adding = 0x64,
        /// <summary>
        /// ç¼–辑
        /// </summary>
        Editing = 0x128,
    }
Yw.WpfUI.Hydro.L3d.Core/03-logical/05-camera/LogicalCamera3D.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@

namespace Yw.WpfUI.Hydro
{
    /// <summary>
    ///
    /// </summary>
    internal class LogicalCamera3D
    {
        /// <summary>
        ///
        /// </summary>
        public LogicalCamera3D() { }
        /// <summary>
        ///
        /// </summary>
        public LogicalCamera3D(CameraSetting rhs)
        {
            this.FarPlaneDistance = rhs.FarPlaneDistance;
            this.FieldOfView = rhs.FieldOfView;
            this.LookDirection = rhs.LookDirection;
            this.NearPlaneDistance = rhs.NearPlaneDistance;
            this.Position = rhs.Position;
            this.UpDirection = rhs.UpDirection;
            this.Width = rhs.Width;
        }
        /// <summary>
        /// è¿œå¹³é¢è·ç¦»
        /// </summary>
        public double FarPlaneDistance { get; set; }
        /// <summary>
        /// è§†é‡Ž
        /// </summary>
        public double FieldOfView { get; set; }
        /// <summary>
        /// è§‚察方向
        /// </summary>
        public Vector3D LookDirection { get; set; }
        /// <summary>
        /// è¿‘平面距离
        /// </summary>
        public double NearPlaneDistance { get; set; }
        /// <summary>
        /// ç›¸æœºä½ç½®
        /// </summary>
        public Point3D Position { get; set; }
        /// <summary>
        /// ç›¸æœºæ–¹å‘
        /// </summary>
        public Vector3D UpDirection { get; set; }
        /// <summary>
        /// æ­£äº¤ç›¸æœºçš„宽度
        /// </summary>
        public double Width { get; set; }
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public CameraL3d ToCameraL3d()
        {
            return new CameraL3d()
            {
                Width = this.Width,
                NearPlaneDistance = this.NearPlaneDistance,
                FarPlaneDistance = this.FarPlaneDistance,
                FieldOfView = this.FieldOfView,
                Position = this.Position.ToPointL3d(),
                LookDirection = this.LookDirection.ToPointL3d(),
                UpDirection = this.UpDirection.ToPointL3d()
            };
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/05-camera/LogicalCameraHelper.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// æŠ½è±¡ç›¸æœºè¾…助类
    /// </summary>
    internal class LogicalCameraHelper
    {
        /// <summary>
        ///
        /// </summary>
        public LogicalCameraHelper(HelixViewport3D viewport)
        {
            _viewport = viewport;
        }
        /// <summary>
        /// ä¿å­˜é»˜è®¤ç›¸æœºäº‹ä»¶
        /// </summary>
        public event Action<LogicalCamera3D> SaveDefaultCameraEvent;
        private readonly HelixViewport3D _viewport = null;//三维组件
        private LogicalCamera3D _defaultCamera = null;//默认相机设置
        /// <summary>
        /// è®¾ç½®é»˜è®¤ç›¸æœº
        /// </summary>
        public void SetDefault(CameraL3d camera)
        {
            if (camera == null)
            {
                return;
            }
            _defaultCamera = camera.ToLogicalCamera3D();
        }
        /// <summary>
        /// ä¿å­˜é»˜è®¤ç›¸æœº
        /// </summary>
        public void SaveDefault()
        {
            var cameraSetting = new CameraSetting(_viewport.Camera);
            var logicalCamera = new LogicalCamera3D(cameraSetting);
            _defaultCamera = logicalCamera;
            this.SaveDefaultCameraEvent?.Invoke(logicalCamera);
        }
        /// <summary>
        /// èŽ·å–é»˜è®¤ç›¸æœº
        /// </summary>
        /// <returns></returns>
        public LogicalCamera3D GetDefault()
        {
            return _defaultCamera;
        }
        /// <summary>
        /// åº”用默认相机
        /// </summary>
        public void ApplyDefault()
        {
            if (_defaultCamera == null)
            {
                return;
            }
            var camera = _viewport.Camera;
            camera.NearPlaneDistance = _defaultCamera.NearPlaneDistance;
            camera.FarPlaneDistance = _defaultCamera.FarPlaneDistance;
            camera.Position = _defaultCamera.Position;
            camera.LookDirection = _defaultCamera.LookDirection;
            camera.UpDirection = _defaultCamera.UpDirection;
            if (camera is PerspectiveCamera perspectiveCamera)
            {
                perspectiveCamera.FieldOfView = _defaultCamera.FieldOfView;
            }
            else if (camera is OrthographicCamera orghographicCamera)
            {
                orghographicCamera.Width = _defaultCamera.Width;
            }
        }
    }
}
Yw.WpfUI.Hydro.L3d.Core/03-logical/08-manager/LogicalEditManager.cs
@@ -1,6 +1,4 @@
using HelixToolkit.Wpf;
namespace Yw.WpfUI.Hydro
namespace Yw.WpfUI.Hydro
{
    /// <summary>
    /// æŠ½è±¡ç¼–辑管理器
@@ -16,7 +14,8 @@
        }
        private eLogicalEditMode _logicalMode = eLogicalEditMode.None;//编辑模式
        private VisualL3d _visual = null;//可视化对象
        private LogicalVisual3D _addLogicalVisual;//添加抽象对象
        private LogicalNode3D _editLogicalNode;//编辑抽象节点
        #region æ·»åŠ æ–¹æ³•
@@ -87,6 +86,26 @@
        #endregion
        #region ç¼–辑方法
        /// <summary>
        /// å¼€å§‹ç¼–辑
        /// </summary>
        public void StartEdit()
        {
            _logicalMode = eLogicalEditMode.Edit;
        }
        /// <summary>
        /// ç»“束编辑
        /// </summary>
        public void EndEdit()
        {
            _logicalMode = eLogicalEditMode.None;
        }
        #endregion
        #region é‡å†™äº‹ä»¶
        //鼠标按下
@@ -96,7 +115,7 @@
            {
                return;
            }
            _addLogicalVisual = null;
            switch (_logicalMode)
            {
                case eLogicalEditMode.AddJunction:
@@ -105,11 +124,16 @@
                        var sp = _viewport.Viewport.UnProject(pt);
                        if (sp.HasValue)
                        {
                            var visual = new JunctionL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("junction", _nw.Junctions.Select(x => x.Id).ToList());
                            visual.Position = sp.Value.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var junction = new JunctionL3d();
                            junction.Id = _nw.CreateId(CatalogL3d.Junction);
                            junction.Catalog = CatalogL3d.Junction;
                            junction.Name = _nw.CreateName(CatalogL3d.Junction);
                            junction.Position = sp.Value.ToPointL3d();
                            _stateHelper.LoadState(junction, eLogicalState.Adding);
                            var logicalJunction = CreateLogicalVisual(junction);
                            logicalJunction.UpdateVisual();
                            AddLogicalVisual(logicalJunction);
                            _addLogicalVisual = logicalJunction;
                        }
                    }
                    break;
@@ -119,11 +143,16 @@
                        var sp = _viewport.Viewport.UnProject(pt);
                        if (sp.HasValue)
                        {
                            var visual = new ReservoirL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("reservoir", _nw.Reservoirs.Select(x => x.Id).ToList());
                            visual.Position = sp.Value.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var reservoir = new ReservoirL3d();
                            reservoir.Id = _nw.CreateId(CatalogL3d.Reservoir);
                            reservoir.Catalog = CatalogL3d.Reservoir;
                            reservoir.Name = _nw.CreateName(CatalogL3d.Reservoir);
                            reservoir.Position = sp.Value.ToPointL3d();
                            _stateHelper.LoadState(reservoir, eLogicalState.Adding);
                            var logicalReservoir = CreateLogicalVisual(reservoir);
                            logicalReservoir.UpdateVisual();
                            AddLogicalVisual(logicalReservoir);
                            _addLogicalVisual = logicalReservoir;
                        }
                    }
                    break;
@@ -133,11 +162,16 @@
                        var sp = _viewport.Viewport.UnProject(pt);
                        if (sp.HasValue)
                        {
                            var visual = new TankL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("tank", _nw.Reservoirs.Select(x => x.Id).ToList());
                            visual.Position = sp.Value.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var tank = new TankL3d();
                            tank.Id = _nw.CreateId(CatalogL3d.Tank);
                            tank.Catalog = CatalogL3d.Tank;
                            tank.Name = _nw.CreateName(CatalogL3d.Tank);
                            tank.Position = sp.Value.ToPointL3d();
                            _stateHelper.LoadState(tank, eLogicalState.Adding);
                            var logicalTank = CreateLogicalVisual(tank);
                            logicalTank.UpdateVisual();
                            AddLogicalVisual(logicalTank);
                            _addLogicalVisual = logicalTank;
                        }
                    }
                    break;
@@ -147,12 +181,17 @@
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode != null)
                        {
                            var visual = new PipeL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("pipe", _nw.Pipes.Select(x => x.Id).ToList());
                            visual.StartPosition = logicalNode.Position.ToPointL3d();
                            visual.EndPosition = logicalNode.Position.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var pipe = new PipeL3d();
                            pipe.Id = _nw.CreateId(CatalogL3d.Pipe);
                            pipe.Catalog = CatalogL3d.Pipe;
                            pipe.Name = _nw.CreateName(CatalogL3d.Pipe);
                            pipe.StartNode = logicalNode.Vmo;
                            _stateHelper.LoadState(pipe, eLogicalState.Adding);
                            var logicalPipe = CreateLogicalVisual(pipe) as LogicalPipe3D;
                            logicalPipe.EndPosition = logicalNode.Position;
                            logicalPipe.UpdateVisual();
                            AddLogicalVisual(logicalPipe);
                            _addLogicalVisual = logicalPipe;
                        }
                    }
                    break;
@@ -162,12 +201,17 @@
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode != null)
                        {
                            var visual = new PipeL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("pipe", _nw.Pipes.Select(x => x.Id).ToList());
                            visual.StartPosition = logicalNode.Position.ToPointL3d();
                            visual.EndPosition = logicalNode.Position.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var pipe = new PipeL3d();
                            pipe.Id = _nw.CreateId(CatalogL3d.Pipe);
                            pipe.Catalog = CatalogL3d.Pipe;
                            pipe.Name = _nw.CreateName(CatalogL3d.Pipe);
                            pipe.StartNode = logicalNode.Vmo;
                            _stateHelper.LoadState(pipe, eLogicalState.Adding);
                            var logicalPipe = CreateLogicalVisual(pipe) as LogicalPipe3D;
                            logicalPipe.EndPosition = logicalNode.Position;
                            logicalPipe.UpdateVisual();
                            AddLogicalVisual(logicalPipe);
                            _addLogicalVisual = logicalPipe;
                        }
                    }
                    break;
@@ -177,12 +221,17 @@
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode != null)
                        {
                            var visual = new PipeL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("pipe", _nw.Pipes.Select(x => x.Id).ToList());
                            visual.StartPosition = logicalNode.Position.ToPointL3d();
                            visual.EndPosition = logicalNode.Position.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var pipe = new PipeL3d();
                            pipe.Id = _nw.CreateId(CatalogL3d.Pipe);
                            pipe.Catalog = CatalogL3d.Pipe;
                            pipe.Name = _nw.CreateName(CatalogL3d.Pipe);
                            pipe.StartNode = logicalNode.Vmo;
                            _stateHelper.LoadState(pipe, eLogicalState.Adding);
                            var logicalPipe = CreateLogicalVisual(pipe) as LogicalPipe3D;
                            logicalPipe.EndPosition = logicalNode.Position;
                            logicalPipe.UpdateVisual();
                            AddLogicalVisual(logicalPipe);
                            _addLogicalVisual = logicalPipe;
                        }
                    }
                    break;
@@ -192,12 +241,17 @@
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode != null)
                        {
                            var visual = new PumpL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("pump", _nw.Pumps.Select(x => x.Id).ToList());
                            visual.StartPosition = logicalNode.Position.ToPointL3d();
                            visual.EndPosition = logicalNode.Position.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var pump = new PumpL3d();
                            pump.Id = _nw.CreateId(CatalogL3d.Pump);
                            pump.Catalog = CatalogL3d.Pump;
                            pump.Name = _nw.CreateName(CatalogL3d.Pump);
                            pump.StartNode = logicalNode.Vmo;
                            _stateHelper.LoadState(pump, eLogicalState.Adding);
                            var logicalPump = CreateLogicalVisual(pump) as LogicalPump3D;
                            logicalPump.EndPosition = logicalNode.Position;
                            logicalPump.UpdateVisual();
                            AddLogicalVisual(logicalPump);
                            _addLogicalVisual = logicalPump;
                        }
                    }
                    break;
@@ -207,170 +261,35 @@
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode != null)
                        {
                            var visual = new ValveL3d();
                            visual.Id = Yw.Untity.UniqueHelper.CreateFromFirst("valve", _nw.Pumps.Select(x => x.Id).ToList());
                            visual.StartPosition = logicalNode.Position.ToPointL3d();
                            visual.EndPosition = logicalNode.Position.ToPointL3d();
                            AddVisual(visual);
                            _visual = visual;
                            var valve = new ValveL3d();
                            valve.Id = _nw.CreateId(CatalogL3d.Valve);
                            valve.Catalog = CatalogL3d.Valve;
                            valve.Name = _nw.CreateName(CatalogL3d.Valve);
                            valve.StartNode = logicalNode.Vmo;
                            _stateHelper.LoadState(valve, eLogicalState.Adding);
                            var logicalValve = CreateLogicalVisual(valve) as LogicalValve3D;
                            logicalValve.EndPosition = logicalNode.Position;
                            logicalValve.UpdateVisual();
                            AddLogicalVisual(logicalValve);
                            _addLogicalVisual = logicalValve;
                        }
                    }
                    break;
                case eLogicalEditMode.Edit:
                    {
                        var pt = e.GetPosition(_viewport);
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode != null)
                        {
                            var node = logicalNode.Vmo;
                            _stateHelper.LoadState(logicalNode.Vmo, eLogicalState.Editing);
                            _editLogicalNode = logicalNode;
                        }
                    }
                    break;
                default:
                    {
                        base.OnMouseDown(e);
                    }
                    break;
            }
        }
        //鼠标弹起
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            if (!Initialized)
            {
                return;
            }
            switch (_logicalMode)
            {
                case eLogicalEditMode.AddJunction:
                    {
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddReservoir:
                    {
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddTank:
                    {
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddPipe:
                    {
                        var pt = e.GetPosition(_viewport);
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode == null)
                        {
                            var sp = _viewport.Viewport.UnProject(pt);
                            var junction = new JunctionL3d();
                            junction.Id = Yw.Untity.UniqueHelper.CreateFromFirst("junction", _nw.Junctions.Select(x => x.Id).ToList());
                            junction.Position = sp.Value.ToPointL3d();
                            AddVisual(junction);
                        }
                        else
                        {
                            var end = logicalNode.Position.ToPointL3d();
                            var pipe = _visual as PipeL3d;
                            if (pipe != null)
                            {
                                pipe.EndPosition = end;
                                UpdateVisual(pipe);
                            }
                        }
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddHorizPipe:
                    {
                        var pipe = _visual as PipeL3d;
                        if (pipe != null)
                        {
                            var end = pipe.EndPosition.ToPoint3D();
                            var logicalNode = SnapNearestNode(end);
                            if (logicalNode == null)
                            {
                                var junction = new JunctionL3d();
                                junction.Id = Yw.Untity.UniqueHelper.CreateFromFirst("junction", _nw.Junctions.Select(x => x.Id).ToList());
                                junction.Position = end.ToPointL3d();
                                AddVisual(junction);
                            }
                        }
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddVertPipe:
                    {
                        var pipe = _visual as PipeL3d;
                        if (pipe != null)
                        {
                            var end = pipe.EndPosition.ToPoint3D();
                            LogicalNode3D logicalNode = SnapNearestNode(end);
                            if (logicalNode == null)
                            {
                                var junction = new JunctionL3d();
                                junction.Id = Yw.Untity.UniqueHelper.CreateFromFirst("junction", _nw.Junctions.Select(x => x.Id).ToList());
                                junction.Position = end.ToPointL3d();
                                AddVisual(junction);
                            }
                        }
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddPump:
                    {
                        var pt = e.GetPosition(_viewport);
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode == null)
                        {
                            var sp = _viewport.Viewport.UnProject(pt);
                            var junction = new JunctionL3d();
                            junction.Id = Yw.Untity.UniqueHelper.CreateFromFirst("junction", _nw.Junctions.Select(x => x.Id).ToList());
                            junction.Position = sp.Value.ToPointL3d();
                            AddVisual(junction);
                        }
                        else
                        {
                            var end = logicalNode.Position.ToPointL3d();
                            var pump = _visual as PumpL3d;
                            if (pump != null)
                            {
                                pump.EndPosition = end;
                                UpdateVisual(pump);
                            }
                        }
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                case eLogicalEditMode.AddValve:
                    {
                        var pt = e.GetPosition(_viewport);
                        var logicalNode = SnapNearestNode(pt);
                        if (logicalNode == null)
                        {
                            var sp = _viewport.Viewport.UnProject(pt);
                            var junction = new JunctionL3d();
                            junction.Id = Yw.Untity.UniqueHelper.CreateFromFirst("junction", _nw.Junctions.Select(x => x.Id).ToList());
                            junction.Position = sp.Value.ToPointL3d();
                            AddVisual(junction);
                        }
                        else
                        {
                            var end = logicalNode.Position.ToPointL3d();
                            var valve = _visual as ValveL3d;
                            if (valve != null)
                            {
                                valve.EndPosition = end;
                                UpdateVisual(valve);
                            }
                        }
                        _visual = null;
                        _logicalMode = eLogicalEditMode.None;
                    }
                    break;
                default:
                    {
                        base.OnMouseUp(e);
                    }
                    break;
            }
@@ -388,21 +307,61 @@
            {
                case eLogicalEditMode.AddJunction:
                    {
                    }
                    break;
                case eLogicalEditMode.AddPipe:
                    {
                        base.OnMouseMove(e);
                        var pipe = _visual as PipeL3d;
                        if (pipe != null)
                        var logicalJunction = _addLogicalVisual as LogicalJunction3D;
                        if (logicalJunction != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                pipe.EndPosition = sp.Value.ToPointL3d();
                                UpdateVisual(_visual);
                                logicalJunction.Vmo.Position = sp.Value.ToPointL3d();
                                logicalJunction.UpdateVisual();
                            }
                        }
                    }
                    break;
                case eLogicalEditMode.AddReservoir:
                    {
                        var logicalReservoir = _addLogicalVisual as LogicalSource3D;
                        if (logicalReservoir != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                logicalReservoir.Vmo.Position = sp.Value.ToPointL3d();
                                logicalReservoir.UpdateVisual();
                            }
                        }
                    }
                    break;
                case eLogicalEditMode.AddTank:
                    {
                        var logicalTank = _addLogicalVisual as LogicalTank3D;
                        if (logicalTank != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                logicalTank.Vmo.Position = sp.Value.ToPointL3d();
                                logicalTank.UpdateVisual();
                            }
                        }
                    }
                    break;
                case eLogicalEditMode.AddPipe:
                    {
                        base.OnMouseMove(e);
                        var logicalPipe = _addLogicalVisual as LogicalPipe3D;
                        if (logicalPipe != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                logicalPipe.EndPosition = sp.Value;
                                logicalPipe.UpdateVisual();
                            }
                        }
                    }
@@ -410,15 +369,15 @@
                case eLogicalEditMode.AddHorizPipe:
                    {
                        base.OnMouseMove(e);
                        var pipe = _visual as PipeL3d;
                        if (pipe != null)
                        var logicalPipe = _addLogicalVisual as LogicalPipe3D;
                        if (logicalPipe != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                pipe.EndPosition = new PointL3d(sp.Value.X, sp.Value.Y, pipe.StartPosition.Z);
                                UpdateVisual(_visual);
                                logicalPipe.EndPosition = new Point3D(sp.Value.X, sp.Value.Y, logicalPipe.StartPosition.Z);
                                logicalPipe.UpdateVisual();
                            }
                        }
                    }
@@ -426,15 +385,15 @@
                case eLogicalEditMode.AddVertPipe:
                    {
                        base.OnMouseMove(e);
                        var pipe = _visual as PipeL3d;
                        if (pipe != null)
                        var logicalPipe = _addLogicalVisual as LogicalPipe3D;
                        if (logicalPipe != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                pipe.EndPosition = new PointL3d(pipe.StartPosition.X, pipe.StartPosition.Y, sp.Value.Z);
                                UpdateVisual(_visual);
                                logicalPipe.EndPosition = new Point3D(logicalPipe.StartPosition.X, logicalPipe.StartPosition.Y, sp.Value.Z);
                                logicalPipe.UpdateVisual();
                            }
                        }
                    }
@@ -442,15 +401,15 @@
                case eLogicalEditMode.AddPump:
                    {
                        base.OnMouseMove(e);
                        var pump = _visual as PumpL3d;
                        if (pump != null)
                        var logicalPump = _addLogicalVisual as LogicalPump3D;
                        if (logicalPump != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                pump.EndPosition = sp.Value.ToPointL3d();
                                UpdateVisual(_visual);
                                logicalPump.EndPosition = sp.Value;
                                logicalPump.UpdateVisual();
                            }
                        }
                    }
@@ -458,15 +417,33 @@
                case eLogicalEditMode.AddValve:
                    {
                        base.OnMouseMove(e);
                        var valve = _visual as ValveL3d;
                        if (valve != null)
                        var logicalValve = _addLogicalVisual as LogicalValve3D;
                        if (logicalValve != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                valve.EndPosition = sp.Value.ToPointL3d();
                                UpdateVisual(_visual);
                                logicalValve.EndPosition = sp.Value;
                                logicalValve.UpdateVisual();
                            }
                        }
                    }
                    break;
                case eLogicalEditMode.Edit:
                    {
                        base.OnMouseMove(e);
                        if (_editLogicalNode != null)
                        {
                            var pt = e.GetPosition(_viewport);
                            var sp = _viewport.Viewport.UnProject(pt);
                            if (sp.HasValue)
                            {
                                var node = _editLogicalNode.Vmo;
                                node.Position = sp.Value.ToPointL3d();
                                _editLogicalNode.UpdateVisual();
                                var links = _nw.Links.Where(x => x.StartNode == node || x.EndNode == node).ToList();
                                links?.ForEach(x => _allVisualLogicalDict[x].UpdateVisual());
                            }
                        }
                    }
@@ -479,38 +456,327 @@
            }
        }
        #endregion
        //添加
        private void AddVisual(VisualL3d visual)
        //鼠标弹起
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            if (!Initialized)
            {
                return;
            }
            _nw.Append(visual, out string msg);
            _allVisualL3dDict.Add(visual.Id, visual);
            switch (_logicalMode)
            {
                case eLogicalEditMode.AddJunction:
                    {
                        var logicalJunction = _addLogicalVisual as LogicalJunction3D;
                        if (logicalJunction != null)
                        {
                            _stateHelper.UnloadState(logicalJunction.Vmo, eLogicalState.Adding);
                            if (!logicalJunction.Verify())
                            {
                                RemoveLogicalVisual(logicalJunction);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddReservoir:
                    {
                        var logicalReservoir = _addLogicalVisual as LogicalReservoir3D;
                        if (logicalReservoir != null)
                        {
                            _stateHelper.UnloadState(logicalReservoir.Vmo, eLogicalState.Adding);
                            if (!logicalReservoir.Verify())
                            {
                                RemoveLogicalVisual(logicalReservoir);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddTank:
                    {
                        var logicalTank = _addLogicalVisual as LogicalTank3D;
                        if (logicalTank != null)
                        {
                            _stateHelper.UnloadState(logicalTank.Vmo, eLogicalState.Adding);
                            if (!logicalTank.Verify())
                            {
                                RemoveLogicalVisual(logicalTank);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddPipe:
                    {
                        var logicalPipe = _addLogicalVisual as LogicalPipe3D;
                        if (logicalPipe != null)
                        {
                            _stateHelper.UnloadState(logicalPipe.Vmo, eLogicalState.Adding);
                            var pt = e.GetPosition(_viewport);
                            var logicalNode = SnapNearestNode(pt);
                            if (logicalNode == null)
                            {
                                var sp = _viewport.Viewport.UnProject(pt);
                                var junction = new JunctionL3d();
                                junction.Id = _nw.CreateId(CatalogL3d.Junction);
                                junction.Catalog = CatalogL3d.Junction;
                                junction.Name = _nw.CreateName(CatalogL3d.Junction);
                                junction.Position = sp.Value.ToPointL3d();
                                var logicalVisual = CreateLogicalVisual(junction);
                                AddLogicalVisual(logicalVisual);
                                logicalPipe.Vmo.EndNode = junction;
                            }
                            else
                            {
                                logicalPipe.Vmo.EndNode = logicalNode.Vmo;
                            }
                            logicalPipe.UpdateVisual();
                            if (!logicalPipe.Verify())
                            {
                                RemoveLogicalVisual(logicalPipe);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddHorizPipe:
                    {
                        var logicalPipe = _addLogicalVisual as LogicalPipe3D;
                        if (logicalPipe != null)
                        {
                            _stateHelper.UnloadState(logicalPipe.Vmo, eLogicalState.Adding);
                            var end = logicalPipe.EndPosition;
                            var logicalNode = SnapNearestNode(end);
                            if (logicalNode == null)
                            {
                                var junction = new JunctionL3d();
                                junction.Id = _nw.CreateId(CatalogL3d.Junction);
                                junction.Catalog = CatalogL3d.Junction;
                                junction.Name = _nw.CreateName(CatalogL3d.Junction);
                                junction.Position = end.ToPointL3d();
                                var logicalJunction = CreateLogicalVisual(junction);
                                AddLogicalVisual(logicalJunction);
                                logicalPipe.Vmo.EndNode = junction;
                            }
                            else
                            {
                                logicalPipe.Vmo.EndNode = logicalNode.Vmo;
                            }
                            logicalPipe.UpdateVisual();
                            if (!logicalPipe.Verify())
                            {
                                RemoveLogicalVisual(logicalPipe);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddVertPipe:
                    {
                        var logicalPipe = _addLogicalVisual as LogicalPipe3D;
                        if (logicalPipe != null)
                        {
                            _stateHelper.UnloadState(logicalPipe.Vmo, eLogicalState.Adding);
                            var end = logicalPipe.EndPosition;
                            var logicalNode = SnapNearestNode(end);
                            if (logicalNode == null)
                            {
                                var junction = new JunctionL3d();
                                junction.Id = _nw.CreateId(CatalogL3d.Junction);
                                junction.Catalog = CatalogL3d.Junction;
                                junction.Name = _nw.CreateName(CatalogL3d.Junction);
                                junction.Position = end.ToPointL3d();
                                var logicalJunction = CreateLogicalVisual(junction);
                                AddLogicalVisual(logicalJunction);
                                logicalPipe.Vmo.EndNode = junction;
                            }
                            else
                            {
                                logicalPipe.Vmo.EndNode = logicalNode.Vmo;
                            }
                            logicalPipe.UpdateVisual();
                            if (!logicalPipe.Verify())
                            {
                                RemoveLogicalVisual(logicalPipe);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddPump:
                    {
                        var logicalPump = _addLogicalVisual as LogicalPump3D;
                        if (logicalPump != null)
                        {
                            _stateHelper.UnloadState(logicalPump.Vmo, eLogicalState.Adding);
                            var pt = e.GetPosition(_viewport);
                            var logicalNode = SnapNearestNode(pt);
                            if (logicalNode == null)
                            {
                                var sp = _viewport.Viewport.UnProject(pt);
                                var junction = new JunctionL3d();
                                junction.Id = _nw.CreateId(CatalogL3d.Junction);
                                junction.Catalog = CatalogL3d.Junction;
                                junction.Name = _nw.CreateName(CatalogL3d.Junction);
                                junction.Position = sp.Value.ToPointL3d();
                                var logicalVisual = CreateLogicalVisual(junction);
                                AddLogicalVisual(logicalVisual);
                                logicalPump.Vmo.EndNode = junction;
                            }
                            else
                            {
                                logicalPump.Vmo.EndNode = logicalNode.Vmo;
                            }
                            logicalPump.UpdateVisual();
                            if (!logicalPump.Verify())
                            {
                                RemoveLogicalVisual(logicalPump);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.AddValve:
                    {
                        var logicalValve = _addLogicalVisual as LogicalValve3D;
                        if (logicalValve != null)
                        {
                            _stateHelper.UnloadState(logicalValve.Vmo, eLogicalState.Adding);
                            var pt = e.GetPosition(_viewport);
                            var logicalNode = SnapNearestNode(pt);
                            if (logicalNode == null)
                            {
                                var sp = _viewport.Viewport.UnProject(pt);
                                var junction = new JunctionL3d();
                                junction.Id = _nw.CreateId(CatalogL3d.Junction);
                                junction.Catalog = CatalogL3d.Junction;
                                junction.Name = _nw.CreateName(CatalogL3d.Junction);
                                junction.Position = sp.Value.ToPointL3d();
                                var logicalVisual = CreateLogicalVisual(junction);
                                AddLogicalVisual(logicalVisual);
                                logicalValve.Vmo.EndNode = junction;
                            }
                            else
                            {
                                logicalValve.Vmo.EndNode = logicalNode.Vmo;
                            }
                            logicalValve.UpdateVisual();
                            if (!logicalValve.Verify())
                            {
                                RemoveLogicalVisual(logicalValve);
                            }
                            _addLogicalVisual = null;
                            _logicalMode = eLogicalEditMode.None;
                        }
                    }
                    break;
                case eLogicalEditMode.Edit:
                    {
                        if (_editLogicalNode != null)
                        {
                            _stateHelper.UnloadState(_editLogicalNode.Vmo, eLogicalState.Editing);
                        }
                        _editLogicalNode = null;
                    }
                    break;
                default:
                    {
                        base.OnMouseUp(e);
                    }
                    break;
            }
        }
        #endregion
        #region æž„件方法
        //创建抽象可见元素
        private LogicalVisual3D CreateLogicalVisual(VisualL3d visual)
        {
            var logicalVisual = LogicalCreateHelper.Create(visual, _stateHelper, _materialHelper, _overrideColorHelper, _overrideOpacityHelper, _overrideVisibleHelper);
            if (logicalVisual != null)
            return logicalVisual;
        }
        //添加抽象可见元素
        private void AddLogicalVisual(LogicalVisual3D logicalVisual)
        {
            if (!Initialized)
            {
                return;
            }
            if (logicalVisual == null)
            {
                return;
            }
            if (logicalVisual.Vmo == null)
            {
                return;
            }
            var visual = logicalVisual.Vmo;
            if (!_nw.IsExist(visual))
            {
                _nw.Append(visual, out _);
            }
            if (!_allVisualL3dDict.ContainsKey(visual.Id))
            {
                _allVisualL3dDict.Add(visual.Id, visual);
            }
            if (!_allVisualLogicalDict.ContainsKey(visual))
            {
                _allVisualLogicalDict.Add(visual, logicalVisual);
            }
            if (!_viewport.Children.Contains(logicalVisual))
            {
                _viewport.Children.Add(logicalVisual);
            }
        }
        //更新
        private void UpdateVisual(VisualL3d visual)
        //移除抽象可见元素
        private void RemoveLogicalVisual(LogicalVisual3D logicalVisual)
        {
            if (!Initialized)
            {
                return;
            }
            var logicalVisual = _allVisualLogicalDict[visual];
            logicalVisual.UpdateVisual();
            if (logicalVisual == null)
            {
                return;
            }
            if (logicalVisual.Vmo == null)
            {
                return;
            }
            var visual = logicalVisual.Vmo;
            if (_nw.IsExist(visual))
            {
                _nw.Remove(visual);
            }
            if (_allVisualL3dDict.ContainsKey(visual.Id))
            {
                _allVisualL3dDict.Remove(visual.Id);
            }
            if (_allVisualLogicalDict.ContainsKey(visual))
            {
                _allVisualLogicalDict.Remove(visual);
            }
            if (_viewport.Children.Contains(logicalVisual))
            {
                _viewport.Children.Remove(logicalVisual);
            }
        }
        #region è¾…助方法
        #endregion
        #region è¾…助方法
        //吸附最近节点
        private LogicalNode3D SnapNearestNode(Point pt)
Yw.WpfUI.Hydro.L3d.Core/03-logical/08-manager/LogicalManager.cs
@@ -1,4 +1,4 @@
using System.Net;
using System.Windows.Controls;
namespace Yw.WpfUI.Hydro
{
@@ -21,6 +21,7 @@
            _selectionHelper = new LogicalSelectionHelper(_viewport);
            _zoomHelper = new LogicalZoomHelper(_viewport);
            _billboardTextHelper = new LogicalBillboardLeadLabelHelper(_viewport);
            _cameraHelper = new LogicalCameraHelper(_viewport);
        }
        #region äº‹ä»¶é›†åˆ
@@ -46,6 +47,7 @@
        protected readonly LogicalSelectionHelper _selectionHelper = null;//选择辅助类
        protected readonly LogicalZoomHelper _zoomHelper = null;//缩放辅助类
        protected readonly LogicalBillboardLeadLabelHelper _billboardTextHelper = null;//公告版文字辅助类
        protected readonly LogicalCameraHelper _cameraHelper = null;//相机辅助类
        #endregion
@@ -214,9 +216,14 @@
                return;
            }
            _viewport.Children.Clear();
            _viewport.Children.Add(new DefaultLights());
            _viewport.Children.Add(new SunLight());
            _allVisualLogicalDict?.Values.ToList().ForEach(x => _viewport.Children.Add(x));
            _viewport.ZoomExtents();
            if (_nw.DefaultCamera != null)
            {
                _cameraHelper.SetDefault(_nw.DefaultCamera);
                _cameraHelper.ApplyDefault();
            }
        }
@@ -368,6 +375,10 @@
                _selectionHelper.StateChangedEvent += SelectionHelper_StateChangedEvent;
                _selectionHelper.SelectionChangedEvent += SelectionHelper_SelectionChangedEvent;
            }
            if (_cameraHelper != null)
            {
                _cameraHelper.SaveDefaultCameraEvent += CameraHelper_SaveDefaultCameraEvent;
            }
        }
        //高亮状态改变事件
@@ -411,6 +422,13 @@
        {
            var visuals = obj?.Select(x => x.Vmo).ToList();
            this.SelectionChangedEvent?.Invoke(visuals);
        }
        //默认相机保存事件
        private void CameraHelper_SaveDefaultCameraEvent(LogicalCamera3D logicalCamera)
        {
            var camera = logicalCamera.ToCameraL3d();
            _nw.DefaultCamera = camera;
        }
@@ -462,7 +480,6 @@
            _viewport.FitView(lookDirection, upDirection);
        }
        /// <summary>
        /// è®¾ç½®å³è§†å›¾
        /// </summary>
@@ -502,6 +519,66 @@
            }
            var lookDirection = new Vector3D(0, -1, 0);   // å‘前看
            var upDirection = new Vector3D(0, 0, 1);// Z轴朝上
            _viewport.FitView(lookDirection, upDirection);
        }
        /// <summary>
        /// è¥¿å—视角
        /// ä»Žç‰©ä½“的左前下方观察
        /// </summary>
        public virtual void SetSouthWestView()
        {
            if (!Initialized)
            {
                return;
            }
            var lookDirection = new Vector3D(1, 1, -1);
            var upDirection = new Vector3D(0, 0, 1);
            _viewport.FitView(lookDirection, upDirection);
        }
        /// <summary>
        /// ä¸œå—视角
        /// ä»Žç‰©ä½“的右前下方观察
        /// </summary>
        public virtual void SetSouthEastView()
        {
            if (!Initialized)
            {
                return;
            }
            var lookDirection = new Vector3D(-1, 1, -1);
            var upDirection = new Vector3D(0, 0, 1);
            _viewport.FitView(lookDirection, upDirection);
        }
        /// <summary>
        /// ä¸œåŒ—视角
        /// ä»Žç‰©ä½“的右后上方观察
        /// </summary>
        public virtual void SetNorthEastView()
        {
            if (!Initialized)
            {
                return;
            }
            var lookDirection = new Vector3D(-1, -1, -1);
            var upDirection = new Vector3D(0, 0, 1);
            _viewport.FitView(lookDirection, upDirection);
        }
        /// <summary>
        /// è¥¿åŒ—视角
        /// ä»Žç‰©ä½“的左后上方观察
        /// </summary>
        public virtual void SetNorthWestView()
        {
            if (!Initialized)
            {
                return;
            }
            var lookDirection = new Vector3D(1, -1, -1);
            var upDirection = new Vector3D(0, 0, 1);
            _viewport.FitView(lookDirection, upDirection);
        }
@@ -686,7 +763,33 @@
        #endregion
        #region ç›¸æœº
        /// <summary>
        /// ä¿å­˜é»˜è®¤ç›¸æœº
        /// </summary>
        public void SaveDefaultCamera()
        {
            if (!Initialized)
            {
                return;
            }
            _cameraHelper.SaveDefault();
        }
        /// <summary>
        /// åº”用默认相机
        /// </summary>
        public void ApplyDefaultCamera()
        {
            if (!Initialized)
            {
                return;
            }
            _cameraHelper.ApplyDefault();
        }
        #endregion
Yw.WpfUI.Hydro.L3d.Core/03-logical/08-manager/eLogicalEditMode.cs
@@ -14,6 +14,7 @@
        AddVertPipe,
        AddPump,
        AddValve,
        Edit,
    }
Yw.WpfUI.Hydro.L3d.Core/03-logical/09-control/LogicalEditerL3d.xaml.cs
@@ -124,6 +124,26 @@
            _manager.SetBackView();
        }
        public void SetNorthWestView()
        {
            _manager.SetNorthWestView();
        }
        public void SetNorthEastView()
        {
            _manager.SetNorthEastView();
        }
        public void SetSouthWestView()
        {
            _manager.SetSouthWestView();
        }
        public void SetSouthEastView()
        {
            _manager.SetSouthEastView();
        }
        #endregion
        #region å…¬å‘Šæ¿æ–‡æœ¬
@@ -201,5 +221,32 @@
        #endregion
        #region ç›¸æœº
        public void SaveDefaultCamera()
        {
            _manager.SaveDefaultCamera();
        }
        public void ApplyDefaultCamera()
        {
            _manager.ApplyDefaultCamera();
        }
        #endregion
        #region ç¼–辑
        public void StartEdit()
        {
            _manager.StartEdit();
        }
        public void EndEdit()
        {
            _manager.EndEdit();
        }
        #endregion
    }
}
Yw.WpfUI.Hydro.L3d.Core/Yw.WpfUI.Hydro.L3d.Core.csproj
@@ -28,8 +28,6 @@
    <ItemGroup>
        <Folder Include="03-logical\01-visual\01-node\01-junction\01-coupling\" />
        <Folder Include="03-logical\01-visual\01-node\02-source\01-reservoir\" />
        <Folder Include="03-logical\01-visual\01-node\02-source\02-tank\" />
        <Folder Include="02-settings\01-paras\01-node\01-junction\" />
        <Folder Include="02-settings\01-paras\01-node\02-source\01-reservoir\" />
        <Folder Include="02-settings\01-paras\01-node\02-source\02-tank\" />
Yw.WpfUI.Hydro.L3d.Core/paras_hydrol3d_settings.json
@@ -6,16 +6,24 @@
        "Logical": {
            "Node": {
                "Normal": {
                    "HtmlColor": "#e1300a",
                    "HtmlColor": "#FF0000",
                    "Radius": 0.3
                },
                "Highlight": {
                    "HtmlColor": "#FF0000",
                    "HtmlColor": "#32CD32",
                    "Radius": 0.5
                },
                "Selection": {
                    "HtmlColor": "#FF0000",
                    "HtmlColor": "#DAA520",
                    "Radius": 0.4
                },
                "Adding": {
                    "HtmlColor": "#808080",
                    "Radius": 0.3
                },
                "Editing": {
                    "HtmlColor": "#00BFFF",
                    "Radius": 0.3
                },
                "SnapDistance": 0.2
            },
@@ -25,12 +33,20 @@
                    "Diameter": 0.5
                },
                "Highlight": {
                    "HtmlColor": "#FF0000",
                    "HtmlColor": "#32CD32",
                    "Diameter": 0.8
                },
                "Selection": {
                    "HtmlColor": "#FF0000",
                    "HtmlColor": "#DAA520",
                    "Diameter": 0.6
                },
                "Adding": {
                    "HtmlColor": "#808080",
                    "Radius": 0.3
                },
                "Editing": {
                    "HtmlColor": "#00BFFF",
                    "Radius": 0.3
                }
            }
        }
Yw.WpfUI.Test.Core/MainWindow.xaml
@@ -91,6 +91,32 @@
                    </ContextMenu>
                </Button.ContextMenu>
            </Button>
            <Button Content="等轴视角">
                <Button.ContextMenu>
                    <ContextMenu>
                        <MenuItem Click="SouthWestMenuItem_Click" Header="西南视角" />
                        <MenuItem Click="SouthEastMenuItem_Click" Header="东南视角" />
                        <MenuItem Click="NonthWestMenuItem_Click" Header="西北视角" />
                        <MenuItem Click="NonthEastMenuItem_Click" Header="东北视角" />
                    </ContextMenu>
                </Button.ContextMenu>
            </Button>
            <Button Content="默认相机">
                <Button.ContextMenu>
                    <ContextMenu>
                        <MenuItem Click="SaveDefaultCameraMenuItem_Click" Header="保存" />
                        <MenuItem Click="ApplyDefaultCameraMenuItem_Click" Header="应用" />
                    </ContextMenu>
                </Button.ContextMenu>
            </Button>
            <Button Content="编辑">
                <Button.ContextMenu>
                    <ContextMenu>
                        <MenuItem Click="StartEditMenuItem_Click" Header="开始" />
                        <MenuItem Click="EndEditCameraMenuItem_Click" Header="结束" />
                    </ContextMenu>
                </Button.ContextMenu>
            </Button>
            <Separator />
            <Button Click="BillboardText_Click" Content="文本" />
            <CheckBox
Yw.WpfUI.Test.Core/MainWindow.xaml.cs
@@ -69,18 +69,8 @@
                var pipe3d = new PipeL3d();
                pipe3d.Id = pipe.Id;
                pipe3d.Name = pipe.Name;
                pipe3d.StartPosition = new PointL3d()
                {
                    X = (float)pipe.StartNode.Position.X,
                    Y = (float)pipe.StartNode.Position.Y,
                    Z = (float)pipe.StartNode.GetElev()
                };
                pipe3d.EndPosition = new PointL3d()
                {
                    X = (float)pipe.EndNode.Position.X,
                    Y = (float)pipe.EndNode.Position.Y,
                    Z = (float)pipe.EndNode.GetElev()
                };
                pipe3d.StartNode = nw3d.Nodes.Find(x => x.Id == pipe.StartNodeId);
                pipe3d.EndNode = nw3d.Nodes.Find(x => x.Id == pipe.EndNodeId);
                nw3d.Append(pipe3d, out msg);
            }
@@ -89,18 +79,8 @@
                var pump3d = new PumpL3d();
                pump3d.Id = pump.Id;
                pump3d.Name = pump.Name;
                pump3d.StartPosition = new PointL3d()
                {
                    X = (float)pump.StartNode.Position.X,
                    Y = (float)pump.StartNode.Position.Y,
                    Z = (float)pump.StartNode.GetElev()
                };
                pump3d.EndPosition = new PointL3d()
                {
                    X = (float)pump.EndNode.Position.X,
                    Y = (float)pump.EndNode.Position.Y,
                    Z = (float)pump.EndNode.GetElev()
                };
                pump3d.StartNode = nw3d.Nodes.Find(x => x.Id == pump.StartNodeId);
                pump3d.EndNode = nw3d.Nodes.Find(x => x.Id == pump.EndNodeId);
                nw3d.Append(pump3d, out msg);
            }
@@ -109,18 +89,8 @@
                var valve3d = new ValveL3d();
                valve3d.Id = valve.Id;
                valve3d.Name = valve.Name;
                valve3d.StartPosition = new PointL3d()
                {
                    X = (float)valve.StartNode.Position.X,
                    Y = (float)valve.StartNode.Position.Y,
                    Z = (float)valve.StartNode.GetElev()
                };
                valve3d.EndPosition = new PointL3d()
                {
                    X = (float)valve.EndNode.Position.X,
                    Y = (float)valve.EndNode.Position.Y,
                    Z = (float)valve.EndNode.GetElev()
                };
                valve3d.StartNode = nw3d.Nodes.Find(x => x.Id == valve.StartNodeId);
                valve3d.EndNode = nw3d.Nodes.Find(x => x.Id == valve.EndNodeId);
                nw3d.Append(valve3d, out msg);
            }
@@ -228,6 +198,46 @@
        {
            this.editer.BackgroudVisible = false;
        }
        private void SouthWestMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.SetSouthWestView();
        }
        private void SouthEastMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.SetSouthEastView();
        }
        private void NonthWestMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.SetNorthWestView();
        }
        private void NonthEastMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.SetNorthEastView();
        }
        private void SaveDefaultCameraMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.SaveDefaultCamera();
        }
        private void ApplyDefaultCameraMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.ApplyDefaultCamera();
        }
        private void StartEditMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.StartEdit();
        }
        private void EndEditCameraMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.editer.EndEdit();
        }
    }