碰撞报告自动对碰撞点截图;纠正插件改名造成的路径错误。

This commit is contained in:
tian 2026-02-17 10:21:35 +08:00
parent 08b4ecc5d5
commit a304191b13
24 changed files with 321 additions and 158 deletions

View File

@ -12,8 +12,8 @@
"Bash(compile.bat)",
"Bash(./compile.bat)",
"Bash(./tool/compile.bat:*)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" NavisworksTransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" NavisworksTransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" TransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" TransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU)",
"WebFetch(domain:forums.autodesk.com)",
"mcp__serena__search_for_pattern",
"mcp__serena__find_symbol",
@ -42,13 +42,13 @@
"Bash(./nuget.exe install:*)",
"Bash(dotnet test:*)",
"Bash(msbuild:*)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" \"C:\\Users\\Tellme\\apps\\NavisworksTransport\\NavisworksTransportPlugin.csproj\" /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" \"C:\\Users\\Tellme\\apps\\NavisworksTransport\\TransportPlugin.csproj\" /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal)",
"Bash(grep:*)",
"Bash(find:*)",
"mcp__context7__get-library-docs",
"Bash(powershell:*)",
"mcp__serena__insert_before_symbol",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" NavisworksTransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:normal)",
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" TransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:normal)",
"Bash(cmd:*)",
"WebFetch(domain:twentytwo.space)",
"Bash(git add:*)",

View File

@ -273,7 +273,7 @@ deploy-plugin.bat
1. 编译生成 `TransportPlugin.dll`
2. 将输出目录内容复制到Navisworks插件目录:
`[Navisworks安装路径]\Plugins\NavisworksTransportPlugin\`
`[Navisworks安装路径]\Plugins\TransportPlugin\`
3. 重启Navisworks即可使用
### 安装包生成
@ -558,7 +558,7 @@ detection_gap_meters = 0.05
### 日志位置
日志文件位于:
`[Navisworks安装路径]\Plugins\NavisworksTransportPlugin\logs\debug.log`
`[Navisworks安装路径]\Plugins\TransportPlugin\logs\debug.log`
### 文档资源

12
QWEN.md
View File

@ -46,7 +46,7 @@ NavisworksTransport 是一个针对 Autodesk Navisworks Manage 2026 开发的插
### 项目结构
```
NavisworksTransportPlugin/
TransportPlugin/
├── src/
│ ├── Core/ # 核心业务逻辑
│ │ ├── Animation/ # 动画管理 (LogisticsAnimationManager, TimeLinerIntegrationManager)
@ -67,7 +67,7 @@ NavisworksTransportPlugin/
│ └── Legacy/ # 为兼容性保留的旧代码
├── Properties/
│ └── AssemblyInfo.cs
├── NavisworksTransportPlugin.csproj # 项目文件
├── TransportPlugin.csproj # 项目文件
└── ...
```
@ -126,19 +126,19 @@ NavisworksTransportPlugin/
2. 确保项目引用指向正确的 Navisworks 2026 API DLL 文件(路径在 `.csproj` 文件中定义)。
3. 选择 "Debug" 或 "Release" 配置。
4. 构建项目 (`Ctrl+Shift+B`)。
5. 输出文件为 `NavisworksTransportPlugin.dll`,位于 `bin\Debug\``bin\Release\` 目录下。
5. 输出文件为 `TransportPlugin.dll`,位于 `bin\Debug\``bin\Release\` 目录下。
### 部署与安装
1. 编译生成 `NavisworksTransportPlugin.dll`。
1. 编译生成 `TransportPlugin.dll`。
2. 插件会自动安装到 Navisworks 插件目录:
`[Navisworks安装路径]\Plugins\NavisworksTransportPlugin\`
`[Navisworks安装路径]\Plugins\TransportPlugin\`
3. 重启 Navisworks 即可在 "附加模块" 选项卡中找到插件。
### 运行与调试
* 在 Visual Studio 中可以直接调试插件,方法是设置 Navisworks 可执行文件(如 `NwAddinDev.exe``Navisworks.exe`)为启动外部程序。
* 插件运行时会在 `%AppData%\Autodesk Navisworks Manage 2026\PluginsData\NavisworksTransportPlugin` 目录下生成日志文件。
* 插件运行时会在 `%AppData%\Autodesk Navisworks Manage 2026\PluginsData\TransportPlugin` 目录下生成日志文件。
## 开发约定与实践

View File

@ -331,6 +331,7 @@
<Compile Include="src\Utils\ModelItemTransformHelper.cs" />
<Compile Include="src\Utils\CachedTriangle3D.cs" />
<Compile Include="src\Utils\PathHelper.cs" />
<Compile Include="src\Utils\CollisionSceneHelper.cs" />
<!-- Assembly Info -->
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View File

@ -553,8 +553,8 @@ public class NavisworksIntegration
```
%ProgramFiles%\Autodesk\Navisworks Manage 2026\Plugins\
└── NavisworksTransportPlugin\
├── NavisworksTransportPlugin.dll # 主程序集
└── TransportPlugin\
├── TransportPlugin.dll # 主程序集
├── RoyT.AStar.dll # A*算法库
├── Newtonsoft.Json.dll # JSON处理库
├── Resources\ # 资源文件

