修改车辆和网格大小默认值,删掉分层的高程检测。
This commit is contained in:
parent
c9cd17c24a
commit
2b92e783bb
@ -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
|
||||
|
||||
@ -7,4 +7,11 @@
|
||||
1. [x]在分层预览列表中,去掉”文件大小“和”状态“列,增加:
|
||||
- “是否保存”列,内容是单选框,默认选中,选中的行,在点击“分层保存”时会保存,未选中的不保存
|
||||
- 在列表下方,增加“单独显示”按钮,点击后会隐藏除了当前行之外的其他部分
|
||||
- 保存文件时,文件名的格式:根节点名_自定义属性名_属性值_时间戳
|
||||
- 保存文件时,文件名的格式:根节点名_自定义属性名_属性值_时间戳
|
||||
|
||||
### [2025/08/28]
|
||||
|
||||
1. [x]将“自动规划路径”中的车辆长度、宽度的默认值改为1米,安全间隙改为0.25米。高级设置中,网格的大小
|
||||
,默认值改为0.5米。
|
||||
2. [ ]将这些参数,作为插件配置文件的参数,在系统管理的插件管理中,统一管理
|
||||
3. [ ]修改分层预览的业务逻辑:从一级节点开始,遍历每个节点查找指定的分层属性,如果找到,记录下来作为一个分层,不再遍历其下级节点;如果没找到,继续遍历其子节点,直到找到为止,遍历深度受到用户指定的遍历深度限制。特殊处理:1、对于智能检测,使用一组候选的分层属性,对每个节点进行查找,按属性的评分(优先级)确定选择何种属性。2、对于自定义查找,用指定的分层属性,在每个节点的"分层信息“属性类别中查找。
|
||||
|
||||
355
doc/working/LayerPreview_Implementation_Analysis.md
Normal file
355
doc/working/LayerPreview_Implementation_Analysis.md
Normal 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. **高效的缓存机制**:避免重复计算,提升用户体验
|
||||
|
||||
系统架构设计合理,为后续的功能扩展(如选择性保存、单独显示等)提供了良好的基础。
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user