NavisworksTransport/doc/working/2026-02-08-human-in-the-loop-collision-optimization.md

9.9 KiB
Raw Blame History

Human-in-the-Loop 碰撞优化功能 - 实现完成报告

当前状态2026-02-09

已完成功能

第一阶段(基础分析)

  1. 碰撞热点自动分析 - 预计算完成后自动分析碰撞结果
  2. 自动高亮显示 - 自动高亮所有预计算碰撞结果(紫色 #9C27B0
  3. 分析对话框 - 显示详细的碰撞统计和建议排除选项
  4. 光标修复 - 对话框显示时光标恢复正常

第二阶段(排除列表集成)

  1. 排除列表数据结构 - PathAnimationManager 中使用 HashSet<ModelItem>
  2. 管理方法 - SetExcludedObjectsAndClearCacheAddExcludedObjectsClearExcludedObjects
  3. 预计算过滤 - 在 PrecomputeAnimationFrames() 中应用排除列表过滤
  4. 对话框集成 - 分析结果自动合并到排除列表
  5. UI区域 - 新增"检测排除对象"区域,支持手工添加和显示排除列表

第三阶段UI优化

  1. 碰撞分析对话框增强

    • 序号列显示
    • 行点击自动高亮对应物体(紫色预计算风格)
    • 候选碰撞数字黑色粗体显示
    • 预计检测时间显示xxx秒/xx.x分钟
    • 智能自动选中(碰撞>100次 或 占比>50%
    • 单一"继续生成动画"按钮(自动判断是否有排除对象)
  2. 排除列表UI完善

    • 添加"清除高亮"按钮
    • 删除列表项时自动清除高亮
    • 使用 ModelItemEquals 正确比较对象(修复 InstanceGuid 问题)

第四阶段(数据库持久化)

  1. 数据库表结构

    • ExcludedObjects - 存储排除对象PathId、DisplayName、ModelIndex等
    • CollisionReportExcludedObjects - 碰撞报告与排除对象关联表
    • ClashDetectiveExcludedObjects - ClashDetective结果与排除对象关联表
  2. 数据持久化

    • 排除对象随碰撞报告一起保存到数据库
    • 支持从数据库加载排除对象通过PathId查找
    • 碰撞历史和报告中显示当时使用的排除对象列表
  3. 核心方法

    • SaveExcludedObjectsToDatabase() - 保存排除对象到数据库
    • LoadExcludedObjectsFromDatabase() - 从数据库加载排除对象
    • LinkExcludedObjectsToCollisionReport() - 关联排除对象到碰撞报告

新增/修改文件

文件 说明
src/UI/WPF/Views/CollisionAnalysisDialog.xaml 分析对话框UI已统一项目风格
src/UI/WPF/Views/CollisionAnalysisDialog.xaml.cs 分析对话框逻辑(序号、高亮、智能选中)
src/UI/WPF/ViewModels/AnimationControlViewModel.cs 添加分析逻辑、排除列表管理、数据库保存
src/Core/Animation/PathAnimationManager.cs 排除列表字段、预计算过滤、数据库加载/保存
src/UI/WPF/Views/AnimationControlView.xaml "检测排除对象"UI区域
src/Utils/ModelItemAnalysisHelper.cs 添加 ModelItemEquals 正确比较方法
src/Core/PathDatabase.cs 添加排除列表相关的数据库表和方法

技术实现细节

排除列表存储与比较

// 使用 HashSet<ModelItem> 存储排除对象
private HashSet<ModelItem> _excludedObjects = new HashSet<ModelItem>();

// 正确的 ModelItem 比较(使用底层原生对象比较)
public static bool ModelItemEquals(ModelItem item1, ModelItem item2)
{
    if (item1 == null && item2 == null) return true;
    if (item1 == null || item2 == null) return false;
    return item1.Equals(item2);  // 使用 Navisworks API 的 Equals
}

重要修复:之前使用 InstanceGuid 比较会导致不同对象被误判为相同,现在统一使用 ModelItemEquals

预计算过滤点

// 在 PrecomputeAnimationFrames 的循环中
var nearbyObjects = spatialIndexManager.FindInAABB(searchBounds, excludeObject: _animatedObject);

// 应用排除列表过滤
nearbyObjects = nearbyObjects.Where(obj => !_excludedObjects.Contains(obj));

智能自动选中逻辑

// 碰撞分析对话框中的智能选中条件
IsExcluded = h.CollisionCount > 100 || h.Percentage > 50

行点击高亮

// 使用预计算碰撞结果高亮类别(紫色 #9C27B0
private const string PrecomputedHighlightCategory = ModelHighlightHelper.PrecomputeCollisionResultsCategory;

// 点击行时高亮对应物体
ModelHighlightHelper.HighlightItems(PrecomputedHighlightCategory, items);

UI 绑定

// ViewModel 属性
public ThreadSafeObservableCollection<ExcludedObjectViewModel> ExcludedObjects { get; }
public string ExcludedObjectSummary { get; set; }
public bool HasExcludedObjects { get; }

// 命令
public ICommand AddExcludedObjectsFromSelectionCommand { get; }
public ICommand ClearExcludedObjectsCommand { get; }
public ICommand RemoveExcludedObjectCommand { get; }
public ICommand HighlightAllExcludedObjectsCommand { get; }
public ICommand ClearExcludedHighlightCommand { get; }

数据库持久化

数据库表结构

-- 排除对象表
CREATE TABLE ExcludedObjects (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    RouteId TEXT,  -- 关联路径ID可为空表示全局排除
    ModelIndex INTEGER NOT NULL,
    PathId TEXT NOT NULL,  -- 格式: "模型索引:路径索引数组"
    DisplayName TEXT,
    ObjectName TEXT,
    ExcludedTime DATETIME DEFAULT CURRENT_TIMESTAMP,
    Reason TEXT,
    IsGlobal INTEGER DEFAULT 0
);

-- 碰撞报告与排除对象关联表
CREATE TABLE CollisionReportExcludedObjects (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    ReportId INTEGER NOT NULL,
    ExcludedObjectId INTEGER NOT NULL
);

保存排除对象到数据库

// 在生成碰撞报告时保存排除对象
public void SaveExcludedObjectsToDatabase(PathDatabase database, string routeId)
{
    var records = new List<ExcludedObjectRecord>();
    foreach (var obj in _excludedObjects)
    {
        var record = new ExcludedObjectRecord
        {
            RouteId = routeId,
            ModelIndex = GetModelIndex(obj),
            PathId = GetModelItemPathId(obj),  // 格式: "0:1,2,3"
            DisplayName = obj.DisplayName,
            ObjectName = GetModelItemObjectName(obj),
            ExcludedTime = DateTime.Now,
            Reason = "用户排除"
        };
        records.Add(record);
    }
    database.SaveExcludedObjects(records);
}

从数据库加载排除对象

// 根据PathId查找ModelItem
private ModelItem FindModelItemByPathId(Document doc, string pathId)
{
    // PathId格式: "模型索引:路径索引数组"
    // 例如: "0:1,2,3" 表示第0个模型的第1->2->3个节点
    var parts = pathId.Split(':');
    int modelIndex = int.Parse(parts[0]);
    var pathIndices = parts[1].Split(',').Select(int.Parse).ToArray();
    
    var model = doc.Models[modelIndex];
    ModelItem current = model.RootItem;
    foreach (var index in pathIndices)
    {
        var childrenList = current.Children.ToList();
        current = childrenList[index];
    }
    return current;
}

工作流程

1. 动画生成流程

用户点击"生成动画"
    ↓
预计算碰撞检测
    ↓
分析碰撞热点(>5次或>10%
    ↓
显示分析对话框(自动选中高频物体)
    ↓
用户调整勾选/点击行查看高亮
    ↓
点击"继续生成动画"
    ↓
[如果有排除对象] 添加到排除列表 → 清除缓存 → 重新生成
[如果无排除对象] 直接继续

2. 排除列表管理流程

手工添加:选择物体 → 点击"从选择添加" → 添加到列表
分析添加:分析对话框勾选 → 点击"继续生成动画" → 合并到列表
删除单个:点击列表项的删除 → 清除高亮 → 同步到Manager
清除所有:点击"清除所有" → 清空列表 → 清除高亮
高亮显示:点击"全部高亮"/"清除高亮"

3. 数据库持久化流程

生成碰撞报告
    ↓
保存排除对象到数据库ExcludedObjects表
    ↓
关联排除对象到碰撞报告CollisionReportExcludedObjects表
    ↓
查看碰撞历史时
    ↓
加载碰撞报告关联的排除对象列表
    ↓
显示"本次检测排除了X个对象"

使用指南

1. 通过分析对话框排除

  1. 生成动画时自动触发预计算分析
  2. 分析对话框显示高频碰撞物体(已自动选中>100次或>50%的)
  3. 点击表格行可在3D视图中高亮对应物体紫色
  4. 调整勾选状态后点击"继续生成动画"
  5. 勾选的对象会被加入排除列表并重新生成

2. 手工管理排除列表

  1. 在动画控制面板的"检测排除对象"区域
  2. 在模型中选择要排除的物体
  3. 点击"从选择添加"
  4. 已排除物体在碰撞检测中会被忽略
  5. 可随时"全部高亮"查看或"清除所有"清空

预期效果

减少 ClashDetective 测试数量

  • 典型场景379 次碰撞 -> 排除地面后可能减少到 50 次以下
  • 按每次测试 133ms 计算50s -> 6.6s,节省 86% 时间

提高结果质量

  • 减少地面/楼板假阳性碰撞
  • 让用户专注于真实障碍物
  • 更准确的碰撞报告

后续可能的改进方向

智能建议增强

  • 基于包围盒大小识别大型物体(>1000立方米
  • 基于位置识别:位于路径下方的物体可能是地面
  • 学习用户历史排除选择,优先建议

碰撞类型分类

  • 区分"疑似假阳性"(地面/楼板接触)
  • 区分"真实碰撞"(墙体/障碍物)

性能优化

  • 增量预计算(仅重新计算受影响的帧)
  • 分析结果持久化到数据库

注意事项

  1. 排除列表生命周期:当前为运行时管理,切换路径后保留,关闭文档后失效
  2. ModelItem比较:必须使用 ModelItemEquals 方法,不能用 InstanceGuid
  3. 缓存机制:排除列表变更会触发 SetExcludedObjectsAndClearCache 清除动画缓存
  4. 高亮颜色
    • 排除对象:绿色 (#4CAF50)
    • 预计算碰撞:紫色 (#9C27B0)
    • 手动目标:橙色 (#FFAA00)