View File

@ -654,7 +654,7 @@ public class PluginInstaller
private static readonly string NavisworksPath =
@"%PROGRAMFILES%\Autodesk\Navisworks Manage 2017";
private static readonly string PluginTargetPath =
@"%PROGRAMFILES%\Autodesk\Navisworks Manage 2017\Plugins\NavisworksTransportPlugin";
@"%PROGRAMFILES%\Autodesk\Navisworks Manage 2017\Plugins\TransportPlugin";
public static bool InstallPlugin()
{

View File

@ -40,5 +40,5 @@
## 编译命令
```
& "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" NavisworksTransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal
& "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" TransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal
```

View File

@ -95,7 +95,7 @@
`添加项目输出组` 对话框中:
- 项目:`NavisworksTransportPlugin`
- 项目:`TransportPlugin`
- 选择配置:`Release`
- 平台:`x64`**重要:必须与项目平台一致,否则会出现平台不兼容错误**
- 输出类型:选择 `Primary output`
@ -142,7 +142,7 @@
**排除方法:**
1. **通过文件系统编辑器排除**
- 在文件系统编辑器中,展开 `Application Folder``Primary output from NavisworksTransportPlugin (Active)`
- 在文件系统编辑器中,展开 `Application Folder``Primary output from TransportPlugin (Active)`
- 找到需要排除的DLL如System.Net.Http.dll
- 右键点击该DLL → `属性窗口`
- 将 `Exclude` 属性设置为 `TRUE`
@ -190,7 +190,7 @@
插件需要安装到 Navisworks 的插件目录:
默认路径: C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\NavisworksTransportPlugin\
默认路径: C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin\
### 设置方法
@ -199,7 +199,7 @@
3. 将AlwaysCreate属性设置为`TRUE`
4. 在属性窗口中设置 `DefaultLocation`
DefaultLocation = [CommonAppDataFolder]\Autodesk\Navisworks Manage 2026\plugins\NavisworksTransportPlugin
DefaultLocation = [CommonAppDataFolder]\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin
### 目录结构说明
@ -328,8 +328,8 @@ Visual Studio Installer Projects 会生成两个安装文件,它们各有不
- `{ProductCode}`: 安装日志中的产品代码
3. **验证安装**:
- 检查目录:`C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\NavisworksTransportPlugin\`
- 确认文件存在:`NavisworksTransportPlugin.dll`
- 检查目录:`C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin\`
- 确认文件存在:`TransportPlugin.dll`
- 启动Navisworks 2026检查插件是否加载
---
@ -397,7 +397,7 @@ pause
msiexec /i "bin\Release\NavisworksTransport.Setup.msi" /qn /l*v "logs\install.log"
@echo 检查安装结果...
if exist "C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\NavisworksTransportPlugin\NavisworksTransportPlugin.dll" (
if exist "C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin\TransportPlugin.dll" (
echo ✅ 部署成功!
) else (
echo ❌ 部署失败请检查安装日志logs\install.log
@ -416,7 +416,7 @@ pause
**解决**:
1. 检查项目引用:`NavisworksTransportPlugin.csproj`
1. 检查项目引用:`TransportPlugin.csproj`
2. 确保所有NuGet包已还原
3. 清理解决方案:`Build` → `Clean Solution`
4. 重新构建:`Build` → `Rebuild Solution`
@ -433,7 +433,7 @@ pause
1. 检查安装目录:
C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\NavisworksTransportPlugin\
C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin\
2. 检查Navisworks加载日志
@ -492,8 +492,8 @@ pause
### 1. 项目组织
NavisworksTransport/
├── NavisworksTransportPlugin.sln
├── NavisworksTransportPlugin/ # 插件项目
├── TransportPlugin.sln
├── TransportPlugin/ # 插件项目
├── NavisworksTransport.Setup/ # 安装项目
├── doc/design/2026/
│ └── MSI_Installation_Guide.md
@ -529,7 +529,7 @@ NavisworksTransport/
**仅包含必要的文件:**
- 自定义应用程序DLLNavisworksTransportPlugin.dll
- 自定义应用程序DLLTransportPlugin.dll
- 第三方库DLL如SQLite.NET.dll等
- 配置文件config.json等
- 资源文件(图标、文档等)
@ -590,7 +590,7 @@ NavisworksTransport/
NavisworksTransport.Setup/
├── 文件系统编辑器
│ ├── Application Folder/
│ │ ├── Primary output from NavisworksTransportPlugin (Active)
│ │ ├── Primary output from TransportPlugin (Active)
│ │ └── license.txt
└── 系统必备组件
├── Microsoft .NET Framework 4.8 (x86 and x64)

View File

@ -203,22 +203,22 @@
4.2 构建与产物
- 解决方案NavisworksTransport.sln
- 项目:NavisworksTransportPlugin.csproj
- 产物:NavisworksTransportPlugin.dllbin/Debug 或 bin/Release
- 项目TransportPlugin.csproj
- 产物TransportPlugin.dllbin/Debug 或 bin/Release
4.3 安装与部署流程
- 自动安装(一键安装):
1) 识别 Navisworks 安装目录;
2) 将插件 DLL 与必要资源复制到 [Navisworks安装路径]\Plugins\NavisworksTransportPlugin\
2) 将插件 DLL 与必要资源复制到 [Navisworks安装路径]\Plugins\TransportPlugin\
3) 注册菜单/配置(如需);
- 手动安装:
1) 复制 NavisworksTransportPlugin.dll 至上述目录;
1) 复制 TransportPlugin.dll 至上述目录;
2) 重启 Navisworks在“附加模块”中查看“物流路径规划”。
4.4 运行配置
- 日志输出:%AppData%\Autodesk Navisworks Manage 2017\PluginsData\NavisworksTransportPlugin按 QWEN 中 2026 路径作适配);
- 日志输出:%AppData%\Autodesk Navisworks Manage 2017\PluginsData\TransportPlugin按 QWEN 中 2026 路径作适配);
- 配置切换:
- 自动路径规划模式网格参数cellSize、objectSize、safetyMargin、objectHeight
- 2.5D/BoundingBox 模式选择(自动/强制选项)。

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 物流路径规划数据交换文件 -->
<PathPlanningData xmlns="http://example.com/navisworks/delmia/pathplanning"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/navisworks/delmia/pathplanning pathplanning.xsd" exportTimestamp="2023-10-27T10:30:00Z" sourceApplication="NavisworksTransportPlugin"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/navisworks/delmia/pathplanning pathplanning.xsd" exportTimestamp="2023-10-27T10:30:00Z" sourceApplication="TransportPlugin"
version="1.0"> <ProjectInfo>
<ProjectId>PRJ-12345</ProjectId>
<ProjectName>Main Hospital Construction</ProjectName> <Description>Path for Delivery Truck #1 and Crane #2</Description> </ProjectInfo>

View File

@ -15,7 +15,7 @@
3. [ ] (优化)考虑是否给截图按碰撞记录组织成子文件夹
4. [ ] 优化研究如何利用ClashDetective的多线程支持打开多线程
5. [x] 优化给每个检测到的碰撞对象增加单独视角45度右上方
6. [ ] (优化)点击碰撞列表的碰撞详情时,要把运动物体移动到位,查看碰撞情况
6. [x] (优化)点击碰撞列表的碰撞详情时,要把运动物体移动到位,查看碰撞情况
### [2026/2/6]

View File

@ -28,7 +28,7 @@ NavisworksTransport是一个Navisworks 2017插件项目旨在实现物流路
2. **关键文件发现**
- `PathAnimationManager.cs`已实现但使用了不可用的API
- `NavigationMapWindow.cs`:已集成动画控制面板和事件处理
- `NavisworksTransportPlugin.csproj`已包含PathAnimationManager.cs
- `TransportPlugin.csproj`已包含PathAnimationManager.cs
3. **API限制发现**
- Navisworks 2017项目引用`Autodesk.Navisworks.Api.dll`、`Autodesk.Navisworks.ComApi.dll`等

View File

@ -302,7 +302,7 @@ cpropcates.SetUserDefined(0, "物流分类", "Logistics_InternalName", newcate);
* [2025-01-11 当前时间]
* 步骤4. 更新项目引用
* 修改:在NavisworksTransportPlugin.csproj中添加System.Drawing引用
* 修改在TransportPlugin.csproj中添加System.Drawing引用
* 更改摘要验证现有COM API引用完整性添加缺失的System.Drawing引用以支持UI控件的Font、Color等类型确保编译所需的所有引用都已包含
* 原因执行计划步骤4
* 阻碍:无

View File

@ -156,7 +156,7 @@ public class PathRoute
* [2024-12-22 01:32:15]
* 步骤9. 添加路径验证/优化功能
* 修改PathPlanningManager.cs添加路径验证和优化方法、无参构造函数、ShowPathPlanningInterface和AddRoute方法、PathPlanningModels.cs添加验证优化数据结构、NavigationMapWindow.cs添加验证优化UINavisworksTransportPlugin.csproj添加源文件引用
* 修改PathPlanningManager.cs添加路径验证和优化方法、无参构造函数、ShowPathPlanningInterface和AddRoute方法、PathPlanningModels.cs添加验证优化数据结构、NavigationMapWindow.cs添加验证优化UI、TransportPlugin.csproj添加源文件引用
* 更改摘要成功实现路径验证和优化功能包括基本结构验证、几何有效性验证、通道约束验证、碰撞检测、可达性验证以及路径优化算法移除重复点、路径平滑、角度优化、通道中心调整。修复了MainPlugin.cs兼容性问题确保项目能正确编译
* 原因:执行计划步骤 9包含微小修正
* 阻碍:无

View File

@ -76,7 +76,7 @@
### 最终编译结果
```
NavisworksTransportPlugin -> bin\Debug\NavisworksTransportPlugin.dll
TransportPlugin -> bin\Debug\TransportPlugin.dll
Build successful!
```

View File

@ -69,7 +69,7 @@
**编译结果**: ✅ 成功
- 仅有警告,无错误
- 生成的DLL: `bin/Debug/NavisworksTransportPlugin.dll`
- 生成的DLL: `bin/Debug/TransportPlugin.dll`
---

View File

@ -268,7 +268,7 @@ private async void OnExportVideoButtonClick(object sender, RoutedEventArgs e)
### 文件分发清单
```
NavisworksTransport插件目录/
├── NavisworksTransportPlugin.dll主插件
├── TransportPlugin.dll主插件
├── FFMpegCore.dllNuGet自动处理
├── Instances.dll依赖项
├── System.Text.Json.dll依赖项

View File

@ -325,7 +325,7 @@ public class ChannelSlopeInfo
#### **编译验证**
- ✅ 项目编译成功:`NavisworksTransportPlugin.dll`
- ✅ 项目编译成功:`TransportPlugin.dll`
- ✅ 新文件已添加到项目文件中
- ✅ 语法错误已修复
- ✅ 依赖关系正确配置

View File

@ -6,6 +6,8 @@ using System.Threading;
using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Animation;
using NavisworksTransport.Utils;
using NavisworksTransport.UI.WPF.Views;
@ -278,6 +280,7 @@ namespace NavisworksTransport.Commands
UpdateProgress(95, "加载碰撞报告截图...");
// 加载多截图数据(从新的截图表)
bool loadedScreenshotsFromDatabase = false;
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null)
{
@ -306,6 +309,7 @@ namespace NavisworksTransport.Commands
Description = r.Description,
SortOrder = r.SortOrder
}).ToList();
loadedScreenshotsFromDatabase = true;
LogManager.Info($"从数据库加载了 {result.Screenshots.Count} 张截图 (ResultId={testRecord.Id})");
}
@ -331,17 +335,25 @@ namespace NavisworksTransport.Commands
}
}
// 如果没有加载到任何截图,生成一张默认截图
// 检查并创建路径子目录(用于存放该路径的所有截图)
string pathDir = System.IO.Path.Combine(PathHelper.GetScreenshotDirectory(),
string.Format("{0}_{1}", result.RouteId.Substring(0, 8), result.PathName ?? "collision"));
PathHelper.EnsureDirectoryExists(pathDir);
// 先生成默认截图(整条路径的概览)
if (result.Screenshots == null || result.Screenshots.Count == 0)
{
try
{
string screenshotPath = PathHelper.GenerateSceneScreenshot(
result.PathName ?? "collision",
UpdateProgress(92, "生成默认路径截图...");
string screenshotPath = PathHelper.GenerateSceneScreenshotToDirectory(
pathDir,
"path",
1920,
1080,
System.Drawing.Imaging.ImageFormat.Jpeg,
"collision"
"overview"
);
if (screenshotPath != null)
@ -354,17 +366,18 @@ namespace NavisworksTransport.Commands
Format = "JPG",
Width = 1920,
Height = 1080,
CaptureTime = DateTime.Now
CaptureTime = DateTime.Now,
Description = "路径概览",
SortOrder = 0
}
};
LogManager.Info($"自动生成默认截图: {screenshotPath}");
LogManager.Info(string.Format("生成默认截图: {0}", screenshotPath));
// 保存截图到数据库(新表支持多截图)
// 保存截图到数据库
if (pathDatabase != null && result.ResultId > 0)
{
try
{
// 保存到新表 CollisionReportScreenshots
int screenshotId = pathDatabase.SaveCollisionReportScreenshot(
result.ResultId,
screenshotPath,
@ -372,20 +385,106 @@ namespace NavisworksTransport.Commands
1920,
1080,
0,
"默认场景截图"
"路径概览"
);
LogManager.Info($"默认截图已保存到数据库: ScreenshotId={screenshotId}, ResultId={result.ResultId}, Path={screenshotPath}");
LogManager.Info(string.Format("默认截图已保存到数据库: ScreenshotId={0}", screenshotId));
}
catch (Exception dbEx)
{
LogManager.Error($"保存默认截图到数据库失败: {dbEx.Message}");
LogManager.Error(string.Format("保存默认截图到数据库失败: {0}", dbEx.Message));
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"自动生成碰撞场景截图失败: {ex.Message}");
LogManager.Error(string.Format("生成默认截图失败: {0}", ex.Message));
}
}
// 再生成每个碰撞对的单独截图(需要移动物体到碰撞位置)
// 只有未从数据库加载截图时才生成
if (!loadedScreenshotsFromDatabase && collisionData.AllCollisions?.Count > 0)
{
try
{
UpdateProgress(94, string.Format("正在为 {0} 个碰撞对生成视角截图...", collisionData.AllCollisions.Count));
int sortOrder = result.Screenshots?.Count ?? 0;
int successCount = 0;
// 保存动画物体原始状态(用于截图后恢复)
ModelItem animatedObject = null;
ModelItemTransformHelper.ObjectStateSnapshot savedObjectState = null;
// 找到第一个有位置信息的碰撞来获取动画物体
foreach (var c in collisionData.AllCollisions)
{
if (c.Item1 != null && c.HasPositionInfo)
{
animatedObject = c.Item1;
savedObjectState = CollisionSceneHelper.SaveAnimatedObjectState(animatedObject);
break;
}
}
for (int i = 0; i < collisionData.AllCollisions.Count; i++)
{
var collision = collisionData.AllCollisions[i];
try
{
// 使用通用方法移动物体到碰撞位置并聚焦
CollisionSceneHelper.MoveToCollisionAndFocus(collision, animatedObject, waitForViewUpdate: true);
// 生成截图
string screenshotPath = PathHelper.GenerateSceneScreenshotToDirectory(
pathDir, "collision", 1920, 1080,
System.Drawing.Imaging.ImageFormat.Jpeg, string.Format("collision_{0:D3}", i + 1));
if (screenshotPath != null)
{
string item1Name = collision.Item1 != null ? collision.Item1.DisplayName : "物体1";
string item2Name = collision.Item2 != null ? collision.Item2.DisplayName : "物体2";
string description = string.Format("碰撞对 {0}/{1}: {2} vs {3}", i + 1, collisionData.AllCollisions.Count, item1Name, item2Name);
var screenshot = new CollisionReportScreenshot
{
FilePath = screenshotPath,
Format = "JPG",
Width = 1920,
Height = 1080,
CaptureTime = DateTime.Now,
Description = description,
SortOrder = sortOrder++
};
result.Screenshots.Add(screenshot);
// 保存到数据库
if (pathDatabase != null && result.ResultId > 0)
{
pathDatabase.SaveCollisionReportScreenshot(result.ResultId, screenshotPath, "JPG",
1920, 1080, screenshot.SortOrder, description);
}
successCount++;
}
Thread.Sleep(50); // 短暂延迟
}
catch (Exception ex)
{
LogManager.Error(string.Format("生成碰撞对 {0} 截图失败: {1}", i + 1, ex.Message));
}
}
// 恢复动画物体到原始状态
CollisionSceneHelper.RestoreAnimatedObjectState(animatedObject, savedObjectState);
LogManager.Info(string.Format("已生成 {0}/{1} 个碰撞对视角截图", successCount, collisionData.AllCollisions.Count));
}
catch (Exception ex)
{
LogManager.Error(string.Format("生成碰撞对视角截图失败: {0}", ex.Message));
}
}

