From a938afd94630e6606aa86199e25cfdd1d9278e97 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sat, 11 Oct 2025 17:36:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E9=BD=90=E9=85=8D=E7=BD=AE=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NavisworksTransportPlugin.csproj | 1 - TestConfigManager.cs | 56 - config.toml.example | 85 +- src/Commands/SetLogisticsAttributeCommand.cs | 4 +- src/Core/Animation/PathAnimationManager.cs | 4 +- .../Collision/ClashDetectiveIntegration.cs | 3 - src/Core/Config/ConfigManager.cs | 156 +-- src/Core/Config/SystemConfig.cs | 159 +-- src/Core/PathPointRenderPlugin.cs | 73 +- .../Properties/CategoryAttributeManager.cs | 37 +- src/PathPlanning/AutoPathFinder.cs | 15 +- src/PathPlanning/GridMapGenerator.cs | 18 +- src/PathPlanning/VerticalScanProcessor.cs | 1059 ----------------- .../ViewModels/AnimationControlViewModel.cs | 21 +- src/UI/WPF/ViewModels/PathEditingViewModel.cs | 77 +- src/UI/WPF/Views/LogisticsControlPanel.xaml | 2 +- .../WPF/Views/LogisticsControlPanel.xaml.cs | 25 +- src/UI/WPF/Views/PathEditingView.xaml.cs | 13 +- src/Utils/CoordinateConverter.cs | 38 +- src/Utils/FloorDetector.cs | 25 +- 20 files changed, 302 insertions(+), 1569 deletions(-) delete mode 100644 TestConfigManager.cs delete mode 100644 src/PathPlanning/VerticalScanProcessor.cs diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj index 2dbc9e5..d3807ca 100644 --- a/NavisworksTransportPlugin.csproj +++ b/NavisworksTransportPlugin.csproj @@ -180,7 +180,6 @@ - diff --git a/TestConfigManager.cs b/TestConfigManager.cs deleted file mode 100644 index ea7645a..0000000 --- a/TestConfigManager.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using NavisworksTransport.Core.Config; - -namespace NavisworksTransport.Tests -{ - /// - /// 配置管理器测试程序 - /// 使用方法:在 MainPlugin 的 Execute 方法中临时调用 TestConfigManager.RunTests() - /// - public class TestConfigManager - { - public static void RunTests() - { - Console.WriteLine("=== 开始测试配置管理器 ==="); - Console.WriteLine(); - - // 测试 1:加载或创建默认配置 - Console.WriteLine("测试 1: 加载或创建默认配置"); - var config = ConfigManager.Instance.Current; - Console.WriteLine($"配置文件路径: {ConfigManager.ConfigFilePath}"); - Console.WriteLine($"网格尺寸: {config.GridGeneration.CellSizeMeters} 米"); - Console.WriteLine($"车辆半径: {config.GridGeneration.VehicleRadiusMeters} 米"); - Console.WriteLine($"路径策略: {config.PathPlanning.DefaultPathStrategy}"); - Console.WriteLine(); - - // 测试 2:修改配置并保存 - Console.WriteLine("测试 2: 修改配置并保存"); - config.GridGeneration.CellSizeMeters = 0.6; - config.PathPlanning.DefaultPathStrategy = "SafetyFirst"; - config.Visualization.EnableGridVisualization = true; - ConfigManager.Instance.SaveConfig(config); - Console.WriteLine("配置已保存"); - Console.WriteLine(); - - // 测试 3:重新加载配置 - Console.WriteLine("测试 3: 重新加载配置"); - ConfigManager.Instance.Reload(); - var reloadedConfig = ConfigManager.Instance.Current; - Console.WriteLine($"重载后网格尺寸: {reloadedConfig.GridGeneration.CellSizeMeters} 米"); - Console.WriteLine($"重载后路径策略: {reloadedConfig.PathPlanning.DefaultPathStrategy}"); - Console.WriteLine($"重载后网格可视化: {reloadedConfig.Visualization.EnableGridVisualization}"); - Console.WriteLine(); - - // 测试 4:重置为默认配置 - Console.WriteLine("测试 4: 重置为默认配置"); - ConfigManager.Instance.ResetToDefault(); - var defaultConfig = ConfigManager.Instance.Current; - Console.WriteLine($"重置后网格尺寸: {defaultConfig.GridGeneration.CellSizeMeters} 米"); - Console.WriteLine($"重置后路径策略: {defaultConfig.PathPlanning.DefaultPathStrategy}"); - Console.WriteLine(); - - Console.WriteLine("=== 测试完成 ==="); - Console.WriteLine($"请检查配置文件: {ConfigManager.ConfigFilePath}"); - } - } -} diff --git a/config.toml.example b/config.toml.example index f5d7f77..3538812 100644 --- a/config.toml.example +++ b/config.toml.example @@ -1,76 +1,51 @@ # NavisworksTransport 系统配置文件 -# 单位说明:长度单位均为米(m),时间单位为秒(s) -# 自动生成时间: 2025-10-11 10:50:00 +# 单位说明:长度单位均为米(m) -[grid_generation] -# 网格单元大小(米)- 推荐值:0.3-0.8 +[path_editing] +# 网格单元大小(米)- 推荐值:0.3-1.0 cell_size_meters = 0.5 -# 车辆半径(米)- 用于障碍物膨胀计算 -vehicle_radius_meters = 0.3 - -# 安全间隙(米)- 额外的安全距离 -safety_margin_meters = 0.2 - -# 膨胀半径(网格单元数) -inflation_radius_cells = 2 - -# 扫描高度(米) -scan_height_meters = 0.1 - -[path_planning] # 最大高度差(米)- 楼梯、坡道可接受的高度阈值 max_height_diff_meters = 0.35 -# 车辆尺寸(米) +# 车辆长度(米) +vehicle_length_meters = 1.0 + +# 车辆宽度(米) +vehicle_width_meters = 1.0 + +# 车辆高度(米) vehicle_height_meters = 2.0 -vehicle_length_meters = 2.5 -vehicle_width_meters = 1.8 -# 默认路径策略:"Shortest" | "Straightest" | "SafetyFirst" -default_path_strategy = "Straightest" - -# 启用路径优化(减少转弯点) -enable_path_optimization = true +# 安全间隙(米) +safety_margin_meters = 0.05 [visualization] -# 网格点大小(像素) -grid_point_size = 5.0 - -# 路径线宽(像素) -path_line_width = 2.0 - -# 路径点大小(模型单位) -path_point_size = 50.0 - -# 启用网格可视化 -enable_grid_visualization = false - -# 启用多层网格可视化 -enable_multi_layer_visualization = true +# 地图边距比例(0-1之间) +margin_ratio = 0.1 [animation] -# 默认动画速度(米/秒) -default_speed_meters_per_second = 1.5 - # 动画帧率(帧/秒) frame_rate = 30 -# 步进间隔(毫秒) -step_interval_ms = 100 +# 动画持续时间(秒) +duration_seconds = 10.0 -# 启用平滑插值 -enable_smooth_interpolation = true +# 检测间隙(米) +detection_gap_meters = 0.05 -[collision] -# 碰撞检测步长(米) -detection_step_meters = 0.1 +[logistics] +# 可通行性(默认值:true) +traversable = true -# 碰撞容差(米) -collision_tolerance_meters = 0.01 +# 优先级(默认值:5,范围:1-5) +priority = 5 -# 启用实时碰撞检测 -enable_realtime_detection = true +# 高度限制(默认值:3.0米) +height_limit_meters = 3.0 -# 自动生成碰撞报告 -auto_generate_report = true +# 速度限制(默认值:0.8米/秒) +speed_limit_meters_per_second = 0.8 + +# 宽度限制(默认值:3.0米) +width_limit_meters = 3.0 diff --git a/src/Commands/SetLogisticsAttributeCommand.cs b/src/Commands/SetLogisticsAttributeCommand.cs index ec362ec..d0fbfe4 100644 --- a/src/Commands/SetLogisticsAttributeCommand.cs +++ b/src/Commands/SetLogisticsAttributeCommand.cs @@ -277,10 +277,10 @@ namespace NavisworksTransport.Commands item }; + // 使用配置的默认值添加物流属性 int affectedCount = CategoryAttributeManager.AddLogisticsAttributes( itemCollection, - _parameters.ElementType, - _parameters.IsTraversable ?? true); + _parameters.ElementType); bool success = affectedCount > 0; diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index 0fed9d2..ee5e535 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -95,7 +95,7 @@ namespace NavisworksTransport.Core.Animation // === 预计算动画系统 === private List _animationFrames; // 所有帧数据 private int _currentFrameIndex = 0; // 当前帧索引 - private HashSet _currentHighlightedItems; // 当前高亮的对象 + private List _allCollisionResults; // 所有碰撞结果(不去重) private bool _lastHighlightState = false; // 上一帧的高亮状态 @@ -158,7 +158,6 @@ namespace NavisworksTransport.Core.Animation { _pathPoints = new List(); _animationFrames = new List(); - _currentHighlightedItems = new HashSet(); _allCollisionResults = new List(); // 初始化动画模式 @@ -363,7 +362,6 @@ namespace NavisworksTransport.Core.Animation // 初始化帧列表 _animationFrames = new List(); - _currentHighlightedItems = new HashSet(); _allCollisionResults = new List(); _currentFrameIndex = 0; diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs index 690612d..a87664f 100644 --- a/src/Core/Collision/ClashDetectiveIntegration.cs +++ b/src/Core/Collision/ClashDetectiveIntegration.cs @@ -138,9 +138,6 @@ namespace NavisworksTransport } } - private DateTime _lastCollisionTestTime = DateTime.MinValue; - private readonly TimeSpan _minTestInterval = TimeSpan.FromMilliseconds(500); // 最小间隔500ms - /// /// 缓存碰撞结果(动画过程中使用)- 现在包含位置信息用于恢复测试 /// 使用精确的碰撞检测算法替代简化检测 diff --git a/src/Core/Config/ConfigManager.cs b/src/Core/Config/ConfigManager.cs index ab82acb..873c3f8 100644 --- a/src/Core/Config/ConfigManager.cs +++ b/src/Core/Config/ConfigManager.cs @@ -119,32 +119,18 @@ namespace NavisworksTransport.Core.Config var model = Toml.ToModel(tomlContent); var config = new SystemConfig(); - // 加载网格生成配置 - if (model.ContainsKey("grid_generation")) + // 加载路径编辑配置 + if (model.ContainsKey("path_editing")) { - var gridGen = model["grid_generation"] as TomlTable; - if (gridGen != null) + var pathEdit = model["path_editing"] as TomlTable; + if (pathEdit != null) { - config.GridGeneration.CellSizeMeters = GetDouble(gridGen, "cell_size_meters", 0.5); - config.GridGeneration.VehicleRadiusMeters = GetDouble(gridGen, "vehicle_radius_meters", 0.3); - config.GridGeneration.SafetyMarginMeters = GetDouble(gridGen, "safety_margin_meters", 0.2); - config.GridGeneration.InflationRadiusCells = GetInt(gridGen, "inflation_radius_cells", 2); - config.GridGeneration.ScanHeightMeters = GetDouble(gridGen, "scan_height_meters", 0.1); - } - } - - // 加载路径规划配置 - if (model.ContainsKey("path_planning")) - { - var pathPlan = model["path_planning"] as TomlTable; - if (pathPlan != null) - { - config.PathPlanning.MaxHeightDiffMeters = GetDouble(pathPlan, "max_height_diff_meters", 0.35); - config.PathPlanning.VehicleHeightMeters = GetDouble(pathPlan, "vehicle_height_meters", 2.0); - config.PathPlanning.VehicleLengthMeters = GetDouble(pathPlan, "vehicle_length_meters", 2.5); - config.PathPlanning.VehicleWidthMeters = GetDouble(pathPlan, "vehicle_width_meters", 1.8); - config.PathPlanning.DefaultPathStrategy = GetString(pathPlan, "default_path_strategy", "Straightest"); - config.PathPlanning.EnablePathOptimization = GetBool(pathPlan, "enable_path_optimization", true); + config.PathEditing.CellSizeMeters = GetDouble(pathEdit, "cell_size_meters", 0.5); + config.PathEditing.MaxHeightDiffMeters = GetDouble(pathEdit, "max_height_diff_meters", 0.35); + config.PathEditing.VehicleLengthMeters = GetDouble(pathEdit, "vehicle_length_meters", 1.0); + config.PathEditing.VehicleWidthMeters = GetDouble(pathEdit, "vehicle_width_meters", 1.0); + config.PathEditing.VehicleHeightMeters = GetDouble(pathEdit, "vehicle_height_meters", 2.0); + config.PathEditing.SafetyMarginMeters = GetDouble(pathEdit, "safety_margin_meters", 0.05); } } @@ -154,11 +140,7 @@ namespace NavisworksTransport.Core.Config var visual = model["visualization"] as TomlTable; if (visual != null) { - config.Visualization.GridPointSize = GetDouble(visual, "grid_point_size", 5.0); - config.Visualization.PathLineWidth = GetDouble(visual, "path_line_width", 2.0); - config.Visualization.PathPointSize = GetDouble(visual, "path_point_size", 50.0); - config.Visualization.EnableGridVisualization = GetBool(visual, "enable_grid_visualization", false); - config.Visualization.EnableMultiLayerVisualization = GetBool(visual, "enable_multi_layer_visualization", true); + config.Visualization.MarginRatio = GetDouble(visual, "margin_ratio", 0.1); } } @@ -168,23 +150,23 @@ namespace NavisworksTransport.Core.Config var anim = model["animation"] as TomlTable; if (anim != null) { - config.Animation.DefaultSpeedMetersPerSecond = GetDouble(anim, "default_speed_meters_per_second", 1.5); config.Animation.FrameRate = GetInt(anim, "frame_rate", 30); - config.Animation.StepIntervalMs = GetInt(anim, "step_interval_ms", 100); - config.Animation.EnableSmoothInterpolation = GetBool(anim, "enable_smooth_interpolation", true); + config.Animation.DurationSeconds = GetDouble(anim, "duration_seconds", 10.0); + config.Animation.DetectionGapMeters = GetDouble(anim, "detection_gap_meters", 0.05); } } - // 加载碰撞检测配置 - if (model.ContainsKey("collision")) + // 加载物流属性配置 + if (model.ContainsKey("logistics")) { - var collision = model["collision"] as TomlTable; - if (collision != null) + var logistics = model["logistics"] as TomlTable; + if (logistics != null) { - config.Collision.DetectionStepMeters = GetDouble(collision, "detection_step_meters", 0.1); - config.Collision.CollisionToleranceMeters = GetDouble(collision, "collision_tolerance_meters", 0.01); - config.Collision.EnableRealtimeDetection = GetBool(collision, "enable_realtime_detection", true); - config.Collision.AutoGenerateReport = GetBool(collision, "auto_generate_report", true); + config.Logistics.Traversable = GetBool(logistics, "traversable", true); + config.Logistics.Priority = GetInt(logistics, "priority", 5); + config.Logistics.HeightLimitMeters = GetDouble(logistics, "height_limit_meters", 3.0); + config.Logistics.SpeedLimitMetersPerSecond = GetDouble(logistics, "speed_limit_meters_per_second", 0.8); + config.Logistics.WidthLimitMeters = GetDouble(logistics, "width_limit_meters", 3.0); } } @@ -224,91 +206,65 @@ namespace NavisworksTransport.Core.Config // 添加文件头注释 sb.AppendLine("# NavisworksTransport 系统配置文件"); - sb.AppendLine("# 单位说明:长度单位均为米(m),时间单位为秒(s)"); + sb.AppendLine("# 单位说明:长度单位均为米(m)"); sb.AppendLine("# 自动生成时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); sb.AppendLine(); - // 网格生成配置 - sb.AppendLine("[grid_generation]"); - sb.AppendLine("# 网格单元大小(米)- 推荐值:0.3-0.8"); - sb.AppendLine($"cell_size_meters = {config.GridGeneration.CellSizeMeters}"); + // 路径编辑配置 + sb.AppendLine("[path_editing]"); + sb.AppendLine("# 网格单元大小(米)- 推荐值:0.3-1.0"); + sb.AppendLine($"cell_size_meters = {config.PathEditing.CellSizeMeters}"); sb.AppendLine(); - sb.AppendLine("# 车辆半径(米)- 用于障碍物膨胀计算"); - sb.AppendLine($"vehicle_radius_meters = {config.GridGeneration.VehicleRadiusMeters}"); - sb.AppendLine(); - sb.AppendLine("# 安全间隙(米)- 额外的安全距离"); - sb.AppendLine($"safety_margin_meters = {config.GridGeneration.SafetyMarginMeters}"); - sb.AppendLine(); - sb.AppendLine("# 膨胀半径(网格单元数)"); - sb.AppendLine($"inflation_radius_cells = {config.GridGeneration.InflationRadiusCells}"); - sb.AppendLine(); - sb.AppendLine("# 扫描高度(米)"); - sb.AppendLine($"scan_height_meters = {config.GridGeneration.ScanHeightMeters}"); - sb.AppendLine(); - - // 路径规划配置 - sb.AppendLine("[path_planning]"); sb.AppendLine("# 最大高度差(米)- 楼梯、坡道可接受的高度阈值"); - sb.AppendLine($"max_height_diff_meters = {config.PathPlanning.MaxHeightDiffMeters}"); + sb.AppendLine($"max_height_diff_meters = {config.PathEditing.MaxHeightDiffMeters}"); sb.AppendLine(); - sb.AppendLine("# 车辆尺寸(米)"); - sb.AppendLine($"vehicle_height_meters = {config.PathPlanning.VehicleHeightMeters}"); - sb.AppendLine($"vehicle_length_meters = {config.PathPlanning.VehicleLengthMeters}"); - sb.AppendLine($"vehicle_width_meters = {config.PathPlanning.VehicleWidthMeters}"); + sb.AppendLine("# 车辆长度(米)"); + sb.AppendLine($"vehicle_length_meters = {config.PathEditing.VehicleLengthMeters}"); sb.AppendLine(); - sb.AppendLine("# 默认路径策略:\"Shortest\" | \"Straightest\" | \"SafetyFirst\""); - sb.AppendLine($"default_path_strategy = \"{config.PathPlanning.DefaultPathStrategy}\""); + sb.AppendLine("# 车辆宽度(米)"); + sb.AppendLine($"vehicle_width_meters = {config.PathEditing.VehicleWidthMeters}"); sb.AppendLine(); - sb.AppendLine("# 启用路径优化(减少转弯点)"); - sb.AppendLine($"enable_path_optimization = {config.PathPlanning.EnablePathOptimization.ToString().ToLower()}"); + sb.AppendLine("# 车辆高度(米)"); + sb.AppendLine($"vehicle_height_meters = {config.PathEditing.VehicleHeightMeters}"); + sb.AppendLine(); + sb.AppendLine("# 安全间隙(米)"); + sb.AppendLine($"safety_margin_meters = {config.PathEditing.SafetyMarginMeters}"); sb.AppendLine(); // 可视化配置 sb.AppendLine("[visualization]"); - sb.AppendLine("# 网格点大小(像素)"); - sb.AppendLine($"grid_point_size = {config.Visualization.GridPointSize}"); - sb.AppendLine(); - sb.AppendLine("# 路径线宽(像素)"); - sb.AppendLine($"path_line_width = {config.Visualization.PathLineWidth}"); - sb.AppendLine(); - sb.AppendLine("# 路径点大小(模型单位)"); - sb.AppendLine($"path_point_size = {config.Visualization.PathPointSize}"); - sb.AppendLine(); - sb.AppendLine("# 启用网格可视化"); - sb.AppendLine($"enable_grid_visualization = {config.Visualization.EnableGridVisualization.ToString().ToLower()}"); - sb.AppendLine(); - sb.AppendLine("# 启用多层网格可视化"); - sb.AppendLine($"enable_multi_layer_visualization = {config.Visualization.EnableMultiLayerVisualization.ToString().ToLower()}"); + sb.AppendLine("# 地图边距比例(0-1之间)"); + sb.AppendLine($"margin_ratio = {config.Visualization.MarginRatio}"); sb.AppendLine(); // 动画配置 sb.AppendLine("[animation]"); - sb.AppendLine("# 默认动画速度(米/秒)"); - sb.AppendLine($"default_speed_meters_per_second = {config.Animation.DefaultSpeedMetersPerSecond}"); - sb.AppendLine(); sb.AppendLine("# 动画帧率(帧/秒)"); sb.AppendLine($"frame_rate = {config.Animation.FrameRate}"); sb.AppendLine(); - sb.AppendLine("# 步进间隔(毫秒)"); - sb.AppendLine($"step_interval_ms = {config.Animation.StepIntervalMs}"); + sb.AppendLine("# 动画持续时间(秒)"); + sb.AppendLine($"duration_seconds = {config.Animation.DurationSeconds}"); sb.AppendLine(); - sb.AppendLine("# 启用平滑插值"); - sb.AppendLine($"enable_smooth_interpolation = {config.Animation.EnableSmoothInterpolation.ToString().ToLower()}"); + sb.AppendLine("# 检测间隙(米)"); + sb.AppendLine($"detection_gap_meters = {config.Animation.DetectionGapMeters}"); sb.AppendLine(); - // 碰撞检测配置 - sb.AppendLine("[collision]"); - sb.AppendLine("# 碰撞检测步长(米)"); - sb.AppendLine($"detection_step_meters = {config.Collision.DetectionStepMeters}"); + // 物流属性配置 + sb.AppendLine("[logistics]"); + sb.AppendLine("# 可通行性(默认值:true)"); + sb.AppendLine($"traversable = {config.Logistics.Traversable.ToString().ToLower()}"); sb.AppendLine(); - sb.AppendLine("# 碰撞容差(米)"); - sb.AppendLine($"collision_tolerance_meters = {config.Collision.CollisionToleranceMeters}"); + sb.AppendLine("# 优先级(默认值:5,范围:1-5)"); + sb.AppendLine($"priority = {config.Logistics.Priority}"); sb.AppendLine(); - sb.AppendLine("# 启用实时碰撞检测"); - sb.AppendLine($"enable_realtime_detection = {config.Collision.EnableRealtimeDetection.ToString().ToLower()}"); + sb.AppendLine("# 高度限制(默认值:3.0米)"); + sb.AppendLine($"height_limit_meters = {config.Logistics.HeightLimitMeters}"); sb.AppendLine(); - sb.AppendLine("# 自动生成碰撞报告"); - sb.AppendLine($"auto_generate_report = {config.Collision.AutoGenerateReport.ToString().ToLower()}"); + sb.AppendLine("# 速度限制(默认值:0.8米/秒)"); + sb.AppendLine($"speed_limit_meters_per_second = {config.Logistics.SpeedLimitMetersPerSecond}"); + sb.AppendLine(); + sb.AppendLine("# 宽度限制(默认值:3.0米)"); + sb.AppendLine($"width_limit_meters = {config.Logistics.WidthLimitMeters}"); return sb.ToString(); } diff --git a/src/Core/Config/SystemConfig.cs b/src/Core/Config/SystemConfig.cs index 62f6d78..19721e5 100644 --- a/src/Core/Config/SystemConfig.cs +++ b/src/Core/Config/SystemConfig.cs @@ -17,6 +17,11 @@ namespace NavisworksTransport.Core.Config /// public PathPlanningConfig PathPlanning { get; set; } = new PathPlanningConfig(); + /// + /// 路径编辑配置 + /// + public PathEditingConfig PathEditing { get; set; } = new PathEditingConfig(); + /// /// 可视化配置 /// @@ -28,9 +33,9 @@ namespace NavisworksTransport.Core.Config public AnimationConfig Animation { get; set; } = new AnimationConfig(); /// - /// 碰撞检测配置 + /// 物流属性配置 /// - public CollisionConfig Collision { get; set; } = new CollisionConfig(); + public LogisticsConfig Logistics { get; set; } = new LogisticsConfig(); } /// @@ -38,35 +43,6 @@ namespace NavisworksTransport.Core.Config /// public class GridGenerationConfig { - /// - /// 网格单元大小(米) - /// 推荐值:0.3-0.8 - /// - public double CellSizeMeters { get; set; } = 0.5; - - /// - /// 车辆半径(米) - /// 用于障碍物膨胀计算 - /// - public double VehicleRadiusMeters { get; set; } = 0.3; - - /// - /// 安全间隙(米) - /// 额外的安全距离 - /// - public double SafetyMarginMeters { get; set; } = 0.2; - - /// - /// 膨胀半径(网格单元数) - /// 障碍物膨胀的单元格数量 - /// - public int InflationRadiusCells { get; set; } = 2; - - /// - /// 扫描高度(米) - /// 障碍物扫描的垂直步进高度 - /// - public double ScanHeightMeters { get; set; } = 0.1; } /// @@ -74,43 +50,44 @@ namespace NavisworksTransport.Core.Config /// public class PathPlanningConfig { + } + + /// + /// 路径编辑配置 + /// + public class PathEditingConfig + { + /// + /// 网格单元大小(米) + /// 推荐值:0.3-1.0 + /// + public double CellSizeMeters { get; set; } = 0.5; + /// /// 最大高度差(米) /// 楼梯、坡道可接受的高度阈值 /// public double MaxHeightDiffMeters { get; set; } = 0.35; + /// + /// 车辆长度(米) + /// + public double VehicleLengthMeters { get; set; } = 1.0; + + /// + /// 车辆宽度(米) + /// + public double VehicleWidthMeters { get; set; } = 1.0; + /// /// 车辆高度(米) - /// 用于通行性判断 /// public double VehicleHeightMeters { get; set; } = 2.0; /// - /// 车辆长度(米) - /// 用于转弯半径计算 + /// 安全间隙(米) /// - public double VehicleLengthMeters { get; set; } = 2.5; - - /// - /// 车辆宽度(米) - /// 用于通道宽度判断 - /// - public double VehicleWidthMeters { get; set; } = 1.8; - - /// - /// 默认路径策略 - /// "Shortest" - 最短路径优先 - /// "Straightest" - 直线优先 - /// "SafetyFirst" - 安全优先 - /// - public string DefaultPathStrategy { get; set; } = "Straightest"; - - /// - /// 路径优化启用 - /// 是否启用路径点优化(减少转弯点) - /// - public bool EnablePathOptimization { get; set; } = true; + public double SafetyMarginMeters { get; set; } = 0.05; } /// @@ -119,31 +96,10 @@ namespace NavisworksTransport.Core.Config public class VisualizationConfig { /// - /// 网格点大小(像素) + /// 地图边距比例 + /// 地图显示时的边距占比(0-1之间) /// - public double GridPointSize { get; set; } = 5.0; - - /// - /// 路径线宽(像素) - /// - public double PathLineWidth { get; set; } = 2.0; - - /// - /// 路径点大小(模型单位) - /// - public double PathPointSize { get; set; } = 50.0; - - /// - /// 启用网格可视化 - /// 是否默认显示网格地图 - /// - public bool EnableGridVisualization { get; set; } = false; - - /// - /// 启用多层网格可视化 - /// 是否显示多个高度层的网格 - /// - public bool EnableMultiLayerVisualization { get; set; } = true; + public double MarginRatio { get; set; } = 0.1; } /// @@ -151,57 +107,50 @@ namespace NavisworksTransport.Core.Config /// public class AnimationConfig { - /// - /// 动画速度(米/秒) - /// 默认车辆移动速度 - /// - public double DefaultSpeedMetersPerSecond { get; set; } = 1.5; - /// /// 动画帧率(帧/秒) /// public int FrameRate { get; set; } = 30; /// - /// 步进间隔(毫秒) - /// 步进模式下每步的时间间隔 + /// 动画持续时间(秒) /// - public int StepIntervalMs { get; set; } = 100; + public double DurationSeconds { get; set; } = 10.0; /// - /// 启用平滑插值 - /// 是否在路径点之间使用平滑插值 + /// 检测间隙(米) /// - public bool EnableSmoothInterpolation { get; set; } = true; + public double DetectionGapMeters { get; set; } = 0.05; } /// - /// 碰撞检测配置 + /// 物流属性配置 /// - public class CollisionConfig + public class LogisticsConfig { /// - /// 碰撞检测步长(米) - /// 动画过程中每次检测的移动距离 + /// 可通行性(默认值:true) /// - public double DetectionStepMeters { get; set; } = 0.1; + public bool Traversable { get; set; } = true; /// - /// 碰撞容差(米) - /// 判定为碰撞的最小距离 + /// 优先级(默认值:5,范围:1-5) /// - public double CollisionToleranceMeters { get; set; } = 0.01; + public int Priority { get; set; } = 5; /// - /// 启用实时碰撞检测 - /// 动画播放时是否实时检测碰撞 + /// 高度限制(默认值:3.0米) /// - public bool EnableRealtimeDetection { get; set; } = true; + public double HeightLimitMeters { get; set; } = 3.0; /// - /// 自动生成碰撞报告 - /// 动画结束后是否自动生成报告 + /// 速度限制(默认值:0.8米/秒) /// - public bool AutoGenerateReport { get; set; } = true; + public double SpeedLimitMetersPerSecond { get; set; } = 0.8; + + /// + /// 宽度限制(默认值:3.0米) + /// + public double WidthLimitMeters { get; set; } = 3.0; } } diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index a802623..0ff964b 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Api.Plugins; -using NavisworksTransport.Core; +using NavisworksTransport.Utils; namespace NavisworksTransport { @@ -776,7 +776,7 @@ namespace NavisworksTransport private VehicleSpaceMarker CreateVehicleSpaceMarker(PathPoint fromPoint, PathPoint toPoint) { // 获取单位转换系数 - double metersToModelUnits = GetMetersToModelUnitsConversionFactor(); + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); // 计算车辆通行空间宽度(米) double vehicleInflationWidthInMeters = Math.Max(_vehicleLength, _vehicleWidth) + 2 * _safetyMargin; @@ -848,7 +848,7 @@ namespace NavisworksTransport { Center = point.Position, Normal = new Vector3D(0, 0, 1), - Radius = isGridVisualization ? GetRadiusForGridVisualization(_currentGridSizeInMeters) : GetRadiusForPointType(point.Type), + Radius = isGridVisualization ? GetRadiusForGridVisualization() : GetRadiusForPointType(point.Type), Color = isGridVisualization ? gridStyle.Color : GetColorForPointType(point.Type), Alpha = isGridVisualization ? gridStyle.Alpha : 1.0, Filled = true, @@ -862,13 +862,13 @@ namespace NavisworksTransport /// 获取网格可视化小球的半径(比正常路径点小) /// /// 网格可视化半径 - private double GetRadiusForGridVisualization(double gridSizeInMeters = 0.5) + private double GetRadiusForGridVisualization() { // 使用标准尺寸的1/5作为网格点尺寸 double standardRadiusInMeters = GetStandardRadiusInMeters(); double radiusInMeters = standardRadiusInMeters / 5.0; - - double metersToModelUnits = GetMetersToModelUnitsConversionFactor(); + + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); return radiusInMeters * metersToModelUnits; } @@ -972,11 +972,11 @@ namespace NavisworksTransport { // 获取标准尺寸(起点尺寸) double standardRadiusInMeters = GetStandardRadiusInMeters(); - + // 连线半径为标准尺寸的40% double lineRadiusInMeters = standardRadiusInMeters * 0.4; - - return lineRadiusInMeters * GetMetersToModelUnitsConversionFactor(); + + return lineRadiusInMeters * UnitsConverter.GetMetersToUnitsConversionFactor(); } /// @@ -1411,7 +1411,7 @@ namespace NavisworksTransport { // 获取标准尺寸(起点尺寸) double standardRadiusInMeters = GetStandardRadiusInMeters(); - + // 根据点类型应用比例系数 double baseRadiusInMeters; if (pointType == PathPointType.WayPoint) @@ -1424,59 +1424,14 @@ namespace NavisworksTransport // 起点/终点使用标准尺寸(100%) baseRadiusInMeters = standardRadiusInMeters * 1.0; } - + // 获取真实文档单位转换系数 - double metersToModelUnits = GetMetersToModelUnitsConversionFactor(); - + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); + // 转换为模型单位 double radiusInModelUnits = baseRadiusInMeters * metersToModelUnits; - - return radiusInModelUnits; - } - /// - /// 获取米转换为文档单位的转换系数 - /// 使用Navisworks API直接获取真实文档单位,而不是猜测 - /// - private double GetMetersToModelUnitsConversionFactor() - { - try - { - var units = Application.ActiveDocument.Units; - - switch (units) - { - case Units.Millimeters: - return 1000.0; // 1米 = 1000毫米 - case Units.Centimeters: - return 100.0; // 1米 = 100厘米 - case Units.Meters: - return 1.0; // 1米 = 1米 - case Units.Inches: - return 39.37; // 1米 = 39.37英寸 - case Units.Feet: - return 3.281; // 1米 = 3.281英尺 - case Units.Kilometers: - return 0.001; // 1米 = 0.001公里 - case Units.Micrometers: - return 1000000.0; // 1米 = 1000000微米 - case Units.Microinches: - return 39370078.74; // 1米 = 39370078.74微英寸 - case Units.Mils: - return 39370.08; // 1米 = 39370.08密尔 - case Units.Yards: - return 1.094; // 1米 = 1.094码 - case Units.Miles: - return 0.000621; // 1米 = 0.000621英里 - default: - return 1.0; - } - } - catch (Exception ex) - { - LogManager.Error($"[单位检测] API调用失败: {ex.Message}"); - return 1.0; - } + return radiusInModelUnits; } /// diff --git a/src/Core/Properties/CategoryAttributeManager.cs b/src/Core/Properties/CategoryAttributeManager.cs index 04e3577..c85e276 100644 --- a/src/Core/Properties/CategoryAttributeManager.cs +++ b/src/Core/Properties/CategoryAttributeManager.cs @@ -95,7 +95,28 @@ namespace NavisworksTransport } /// - /// 为选定的模型项添加物流属性 + /// 为选定的模型项添加物流属性(使用配置默认值) + /// + /// 要处理的模型项集合 + /// 物流元素类型 + /// 成功处理的项目数量 + public static int AddLogisticsAttributes( + ModelItemCollection items, + LogisticsElementType elementType) + { + var logistics = NavisworksTransport.Core.Config.ConfigManager.Instance.Current.Logistics; + return AddLogisticsAttributes( + items, + elementType, + logistics.Traversable, + logistics.Priority, + logistics.HeightLimitMeters, + logistics.SpeedLimitMetersPerSecond, + logistics.WidthLimitMeters); + } + + /// + /// 为选定的模型项添加物流属性(指定所有参数) /// /// 要处理的模型项集合 /// 物流元素类型 @@ -108,11 +129,11 @@ namespace NavisworksTransport public static int AddLogisticsAttributes( ModelItemCollection items, LogisticsElementType elementType, - bool isTraversable = true, - int priority = 5, - double heightLimit = 3.0, - double speedLimit = 10.0, - double widthLimit = 3.0) + bool isTraversable, + int priority, + double heightLimit, + double speedLimit, + double widthLimit) { if (items == null || items.Count == 0) { @@ -142,8 +163,8 @@ namespace NavisworksTransport }; int successCount = NavisworksComPropertyManager.SetUserDefinedProperties( - items, - LogisticsCategories.LOGISTICS, + items, + LogisticsCategories.LOGISTICS, LogisticsCategories.CATEGORY_INTERNAL_NAME, properties, propertyInternalNames); diff --git a/src/PathPlanning/AutoPathFinder.cs b/src/PathPlanning/AutoPathFinder.cs index 8404cb5..1a60271 100644 --- a/src/PathPlanning/AutoPathFinder.cs +++ b/src/PathPlanning/AutoPathFinder.cs @@ -6,6 +6,7 @@ using Roy_T.AStar.Grids; using Roy_T.AStar.Primitives; using Roy_T.AStar.Paths; using NavisworksTransport.Utils; +using NavisworksTransport.Core.Config; namespace NavisworksTransport.PathPlanning { @@ -48,11 +49,14 @@ namespace NavisworksTransport.PathPlanning public class AutoPathFinder { /// - /// 水平边连接允许的最大高度差(米) - /// 足以支持台阶连接(约0.15米)和地面到楼梯底部的连接(约0.3米) + /// 获取水平边连接允许的最大高度差(米) + /// 从配置文件读取,足以支持台阶连接(约0.15米)和地面到楼梯底部的连接(约0.3米) /// 使用时需转换为模型单位 /// - private const double MAX_HEIGHT_DIFF_METERS = 0.35; + private double GetMaxHeightDiffMeters() + { + return ConfigManager.Instance.Current.PathEditing.MaxHeightDiffMeters; + } /// /// 3D节点信息(存储每个A*节点对应的3D坐标和类型) @@ -143,8 +147,9 @@ namespace NavisworksTransport.PathPlanning float maxSpeed = (float)baseSpeed; // 将米转换为模型单位 - double maxHeightDiffInModelUnits = MAX_HEIGHT_DIFF_METERS * UnitsConverter.GetMetersToUnitsConversionFactor(); - LogManager.Info($"[3D图构建] 高度差阈值: {MAX_HEIGHT_DIFF_METERS}米 = {maxHeightDiffInModelUnits:F2}模型单位"); + double maxHeightDiffMeters = GetMaxHeightDiffMeters(); + double maxHeightDiffInModelUnits = maxHeightDiffMeters * UnitsConverter.GetMetersToUnitsConversionFactor(); + LogManager.Info($"[3D图构建] 高度差阈值: {maxHeightDiffMeters}米 = {maxHeightDiffInModelUnits:F2}模型单位"); // === 阶段1:创建所有3D节点 === int totalNodesCreated = 0; diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index bd1cb54..5310f13 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; using NavisworksTransport.Utils; +using NavisworksTransport.Core.Config; namespace NavisworksTransport.PathPlanning { @@ -30,11 +31,14 @@ namespace NavisworksTransport.PathPlanning public class GridMapGenerator { /// - /// 边界检测的最大高度差(米) - /// 与AutoPathFinder中的MAX_HEIGHT_DIFF_METERS保持一致 + /// 获取边界检测的最大高度差(米) + /// 从配置文件读取,与AutoPathFinder保持一致 /// 用于判断层间是否构成边界(超过此阈值则标记为边界) /// - private const double MAX_HEIGHT_DIFF_METERS = 0.35; + private double GetMaxHeightDiffMeters() + { + return ConfigManager.Instance.Current.PathEditing.MaxHeightDiffMeters; + } private readonly CategoryAttributeManager _categoryManager; private readonly ChannelBasedGridBuilder _channelBuilder; @@ -153,8 +157,9 @@ namespace NavisworksTransport.PathPlanning // 2.7. 标记边界层(用于膨胀计算) LogManager.Info("【生成网格地图】步骤2.7: 标记边界层"); - double maxHeightDiffInModelUnits = MAX_HEIGHT_DIFF_METERS * metersToModelUnitsConversionFactor; - LogManager.Info($"【生成网格地图】边界高度差阈值: {MAX_HEIGHT_DIFF_METERS}米 = {maxHeightDiffInModelUnits:F2}模型单位"); + double maxHeightDiffMeters = GetMaxHeightDiffMeters(); + double maxHeightDiffInModelUnits = maxHeightDiffMeters * metersToModelUnitsConversionFactor; + LogManager.Info($"【生成网格地图】边界高度差阈值: {maxHeightDiffMeters}米 = {maxHeightDiffInModelUnits:F2}模型单位"); MarkBoundaryLayers(channelCoverage.GridMap, maxHeightDiffInModelUnits); LogManager.Info($"【阶段2.7完成】边界层标记后网格统计: {channelCoverage.GridMap.GetStatistics()}"); @@ -849,7 +854,8 @@ namespace NavisworksTransport.PathPlanning // 计算高度差阈值(用于楼梯口保护) double metersToModelUnitsConversionFactor = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units); - double maxHeightDiffInModelUnits = MAX_HEIGHT_DIFF_METERS * metersToModelUnitsConversionFactor; + double maxHeightDiffMeters = GetMaxHeightDiffMeters(); + double maxHeightDiffInModelUnits = maxHeightDiffMeters * metersToModelUnitsConversionFactor; // 统计每个层索引的处理情况 var layerStats = new Dictionary(); diff --git a/src/PathPlanning/VerticalScanProcessor.cs b/src/PathPlanning/VerticalScanProcessor.cs deleted file mode 100644 index aa1549b..0000000 --- a/src/PathPlanning/VerticalScanProcessor.cs +++ /dev/null @@ -1,1059 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Concurrent; -using Autodesk.Navisworks.Api; - -namespace NavisworksTransport.PathPlanning -{ - /// - /// 垂直扫描处理器 - /// 实现多级筛选优化的障碍物检测和2.5D高度区间计算 - /// 预期实现10,000-40,000倍性能提升 - /// - public class VerticalScanProcessor - { - #region 私有字段 - - /// - /// 空间哈希表,用于快速查找邻域内的模型项 - /// - private readonly Dictionary> _spatialHashMap; - - /// - /// 空间哈希的网格大小(米) - /// - private readonly double _spatialHashSize; - - /// - /// 并行处理的任务数量 - /// - private readonly int _parallelDegree; - - /// - /// 候选项缓存,基于空间哈希桶坐标(线程安全) - /// - private readonly ConcurrentDictionary> _candidateCache; - - /// - /// 缓存大小限制 - /// - private int _maxCacheSize; - - /// - /// 缓存命中统计 - /// - private int _cacheHits; - - /// - /// 缓存未命中统计 - /// - private int _cacheMisses; - - /// - /// 高度筛选的容差值(米) - /// - private const double HEIGHT_TOLERANCE = 0.1; - - /// - /// 默认人行高度(米)- 用于未检测到通道时的默认高度 - /// - private const double DEFAULT_WALKING_HEIGHT = 2.5; - - /// - /// 最小通行高度(米) - /// - private const double MIN_PASSABLE_HEIGHT = 1.8; - - #endregion - - #region 构造函数 - - /// - /// 构造函数 - /// - /// 空间哈希网格大小(米)或实际网格大小。 - /// 并行度,默认为CPU核心数的一半,避免过度并行导致崩溃 - public VerticalScanProcessor(double spatialHashSize, int parallelDegree = 0) - { - _spatialHashSize = spatialHashSize; - - // 限制并行度,避免过度并行导致崩溃,最大为CPU核心数的一半 - int maxParallelism = Math.Max(1, Environment.ProcessorCount / 2); - _parallelDegree = parallelDegree > 0 ? Math.Min(parallelDegree, maxParallelism) : maxParallelism; - _spatialHashMap = new Dictionary>(); - - // 初始化线程安全的缓存 - _candidateCache = new ConcurrentDictionary>(); - _maxCacheSize = 1000; // 默认值,将在BuildSpatialHashIndex中动态调整 - _cacheHits = 0; - _cacheMisses = 0; - - // 确定查询策略 - string queryStrategy; - int bucketCount; - // 只用单桶模式测试 - queryStrategy = "单桶模式"; - bucketCount = 1; - // if (_spatialHashSize < 1.5) - // { - // queryStrategy = "单桶模式"; - // bucketCount = 1; - // } - // else if (_spatialHashSize < 3.0) - // { - // queryStrategy = "十字形模式"; - // bucketCount = 5; - // } - // else - // { - // queryStrategy = "3x3模式"; - // bucketCount = 9; - // } - - LogManager.Info($"[垂直扫描处理器] 初始化完成,空间哈希大小: {_spatialHashSize}m, 查询策略: {queryStrategy}({bucketCount}桶), 并行度: {_parallelDegree}"); - } - - #endregion - - #region 公共方法 - - /// - /// 构建空间哈希索引 - /// 第一级筛选:邻域筛选 - /// - /// 所有模型项 - /// 扫描边界 - /// 已确定的通道元素列表(将被排除) - public void BuildSpatialHashIndex(IEnumerable modelItems, BoundingBox3D bounds, IEnumerable channelItems = null) - { - LogManager.Info("【垂直扫描处理器】 开始构建空间哈希索引"); - var startTime = DateTime.Now; - - _spatialHashMap.Clear(); - - // 创建通道元素的HashSet以便快速查找 - var channelItemsSet = new HashSet(channelItems ?? new List()); - - // 统计变量 - var totalItems = modelItems?.Count() ?? 0; - - LogManager.Info($"【垂直扫描处理器】 输入统计 - 总模型项: {totalItems}, 将排除通道元素: {channelItemsSet.Count}"); - LogManager.Info($"【垂直扫描处理器】 扫描边界: [{bounds.Min.X:F1},{bounds.Min.Y:F1},{bounds.Min.Z:F1}] - [{bounds.Max.X:F1},{bounds.Max.Y:F1},{bounds.Max.Z:F1}]"); - - var itemsWithBounds = new ConcurrentBag<(ModelItem item, BoundingBox3D bbox)>(); - - // 统计计数器(线程安全) - var geometryCounter = 0; - var channelExcludedCounter = 0; - var noBoundsCounter = 0; - var outOfBoundsCounter = 0; - - try - { - // 并行计算所有模型项的边界框,使用ConcurrentBag避免锁竞争 - Parallel.ForEach(modelItems, new ParallelOptions { MaxDegreeOfParallelism = _parallelDegree }, item => - { - try - { - // 🔥 关键修复:检查是否需要排除通道元素(包括容器节点和子节点) - if (channelItemsSet.Contains(item) || IsChildOfChannelItems(item, channelItemsSet)) - { - Interlocked.Increment(ref channelExcludedCounter); - return; // 跳过通道元素及其子节点 - } - - // 然后检查是否有几何体,只对有几何体的元素进行空间哈希处理 - if (item?.HasGeometry == true) - { - Interlocked.Increment(ref geometryCounter); - - var bbox = item.BoundingBox(); - if (bbox == null) - { - Interlocked.Increment(ref noBoundsCounter); - return; - } - - if (IsWithinBounds(bbox, bounds)) - { - itemsWithBounds.Add((item, bbox)); - } - else - { - Interlocked.Increment(ref outOfBoundsCounter); - } - } - } - catch (Exception ex) - { - LogManager.Debug($"【垂直扫描处理器】 获取边界框失败: {item?.DisplayName ?? "NULL"}, {ex.Message}"); - } - }); - } - catch (Exception ex) - { - LogManager.Error($"【垂直扫描处理器】 并行计算边界框时发生严重错误: {ex.Message}"); - return; // 提前退出,避免进一步崩溃 - } - - // 输出详细统计信息 - LogManager.Info($"【垂直扫描处理器】 元素筛选统计:"); - LogManager.Info($" - 有几何体的元素: {geometryCounter}"); - LogManager.Info($" - 被排除的通道元素: {channelExcludedCounter}"); - LogManager.Info($" - 无边界框的元素: {noBoundsCounter}"); - LogManager.Info($" - 超出扫描边界的元素: {outOfBoundsCounter}"); - LogManager.Info($" - 符合条件并添加到空间哈希的元素: {itemsWithBounds.Count}"); - - // 记录待索引元素数量 - LogManager.Info($"【垂直扫描处理器】 待索引元素数量: {itemsWithBounds.Count()}"); - - // 构建空间哈希 - var totalHashEntries = 0; - var hashKeyDistribution = new Dictionary(); - - foreach (var (item, bbox) in itemsWithBounds) - { - var hashKeys = GetSpatialHashKeys(bbox); - foreach (var key in hashKeys) - { - if (!_spatialHashMap.ContainsKey(key)) - { - _spatialHashMap[key] = new List(); - } - _spatialHashMap[key].Add(item); - totalHashEntries++; - - // 统计哈希键分布 - 修复 .NET Framework 4.8 兼容性 - if (hashKeyDistribution.ContainsKey(key)) - { - hashKeyDistribution[key]++; - } - else - { - hashKeyDistribution[key] = 1; - } - } - } - - // 统计哈希桶信息(简化版) - var bucketSizes = _spatialHashMap.Values.Select(list => list.Count).ToList(); - LogManager.Info($"【垂直扫描处理器】 空间哈希构建完成 - 哈希桶数量: {_spatialHashMap.Count}, 平均桶大小: {bucketSizes.DefaultIfEmpty(0).Average():F1}"); - - // 空间哈希索引构建完成 - - // 🔥 关键新增:动态计算缓存容量 - double rangeX = bounds.Max.X - bounds.Min.X; - double rangeY = bounds.Max.Y - bounds.Min.Y; - int bucketsX = (int)(rangeX / _spatialHashSize + 1); - int bucketsY = (int)(rangeY / _spatialHashSize + 1); - int estimatedBuckets = bucketsX * bucketsY; - - if (estimatedBuckets > 50000) // 大量哈希桶,需要更大缓存 - { - _maxCacheSize = Math.Min(200000, estimatedBuckets); // 直接匹配桶数量 - } - else if (estimatedBuckets > 20000) - { - _maxCacheSize = Math.Min(100000, estimatedBuckets); - } - else - { - _maxCacheSize = Math.Min(20000, estimatedBuckets * 2); - } - LogManager.Info($"【垂直扫描处理器】 空间哈希大小: {_spatialHashSize}m"); - LogManager.Info($"【垂直扫描处理器】 范围: X={rangeX:F1}m, Y={rangeY:F1}m"); - LogManager.Info($"【垂直扫描处理器】 桶数: X={bucketsX}, Y={bucketsY}, 总计={estimatedBuckets}"); - LogManager.Info($"【垂直扫描处理器】 动态设置缓存容量: {_maxCacheSize} (预计哈希桶: {estimatedBuckets}, 实际哈希桶: {_spatialHashMap.Count})"); - - var elapsed = (DateTime.Now - startTime).TotalMilliseconds; - LogManager.Info($"【垂直扫描处理器】 空间哈希索引构建完成,耗时: {elapsed:F1}ms"); - LogManager.Info($"【垂直扫描处理器】 最终统计 - 参与元素: {itemsWithBounds.Count}, 哈希桶: {_spatialHashMap.Count}, 总哈希条目: {totalHashEntries}"); - } - - - /// - /// 并行扫描网格点的2.5D高度区间 - /// - /// 网格点列表 - /// 扫描高度(从地面向上的距离) - /// 车辆高度(用于通行检查) - /// 每个网格点的高度区间 - public Dictionary ParallelScanHeightIntervals( - IEnumerable gridPoints, - double scanHeight = 20.0, - double vehicleHeight = 3.0) - { - LogManager.Info($"[垂直扫描处理器] 开始并行扫描高度区间,扫描高度: {scanHeight}m, 车辆高度: {vehicleHeight}m"); - var startTime = DateTime.Now; - - var results = new ConcurrentDictionary(); - var pointsList = gridPoints?.ToList() ?? new List(); - - LogManager.Info($"[垂直扫描处理器] 准备处理{pointsList.Count}个点"); - - try - { - // 并行处理每个网格点,添加更严格的异常处理 - Parallel.ForEach(pointsList, new ParallelOptions - { - MaxDegreeOfParallelism = _parallelDegree, - CancellationToken = System.Threading.CancellationToken.None - }, gridPoint => - { - try - { - if (gridPoint != null) - { - var interval = ScanVerticalLine(gridPoint, scanHeight, vehicleHeight); - results[gridPoint] = interval; - } - } - catch (OutOfMemoryException) - { - LogManager.Error($"[垂直扫描处理器] 内存不足,跳过点 {gridPoint}"); - if (gridPoint != null) - { - results[gridPoint] = new HeightInterval(); - } - throw; // 内存不足需要重新抛出 - } - catch (Exception ex) - { - LogManager.Error($"[垂直扫描处理器] 扫描点 {gridPoint} 失败: {ex.Message}"); - if (gridPoint != null) - { - results[gridPoint] = new HeightInterval(); - } - } - }); - } - catch (Exception ex) - { - LogManager.Error($"[垂直扫描处理器] 并行扫描过程中发生严重错误: {ex.Message}"); - // 如果并行处理失败,尝试串行处理作为备选方案 - LogManager.Info("[垂直扫描处理器] 尝试串行处理作为备选方案"); - - foreach (var gridPoint in pointsList) - { - try - { - if (gridPoint != null && !results.ContainsKey(gridPoint)) - { - var interval = ScanVerticalLine(gridPoint, scanHeight, vehicleHeight); - results[gridPoint] = interval; - } - } - catch (Exception serialEx) - { - LogManager.Error($"[垂直扫描处理器] 串行扫描点 {gridPoint} 也失败: {serialEx.Message}"); - if (gridPoint != null) - { - results[gridPoint] = new HeightInterval(); - } - } - } - } - - var elapsed = (DateTime.Now - startTime).TotalMilliseconds; - var validIntervals = results.Values.Count(interval => interval.MaxZ > interval.MinZ); - - // 输出缓存统计信息 - if (_cacheHits + _cacheMisses > 0) - { - double hitRate = (double)_cacheHits / (_cacheHits + _cacheMisses) * 100; - LogManager.Info($"[垂直扫描处理器] 缓存统计 - 命中: {_cacheHits}, 未命中: {_cacheMisses}, 命中率: {hitRate:F1}%, 缓存大小: {_candidateCache.Count}"); - } - - LogManager.Info($"[垂直扫描处理器] 并行扫描完成,耗时: {elapsed:F1}ms, 扫描点: {pointsList.Count}, 有效区间: {validIntervals}"); - - // 清理缓存,为下次扫描做准备 - _candidateCache.Clear(); - _cacheHits = 0; - _cacheMisses = 0; - - return new Dictionary(results); - } - - /// - /// 扫描单个垂直线上的通行区间 - /// - /// 基础点(X,Y坐标) - /// 扫描高度 - /// 车辆高度 - /// 可通行的高度区间 - public HeightInterval ScanVerticalLine(Point3D basePoint, double scanHeight, double vehicleHeight) - { - // 第一级筛选:邻域筛选 - 获取空间哈希邻域内的候选项 - var candidateItems = GetCandidateItemsFromSpatialHash(basePoint); - - // 第二级筛选:高度筛选 - 只保留在扫描高度范围内的项目 - var heightFilteredItems = HeightFiltering(candidateItems, basePoint.Z, basePoint.Z + scanHeight); - - // 第三级筛选:空间哈希 - 精确的几何相交测试 - var intersectionResults = PerformIntersectionTests(heightFilteredItems, basePoint, scanHeight); - - // 计算可通行区间 - var passableInterval = CalculatePassableInterval(intersectionResults, basePoint.Z, scanHeight, vehicleHeight); - - return passableInterval; - } - - - #endregion - - #region 私有方法 - 多级筛选实现 - - /// - /// 第一级筛选:从空间哈希中获取候选模型项(邻域筛选) - /// - /// 查询点 - /// 候选模型项列表 - private IEnumerable GetCandidateItemsFromSpatialHash(Point3D point) - { - try - { - // 🔥 关键修复:检查坐标值的有效性 - if (double.IsNaN(point.X) || double.IsNaN(point.Y) || - double.IsInfinity(point.X) || double.IsInfinity(point.Y)) - { - LogManager.Warning($"[垂直扫描处理器] 无效的坐标点: ({point.X}, {point.Y}, {point.Z})"); - return new HashSet(); // 返回空集合 - } - - // 使用粗粒度的缓存键(基于空间哈希桶坐标) - int gridX = (int)Math.Floor(point.X / _spatialHashSize); - int gridY = (int)Math.Floor(point.Y / _spatialHashSize); - string cacheKey = $"{gridX},{gridY}"; - - // 🔥 关键修复:使用ConcurrentDictionary的GetOrAdd原子操作 - var candidates = _candidateCache.GetOrAdd(cacheKey, key => - { - // 这个函数只在缓存未命中时执行 - Interlocked.Increment(ref _cacheMisses); - - // 构建候选集合 - var items = new HashSet(); - var hashKeys = GetSpatialHashKeysForPoint(point); - - foreach (var hkey in hashKeys) - { - if (_spatialHashMap.TryGetValue(hkey, out var bucketItems)) - { - items.UnionWith(bucketItems); - } - } - - // 检查缓存大小,如果超限则清空整个缓存 - if (_candidateCache.Count > _maxCacheSize) - { - _candidateCache.Clear(); - LogManager.Info($"[垂直扫描处理器] 缓存已满({_candidateCache.Count}>{_maxCacheSize}),清空缓存重新开始"); - } - - return items; - }); - - // 如果键已存在(GetOrAdd没有执行工厂函数),说明是缓存命中 - if (_candidateCache.ContainsKey(cacheKey)) - { - Interlocked.Increment(ref _cacheHits); - } - - // 🔥 关键修复:返回副本以保证线程安全,避免并发修改原HashSet - return new HashSet(candidates); - } - catch (Exception ex) - { - LogManager.Error($"[垂直扫描处理器] 获取候选项失败: {ex.Message},点坐标: ({point.X}, {point.Y}, {point.Z})"); - return new HashSet(); // 返回空集合 - } - } - - - - /// - /// 第二级筛选:高度筛选 - /// - /// 候选模型项 - /// 最小Z坐标 - /// 最大Z坐标 - /// 高度筛选后的模型项 - private List HeightFiltering(IEnumerable items, double minZ, double maxZ) - { - var filtered = new ConcurrentBag(); - - try - { - // 使用ConcurrentBag避免锁竞争,提高性能和稳定性 - Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = _parallelDegree }, item => - { - try - { - if (item?.HasGeometry == true) - { - var bbox = item.BoundingBox(); - if (bbox != null) - { - // 检查高度范围是否有重叠(加上容差) - if (bbox.Max.Z >= (minZ - HEIGHT_TOLERANCE) && - bbox.Min.Z <= (maxZ + HEIGHT_TOLERANCE)) - { - filtered.Add(item); - } - } - } - } - catch (Exception ex) - { - LogManager.Debug($"[垂直扫描处理器] 高度筛选失败: {item?.DisplayName ?? "NULL"}, {ex.Message}"); - } - }); - } - catch (Exception ex) - { - LogManager.Warning($"[垂直扫描处理器] 高度筛选并行处理失败: {ex.Message},回退到串行处理"); - - // 如果并行处理失败,使用串行处理 - foreach (var item in items ?? new List()) - { - try - { - if (item?.HasGeometry == true) - { - var bbox = item.BoundingBox(); - if (bbox != null && - bbox.Max.Z >= (minZ - HEIGHT_TOLERANCE) && - bbox.Min.Z <= (maxZ + HEIGHT_TOLERANCE)) - { - filtered.Add(item); - } - } - } - catch (Exception serialEx) - { - LogManager.Debug($"[垂直扫描处理器] 串行高度筛选失败: {item?.DisplayName ?? "NULL"}, {serialEx.Message}"); - } - } - } - - return filtered.ToList(); - } - - /// - /// 第三级筛选:精确几何相交测试(空间哈希优化) - /// - /// 高度筛选后的模型项 - /// 基础点 - /// 扫描高度 - /// 相交测试结果 - private List PerformIntersectionTests(List items, Point3D basePoint, double scanHeight) - { - var results = new ConcurrentBag(); - - try - { - // 并行执行相交测试,添加更强的异常处理 - Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = _parallelDegree }, item => - { - try - { - if (item != null) - { - var intersectionData = TestVerticalLineIntersection(item, basePoint, scanHeight); - if (intersectionData != null) - { - // 简化逻辑:所有与垂直射线相交的非通道元素都视为障碍物 - // 因为在空间哈希构建时已经排除了通道元素 - results.Add(new IntersectionResult - { - ModelItem = item, - IntersectionData = intersectionData, - IsObstacle = true, // 所有相交的非通道元素都是障碍物 - IsPassable = false // 不可通行 - }); - } - } - } - catch (Exception ex) - { - LogManager.Debug($"【垂直扫描处理器】 相交测试失败: {item?.DisplayName ?? "NULL"}, {ex.Message}"); - } - }); - } - catch (Exception ex) - { - LogManager.Warning($"【垂直扫描处理器】 并行相交测试失败: {ex.Message},回退到串行处理"); - - // 如果并行处理失败,使用串行处理 - foreach (var item in items ?? new List()) - { - try - { - if (item != null) - { - var intersectionData = TestVerticalLineIntersection(item, basePoint, scanHeight); - if (intersectionData != null) - { - // 简化逻辑:所有与垂直射线相交的非通道元素都视为障碍物 - results.Add(new IntersectionResult - { - ModelItem = item, - IntersectionData = intersectionData, - IsObstacle = true, // 所有相交的非通道元素都是障碍物 - IsPassable = false // 不可通行 - }); - } - } - } - catch (Exception serialEx) - { - LogManager.Debug($"【垂直扫描处理器】 串行相交测试失败: {item?.DisplayName ?? "NULL"}, {serialEx.Message}"); - } - } - } - - return results.ToList(); - } - - /// - /// 计算可通行区间 - /// - /// 相交测试结果 - /// 基础Z坐标 - /// 扫描高度 - /// 车辆高度 - /// 可通行区间 - private HeightInterval CalculatePassableInterval( - List intersectionResults, - double baseZ, - double scanHeight, - double vehicleHeight) - { - - // 收集所有障碍物的高度范围 - var obstacles = new List(); - var floors = new List(); - - foreach (var result in intersectionResults) - { - if (result.IsObstacle && result.IntersectionData != null) - { - var obstacleInterval = new HeightInterval( - result.IntersectionData.MinZ, - result.IntersectionData.MaxZ - ); - obstacles.Add(obstacleInterval); - } - else if (result.IsPassable && result.IntersectionData != null) - { - // 检查是否为地面/楼板 - if (IsFloorLike(result.ModelItem, result.IntersectionData)) - { - var floorInterval = new HeightInterval( - result.IntersectionData.MinZ, - result.IntersectionData.MaxZ - ); - floors.Add(floorInterval); - } - } - } - - // 合并重叠的障碍物区间 - var mergedObstacles = MergeOverlappingIntervals(obstacles); - - // 计算最优可通行区间(取最大的一个) - HeightInterval bestInterval = new HeightInterval(); - var scanRange = new HeightInterval(baseZ, baseZ + scanHeight); - - // 如果没有找到地面,使用基础Z坐标作为默认地面 - if (!floors.Any()) - { - floors.Add(new HeightInterval(baseZ - 0.1, baseZ + 0.1)); - } - - // 对每个潜在的地面,计算其上方的可通行空间,取最大的 - foreach (var floor in floors) - { - double floorTop = floor.MaxZ; - double availableTop = baseZ + scanHeight; - - // 检查地面上方是否有足够的净空高度 - var conflictingObstacles = mergedObstacles - .Where(obs => obs.MinZ < availableTop && obs.MaxZ > floorTop) - .OrderBy(obs => obs.MinZ) - .ToList(); - - if (!conflictingObstacles.Any()) - { - // 没有障碍物,整个高度范围都可通行 - double clearHeight = availableTop - floorTop; - - if (clearHeight >= MIN_PASSABLE_HEIGHT) - { - var interval = new HeightInterval(floorTop, availableTop); - if (interval.MaxZ - interval.MinZ > bestInterval.MaxZ - bestInterval.MinZ) - { - bestInterval = interval; - } - } - } - else - { - // 有障碍物,计算障碍物之间的可通行空间,取最大的 - double currentBottom = floorTop; - - foreach (var obstacle in conflictingObstacles) - { - double obstacleBottom = Math.Max(obstacle.MinZ, currentBottom); - - if (obstacleBottom > currentBottom) - { - double clearHeight = obstacleBottom - currentBottom; - - if (clearHeight >= vehicleHeight) - { - var interval = new HeightInterval(currentBottom, obstacleBottom); - if (interval.MaxZ - interval.MinZ > bestInterval.MaxZ - bestInterval.MinZ) - { - bestInterval = interval; - } - } - } - - currentBottom = Math.Max(currentBottom, obstacle.MaxZ); - } - - // 检查最后一个障碍物之后的空间 - if (currentBottom < availableTop) - { - double clearHeight = availableTop - currentBottom; - - if (clearHeight >= vehicleHeight) - { - var interval = new HeightInterval(currentBottom, availableTop); - if (interval.MaxZ - interval.MinZ > bestInterval.MaxZ - bestInterval.MinZ) - { - bestInterval = interval; - } - } - } - } - } - - return bestInterval; - } - - #endregion - - #region 私有辅助方法 - - /// - /// 检查边界框是否在指定范围内 - /// - /// 要检查的边界框 - /// 范围边界框 - /// 是否在范围内 - private bool IsWithinBounds(BoundingBox3D bbox, BoundingBox3D bounds) - { - return bbox.Max.X >= bounds.Min.X && bbox.Min.X <= bounds.Max.X && - bbox.Max.Y >= bounds.Min.Y && bbox.Min.Y <= bounds.Max.Y && - bbox.Max.Z >= bounds.Min.Z && bbox.Min.Z <= bounds.Max.Z; - } - - /// - /// 获取边界框的所有空间哈希键 - /// - /// 边界框 - /// 空间哈希键列表 - private List GetSpatialHashKeys(BoundingBox3D bbox) - { - var keys = new List(); - - int minX = (int)Math.Floor(bbox.Min.X / _spatialHashSize); - int maxX = (int)Math.Floor(bbox.Max.X / _spatialHashSize); - int minY = (int)Math.Floor(bbox.Min.Y / _spatialHashSize); - int maxY = (int)Math.Floor(bbox.Max.Y / _spatialHashSize); - - for (int x = minX; x <= maxX; x++) - { - for (int y = minY; y <= maxY; y++) - { - keys.Add($"{x},{y}"); - } - } - - return keys; - } - - /// - /// 获取点的空间哈希键(包括相邻网格) - /// - /// 查询点 - /// 空间哈希键列表 - private List GetSpatialHashKeysForPoint(Point3D point) - { - var keys = new List(); - int centerX = (int)Math.Floor(point.X / _spatialHashSize); - int centerY = (int)Math.Floor(point.Y / _spatialHashSize); - - // 单桶模式 - keys.Add($"{centerX},{centerY}"); - - // 智能查询范围策略:根据空间哈希大小调整查询桶数量 - // - 哈希桶<1.5m:只查询中心桶(1个) - // - 哈希桶1.5-3m:查询十字形(5个桶) - // - 哈希桶>3m:查询3x3(9个桶) - - // if (_spatialHashSize < 1.5) - // { - // // 单桶模式 - 对于很小的哈希桶,相邻桶基本不会有相关模型 - // keys.Add($"{centerX},{centerY}"); - // } - // else if (_spatialHashSize < 3.0) - // { - // // 5个桶模式(十字形)- 只查询上下左右和中心 - // keys.Add($"{centerX},{centerY}"); - // keys.Add($"{centerX-1},{centerY}"); - // keys.Add($"{centerX+1},{centerY}"); - // keys.Add($"{centerX},{centerY-1}"); - // keys.Add($"{centerX},{centerY+1}"); - // } - // else - // { - // // 9个桶模式(3x3)- 原有方式,用于较大的哈希桶 - // for (int dx = -1; dx <= 1; dx++) - // { - // for (int dy = -1; dy <= 1; dy++) - // { - // keys.Add($"{centerX + dx},{centerY + dy}"); - // } - // } - // } - - return keys; - } - - /// - /// 测试垂直线与模型项的相交 - /// - /// 模型项 - /// 基础点 - /// 扫描高度 - /// 相交数据,如果不相交则返回null - private IntersectionData TestVerticalLineIntersection(ModelItem item, Point3D basePoint, double scanHeight) - { - try - { - if (!item.HasGeometry) - return null; - - // 直接提取模型项的三角形几何数据 - var triangles = GeometryHelper.ExtractTriangles(item); - if (triangles == null || triangles.Count == 0) - { - return null; - } - - // 执行射线-三角形相交检测 - var intersections = PerformVerticalRayIntersection(basePoint, scanHeight, triangles); - if (intersections == null || intersections.Count == 0) - { - return null; - } - - // 计算相交的Z范围 - double minZ = intersections.Min(); - double maxZ = intersections.Max(); - - // 确保相交区间与扫描区间有重叠 - double scanMinZ = basePoint.Z; - double scanMaxZ = basePoint.Z + scanHeight; - - double intersectionMinZ = Math.Max(minZ, scanMinZ); - double intersectionMaxZ = Math.Min(maxZ, scanMaxZ); - - if (intersectionMaxZ > intersectionMinZ) - { - return new IntersectionData - { - MinZ = intersectionMinZ, - MaxZ = intersectionMaxZ, - IntersectionPoint = new Point3D(basePoint.X, basePoint.Y, (intersectionMinZ + intersectionMaxZ) / 2) - }; - } - - return null; - } - catch (Exception ex) - { - LogManager.Debug($"【垂直扫描处理器】 射线相交测试异常: {item.DisplayName}, {ex.Message}"); - return null; - } - } - - /// - /// 执行垂直射线与三角形相交检测 - /// - /// 射线起点 - /// 扫描高度 - /// 三角形列表 - /// 相交点Z坐标列表 - private List PerformVerticalRayIntersection(Point3D basePoint, double scanHeight, List triangles) - { - var intersectionPoints = new List(); - - try - { - // 创建垂直向上的射线(从basePoint开始向上扫描scanHeight距离) - var rayOrigin = new Point3D(basePoint.X, basePoint.Y, basePoint.Z); - var rayDirection = new Point3D(0, 0, 1); // 向上 - - foreach (var triangle in triangles) - { - if (GeometryHelper.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ)) - { - // 检查相交点是否在扫描范围内 - if (intersectionZ >= basePoint.Z && intersectionZ <= basePoint.Z + scanHeight) - { - intersectionPoints.Add(intersectionZ); - } - } - } - - return intersectionPoints; - } - catch (Exception ex) - { - LogManager.Debug($"【垂直扫描处理器】 射线-三角形相交计算失败: {ex.Message}"); - return intersectionPoints; - } - } - - - /// - /// 检查模型项是否类似地面/楼板 - /// - /// 模型项 - /// 相交数据 - /// 是否为地面类型 - private bool IsFloorLike(ModelItem item, IntersectionData intersectionData) - { - string displayName = item.DisplayName?.ToLower() ?? ""; - - // 通过名称判断 - string[] floorKeywords = { - "地面", "floor", "楼板", "slab", "板", "deck", - "地板", "flooring", "基础", "foundation" - }; - - foreach (string keyword in floorKeywords) - { - if (displayName.Contains(keyword)) - { - return true; - } - } - - // 通过几何特征判断(薄的水平结构) - double thickness = intersectionData.MaxZ - intersectionData.MinZ; - return thickness < 0.5; // 小于50cm厚度认为是楼板 - } - - /// - /// 合并重叠的区间 - /// - /// 区间列表 - /// 合并后的区间列表 - private List MergeOverlappingIntervals(List intervals) - { - if (!intervals.Any()) - return new List(); - - var sortedIntervals = intervals.OrderBy(i => i.MinZ).ToList(); - var merged = new List(); - - var current = sortedIntervals[0]; - for (int i = 1; i < sortedIntervals.Count; i++) - { - var next = sortedIntervals[i]; - - if (next.MinZ <= current.MaxZ + HEIGHT_TOLERANCE) - { - // 合并重叠区间 - current = new HeightInterval(current.MinZ, Math.Max(current.MaxZ, next.MaxZ)); - } - else - { - merged.Add(current); - current = next; - } - } - merged.Add(current); - - return merged; - } - - /// - /// 检查指定的ModelItem是否为通道集合中任意一个通道的子节点 - /// - /// 要检查的ModelItem - /// 通道集合 - /// 如果是通道的子节点则返回true - private bool IsChildOfChannelItems(ModelItem item, HashSet channelItemsSet) - { - try - { - if (item?.Parent == null) - return false; - - // 递归向上检查父节点链 - var currentParent = item.Parent; - while (currentParent != null) - { - if (channelItemsSet.Contains(currentParent)) - { - return true; // 找到了通道父节点 - } - currentParent = currentParent.Parent; - } - - return false; - } - catch (Exception ex) - { - LogManager.Debug($"【垂直扫描处理器】 检查子节点关系时出错: {item?.DisplayName ?? "NULL"}, {ex.Message}"); - return false; // 出错时保守处理,不排除 - } - } - - #endregion - - #region 内部数据结构 - - /// - /// 相交数据结构 - /// - private class IntersectionData - { - public double MinZ { get; set; } - public double MaxZ { get; set; } - public Point3D IntersectionPoint { get; set; } - } - - /// - /// 相交测试结果 - /// - private class IntersectionResult - { - public ModelItem ModelItem { get; set; } - public IntersectionData IntersectionData { get; set; } - public bool IsObstacle { get; set; } - public bool IsPassable { get; set; } - } - - #endregion - } -} \ No newline at end of file diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs index 1714109..3e84b9c 100644 --- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs +++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs @@ -501,6 +501,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// private void InitializeAnimationSettings() { + // 从配置加载动画参数 + var config = NavisworksTransport.Core.Config.ConfigManager.Instance.Current; + // 初始化可用帧率 AvailableFrameRates.Clear(); var frameRates = new[] { 15, 24, 30, 60 }; @@ -508,10 +511,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels { AvailableFrameRates.Add(rate); } - SelectedFrameRate = 30; // 默认30fps - // 设置默认动画参数 - AnimationDuration = 10.0; + // 从配置读取帧率 + SelectedFrameRate = config.Animation.FrameRate; + + // 从配置读取动画持续时间 + AnimationDuration = config.Animation.DurationSeconds; // 设置初始状态 - 修改: 默认状态应该是未激活,等待有效动画 CanPauseAnimation = false; @@ -520,16 +525,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 初始化碰撞检测状态 HasCollisionResults = false; UpdateMainStatus("碰撞检测就绪"); - - // 初始化碰撞检测参数 - DetectionGap = 0.05; // 0.05米 - AnimationFrameRate = 30; // 30FPS + + // 从配置读取碰撞检测参数 + DetectionGap = config.Animation.DetectionGapMeters; + AnimationFrameRate = config.Animation.FrameRate; // 注意:CollisionDetectionAccuracy(步长)和MovementSpeed(速度)现在是计算属性,不需要设置初始值 // 修改: 使用新的按钮状态更新方法来设置正确的初始状态 UpdateAnimationButtonStates(); - LogManager.Info("动画设置初始化完成"); + LogManager.Info($"动画设置初始化完成 - 帧率:{SelectedFrameRate}fps, 持续时间:{AnimationDuration}秒, 检测间隙:{DetectionGap}米"); } /// diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 9eebedf..65c1c39 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -11,6 +11,7 @@ using System.IO; using Microsoft.Win32; using Autodesk.Navisworks.Api; using NavisworksTransport.Core; +using NavisworksTransport.Core.Config; using NavisworksTransport.UI.WPF.Collections; using NavisworksTransport.UI.WPF.Models; using NavisworksTransport.UI.WPF.Commands; @@ -73,7 +74,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private double _vehicleLength = 1.0; // 车辆长度(米) private double _vehicleWidth = 1.0; // 车辆宽度(米) private double _vehicleHeight = 2.0; // 车辆高度(米) - private double _safetyMargin = 0.25; // 安全间隙(米) + private double _safetyMargin = 0.05; // 安全间隙(米) // 网格大小参数 private bool _isGridSizeManuallyEnabled = false; // 是否启用手动网格大小设置 @@ -481,50 +482,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels #region 构造函数 - public PathEditingViewModel() : base() - { - try - { - _uiStateManager = UIStateManager.Instance; - _pathDataManager = new PathDataManager(); - // 不在构造函数中创建PathPlanningManager,由外部设置 - _pathPlanningManager = null; - - if (_uiStateManager == null) - { - throw new InvalidOperationException("UIStateManager初始化失败"); - } - - // PathPlanningManager将在外部设置,这里不需要检查 - - // 初始化集合 - PathRoutes = new ObservableCollection(); - - // 初始化路径策略选项 - InitializePathStrategyOptions(); - - // 初始化命令 - InitializeCommands(); - - // 初始化车辆参数同步(在PathPointRenderPlugin实例不为空时才同步) - // 如果PathPointRenderPlugin尚未初始化,将在属性变更时自动同步 - SyncVehicleParametersToRenderPlugin(); - - // 注意:不在这里订阅PathPlanningManager事件, - // 因为PathPlanningManager还没有设置,事件订阅在PathPlanningManager属性setter中处理 - - LogManager.Info("PathEditingViewModel构造函数执行完成,已迁移原有业务逻辑"); - } - catch (Exception ex) - { - LogManager.Error($"PathEditingViewModel构造函数异常: {ex.Message}", ex); - UpdateMainStatus("初始化失败,请检查日志"); - throw; - } - } - /// - /// 带主ViewModel参数的构造函数,支持统一状态栏 + /// 构造函数,需要传入主ViewModel以支持统一状态栏 /// /// 主ViewModel,用于统一状态栏 public PathEditingViewModel(LogisticsControlViewModel mainViewModel) : base() @@ -551,7 +510,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 初始化路径策略选项 InitializePathStrategyOptions(); - + + // 从配置加载参数 + LoadParametersFromConfig(); + // 初始化命令 InitializeCommands(); @@ -607,6 +569,31 @@ namespace NavisworksTransport.UI.WPF.ViewModels _selectedPathStrategy = _pathStrategyOptions.FirstOrDefault(x => x.Value == PathStrategy.Shortest); } + /// + /// 从配置文件加载参数 + /// + private void LoadParametersFromConfig() + { + try + { + var config = ConfigManager.Instance.Current; + + // 从 PathEditing 配置加载所有参数 + _gridSize = config.PathEditing.CellSizeMeters; + _vehicleLength = config.PathEditing.VehicleLengthMeters; + _vehicleWidth = config.PathEditing.VehicleWidthMeters; + _vehicleHeight = config.PathEditing.VehicleHeightMeters; + _safetyMargin = config.PathEditing.SafetyMarginMeters; + + LogManager.Info($"从配置加载参数 - 车辆: {_vehicleLength}x{_vehicleWidth}x{_vehicleHeight}米, 安全间隙: {_safetyMargin}米, 网格: {_gridSize}米"); + } + catch (Exception ex) + { + LogManager.Error($"加载配置参数失败: {ex.Message}", ex); + // 使用默认值,不抛出异常 + } + } + private void InitializeCommands() { NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync(), () => CanExecuteNewPath); diff --git a/src/UI/WPF/Views/LogisticsControlPanel.xaml b/src/UI/WPF/Views/LogisticsControlPanel.xaml index 0644871..3f12e0d 100644 --- a/src/UI/WPF/Views/LogisticsControlPanel.xaml +++ b/src/UI/WPF/Views/LogisticsControlPanel.xaml @@ -56,7 +56,7 @@ NavisworksTransport 主控制面板 - 采用与其他视图一致的Navisworks 2 - + diff --git a/src/UI/WPF/Views/LogisticsControlPanel.xaml.cs b/src/UI/WPF/Views/LogisticsControlPanel.xaml.cs index 0047164..b039187 100644 --- a/src/UI/WPF/Views/LogisticsControlPanel.xaml.cs +++ b/src/UI/WPF/Views/LogisticsControlPanel.xaml.cs @@ -170,6 +170,11 @@ namespace NavisworksTransport.UI.WPF } } + /// + /// PathEditingView引用 + /// + private PathEditingView PathEditingView; + /// /// 初始化PathEditingView /// @@ -177,25 +182,29 @@ namespace NavisworksTransport.UI.WPF { try { - // 重新创建PathEditingView,传入主ViewModel引用 + // 创建PathEditingView,传入主ViewModel引用 var newPathEditingView = new PathEditingView(ViewModel); - - // 替换XAML中的PathEditingView - if (PathEditingTab?.Content is Border pathEditingBorder) + + // 设置到ContentControl中 + if (PathEditingContent != null) { - pathEditingBorder.Child = newPathEditingView; - + PathEditingContent.Content = newPathEditingView; + // 更新引用 PathEditingView = newPathEditingView; } - + if (PathEditingView?.ViewModel != null) { // 将PathPlanningManager传递给PathEditingView PathEditingView.ViewModel.PathPlanningManager = _pathPlanningManager; - + LogManager.Info("PathEditingView初始化完成 - 支持统一状态栏"); } + else + { + LogManager.Error("PathEditingContent占位符未找到"); + } } catch (Exception ex) { diff --git a/src/UI/WPF/Views/PathEditingView.xaml.cs b/src/UI/WPF/Views/PathEditingView.xaml.cs index 714cce6..5141154 100644 --- a/src/UI/WPF/Views/PathEditingView.xaml.cs +++ b/src/UI/WPF/Views/PathEditingView.xaml.cs @@ -19,19 +19,8 @@ namespace NavisworksTransport.UI.WPF.Views /// public PathEditingViewModel ViewModel { get; private set; } - public PathEditingView() - { - InitializeComponent(); - - // 创建并设置ViewModel - ViewModel = new PathEditingViewModel(); - DataContext = ViewModel; - - LogManager.Info("PathEditingView初始化完成"); - } - /// - /// 带主ViewModel参数的构造函数,支持统一状态栏 + /// 构造函数,需要传入主ViewModel以支持统一状态栏 /// /// 主ViewModel,用于统一状态栏 public PathEditingView(LogisticsControlViewModel mainViewModel) diff --git a/src/Utils/CoordinateConverter.cs b/src/Utils/CoordinateConverter.cs index b62090d..4c39364 100644 --- a/src/Utils/CoordinateConverter.cs +++ b/src/Utils/CoordinateConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Autodesk.Navisworks.Api; +using NavisworksTransport.Core.Config; namespace NavisworksTransport { @@ -17,7 +18,24 @@ namespace NavisworksTransport private double _zoomFactor = 1.0; private double _offsetX = 0.0; private double _offsetY = 0.0; - private const double _marginRatio = 0.1; // 10%边距 + + /// + /// 获取地图边距比例(从配置读取) + /// + private double MarginRatio + { + get + { + try + { + return ConfigManager.Instance.Current.Visualization.MarginRatio; + } + catch + { + return 0.1; // 配置加载失败时使用默认值10% + } + } + } /// /// 地图宽度(像素) @@ -138,12 +156,13 @@ namespace NavisworksTransport throw new ArgumentNullException(nameof(mapPoint)); // 计算可用的地图尺寸(减去边距) - double availableWidth = _mapWidth * (1.0 - 2 * _marginRatio); - double availableHeight = _mapHeight * (1.0 - 2 * _marginRatio); + double marginRatio = MarginRatio; + double availableWidth = _mapWidth * (1.0 - 2 * marginRatio); + double availableHeight = _mapHeight * (1.0 - 2 * marginRatio); // 从地图坐标计算相对位置 - double relativeX = (mapPoint.X - _mapWidth * _marginRatio) / availableWidth; - double relativeY = 1.0 - ((mapPoint.Y - _mapHeight * _marginRatio) / availableHeight); // Y轴翻转 + double relativeX = (mapPoint.X - _mapWidth * marginRatio) / availableWidth; + double relativeY = 1.0 - ((mapPoint.Y - _mapHeight * marginRatio) / availableHeight); // Y轴翻转 // 转换为世界坐标 double worldX = _channelBounds.MinPoint.X + relativeX * (_channelBounds.MaxPoint.X - _channelBounds.MinPoint.X); @@ -172,12 +191,13 @@ namespace NavisworksTransport double relativeY = (worldPoint.Y - _channelBounds.MinPoint.Y) / worldHeight; // 计算可用的地图尺寸(减去边距) - double availableWidth = _mapWidth * (1.0 - 2 * _marginRatio); - double availableHeight = _mapHeight * (1.0 - 2 * _marginRatio); + double marginRatio = MarginRatio; + double availableWidth = _mapWidth * (1.0 - 2 * marginRatio); + double availableHeight = _mapHeight * (1.0 - 2 * marginRatio); // 转换为地图像素坐标 - double mapX = relativeX * availableWidth + _mapWidth * _marginRatio; - double mapY = (1.0 - relativeY) * availableHeight + _mapHeight * _marginRatio; // Y轴翻转 + double mapX = relativeX * availableWidth + _mapWidth * marginRatio; + double mapY = (1.0 - relativeY) * availableHeight + _mapHeight * marginRatio; // Y轴翻转 // 确保坐标在地图范围内 mapX = Math.Max(0, Math.Min(_mapWidth, mapX)); diff --git a/src/Utils/FloorDetector.cs b/src/Utils/FloorDetector.cs index d091598..756e0fd 100644 --- a/src/Utils/FloorDetector.cs +++ b/src/Utils/FloorDetector.cs @@ -10,19 +10,12 @@ namespace NavisworksTransport /// public class FloorDetector { - #region 常量定义 - - private const double DEFAULT_FLOOR_HEIGHT_THRESHOLD = 2.5; // 默认最小楼层高度(米) - private const double DEFAULT_ELEVATION_TOLERANCE = 0.5; // 默认高程容差(米) - // 常见的楼层属性名称 private readonly string[] COMMON_FLOOR_ATTRIBUTES = { "Level", "Floor", "Storey", "楼层", "层", "Level Name", "Story", "Building Level", "Floor Level", "Elevation", "Z", "Height" }; - #endregion - #region 公共方法 /// @@ -190,9 +183,6 @@ namespace NavisworksTransport } } - - // GetFloorByElevation方法已删除 - 性能优化:不再支持基于高程的楼层查找 - /// /// 从给定的模型项集合中检测楼层信息 - 不进行深度遍历 /// @@ -346,7 +336,7 @@ namespace NavisworksTransport { try { - string floorValue = GetAttributeValue(item, attributeName); + string floorValue = CategoryAttributeManager.GetGeneralPropertyValueSafe(item, attributeName); if (!string.IsNullOrEmpty(floorValue)) { if (!floorGroups.ContainsKey(floorValue)) @@ -388,19 +378,6 @@ namespace NavisworksTransport return floors.OrderBy(f => f.Elevation).ToList(); } - private string GetAttributeValue(ModelItem item, string attributeName) - { - try - { - // 使用 CategoryAttributeManager 的安全属性获取方法,简化代码 - return CategoryAttributeManager.GetGeneralPropertyValueSafe(item, attributeName); - } - catch - { - return null; - } - } - #endregion