修改车辆和网格大小默认值,删掉分层的高程检测。

This commit is contained in:
tian 2025-08-28 14:50:41 +08:00
parent c9cd17c24a
commit 2b92e783bb
5 changed files with 384 additions and 111 deletions

View File

@ -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

View File

@ -7,4 +7,11 @@
1. [x]在分层预览列表中,去掉”文件大小“和”状态“列,增加:
- “是否保存”列,内容是单选框,默认选中,选中的行,在点击“分层保存”时会保存,未选中的不保存
- 在列表下方,增加“单独显示”按钮,点击后会隐藏除了当前行之外的其他部分
- 保存文件时文件名的格式根节点名_自定义属性名_属性值_时间戳
- 保存文件时文件名的格式根节点名_自定义属性名_属性值_时间戳
### [2025/08/28]
1. [x]将“自动规划路径”中的车辆长度、宽度的默认值改为1米安全间隙改为0.25米。高级设置中,网格的大小
默认值改为0.5米。
2. [ ]将这些参数,作为插件配置文件的参数,在系统管理的插件管理中,统一管理
3. [ ]修改分层预览的业务逻辑从一级节点开始遍历每个节点查找指定的分层属性如果找到记录下来作为一个分层不再遍历其下级节点如果没找到继续遍历其子节点直到找到为止遍历深度受到用户指定的遍历深度限制。特殊处理1、对于智能检测使用一组候选的分层属性对每个节点进行查找按属性的评分优先级确定选择何种属性。2、对于自定义查找用指定的分层属性在每个节点的"分层信息“属性类别中查找。

View File

@ -0,0 +1,355 @@
# 分层预览中自定义预览的分层实现逻辑分析
## 概述
本文档详细分析NavisworksTransport插件中分层预览功能的实现逻辑特别是自定义预览的分层机制、节点遍历深度控制和界面交互方式。
## 核心架构分析
### 1. 节点遍历深度控制机制
#### 深度选择界面设计
系统在UI层提供了灵活的深度控制选项
```csharp
// LayerManagementViewModel.cs - 深度选项定义
public ThreadSafeObservableCollection<string> DepthOptions { get; } =
new ThreadSafeObservableCollection<string>
{
"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<SplitPreviewResult>
```
#### 分层策略处理
**支持的分层策略**
- `BySmartFloorDetection`:智能楼层检测分层
- `ByFloorAttribute`:基于楼层属性分层
- `ByZoneAttribute`:基于分区属性分层
- `BySubSystemAttribute`:基于子系统属性分层
**分层处理核心方法**`PreviewSplitByAttribute()`
```csharp
private List<SplitPreviewResult> 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<SplitPreviewItem> SplitPreviewResults { get; }
// 当前选中的预览项
public SplitPreviewItem SelectedPreviewResult { get; set; }
// 控制预览列表显示
public bool ShowPreviewResults { get; set; }
```
### 4. 可见性控制机制
#### 参考实现SaveSelectedItemsAsync方法
系统已有完整的节点隔离显示逻辑,核心步骤:
**1. 收集需要保持可见的节点**
```csharp
var nodesToKeepVisible = new HashSet<ModelItem>();
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<ModelItem>();
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<SplitPreviewResult> 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. **高效的缓存机制**:避免重复计算,提升用户体验
系统架构设计合理,为后续的功能扩展(如选择性保存、单独显示等)提供了良好的基础。

View File

@ -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;

View File

@ -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<ModelSplitterManager.FloorInfo>();
}
}
@ -191,14 +191,7 @@ namespace NavisworksTransport
}
/// <summary>
/// 根据高程获取楼层信息
/// </summary>
public ModelSplitterManager.FloorInfo GetFloorByElevation(double elevation, List<ModelSplitterManager.FloorInfo> floors)
{
return floors?.FirstOrDefault(f =>
Math.Abs(f.Elevation - elevation) <= DEFAULT_ELEVATION_TOLERANCE);
}
// GetFloorByElevation方法已删除 - 性能优化:不再支持基于高程的楼层查找
/// <summary>
/// 从给定的模型项集合中检测楼层信息 - 不进行深度遍历
@ -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<ModelSplitterManager.FloorInfo>();
}
}
@ -447,85 +440,7 @@ namespace NavisworksTransport
#endregion
#region -
private List<ModelSplitterManager.FloorInfo> DetectFloorsByElevation(ModelItemCollection items)
{
var elevationGroups = new Dictionary<double, List<ModelItem>>();
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<ModelItem> { item };
}
else
{
// 添加到现有楼层组
elevationGroups[floorElevation].Add(item);
}
}
}
catch
{
// 忽略无法获取边界框的元素
}
}
// 转换为FloorInfo列表
var floors = new List<ModelSplitterManager.FloorInfo>();
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<string, object>
{
["DetectionMethod"] = "Elevation",
["ElevationTolerance"] = DEFAULT_ELEVATION_TOLERANCE,
["FloorIndex"] = floorIndex
}
};
floors.Add(floorInfo);
floorIndex++;
}
return floors;
}
private double FindNearestFloorElevation(IEnumerable<double> 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
}