diff --git a/CLAUDE.md b/CLAUDE.md index 89eff2d..059d49b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -82,6 +82,12 @@ Eight predefined categories with inheritance from parent to child nodes: ## Development Guidelines +### Claude Code Work Delegation Principle + +- **任何具体的开发工作,全部让子代理完成** - All concrete development work must be delegated to specialized sub-agents +- Claude Code主要负责需求理解、任务分解和工作协调,不直接进行代码编写 +- 使用专业子代理处理具体实现:`navisworks-feature-developer`、`navisworks-api-researcher`、`navisworks-ui-designer`、`technical-architect`、`project-task-manager` + ### Language and Communication - **使用中文进行所有交流和代码注释** - Primary language for user interaction and code documentation diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md index 1d380a3..a1f9a09 100644 --- a/doc/requirement/todo_features.md +++ b/doc/requirement/todo_features.md @@ -7,4 +7,11 @@ 1. [x]在分层预览列表中,去掉”文件大小“和”状态“列,增加: - “是否保存”列,内容是单选框,默认选中,选中的行,在点击“分层保存”时会保存,未选中的不保存 - 在列表下方,增加“单独显示”按钮,点击后会隐藏除了当前行之外的其他部分 - - 保存文件时,文件名的格式:根节点名_自定义属性名_属性值_时间戳 \ No newline at end of file + - 保存文件时,文件名的格式:根节点名_自定义属性名_属性值_时间戳 + +### [2025/08/28] + +1. [x]将“自动规划路径”中的车辆长度、宽度的默认值改为1米,安全间隙改为0.25米。高级设置中,网格的大小 + ,默认值改为0.5米。 +2. [ ]将这些参数,作为插件配置文件的参数,在系统管理的插件管理中,统一管理 +3. [ ]修改分层预览的业务逻辑:从一级节点开始,遍历每个节点查找指定的分层属性,如果找到,记录下来作为一个分层,不再遍历其下级节点;如果没找到,继续遍历其子节点,直到找到为止,遍历深度受到用户指定的遍历深度限制。特殊处理:1、对于智能检测,使用一组候选的分层属性,对每个节点进行查找,按属性的评分(优先级)确定选择何种属性。2、对于自定义查找,用指定的分层属性,在每个节点的"分层信息“属性类别中查找。 diff --git a/doc/working/LayerPreview_Implementation_Analysis.md b/doc/working/LayerPreview_Implementation_Analysis.md new file mode 100644 index 0000000..3278a01 --- /dev/null +++ b/doc/working/LayerPreview_Implementation_Analysis.md @@ -0,0 +1,355 @@ +# 分层预览中自定义预览的分层实现逻辑分析 + +## 概述 + +本文档详细分析NavisworksTransport插件中分层预览功能的实现逻辑,特别是自定义预览的分层机制、节点遍历深度控制和界面交互方式。 + +## 核心架构分析 + +### 1. 节点遍历深度控制机制 + +#### 深度选择界面设计 + +系统在UI层提供了灵活的深度控制选项: + +```csharp +// LayerManagementViewModel.cs - 深度选项定义 +public ThreadSafeObservableCollection DepthOptions { get; } = + new ThreadSafeObservableCollection + { + "1级", "2级", "3级", "4级", "5级", "全部" + }; +``` + +#### 深度值转换逻辑 + +界面选择通过`CurrentDepthValue`属性转换为具体的遍历参数: + +```csharp +public int CurrentDepthValue +{ + get + { + switch (SelectedDepth) + { + case "1级": return 1; + case "2级": return 2; + case "3级": return 3; + case "4级": return 4; + case "5级": return 5; + case "全部": return 0; // 0表示无限深度 + default: return 1; + } + } +} +``` + +#### 核心遍历算法 + +**主遍历方法**:`GetItemsByDepthUnified(int maxDepth)` + +```csharp +private ModelItemCollection GetItemsByDepthUnified(int maxDepth) +{ + // 深度为0表示包含所有级别 + if (maxDepth <= 0) + { + result.AddRange(document.Models.RootItemDescendantsAndSelf); + return result; + } + + // 精确深度控制:从文档的根模型开始遍历 + foreach (Model model in document.Models) + { + if (model.RootItem != null && maxDepth >= 1) + { + // 添加第一级节点(模型根的直接子节点) + foreach (ModelItem firstLevelItem in model.RootItem.Children) + { + result.Add(firstLevelItem); + // 递归添加更深层次的节点 + AddChildrenToDepth(firstLevelItem, result, 2, maxDepth); + } + } + } +} +``` + +**递归遍历方法**:`AddChildrenToDepth()` + +```csharp +private void AddChildrenToDepth(ModelItem parentItem, ModelItemCollection result, + int currentDepth, int maxDepth) +{ + if (currentDepth > maxDepth || parentItem?.Children == null) + return; + + foreach (ModelItem child in parentItem.Children) + { + result.Add(child); + + // 如果还没有达到最大深度,继续遍历子项 + if (currentDepth < maxDepth) + { + AddChildrenToDepth(child, result, currentDepth + 1, maxDepth); + } + } +} +``` + +### 2. 分层预览处理流程 + +#### 预览触发链路 + +``` +用户界面操作 +└── LayerManagementViewModel.PreviewSplitAsync() + ├── 生成SplitConfiguration(包含MaxDepth参数) + └── SimplifiedModelSplitterManager.PreviewSplit(config) + ├── GetAllModelItems(config.MaxDepth) + │ └── GetItemsByDepthUnified(maxDepth) // 深度控制核心 + ├── PreviewSplitByAttribute(allItems, config, strategy) + │ └── strategy.GroupItems() // 按选定策略分组 + └── 返回List +``` + +#### 分层策略处理 + +**支持的分层策略**: + +- `BySmartFloorDetection`:智能楼层检测分层 +- `ByFloorAttribute`:基于楼层属性分层 +- `ByZoneAttribute`:基于分区属性分层 +- `BySubSystemAttribute`:基于子系统属性分层 + +**分层处理核心方法**:`PreviewSplitByAttribute()` + +```csharp +private List PreviewSplitByAttribute( + ModelItemCollection items, SplitConfiguration config, IGroupingStrategy strategy) +{ + // 属性分组阶段 (35-70%) + var groups = strategy.GroupItems(items, config); + + // 生成预览结果阶段 (70-85%) + foreach (var group in groups.Values) + { + var previewResult = new SplitPreviewResult + { + LayerName = SanitizeLayerName(group.GroupName), + ItemCount = group.Items.Count, + EstimatedFileSize = EstimateFileSize(group.Items.Count), + Status = "就绪", + Items = new ModelItemCollection() // 关键:保存分组的模型项 + }; + + // 添加模型项到集合中(用于后续的隔离显示) + if (group.Items != null && group.Items.Count > 0) + { + previewResult.Items.AddRange(group.Items); + } + + results.Add(previewResult); + } +} +``` + +### 3. 数据结构设计 + +#### 预览结果数据模型 + +**SplitPreviewItem类**(已具备选择保存功能): + +```csharp +public class SplitPreviewItem : INotifyPropertyChanged +{ + public string LayerName { get; set; } // 分层名称 + public int ObjectCount { get; set; } // 对象数量 + public string EstimatedSize { get; set; } // 预估文件大小 + public string Status { get; set; } // 状态信息 + + // 重要:已存在的选择保存功能 + private bool _isSelectedForSave = true; + public bool IsSelectedForSave { get; set; } // 是否选中用于保存,默认true + + // 关联的模型项集合(用于隔离显示功能) + public ModelItemCollection Items { get; set; } = new ModelItemCollection(); +} +``` + +#### UI数据绑定 + +**ViewModel中的集合管理**: + +```csharp +// 预览结果集合 +public ThreadSafeObservableCollection SplitPreviewResults { get; } + +// 当前选中的预览项 +public SplitPreviewItem SelectedPreviewResult { get; set; } + +// 控制预览列表显示 +public bool ShowPreviewResults { get; set; } +``` + +### 4. 可见性控制机制 + +#### 参考实现:SaveSelectedItemsAsync方法 + +系统已有完整的节点隔离显示逻辑,核心步骤: + +**1. 收集需要保持可见的节点** +```csharp +var nodesToKeepVisible = new HashSet(); + +foreach (var selectedItem in originalSelection) +{ + // 添加选中节点本身 + nodesToKeepVisible.Add(selectedItem); + + // 添加所有祖先节点,确保选中节点的路径可见 + var current = selectedItem.Parent; + while (current != null) + { + nodesToKeepVisible.Add(current); + current = current.Parent; + } + + // 如果启用了包含子节点选项,添加所有子节点 + if (IncludeChildNodes) + { + var childItems = selectedItem.DescendantsAndSelf.Where(x => x != selectedItem); + foreach (ModelItem child in childItems) + { + nodesToKeepVisible.Add(child); + } + } +} +``` + +**2. 智能隐藏策略** +```csharp +// 收集所有顶级节点 +var allTopLevelItems = new List(); +foreach (Model model in document.Models) +{ + foreach (ModelItem topLevelItem in model.RootItem.Children) + { + allTopLevelItems.Add(topLevelItem); + } +} + +// 决定哪些顶级分支需要隐藏 +var itemsToHide = new ModelItemCollection(); +foreach (ModelItem topLevelItem in allTopLevelItems) +{ + bool shouldKeepTopLevel = false; + + // 检查这个顶级节点或其任何子节点是否需要保持可见 + if (nodesToKeepVisible.Contains(topLevelItem)) + { + shouldKeepTopLevel = true; + } + else + { + // 检查是否有任何选中节点是这个顶级节点的后代 + foreach (var selectedItem in originalSelection) + { + var current = selectedItem; + while (current != null) + { + if (current == topLevelItem) + { + shouldKeepTopLevel = true; + break; + } + current = current.Parent; + } + if (shouldKeepTopLevel) break; + } + } + + // 如果这个顶级节点不需要保持可见,则隐藏它 + if (!shouldKeepTopLevel) + { + itemsToHide.Add(topLevelItem); + } +} +``` + +**3. 执行可见性操作** +```csharp +// 执行隐藏操作 +if (itemsToHide.Count > 0) +{ + document.Models.SetHidden(itemsToHide, true); +} + +// 操作完成后恢复所有项目可见性 +var allItems = new ModelItemCollection(); +foreach (Model model in document.Models) +{ + foreach (ModelItem item in model.RootItem.Children) + { + allItems.Add(item); + } +} + +if (allItems.Count > 0) +{ + document.Models.SetHidden(allItems, false); +} +``` + +## 深度遍历使用场景分析 + +### 遍历深度对分层效果的影响 + +| 深度级别 | 遍历范围 | 适用场景 | 性能特点 | +|----------|----------|----------|----------| +| 1级 | 只包含模型根节点的直接子节点 | 粗粒度分层,适合大型模型的快速概览 | 处理速度最快,内存占用最小 | +| 2级 | 包含二级子节点 | 中等粒度分层,平衡详细度与性能 | 适中的处理时间和内存使用 | +| 3-5级 | 包含更深层次的节点结构 | 细粒度分层,适合详细分析 | 处理时间较长,内存占用较大 | +| 全部 | 包含所有层级的节点 | 最详细的分层分析 | 处理时间最长,内存占用最大 | + +### 深度设置的实际应用 + +**楼层分析场景**: +- 1-2级:适合按建筑主要结构分层 +- 3-5级:适合按房间或具体构件分层 +- 全部:适合最详细的构件级别分析 + +**系统分析场景**: +- 较低深度:按主要系统分类(如电气、暖通、给排水) +- 较高深度:按具体设备或管线分层 + +## 缓存机制 + +系统实现了分层结果的缓存机制,提高重复操作的性能: + +```csharp +// 生成缓存键 +string cacheKey = GenerateCacheKey(config); + +// 检查缓存 +List cachedResults = GetFromCache(config.Strategy, cacheKey); +if (cachedResults != null && cachedResults.Count > 0) +{ + return cachedResults; // 直接返回缓存结果 +} + +// 处理完成后保存到缓存 +SaveToCache(config.Strategy, cacheKey, previewResults); +``` + +## 总结 + +NavisworksTransport的分层预览功能具备以下关键特性: + +1. **灵活的深度控制**:支持1-5级精确遍历和无限深度选项 +2. **完善的数据结构**:`SplitPreviewItem`已包含选择保存和模型项关联功能 +3. **成熟的可见性控制**:基于`SaveSelectedItemsAsync`的参考实现 +4. **多样的分层策略**:支持楼层、分区、子系统等多种分层方式 +5. **高效的缓存机制**:避免重复计算,提升用户体验 + +系统架构设计合理,为后续的功能扩展(如选择性保存、单独显示等)提供了良好的基础。 \ No newline at end of file diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index f9a7512..ced909c 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -48,14 +48,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels private bool _isSelectingEndPoint = false; // 车辆参数 - 改为三个独立参数 - private double _vehicleLength = 2.0; // 车辆长度(米) - private double _vehicleWidth = 2.0; // 车辆宽度(米) + private double _vehicleLength = 1.0; // 车辆长度(米) + private double _vehicleWidth = 1.0; // 车辆宽度(米) private double _vehicleHeight = 2.0; // 车辆高度(米) - private double _safetyMargin = 0.5; // 安全间隙(米) + private double _safetyMargin = 0.25; // 安全间隙(米) // 网格大小参数 private bool _isGridSizeManuallyEnabled = false; // 是否启用手动网格大小设置 - private double _gridSize = 1.0; // 网格大小(米) + private double _gridSize = 0.5; // 网格大小(米) // 自动路径起点和终点的路径对象引用(用于正确的ID管理) private PathRoute _autoPathStartPointRoute = null; diff --git a/src/Utils/FloorDetector.cs b/src/Utils/FloorDetector.cs index 131b90b..e60db75 100644 --- a/src/Utils/FloorDetector.cs +++ b/src/Utils/FloorDetector.cs @@ -76,10 +76,10 @@ namespace NavisworksTransport } else { - LogManager.Info($"[FloorDetector] 未找到合适属性,使用高程检测"); - // 使用高程检测 - floors = DetectFloorsByElevation(itemsToAnalyze); - LogManager.Info($"[FloorDetector] 使用高程检测到 {floors.Count} 个楼层"); + LogManager.Info($"[FloorDetector] 未找到合适的楼层属性,无法进行楼层检测"); + LogManager.Info($"[FloorDetector] 建议:请确保模型包含标准的楼层属性(如Level、Floor、楼层等)"); + // 不再使用高程检测,直接返回空结果 + floors = new List(); } } @@ -191,14 +191,7 @@ namespace NavisworksTransport } - /// - /// 根据高程获取楼层信息 - /// - public ModelSplitterManager.FloorInfo GetFloorByElevation(double elevation, List floors) - { - return floors?.FirstOrDefault(f => - Math.Abs(f.Elevation - elevation) <= DEFAULT_ELEVATION_TOLERANCE); - } + // GetFloorByElevation方法已删除 - 性能优化:不再支持基于高程的楼层查找 /// /// 从给定的模型项集合中检测楼层信息 - 不进行深度遍历 @@ -245,10 +238,10 @@ namespace NavisworksTransport } else { - LogManager.Info($"[FloorDetector] 未找到合适属性,使用高程检测"); - // 使用高程检测 - floors = DetectFloorsByElevation(items); - LogManager.Info($"[FloorDetector] 使用高程检测到 {floors.Count} 个楼层"); + LogManager.Info($"[FloorDetector] 未找到合适的楼层属性,无法进行楼层检测"); + LogManager.Info($"[FloorDetector] 建议:请确保模型包含标准的楼层属性(如Level、Floor、楼层等)"); + // 不再使用高程检测,直接返回空结果 + floors = new List(); } } @@ -447,85 +440,7 @@ namespace NavisworksTransport #endregion - #region 私有方法 - 基于高程的检测 - - private List DetectFloorsByElevation(ModelItemCollection items) - { - var elevationGroups = new Dictionary>(); - - foreach (ModelItem item in items) - { - try - { - var bounds = item.BoundingBox(); - if (bounds.HasVolume) - { - double elevation = bounds.Min.Z; // 使用Z坐标最小值作为高程 - - // 查找相近的楼层组 - double floorElevation = FindNearestFloorElevation(elevationGroups.Keys, elevation); - - if (floorElevation == double.MinValue) - { - // 创建新楼层组 - elevationGroups[elevation] = new List { item }; - } - else - { - // 添加到现有楼层组 - elevationGroups[floorElevation].Add(item); - } - } - } - catch - { - // 忽略无法获取边界框的元素 - } - } - - // 转换为FloorInfo列表 - var floors = new List(); - int floorIndex = 1; - - foreach (var kvp in elevationGroups.OrderBy(x => x.Key)) - { - var floorItems = new ModelItemCollection(); - floorItems.AddRange(kvp.Value); - - var floorInfo = new ModelSplitterManager.FloorInfo - { - FloorName = GenerateFloorName(floorIndex, kvp.Key), - Elevation = kvp.Key, - Items = floorItems, - Bounds = CalculateBounds(floorItems), - Properties = new Dictionary - { - ["DetectionMethod"] = "Elevation", - ["ElevationTolerance"] = DEFAULT_ELEVATION_TOLERANCE, - ["FloorIndex"] = floorIndex - } - }; - - floors.Add(floorInfo); - floorIndex++; - } - - return floors; - } - - private double FindNearestFloorElevation(IEnumerable existingElevations, double targetElevation) - { - foreach (double elevation in existingElevations) - { - if (Math.Abs(elevation - targetElevation) <= DEFAULT_ELEVATION_TOLERANCE) - { - return elevation; - } - } - return double.MinValue; - } - - #endregion + // 高程检测方法已删除 - 性能优化:避免高耗时的高程遍历算法 #region 私有方法 - 属性检测和验证 @@ -846,17 +761,7 @@ namespace NavisworksTransport return string.IsNullOrEmpty(sanitized) ? "Unknown_Floor" : sanitized; } - private string GenerateFloorName(int floorIndex, double elevation) - { - if (elevation < 0) - { - return $"Basement_{Math.Abs((int)Math.Round(elevation)):D2}"; - } - else - { - return $"Floor_{floorIndex:D2}"; - } - } + // GenerateFloorName方法已删除 - 随高程检测算法一起移除 #endregion }