View File

@ -107,7 +107,7 @@ namespace NavisworksTransport.Core.Config
"Autodesk",
"Navisworks Manage 2026",
"plugins",
"NavisworksTransportPlugin",
"TransportPlugin",
"config.toml"
);
}

View File

@ -1244,52 +1244,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
var collisionData = collisionItem.CollisionData;
// 获取被撞对象Item2是运动物体碰撞的静态对象
var hitObject = collisionData.Item2;
if (hitObject == null)
{
// 如果没有Item2使用Item1
hitObject = collisionData.Item1;
}
if (hitObject != null)
{
// 清除之前的高亮
ModelHighlightHelper.ClearAllHighlights();
// 高亮被撞对象(使用碰撞检测结果专用红色)
ModelHighlightHelper.HighlightItems(ModelHighlightHelper.ClashDetectiveResultsCategory, new[] { hitObject });
// 聚焦到被撞对象
ViewpointHelper.FocusOnModelItem(hitObject, viewAngleDegrees: 60.0, targetViewRatio: 0.25);
LogManager.Info($"聚焦到被撞对象: {collisionItem.Title}");
}
// 还原运动物体到碰撞时的位置和朝向
LogManager.Debug($"[碰撞详情点击] Item1={collisionData.Item1?.DisplayName}, HasPositionInfo={collisionData.HasPositionInfo}, Item1Position={collisionData.Item1Position}");
// 首次查看碰撞时保存运动物体状态
if (collisionData.Item1 != null && collisionData.HasPositionInfo && collisionData.Item1Position != null)
{
// 首次查看碰撞时保存运动物体状态
if (_savedAnimatedObjectState == null || _currentAnimatedObject != collisionData.Item1)
{
_currentAnimatedObject = collisionData.Item1;
// 🔥 关键:从 PathAnimationManager 获取当前实际朝向
// 因为 Transform 返回的是CAD原始朝向不是当前实际朝向
var pam = PathAnimationManager.GetInstance();
var currentYaw = pam.CurrentYaw;
var transformYaw = ModelItemTransformHelper.GetYawFromTransform(collisionData.Item1.Transform);
LogManager.Debug($"[碰撞查看调试] PathAnimationManager.CurrentYaw={currentYaw * 180 / Math.PI:F2}°, Transform.Yaw={transformYaw * 180 / Math.PI:F2}°");
_savedAnimatedObjectState = ModelItemTransformHelper.SaveObjectState(collisionData.Item1, currentYaw);
LogManager.Info($"[碰撞查看] 已保存运动物体原始状态: pos=({_savedAnimatedObjectState.Position.X:F2},{_savedAnimatedObjectState.Position.Y:F2},{_savedAnimatedObjectState.Position.Z:F2}), yaw={currentYaw * 180 / Math.PI:F2}°");
_savedAnimatedObjectState = CollisionSceneHelper.SaveAnimatedObjectState(collisionData.Item1);
}
RestoreAnimatedObjectToCollisionPosition(collisionData);
}
else
{
LogManager.Warning($"[碰撞详情点击] 无法还原运动物体位置: Item1={(collisionData.Item1 != null ? "" : "")}, HasPositionInfo={collisionData.HasPositionInfo}, Item1Position={(collisionData.Item1Position != null ? "" : "")}");
}
// 使用通用方法移动并聚焦到碰撞位置
CollisionSceneHelper.MoveToCollisionAndFocus(collisionData, _currentAnimatedObject, waitForViewUpdate: false);
}
catch (Exception ex)
{
@ -1297,65 +1263,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 将运动物体还原到碰撞时的位置和朝向
/// 使用ModelItemTransformHelper.MoveItemToPositionAndYaw从CAD原始状态精确定位
/// </summary>
private void RestoreAnimatedObjectToCollisionPosition(CollisionResult collisionData)
{
try
{
var animatedObject = collisionData.Item1;
var targetPosition = collisionData.Item1Position;
var targetYaw = collisionData.Item1YawRadians;
if (animatedObject == null || targetPosition == null)
{
LogManager.Warning("[还原碰撞位置] 运动物体或位置信息为空");
return;
}
// 计算目标底面位置Item1Position存储的是包围盒中心需要转换为底面
var currentBounds = animatedObject.BoundingBox();
double halfHeight = (currentBounds.Max.Z - currentBounds.Min.Z) / 2.0;
var targetGroundPosition = new Point3D(
targetPosition.X,
targetPosition.Y,
targetPosition.Z - halfHeight
);
// 使用工具方法从CAD原始状态移动到目标位置
ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, targetYaw);
LogManager.Info($"[还原碰撞位置] 运动物体已移动到碰撞位置: ({targetGroundPosition.X:F2}, {targetGroundPosition.Y:F2}, {targetGroundPosition.Z:F2}), 朝向: {targetYaw * 180 / Math.PI:F2}°");
}
catch (Exception ex)
{
LogManager.Error($"[还原碰撞位置] 失败: {ex.Message}", ex);
}
}
/// <summary>
/// 恢复运动物体到原始状态(查看碰撞前保存的状态)
/// </summary>
public void RestoreAnimatedObjectToOriginalState()
{
try
{
if (_currentAnimatedObject != null && _savedAnimatedObjectState != null)
{
ModelItemTransformHelper.RestoreObjectState(_currentAnimatedObject, _savedAnimatedObjectState);
LogManager.Info("[碰撞查看] 运动物体已恢复到原始状态");
// 清除保存的状态
_savedAnimatedObjectState = null;
_currentAnimatedObject = null;
}
}
catch (Exception ex)
{
LogManager.Error($"[恢复运动物体状态] 失败: {ex.Message}", ex);
}
CollisionSceneHelper.RestoreAnimatedObjectState(_currentAnimatedObject, _savedAnimatedObjectState);
// 清除保存的状态
_savedAnimatedObjectState = null;
_currentAnimatedObject = null;
}
#endregion

