完成工具类skills

This commit is contained in:
tian 2026-02-25 02:01:38 +08:00
parent cb7e1c0e17
commit 2b151ebee5
9 changed files with 1311 additions and 17 deletions

View File

@ -6,18 +6,35 @@
## 工具类总览
| 工具类 | 文件 | 用途 | 优先级 |
|--------|------|------|--------|
| **DialogHelper** | `src/Utils/DialogHelper.cs` | 对话框Owner设置和置顶显示 | ⭐⭐⭐ 必须 |
| **UnitsConverter** | `src/Utils/UnitsConverter.cs` | 单位转换(米↔模型单位) | ⭐⭐⭐ 必须 |
| **LogManager** | `src/Utils/LogManager.cs` | 日志记录 | ⭐⭐⭐ 必须 |
| **GeometryHelper** | `src/Utils/GeometryHelper.cs` | 3D几何计算 | ⭐⭐⭐ |
| **PathHelper** | `src/Utils/PathHelper.cs` | 路径相关工具 | ⭐⭐⭐ |
| **CoordinateConverter** | `src/Utils/CoordinateConverter.cs` | 坐标系转换 | ⭐⭐ |
| **ModelHighlightHelper** | `src/Utils/ModelHighlightHelper.cs` | 模型高亮显示 | ⭐⭐ |
| **NavisworksSelectionHelper** | `src/Utils/NavisworksSelectionHelper.cs` | 选择集操作 | ⭐⭐ |
| **ViewpointHelper** | `src/Utils/ViewpointHelper.cs` | 视点操作 | ⭐⭐ |
| **SectionClipHelper** | `src/Utils/SectionClipHelper.cs` | 剖切操作 | ⭐ |
### 核心工具类(⭐⭐⭐ 必须)
| 工具类 | 文件 | 用途 |
|--------|------|------|
| **DialogHelper** | `src/Utils/DialogHelper.cs` | 对话框Owner设置和置顶显示 |
| **UnitsConverter** | `src/Utils/UnitsConverter.cs` | 单位转换(米↔模型单位) |
| **LogManager** | `src/Utils/LogManager.cs` | 日志记录 |
| **GeometryHelper** | `src/Utils/GeometryHelper.cs` | 3D几何计算 |
| **PathHelper** | `src/Utils/PathHelper.cs` | 文件路径相关工具 |
### 重要工具类(⭐⭐ 推荐)
| 工具类 | 文件 | 用途 |
|--------|------|------|
| **CoordinateConverter** | `src/Utils/CoordinateConverter.cs` | 2D地图↔3D世界坐标转换 |
| **ModelHighlightHelper** | `src/Utils/ModelHighlightHelper.cs` | 模型高亮显示 |
| **NavisworksSelectionHelper** | `src/Utils/NavisworksSelectionHelper.cs` | 选择集操作 |
| **ViewpointHelper** | `src/Utils/ViewpointHelper.cs` | 视点操作 |
| **NavisworksApiHelper** | `src/Utils/NavisworksApiHelper.cs` | API通用工具 |
| **VisibilityHelper** | `src/Utils/VisibilityHelper.cs` | 可见性管理 |
### 专项工具类(⭐ 按需使用)
| 工具类 | 文件 | 用途 |
|--------|------|------|
| **SectionClipHelper** | `src/Utils/SectionClipHelper.cs` | 剖切操作(性能优化) |
| **BoundingBoxGeometryUtils** | `src/Utils/BoundingBoxGeometryUtils.cs` | 包围盒几何计算 |
| **FloorDetector** | `src/Utils/FloorDetector.cs` | 楼层检测 |
| **GeometryCacheManager** | `src/Utils/GeometryCacheManager.cs` | 几何缓存管理 |
## 快速参考
@ -58,13 +75,52 @@ LogManager.Warning("警告信息");
LogManager.Error("错误信息", exception);
```
### 几何计算
```csharp
// ❌ 错误:自己实现距离计算
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
double distance = Math.Sqrt(dx*dx + dy*dy);
// ✅ 正确:使用 GeometryHelper
double distance = GeometryHelper.Distance(p1, p2);
double angle = GeometryHelper.AngleBetweenDegrees(v1, v2);
```
### 模型高亮
```csharp
// ✅ 使用 ModelHighlightHelper 高亮对象
ModelHighlightHelper.HighlightItems("myCategory", modelItems);
ModelHighlightHelper.ClearCategory("myCategory");
ModelHighlightHelper.ClearAllHighlights();
```
### 视角调整
```csharp
// ✅ 使用 ViewpointHelper 调整视角
ViewpointHelper.AdjustViewpointToPathCenter(path);
ViewpointHelper.FocusOnModelItem(modelItem, 45, 0.25);
Viewpoint saved = ViewpointHelper.SaveCurrentViewpoint();
ViewpointHelper.RestoreViewpoint(saved);
```
## 详细文档
- [DialogHelper 使用指南](utils/DialogHelper.md)
- [UnitsConverter 使用指南](utils/UnitsConverter.md)
- [LogManager 使用指南](utils/LogManager.md)
- [GeometryHelper 使用指南](utils/GeometryHelper.md)
- [ModelHighlightHelper 使用指南](utils/ModelHighlightHelper.md)
- [DialogHelper 使用指南](utils/DialogHelper.md) - 对话框Owner设置和置顶显示
- [UnitsConverter 使用指南](utils/UnitsConverter.md) - 单位转换(极其重要)
- [LogManager 使用指南](utils/LogManager.md) - 日志记录
- [GeometryHelper 使用指南](utils/GeometryHelper.md) - 3D几何计算
- [PathHelper 使用指南](utils/PathHelper.md) - 文件路径工具
- [CoordinateConverter 使用指南](utils/CoordinateConverter.md) - 2D/3D坐标转换
- [ModelHighlightHelper 使用指南](utils/ModelHighlightHelper.md) - 模型高亮
- [NavisworksSelectionHelper 使用指南](utils/NavisworksSelectionHelper.md) - 选择集操作
- [ViewpointHelper 使用指南](utils/ViewpointHelper.md) - 视点操作
- [SectionClipHelper 使用指南](utils/SectionClipHelper.md) - 剖切操作
- [NavisworksApiHelper 使用指南](utils/NavisworksApiHelper.md) - API通用工具
- [VisibilityHelper 使用指南](utils/VisibilityHelper.md) - 可见性管理
## 使用检查清单
@ -76,6 +132,12 @@ LogManager.Error("错误信息", exception);
- [ ] 需要几何计算?→ 使用 **GeometryHelper**
- [ ] 需要高亮模型?→ 使用 **ModelHighlightHelper**
- [ ] 需要处理选择集?→ 使用 **NavisworksSelectionHelper**
- [ ] 需要调整视角?→ 使用 **ViewpointHelper**
- [ ] 需要坐标转换?→ 使用 **CoordinateConverter**
- [ ] 需要文件路径操作?→ 使用 **PathHelper**
- [ ] 需要剖切优化?→ 使用 **SectionClipHelper**
- [ ] 需要显示/隐藏对象?→ 使用 **VisibilityHelper**
- [ ] 需要线程安全操作?→ 使用 **NavisworksApiHelper**
## 禁止行为
@ -86,6 +148,7 @@ LogManager.Error("错误信息", exception);
3. ❌ 使用 `Console.WriteLine` 输出日志
4. ❌ 自己实现距离/角度计算(已有 GeometryHelper
5. ❌ 自己实现集合的线程安全包装(使用 ThreadSafeObservableCollection
6. ❌ 自己实现缓存刷新逻辑(使用 NavisworksApiHelper.SafeCacheRefresh
## 扩展阅读

View File

@ -0,0 +1,182 @@
# CoordinateConverter 使用指南
## 文件位置
`src/Utils/CoordinateConverter.cs`
## 用途
负责 2D 地图坐标与 3D 世界坐标之间的转换,用于导航地图和路径规划可视化。
## 核心概念
- **地图坐标MapPoint2D**2D 像素坐标原点在左上角Y轴向下
- **世界坐标Point3D**3D 模型坐标,单位与文档单位一致
- **通道边界ChannelBounds**:定义了有效转换的区域范围
## 构造函数
```csharp
// 使用通道边界和地图尺寸创建转换器
var converter = new CoordinateConverter(
channelBounds: bounds, // ChannelBounds 对象
mapWidth: 800, // 地图窗口宽度(像素)
mapHeight: 600 // 地图窗口高度(像素)
);
```
## 核心方法
### 坐标转换
```csharp
// 2D地图坐标 → 3D世界坐标
MapPoint2D mapPoint = new MapPoint2D(400, 300);
Point3D worldPoint = converter.MapToWorld(mapPoint);
// 3D世界坐标 → 2D地图坐标
Point3D worldPoint = new Point3D(x, y, z);
MapPoint2D mapPoint = converter.WorldToMap(worldPoint);
// 批量转换
var worldPoints = converter.MapToWorldBatch(mapPoints);
var mapPoints = converter.WorldToMapBatch(worldPoints);
```
### 有效性检查
```csharp
// 检查地图坐标是否在有效范围内
bool isValid = converter.IsValidMapPoint(mapPoint);
// 检查世界坐标是否在通道范围内
bool isValid = converter.IsValidWorldPoint(worldPoint);
```
### 距离计算
```csharp
// 计算两个地图点之间的像素距离
double pixelDistance = converter.CalculateMapDistance(point1, point2);
// 计算两个世界坐标点之间的距离(模型单位)
double worldDistance = converter.CalculateWorldDistance(point1, point2);
```
### 视图操作
```csharp
// 放大视图
converter.ZoomIn(factor: 1.5, centerPoint: mapCenter);
// 缩小视图
converter.ZoomOut(factor: 1.5, centerPoint: mapCenter);
// 平移视图
converter.Pan(deltaX: 100, deltaY: -50);
// 重置视图到最佳适应
converter.ResetView();
// 计算最佳适应视图
converter.CalculateBestFitView();
```
### 尺寸更新
```csharp
// 更新地图尺寸(窗口大小变化时)
converter.UpdateMapSize(newWidth: 1024, newHeight: 768);
// 更新通道边界
converter.UpdateChannelBounds(newChannelBounds);
```
### 辅助方法
```csharp
// 获取地图中心点的世界坐标
Point3D center = converter.GetMapCenterInWorld();
// 调整高程
Point3D adjusted = converter.AdjustElevation(worldPoint, elevationOffset: 0.5);
// 获取地图缩放比例
converter.GetMapScale(out double scaleX, out double scaleY);
// 获取转换器信息摘要
string info = converter.ToString();
```
## 属性
```csharp
// 地图尺寸
converter.MapWidth = 800;
converter.MapHeight = 600;
// 通道边界
converter.ChannelBounds = newChannelBounds;
// 默认高程Z坐标
converter.DefaultElevation = 10.0;
// 缩放因子(限制范围 0.1 - 50.0
converter.ZoomFactor = 2.0;
// 偏移量
converter.OffsetX = 100;
converter.OffsetY = 50;
```
## 使用示例
### 示例1基本的坐标转换
```csharp
// 创建转换器
var bounds = new ChannelBounds(minPoint, maxPoint);
var converter = new CoordinateConverter(bounds, 800, 600);
// 用户在地图上点击的位置
var clickPoint = new MapPoint2D(400, 300);
// 转换为世界坐标进行路径规划
Point3D worldPoint = converter.MapToWorld(clickPoint);
LogManager.Info($"点击位置对应世界坐标: ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2})");
```
### 示例2路径点批量转换
```csharp
// 将规划好的路径点转换为地图坐标显示
var pathWorldPoints = pathRoute.Points.Select(p => p.Position);
var pathMapPoints = converter.WorldToMapBatch(pathWorldPoints);
// 在地图上绘制路径
foreach (var mapPoint in pathMapPoints)
{
DrawPointOnMap(mapPoint.X, mapPoint.Y);
}
```
### 示例3响应窗口大小变化
```csharp
// 地图控件大小变化时更新转换器
void OnMapSizeChanged(double newWidth, double newHeight)
{
converter.UpdateMapSize(newWidth, newHeight);
converter.CalculateBestFitView();
// 重绘地图...
}
```
## 注意事项
1. **Y轴翻转**:地图坐标系 Y 轴向下(屏幕坐标),世界坐标系 Y 轴向上,转换时自动处理
2. **边距设置**:从配置读取 `MarginRatio`,默认 10%,确保内容不会贴边显示
3. **Z坐标处理**`MapToWorld` 返回的 Z 坐标使用 `DefaultElevation`,可通过 `AdjustElevation` 调整
4. **缩放限制**:缩放因子限制在 0.1 - 50.0 范围内,防止过度缩放
5. **线程安全**:此类的实例方法不是线程安全的,需要在单线程中使用或使用外部锁

View File

@ -0,0 +1,130 @@
# ModelHighlightHelper 使用指南
## 文件位置
`src/Utils/ModelHighlightHelper.cs`
## 用途
统一管理 Navisworks 临时高亮,支持按类别高亮与清除。使用不同颜色区分不同类型的对象(碰撞结果、通道预览、动画物体等)。
## 颜色定义
| 类别 | 颜色 | RGB | 用途 |
|------|------|-----|------|
| `PrecomputeCollisionResultsCategory` | Material Purple | (156, 39, 176) | 预计算碰撞结果 |
| `ManualTargetsCategory` | 橙色 | (255, 170, 0) | 手工指定对象 |
| `AnimatedObjectCategory` | Amber/Yellow | (255, 193, 7) | 动画物体 |
| `ClashDetectiveResultsCategory` | 红色 | Color.Red | ClashDetective碰撞结果 |
| `ChannelPreviewCategory` | 绿色 | Color.Green | 通道预览 |
| `excludedObjects` | Material Green | (76, 175, 80) | 排除对象 |
## 核心方法
### 基础高亮操作
```csharp
// 高亮指定对象集合(使用类别默认颜色)
ModelHighlightHelper.HighlightItems("myCategory", modelItems);
// 清除指定类别的高亮
ModelHighlightHelper.ClearCategory("myCategory");
// 清除所有高亮
ModelHighlightHelper.ClearAllHighlights();
// 清除碰撞相关高亮
ModelHighlightHelper.ClearCollisionHighlights();
```
### 碰撞结果高亮
```csharp
// 按类别管理碰撞高亮
ModelHighlightHelper.ManageCollisionHighlightsByCategory(
"collisionResults",
collisionResultsList,
clearOtherCategories: false
);
// 高亮ClashDetective结果
ModelHighlightHelper.HighlightClashDetectiveResults(
testName,
getResultsFunc
);
// 清除ClashDetective高亮
ModelHighlightHelper.ClearClashDetectiveHighlights();
```
### 获取高亮状态
```csharp
// 获取当前高亮快照
var highlights = ModelHighlightHelper.GetActiveHighlightSnapshot();
foreach (var info in highlights)
{
LogManager.Info($"类别: {info.Category}, 颜色: {info.Color}, 数量: {info.Count}");
}
// 获取类别颜色
Color color = ModelHighlightHelper.GetCategoryColor("myCategory");
```
## 使用示例
### 示例1高亮碰撞检测结果
```csharp
// 获取碰撞结果
var collisions = collisionDetector.DetectCollisions();
// 高亮显示碰撞对象(使用预计算碰撞类别)
var collidingItems = collisions.Select(c => c.Item2).ToList();
ModelHighlightHelper.HighlightItems(
ModelHighlightHelper.PrecomputeCollisionResultsCategory,
collidingItems
);
// 操作完成后清除高亮
ModelHighlightHelper.ClearCategory(ModelHighlightHelper.PrecomputeCollisionResultsCategory);
```
### 示例2高亮通道预览
```csharp
// 高亮显示通道对象
var channelItems = GetChannelItems();
ModelHighlightHelper.HighlightItems(
ModelHighlightHelper.ChannelPreviewCategory,
channelItems
);
// 清除时只清除通道预览,不影响其他高亮
ModelHighlightHelper.ClearCategory(ModelHighlightHelper.ChannelPreviewCategory);
```
### 示例3多类别管理
```csharp
// 同时高亮不同类型对象
ModelHighlightHelper.HighlightItems("doors", doorItems);
ModelHighlightHelper.HighlightItems("elevators", elevatorItems);
ModelHighlightHelper.HighlightItems("stairs", stairItems);
// 只清除门的高亮
ModelHighlightHelper.ClearCategory("doors");
// 清除所有高亮
ModelHighlightHelper.ClearAllHighlights();
```
## 注意事项
1. **类别颜色自动管理**:每个类别有预定义颜色,也可以使用自定义类别(默认白色)
2. **自动去重**:同一对象在不同类别中可能被多次高亮,后设置的会覆盖先前的
3. **线程安全**:内部使用锁保护,可从多线程调用
4. **清除策略**
- 清除单个类别:`ClearCategory()`
- 清除所有碰撞相关:`ClearCollisionHighlights()`
- 清除全部:`ClearAllHighlights()`

View File

@ -0,0 +1,108 @@
# NavisworksApiHelper 使用指南
## 文件位置
`src/Utils/NavisworksApiHelper.cs`
## 用途
提供 Navisworks API 通用工具方法,包括缓存刷新、线程安全操作、模型项查找等。
## 核心方法
### 缓存刷新
```csharp
// 安全的缓存刷新(轻量级)
NavisworksApiHelper.SafeCacheRefresh("MyComponent");
// 线程安全的缓存刷新自动确保在UI线程执行
NavisworksApiHelper.SafeCacheRefreshUIThread("MyComponent");
```
### 线程检查与执行
```csharp
// 检查当前是否在主UI线程
bool isUIThread = NavisworksApiHelper.IsOnUIThread();
// 在UI线程执行操作无返回值
NavisworksApiHelper.ExecuteOnUIThread(() =>
{
// 这段代码确保在UI线程执行
document.CurrentSelection.Clear();
});
// 在UI线程执行操作有返回值
var result = NavisworksApiHelper.ExecuteOnUIThread(() =>
{
return document.CurrentSelection.SelectedItems.Count;
});
```
### 模型项操作
```csharp
// 获取ModelItem的显示名称安全
string name = NavisworksApiHelper.GetModelItemName(modelItem);
// 使用PathId查找ModelItem
ModelItem item = NavisworksApiHelper.FindModelItemByPathId(modelIndex, pathId);
```
## 使用示例
### 示例1API操作后刷新缓存
```csharp
// 执行某些API操作
document.Models.SetHidden(items, true);
// 刷新缓存以确保状态同步
NavisworksApiHelper.SafeCacheRefresh("VisibilityManager");
```
### 示例2确保在UI线程操作
```csharp
// 在后台线程中需要操作UI
Task.Run(() =>
{
// 后台计算...
var result = DoCalculation();
// 回到UI线程更新界面
NavisworksApiHelper.ExecuteOnUIThread(() =>
{
StatusText = $"计算完成: {result}";
ProgressBar.Value = 100;
});
});
```
### 示例3查找模型项
```csharp
// 从路径ID恢复模型项引用
var item = NavisworksApiHelper.FindModelItemByPathId(0, "1/2/3/4");
if (item != null)
{
LogManager.Info($"找到模型项: {item.DisplayName}");
}
```
### 示例4获取模型项名称安全
```csharp
// 安全的获取名称,处理空值和异常
string name = NavisworksApiHelper.GetModelItemName(modelItem);
// 如果 modelItem 为 null返回 "未知对象"
// 如果获取失败,返回 "获取失败"
```
## 注意事项
1. **缓存刷新机制**:使用空集合的 `SetHidden` 操作触发缓存更新,开销极小
2. **线程安全**`SafeCacheRefreshUIThread` 会自动检查当前线程必要时切换到UI线程
3. **异常处理**:所有方法内部都处理了异常,不会抛出错误
4. **日志前缀**:传入的日志前缀用于标识调用来源,便于调试

View File

@ -0,0 +1,202 @@
# NavisworksSelectionHelper 使用指南
## 文件位置
`src/Utils/NavisworksSelectionHelper.cs`
## 用途
提供 Navisworks 选择状态管理帮助功能,包括选择状态查询、格式化显示、事件订阅等。
## 核心方法
### 选择状态查询
```csharp
// 获取当前选择状态信息(同步)
SelectionStateResult result = NavisworksSelectionHelper.GetCurrentSelectionState();
// 获取当前选择状态信息(异步)
SelectionStateResult result = await NavisworksSelectionHelper.GetCurrentSelectionStateAsync();
// 快速检查是否有选中项
bool hasSelection = NavisworksSelectionHelper.HasSelectedItems();
```
### 设置选择
```csharp
// 设置单个模型项为选中状态
bool success = NavisworksSelectionHelper.SetModelSelection(modelItem);
// 设置多个模型项为选中状态
bool success = NavisworksSelectionHelper.SetModelSelection(modelItems);
// 传入 null 或空集合会清除选择
```
### 选择文本格式化
```csharp
// 格式化选择状态文本
string text = NavisworksSelectionHelper.FormatSelectionText(
count: result.Count,
selectedItems: result.SelectedItems,
unitName: "个模型", // 单位名称
maxDisplayCount: 3, // 最多显示名称数量
maxTotalLength: 80 // 最大总长度
);
// 基于选择结果对象格式化
string text = NavisworksSelectionHelper.FormatSelectionText(
selectionResult: result,
unitName: "个对象",
maxDisplayCount: 3,
maxTotalLength: 80
);
```
### 选择事件订阅
```csharp
// 订阅选择变化事件
var subscription = NavisworksSelectionHelper.SubscribeToSelectionChanges(
async (selectionResult) =>
{
// 处理选择变化
LogManager.Info($"选择变化: {selectionResult.Count} 个对象");
},
uiStateManager: _uiStateManager // 可选用于确保UI线程执行
);
// 取消订阅(使用完释放)
subscription.Dispose();
```
## SelectionStateResult 属性
```csharp
public class SelectionStateResult
{
public bool Success { get; set; } // 操作是否成功
public int Count { get; set; } // 选择数量
public List<ModelItem> SelectedItems { get; set; } // 选择的项目列表
public bool HasSelection { get; set; } // 是否有选择
public string ErrorMessage { get; set; } // 错误信息(失败时)
}
```
## 使用示例
### 示例1检查并显示选择状态
```csharp
// 获取选择状态
var result = NavisworksSelectionHelper.GetCurrentSelectionState();
if (!result.Success)
{
LogManager.Error($"获取选择状态失败: {result.ErrorMessage}");
return;
}
// 格式化显示
string statusText = NavisworksSelectionHelper.FormatSelectionText(result);
StatusLabel.Text = statusText;
// 启用/禁用相关按钮
EditButton.IsEnabled = result.Count == 1;
DeleteButton.IsEnabled = result.Count > 0;
```
### 示例2同步UI选择状态
```csharp
// 在ViewModel中保持选择状态同步
private void UpdateSelectionState()
{
var result = NavisworksSelectionHelper.GetCurrentSelectionState();
if (result.Count > 0)
{
// 更新属性
SelectedItems = result.SelectedItems;
SelectionText = NavisworksSelectionHelper.FormatSelectionText(result);
// 如果有单个选择,显示详细信息
if (result.Count == 1)
{
var item = result.SelectedItems[0];
SelectedItemName = item.DisplayName;
}
}
else
{
SelectedItems = new List<ModelItem>();
SelectionText = "请选择模型对象";
}
}
```
### 示例3订阅选择变化事件
```csharp
public class MyViewModel : IDisposable
{
private SelectionEventSubscription _selectionSubscription;
public void Initialize()
{
// 订阅选择变化
_selectionSubscription = NavisworksSelectionHelper.SubscribeToSelectionChanges(
OnSelectionChanged,
uiStateManager: _uiStateManager
);
}
private async Task OnSelectionChanged(SelectionStateResult result)
{
// 此方法在UI线程执行通过uiStateManager
if (result.HasSelection)
{
SelectionText = NavisworksSelectionHelper.FormatSelectionText(result);
await LoadSelectionDetails(result.SelectedItems);
}
else
{
SelectionText = "请在主界面中选择需要设置的对象";
}
}
public void Dispose()
{
_selectionSubscription?.Dispose();
}
}
```
### 示例4程序化设置选择
```csharp
// 选择特定对象
var targetItem = FindModelItemById(id);
if (targetItem != null)
{
bool success = NavisworksSelectionHelper.SetModelSelection(targetItem);
if (success)
{
// 聚焦到该对象
ViewpointHelper.FocusOnModelItem(targetItem, 45, 0.25);
}
}
// 清除选择
NavisworksSelectionHelper.SetModelSelection(null);
```
## 注意事项
1. **线程安全**`GetCurrentSelectionState` 等方法是线程安全的但设置选择最好在UI线程执行
2. **事件处理**:选择变化事件使用 `async void` 内部处理确保不会阻塞UI
3. **订阅管理**:使用 `SelectionEventSubscription` 模式确保事件正确取消订阅,避免内存泄漏
4. **格式化限制**`FormatSelectionText` 会自动处理长名称截断和多个项目的显示
5. **空值处理**:所有方法都处理了空值情况,不会抛出异常

View File

@ -0,0 +1,140 @@
# PathHelper 使用指南
## 文件位置
`src/Utils/PathHelper.cs`
## 用途
提供文件路径相关的通用方法,包括插件目录管理、文件名处理、截图生成等。
## 核心方法
### 目录操作
```csharp
// 获取插件目录路径
string pluginDir = PathHelper.GetPluginDirectory();
// 返回: C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin
// 获取截图目录路径
string screenshotDir = PathHelper.GetScreenshotDirectory();
// 获取报告目录路径
string reportDir = PathHelper.GetReportDirectory();
// 确保目录存在(不存在则创建)
PathHelper.EnsureDirectoryExists("C:\\MyFolder\\SubFolder");
```
### 文件名处理
```csharp
// 清理文件名中的非法字符
string safeName = PathHelper.SanitizeFileName("文件<名称>:非法*字符?");
// 返回: "文件名称非法字符"
// 生成带时间戳的文件名
string fileName = PathHelper.GenerateTimestampedFileName("screenshot", "png");
// 返回: "screenshot_20240225_143052.png"
```
### 路径计算
```csharp
// 计算从HTML文件到目标文件的相对路径
string relativePath = PathHelper.GetRelativePath(
@"C:\reports\index.html",
@"C:\images\photo.png"
);
// 返回: "../images/photo.png"
```
### 图像格式转换
```csharp
// ImageFormat 转文件扩展名
string ext = PathHelper.ImageFormatToExtension(ImageFormat.Jpeg); // "jpg"
string ext = PathHelper.ImageFormatToExtension(ImageFormat.Png); // "png"
// 文件扩展名转 ImageFormat
ImageFormat format = PathHelper.ExtensionToImageFormat(".jpg"); // Jpeg
ImageFormat format = PathHelper.ExtensionToImageFormat(".png"); // Png
```
### 截图生成
```csharp
// 生成场景截图到默认目录
string path = PathHelper.GenerateSceneScreenshot(
sceneName: "collision_view",
width: 1920,
height: 1080,
format: ImageFormat.Png,
prefix: "collision"
);
// 返回: 截图文件完整路径
// 生成场景截图到指定目录
string path = PathHelper.GenerateSceneScreenshotToDirectory(
outputDirectory: @"C:\MyScreenshots",
sceneName: "path_view",
width: 1920,
height: 1080,
format: ImageFormat.Jpeg,
prefix: "path"
);
```
## 使用示例
### 示例1生成带时间戳的报告文件
```csharp
// 生成报告文件名
string reportDir = PathHelper.GetReportDirectory();
PathHelper.EnsureDirectoryExists(reportDir);
string safeRouteName = PathHelper.SanitizeFileName(route.Name);
string reportFile = PathHelper.GenerateTimestampedFileName(
$"collision_report_{safeRouteName}",
"html"
);
string fullPath = Path.Combine(reportDir, reportFile);
// 保存报告...
```
### 示例2批量处理文件名
```csharp
// 清理多个文件名
var fileNames = new[] { "文件:1", "名称*2", "测试?3" };
var safeNames = fileNames.Select(f => PathHelper.SanitizeFileName(f));
// 结果: "文件1", "名称2", "测试3"
```
### 示例3生成碰撞报告截图
```csharp
// 在显示碰撞结果后生成截图
string screenshotPath = PathHelper.GenerateSceneScreenshot(
sceneName: $"collision_{collisionId}",
width: 1920,
height: 1080,
format: ImageFormat.Png,
prefix: "collision"
);
if (!string.IsNullOrEmpty(screenshotPath))
{
LogManager.Info($"截图已保存: {screenshotPath}");
}
```
## 注意事项
1. **目录自动创建**`GenerateSceneScreenshot` 会自动创建必要的目录
2. **非法字符处理**`SanitizeFileName` 会移除所有 Windows 文件系统不支持的字符
3. **时间戳格式**:使用 `yyyyMMdd_HHmmss` 格式,确保文件名唯一且可排序
4. **相对路径**`GetRelativePath` 返回的路径使用正斜杠(/),适合在 HTML 中使用

View File

@ -0,0 +1,213 @@
# SectionClipHelper 使用指南
## 文件位置
`src/Utils/SectionClipHelper.cs`
## 用途
剖面盒辅助类,用于优化碰撞检测性能。通过设置视口剖面盒,只处理路径周围的对象,大幅减少需要检测的对象数量。
## 核心方法
### 设置剖面盒
```csharp
// 根据路径点列表设置剖面盒
bool success = SectionClipHelper.SetClipBoxByPath(
pathPoints: points,
marginInMeters: 2.0, // 水平边距(米)
heightMarginInMeters: 1.0 // 高度边距(米)
);
// 根据路径点设置剖面盒(支持分别设置上下边距)
bool success = SectionClipHelper.SetClipBoxByPathWithMargins(
pathPoints: points,
marginInMeters: 2.0,
heightMarginBottomInMeters: 0.5, // 底部边距
heightMarginTopInMeters: 2.0 // 顶部边距
);
// 根据单个点设置剖面盒(用于吊装路径等)
bool success = SectionClipHelper.SetClipBoxByPoint(
centerPoint: point,
rangeMeters: 10.0, // 范围(米)
heightRangeMeters: 5.0 // 高度范围(米)
);
// 根据楼层设置剖面盒
bool success = SectionClipHelper.SetClipBoxByFloor(
floorItem: floor,
marginMeters: 1.0
);
```
### 清除剖面盒
```csharp
// 清除剖面盒,显示全部模型
SectionClipHelper.ClearClipBox();
```
### 状态查询
```csharp
// 检查剖面盒是否已启用
bool isEnabled = SectionClipHelper.IsClipBoxEnabled;
// 获取当前剖面盒
if (SectionClipHelper.TryGetCurrentClipBox(out BoundingBox3D clipBox))
{
LogManager.Info($"剖面盒范围: {clipBox.Min} - {clipBox.Max}");
}
```
### 包含性测试
```csharp
// 测试点是否在剖面盒内
bool isInside = SectionClipHelper.IsPointInClipBox(point);
// 测试包围盒是否与剖面盒相交
bool intersects = SectionClipHelper.IntersectsClipBox(itemBox);
// 使用角点检测(排除大包围盒假阳性)
bool intersects = SectionClipHelper.IntersectsByCornerPoints(itemBox, clipBox);
// 使用棱上自适应采样检测(长物体不会漏检)
bool intersects = SectionClipHelper.IntersectsByEdgeSampling(
itemBox, clipBox, minSamplesPerEdge: 3
);
```
### 统计功能
```csharp
// 统计剖面盒内/外的对象数量
SectionClipHelper.CountObjectsInClipBox(
out int totalCount,
out int insideCount,
out int outsideCount
);
// 使用角点检测统计(排除大包围盒假阳性)
SectionClipHelper.CountObjectsInClipBoxByCorners(
out int totalCount,
out int insideCount,
out int outsideCount,
out int largeBoxFiltered,
debugDetails: detailsList // 可选的详细信息列表
);
// 测试相交检测(调试用)
SectionClipHelper.TestIntersection();
```
## 使用示例
### 示例1路径碰撞检测前设置剖面盒
```csharp
// 获取路径点
var pathPoints = pathRoute.Points.Select(p => p.Position).ToList();
// 设置剖面盒
bool success = SectionClipHelper.SetClipBoxByPath(
pathPoints,
marginInMeters: 2.0, // 路径周围2米
heightMarginInMeters: 1.0 // 上下各1米
);
if (success)
{
// 统计过滤效果
SectionClipHelper.CountObjectsInClipBoxByCorners(
out int total, out int inside, out int outside, out int filtered
);
LogManager.Info($"剖面盒过滤: 总数{total}, 盒内{inside}, 过滤{filtered}");
// 执行碰撞检测(只检测剖面盒内对象)
var collisions = collisionDetector.DetectCollisions();
}
// 完成后清除剖面盒
SectionClipHelper.ClearClipBox();
```
### 示例2吊装路径场景
```csharp
// 吊装路径通常只需要关注起吊点周围区域
if (liftPoint != null)
{
SectionClipHelper.SetClipBoxByPoint(
liftPoint,
rangeMeters: 15.0, // 15米范围
heightRangeMeters: 20.0 // 20米高度范围
);
}
```
### 示例3精确过滤排除大包围盒假阳性
```csharp
// 获取剖面盒
if (!SectionClipHelper.TryGetCurrentClipBox(out var clipBox))
return;
// 遍历模型进行过滤
foreach (var item in modelItems)
{
var itemBox = item.BoundingBox();
// 先用包围盒快速排除
if (!clipBox.Intersects(itemBox))
continue;
// 再用角点检测精确判断
if (!SectionClipHelper.IntersectsByCornerPoints(itemBox, clipBox))
{
// 包围盒相交但没有角点在盒内,可能是大包围盒假阳性
LogManager.Debug($"跳过假阳性: {item.DisplayName}");
continue;
}
// 该对象真正与剖面盒相交,加入检测列表
itemsToCheck.Add(item);
}
```
### 示例4长物体检测使用棱上采样
```csharp
// 对于长管道等物体,使用棱上采样确保不遗漏
if (SectionClipHelper.TryGetCurrentClipBox(out var clipBox))
{
var itemBox = longPipeItem.BoundingBox();
// 使用棱上自适应采样(根据物体大小动态计算采样点)
if (SectionClipHelper.IntersectsByEdgeSampling(itemBox, clipBox, minSamplesPerEdge: 5))
{
// 长管道与剖面盒相交
itemsToCheck.Add(longPipeItem);
}
}
```
## 默认参数
```csharp
private const double DEFAULT_MARGIN_METERS = 2.0; // 默认水平边距
private const double DEFAULT_HEIGHT_MARGIN_METERS = 1.0; // 默认高度边距
```
## 注意事项
1. **性能优化**:剖面盒可以大幅减少碰撞检测的对象数量,提高性能
2. **JSON设置**:使用 JSON 字符串方式设置剖面盒,避免 "Object is Read-Only" 错误
3. **过滤策略**
- 先用 `IntersectsClipBox` 快速排除
- 再用 `IntersectsByCornerPoints` 排除大包围盒假阳性
- 最后用 `IntersectsByEdgeSampling` 确保长物体不遗漏
4. **清除责任**:使用完剖面盒后应调用 `ClearClipBox()` 恢复完整视图
5. **统计功能**:使用 `CountObjectsInClipBoxByCorners` 查看过滤效果和假阳性数量

View File

@ -0,0 +1,144 @@
# ViewpointHelper 使用指南
## 文件位置
`src/Utils/ViewpointHelper.cs`
## 用途
提供 Navisworks 视角调整功能,用于自动调整摄像机位置和视角,支持路径居中显示、碰撞位置聚焦等。
## 核心方法
### 路径视角调整
```csharp
// 智能调整视角到路径中心
ViewpointHelper.AdjustViewpointSmart(path, collisions);
// 调整视角到路径中心,确保整个路径在视野内
ViewpointHelper.AdjustViewpointToPathCenter(path);
// 计算路径的包围盒
BoundingBox3D bounds = ViewpointHelper.CalculatePathBoundingBox(path);
```
### 碰撞视角调整
```csharp
// 计算碰撞位置的包围盒
BoundingBox3D bounds = ViewpointHelper.CalculateCollisionsBoundingBox(collisions);
// 聚焦到碰撞对象(两个对象)
ViewpointHelper.FocusOnCollision(item1, item2, viewAngleDegrees: 45, targetViewRatio: 0.25);
// 聚焦到指定位置
ViewpointHelper.FocusOnPosition(center, targetSize, viewAngleDegrees: 45, targetViewRatio: 0.25);
```
### 模型元素聚焦
```csharp
// 聚焦到单个模型元素
ViewpointHelper.FocusOnModelItem(modelItem, viewAngleDegrees: 45, targetViewRatio: 0.25);
// viewAngleDegrees: 视角角度(度)
// targetViewRatio: 目标占据视图比例0.25 = 1/4
```
### 视角保存与恢复
```csharp
// 保存当前视角
Viewpoint savedViewpoint = ViewpointHelper.SaveCurrentViewpoint();
// 恢复视角
ViewpointHelper.RestoreViewpoint(savedViewpoint);
```
## 使用示例
### 示例1生成碰撞报告前调整视角
```csharp
// 保存当前视角
Viewpoint originalViewpoint = ViewpointHelper.SaveCurrentViewpoint();
try
{
// 调整视角到路径中心
ViewpointHelper.AdjustViewpointToPathCenter(path);
// 生成截图
string screenshotPath = PathHelper.GenerateSceneScreenshot(
"collision_report", 1920, 1080, ImageFormat.Png
);
}
finally
{
// 恢复原始视角
ViewpointHelper.RestoreViewpoint(originalViewpoint);
}
```
### 示例2聚焦到碰撞位置
```csharp
// 检测到碰撞后,聚焦到碰撞对象
if (collision.Item1 != null && collision.Item2 != null)
{
ViewpointHelper.FocusOnCollision(
collision.Item1,
collision.Item2,
viewAngleDegrees: 45, // 45度视角
targetViewRatio: 0.3 // 占据视图30%
);
// 高亮碰撞对象
ModelHighlightHelper.HighlightItems("collision", new[] { collision.Item1, collision.Item2 });
}
```
### 示例3聚焦到特定模型元素
```csharp
// 用户选择了一个门对象
var doorItem = selectedItems.FirstOrDefault();
if (doorItem != null)
{
// 聚焦到该门45度斜上方视角
ViewpointHelper.FocusOnModelItem(doorItem, 45, 0.25);
LogManager.Info($"已聚焦到: {doorItem.DisplayName}");
}
```
### 示例4路径规划完成后调整视角
```csharp
// 路径规划完成
if (pathRoute.Points.Count > 0)
{
// 智能调整视角
ViewpointHelper.AdjustViewpointSmart(pathRoute, collisionResults);
// 高亮路径点
var pathItems = pathRoute.Points.Select(p => p.AssociatedModelItem).Where(i => i != null);
ModelHighlightHelper.HighlightItems("pathPreview", pathItems);
}
```
## 视角参数说明
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| `viewAngleDegrees` | 相机视角角度(度) | 45°斜上方 |
| `targetViewRatio` | 目标占据视图比例 | 0.251/4视图 |
| `baseDimension` | 基准尺寸计算相机距离 | 路径长度或包围盒最大边 |
## 注意事项
1. **视角保存**:在进行视角调整前建议保存当前视角,便于后续恢复
2. **异常处理**:方法会抛出 `InvalidOperationException`(无活动文档)和 `ArgumentException`(参数错误),需要适当捕获
3. **单位转换**:内部自动使用 `UnitsConverter` 进行米和模型单位的转换
4. **标准视角**`FocusOnModelItem` 和 `FocusOnPosition` 使用模型的标准前右上视角(`FrontRightTopViewVector`
5. **性能考虑**:频繁调整视角可能影响性能,建议在关键操作后统一调整

View File

@ -0,0 +1,112 @@
# VisibilityHelper 使用指南
## 文件位置
`src/Utils/VisibilityHelper.cs`
## 用途
可见性管理器,负责 ModelItem 可见性控制的核心业务逻辑,包括显示全部、隔离显示、设置可见性等。
## 核心方法
### 显示所有项目
```csharp
// 显示所有模型项目(重置所有隐藏状态)
bool success = VisibilityHelper.ShowAllItems();
```
### 隔离显示
```csharp
// 仅显示指定的模型项集合,隐藏其他所有项
bool success = VisibilityHelper.IsolateSpecificItems(itemsToShow);
// 示例:隔离显示路径相关的对象
var pathItems = GetPathRelatedItems();
VisibilityHelper.IsolateSpecificItems(pathItems);
```
### 设置可见性
```csharp
// 设置指定模型项集合的可见性
bool success = VisibilityHelper.SetItemsVisibility(items, isHidden: true); // 隐藏
bool success = VisibilityHelper.SetItemsVisibility(items, isHidden: false); // 显示
```
## 使用示例
### 示例1隔离显示碰撞对象
```csharp
// 收集所有碰撞涉及的对象
var collisionItems = new ModelItemCollection();
foreach (var collision in collisionResults)
{
if (collision.Item1 != null) collisionItems.Add(collision.Item1);
if (collision.Item2 != null) collisionItems.Add(collision.Item2);
}
// 只显示碰撞对象
VisibilityHelper.IsolateSpecificItems(collisionItems);
// 高亮碰撞对象
ModelHighlightHelper.HighlightItems("collisions", collisionItems);
```
### 示例2分步显示/隐藏
```csharp
// 隐藏所有障碍物
var obstacles = GetObstacleItems();
VisibilityHelper.SetItemsVisibility(obstacles, isHidden: true);
// 后续操作...
// 重新显示障碍物
VisibilityHelper.SetItemsVisibility(obstacles, isHidden: false);
```
### 示例3完整的显示重置流程
```csharp
// 清除所有高亮
ModelHighlightHelper.ClearAllHighlights();
// 显示所有项目
VisibilityHelper.ShowAllItems();
// 清除剖面盒
SectionClipHelper.ClearClipBox();
LogManager.Info("视图已重置");
```
### 示例4通道可视化
```csharp
// 只显示通道相关的对象
var channelItems = GetChannelItems();
VisibilityHelper.IsolateSpecificItems(channelItems);
// 调整视角到通道
ViewpointHelper.AdjustViewpointToPathCenter(channelPath);
// 生成通道预览图
string screenshot = PathHelper.GenerateSceneScreenshot(
"channel_view", 1920, 1080, ImageFormat.Png
);
// 恢复显示所有
VisibilityHelper.ShowAllItems();
```
## 注意事项
1. **隔离显示原理**`IsolateSpecificItems` 会先显示所有,然后隐藏不需要的项目
2. **祖先节点处理**:隔离显示会自动包含选中项的所有祖先节点,确保路径完整
3. **性能考虑**:大量项目的可见性操作可能影响性能,建议批量处理
4. **与剖面盒配合**:可以与 `SectionClipHelper` 配合使用,进一步减少可见对象
5. **错误处理**:所有方法都返回 `bool` 表示成功/失败,内部已处理异常