View File

@ -0,0 +1,124 @@
using System;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Animation;
namespace NavisworksTransport.Utils
{
/// <summary>
/// 碰撞场景辅助类
/// 提供碰撞场景还原和截图的通用功能
/// </summary>
public static class CollisionSceneHelper
{
/// <summary>
/// 将动画物体移动到碰撞位置并聚焦到碰撞场景
/// </summary>
/// <param name="collision">碰撞结果</param>
/// <param name="animatedObject">动画物体(运动物体)</param>
/// <param name="waitForViewUpdate">是否等待视图更新</param>
public static void MoveToCollisionAndFocus(CollisionResult collision, ModelItem animatedObject, bool waitForViewUpdate = true)
{
if (collision == null) return;
try
{
// 获取被撞对象Item2是运动物体碰撞的静态对象
var hitObject = collision.Item2 ?? collision.Item1;
if (hitObject != null)
{
// 清除之前的高亮
ModelHighlightHelper.ClearAllHighlights();
// 高亮被撞对象(使用碰撞检测结果专用红色)
ModelHighlightHelper.HighlightItems(ModelHighlightHelper.ClashDetectiveResultsCategory, new[] { hitObject });
// 聚焦到被撞对象
ViewpointHelper.FocusOnModelItem(hitObject, viewAngleDegrees: 60.0, targetViewRatio: 0.25);
LogManager.Info(string.Format("聚焦到被撞对象: {0}", hitObject.DisplayName));
}
// 移动动画物体到碰撞位置
if (animatedObject != null && collision.Item1 == animatedObject && collision.HasPositionInfo && collision.Item1Position != null)
{
// 计算目标底面位置Item1Position存储的是包围盒中心需要转换为底面
var bounds = animatedObject.BoundingBox();
double halfHeight = (bounds.Max.Z - bounds.Min.Z) / 2.0;
var targetGroundPosition = new Point3D(
collision.Item1Position.X,
collision.Item1Position.Y,
collision.Item1Position.Z - halfHeight
);
// 使用工具方法从CAD原始状态移动到目标位置
ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, collision.Item1YawRadians);
LogManager.Info(string.Format("运动物体已移动到碰撞位置: ({0:F2}, {1:F2}, {2:F2}), 朝向: {3:F2}°",
targetGroundPosition.X, targetGroundPosition.Y, targetGroundPosition.Z,
collision.Item1YawRadians * 180 / Math.PI));
}
// 等待视图更新
if (waitForViewUpdate)
{
ViewpointHelper.WaitForViewUpdate();
}
}
catch (Exception ex)
{
LogManager.Error(string.Format("移动并聚焦到碰撞位置失败: {0}", ex.Message), ex);
}
}
/// <summary>
/// 保存动画物体当前状态
/// </summary>
/// <param name="animatedObject">动画物体</param>
/// <returns>状态快照</returns>
public static ModelItemTransformHelper.ObjectStateSnapshot SaveAnimatedObjectState(ModelItem animatedObject)
{
if (animatedObject == null) return null;
try
{
// 从PathAnimationManager获取当前实际朝向
var pam = PathAnimationManager.GetInstance();
var currentYaw = pam.CurrentYaw;
var state = ModelItemTransformHelper.SaveObjectState(animatedObject, currentYaw);
LogManager.Info(string.Format("已保存动画物体状态: {0}, 位置=({1:F2},{2:F2},{3:F2}), 朝向={4:F2}°",
animatedObject.DisplayName, state.Position.X, state.Position.Y, state.Position.Z,
currentYaw * 180 / Math.PI));
return state;
}
catch (Exception ex)
{
LogManager.Error(string.Format("保存动画物体状态失败: {0}", ex.Message), ex);
return null;
}
}
/// <summary>
/// 恢复动画物体到保存的状态
/// </summary>
/// <param name="animatedObject">动画物体</param>
/// <param name="state">状态快照</param>
public static void RestoreAnimatedObjectState(ModelItem animatedObject, ModelItemTransformHelper.ObjectStateSnapshot state)
{
if (animatedObject == null || state == null) return;
try
{
ModelItemTransformHelper.RestoreObjectState(animatedObject, state);
LogManager.Info(string.Format("动画物体已恢复到原始状态: {0}", animatedObject.DisplayName));
}
catch (Exception ex)
{
LogManager.Error(string.Format("恢复动画物体状态失败: {0}", ex.Message), ex);
}
}
}
}

View File

@ -22,7 +22,7 @@ namespace NavisworksTransport
{
private static readonly string _logFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Autodesk", "Navisworks Manage 2026", "plugins", "NavisworksTransportPlugin", "logs", "debug.log");
"Autodesk", "Navisworks Manage 2026", "plugins", "TransportPlugin", "logs", "debug.log");
private static readonly object _lockObject = new object();

View File

@ -21,7 +21,7 @@ namespace NavisworksTransport.Utils
"Autodesk",
"Navisworks Manage 2026",
"plugins",
"NavisworksTransportPlugin"
"TransportPlugin"
);
}
@ -147,20 +147,42 @@ namespace NavisworksTransport.Utils
/// <param name="prefix">文件名前缀</param>
/// <returns>截图文件路径失败返回null</returns>
public static string GenerateSceneScreenshot(string sceneName, int width, int height, ImageFormat format, string prefix = "scene")
{
return GenerateSceneScreenshotInternal(GetScreenshotDirectory(), sceneName, width, height, format, prefix);
}
/// <summary>
/// 生成场景截图到指定目录
/// </summary>
/// <param name="outputDirectory">输出目录</param>
/// <param name="sceneName">场景名称</param>
/// <param name="width">图像宽度</param>
/// <param name="height">图像高度</param>
/// <param name="format">图像格式</param>
/// <param name="prefix">文件名前缀</param>
/// <returns>截图文件路径失败返回null</returns>
public static string GenerateSceneScreenshotToDirectory(string outputDirectory, string sceneName, int width, int height, ImageFormat format, string prefix = "scene")
{
return GenerateSceneScreenshotInternal(outputDirectory, sceneName, width, height, format, prefix);
}
/// <summary>
/// 生成场景截图内部实现
/// </summary>
private static string GenerateSceneScreenshotInternal(string outputDirectory, string sceneName, int width, int height, ImageFormat format, string prefix)
{
try
{
LogManager.Info($"开始生成场景截图: {prefix}_{sceneName}, 尺寸={width}x{height}, 格式={ImageFormatToExtension(format)}");
LogManager.Info(string.Format("开始生成场景截图: {0}_{1}, 尺寸={2}x{3}, 目录={4}", prefix, sceneName, width, height, outputDirectory));
// 1. 确保截图目录存在
string screenshotDir = GetScreenshotDirectory();
EnsureDirectoryExists(screenshotDir);
EnsureDirectoryExists(outputDirectory);
// 2. 生成文件名
string extension = ImageFormatToExtension(format);
string sanitizedName = SanitizeFileName(sceneName);
string filename = GenerateTimestampedFileName($"{prefix}_{sanitizedName}", extension);
string screenshotPath = Path.Combine(screenshotDir, filename);
string filename = GenerateTimestampedFileName(string.Format("{0}_{1}", prefix, sanitizedName), extension);
string screenshotPath = Path.Combine(outputDirectory, filename);
// 3. 调用截图生成器(复用 NavigationMapGenerator
var generator = new Core.NavigationMapGenerator();
@ -174,12 +196,12 @@ namespace NavisworksTransport.Utils
// 4. 保存截图
generator.SaveNavigationMapImage(bitmap, screenshotPath, format);
LogManager.Info($"场景截图已生成: {screenshotPath}");
LogManager.Info(string.Format("场景截图已生成: {0}", screenshotPath));
return screenshotPath;
}
catch (Exception ex)
{
LogManager.Error($"生成场景截图失败: {ex.Message}", ex);
LogManager.Error(string.Format("生成场景截图失败: {0}", ex.Message), ex);
return null;
}
}