用PickItemFromPoint方法获取到精确的模型上的路径点坐标
This commit is contained in:
parent
511083cad7
commit
0ac68acb19
19
.cursor/rules/navisworks-plugin-rule.mdc
Normal file
19
.cursor/rules/navisworks-plugin-rule.mdc
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
本项目中设计方案和开发任何代码,都要先参考Navisworks2017的API文档;
|
||||
每次完成一个开发任务,更新 [VERSION.md](mdc:NavisworksTransport/NavisworksTransport/VERSION.md) 和 [change_log.md](mdc:NavisworksTransport/NavisworksTransport/change_log.md);
|
||||
生成的任务清单文件和其他临时文件,放在 doc/working目录下;
|
||||
每次分析错误,要看日志文件[NavisworksTransport_Debug.log](mdc:NavisworksTransport/Desktop/NavisworksTransport_Debug.log);
|
||||
每次增加新的代码文件,要把文件增加到 [NavisworksTransportPlugin.csproj](mdc:NavisworksTransport/NavisworksTransport/NavisworksTransportPlugin.csproj)中;
|
||||
这个项目的开发环境是windows,生成命令时要注意;
|
||||
在对代码进行修改时,不能随意删掉代码中原有的和此次修改无关的代码
|
||||
|
||||
编译使用命令
|
||||
```bash
|
||||
dotnet build NavisworksTransportPlugin.csproj --verbosity minimal
|
||||
|
||||
|
||||
```
|
||||
@ -6,7 +6,6 @@ using System.Runtime.InteropServices;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
|
||||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
|
||||
using System.IO;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
@ -15,22 +14,6 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
public class GeometryExtractor
|
||||
{
|
||||
private static string _logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "NavisworksTransport_Debug.log");
|
||||
|
||||
public static void WriteLog(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
string logEntry = $"[{timestamp}] [GeometryExtractor] {message}";
|
||||
File.AppendAllText(_logFilePath, logEntry + Environment.NewLine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略日志写入错误
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提取模型项的顶视图轮廓
|
||||
/// </summary>
|
||||
@ -43,25 +26,25 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
WriteLog($"开始提取模型 {modelItem.DisplayName} 的几何数据");
|
||||
WriteLog($"模型项类型: {modelItem.GetType().Name}");
|
||||
WriteLog($"模型项GUID: {modelItem.InstanceGuid}");
|
||||
LogManager.WriteLog($"开始提取模型 {modelItem.DisplayName} 的几何数据");
|
||||
LogManager.WriteLog($"模型项类型: {modelItem.GetType().Name}");
|
||||
LogManager.WriteLog($"模型项GUID: {modelItem.InstanceGuid}");
|
||||
|
||||
// 详细检查模型是否有几何数据
|
||||
bool hasGeometry = modelItem.HasGeometry;
|
||||
WriteLog($"[关键检查] ModelItem.HasGeometry = {hasGeometry}");
|
||||
LogManager.WriteLog($"[关键检查] ModelItem.HasGeometry = {hasGeometry}");
|
||||
|
||||
if (!hasGeometry)
|
||||
{
|
||||
WriteLog("[关键发现] 模型项没有几何数据 - HasGeometry 返回 false");
|
||||
WriteLog($"模型项详细信息:");
|
||||
WriteLog($" - 显示名称: {modelItem.DisplayName}");
|
||||
WriteLog($" - 实例GUID: {modelItem.InstanceGuid}");
|
||||
LogManager.WriteLog("[关键发现] 模型项没有几何数据 - HasGeometry 返回 false");
|
||||
LogManager.WriteLog($"模型项详细信息:");
|
||||
LogManager.WriteLog($" - 显示名称: {modelItem.DisplayName}");
|
||||
LogManager.WriteLog($" - 实例GUID: {modelItem.InstanceGuid}");
|
||||
|
||||
// 检查子项数量来判断是否为叶节点
|
||||
int childrenCount = modelItem.Children.Count();
|
||||
WriteLog($" - 子项数量: {childrenCount}");
|
||||
WriteLog($" - 是否为叶节点: {childrenCount == 0}");
|
||||
LogManager.WriteLog($" - 子项数量: {childrenCount}");
|
||||
LogManager.WriteLog($" - 是否为叶节点: {childrenCount == 0}");
|
||||
|
||||
// 尝试检查包围盒
|
||||
try
|
||||
@ -69,72 +52,72 @@ namespace NavisworksTransport
|
||||
var bbox = modelItem.BoundingBox();
|
||||
if (bbox != null)
|
||||
{
|
||||
WriteLog($" - 包围盒存在: {bbox.Min} - {bbox.Max}");
|
||||
LogManager.WriteLog($" - 包围盒存在: {bbox.Min} - {bbox.Max}");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog($" - 包围盒不存在");
|
||||
LogManager.WriteLog($" - 包围盒不存在");
|
||||
}
|
||||
}
|
||||
catch (Exception bboxEx)
|
||||
{
|
||||
WriteLog($" - 获取包围盒失败: {bboxEx.Message}");
|
||||
LogManager.WriteLog($" - 获取包围盒失败: {bboxEx.Message}");
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
WriteLog("[几何检查通过] 模型项有几何数据,继续处理");
|
||||
LogManager.WriteLog("[几何检查通过] 模型项有几何数据,继续处理");
|
||||
|
||||
// 处理多实例问题 - 确保只获取当前实例的几何
|
||||
var targetItem = modelItem;
|
||||
int instanceCount = targetItem.Instances.Count();
|
||||
WriteLog($"原始模型实例数: {instanceCount}");
|
||||
LogManager.WriteLog($"原始模型实例数: {instanceCount}");
|
||||
|
||||
while (targetItem.Instances.Count() > 1)
|
||||
{
|
||||
targetItem = targetItem.Parent;
|
||||
if (targetItem == null) break;
|
||||
WriteLog($"向上查找父项: {targetItem?.DisplayName}, 实例数: {targetItem?.Instances.Count()}");
|
||||
LogManager.WriteLog($"向上查找父项: {targetItem?.DisplayName}, 实例数: {targetItem?.Instances.Count()}");
|
||||
}
|
||||
|
||||
if (targetItem == null)
|
||||
{
|
||||
WriteLog("无法找到合适的几何节点");
|
||||
LogManager.WriteLog("无法找到合适的几何节点");
|
||||
return points;
|
||||
}
|
||||
|
||||
// 再次检查目标项的几何状态
|
||||
bool targetHasGeometry = targetItem.HasGeometry;
|
||||
WriteLog($"[目标项检查] 使用几何节点: {targetItem.DisplayName}");
|
||||
WriteLog($"[目标项检查] 实例数: {targetItem.Instances.Count()}");
|
||||
WriteLog($"[目标项检查] HasGeometry = {targetHasGeometry}");
|
||||
LogManager.WriteLog($"[目标项检查] 使用几何节点: {targetItem.DisplayName}");
|
||||
LogManager.WriteLog($"[目标项检查] 实例数: {targetItem.Instances.Count()}");
|
||||
LogManager.WriteLog($"[目标项检查] HasGeometry = {targetHasGeometry}");
|
||||
|
||||
if (!targetHasGeometry)
|
||||
{
|
||||
WriteLog("[目标项问题] 目标节点也没有几何数据");
|
||||
LogManager.WriteLog("[目标项问题] 目标节点也没有几何数据");
|
||||
return points;
|
||||
}
|
||||
|
||||
// 使用优化的几何提取方法
|
||||
var triangles = ExtractTrianglesOptimized(targetItem);
|
||||
WriteLog($"提取到 {triangles.Count} 个三角形");
|
||||
LogManager.WriteLog($"提取到 {triangles.Count} 个三角形");
|
||||
|
||||
if (triangles.Count > 0)
|
||||
{
|
||||
// 生成轮廓
|
||||
points = GenerateOutlineFromTriangles(triangles, tolerance);
|
||||
WriteLog($"生成轮廓包含 {points.Count} 个点");
|
||||
LogManager.WriteLog($"生成轮廓包含 {points.Count} 个点");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog("未提取到三角形数据");
|
||||
LogManager.WriteLog("未提取到三角形数据");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"提取几何数据失败: {ex.Message}");
|
||||
WriteLog($"堆栈跟踪: {ex.StackTrace}");
|
||||
LogManager.WriteLog($"提取几何数据失败: {ex.Message}");
|
||||
LogManager.WriteLog($"堆栈跟踪: {ex.StackTrace}");
|
||||
}
|
||||
|
||||
return points;
|
||||
@ -158,11 +141,11 @@ namespace NavisworksTransport
|
||||
var comState = ComApiBridge.State;
|
||||
var comSelection = ComApiBridge.ToInwOpSelection(modelCollection);
|
||||
|
||||
WriteLog($"COM 选择创建成功,路径数: {comSelection.Paths().Count}");
|
||||
LogManager.WriteLog($"COM 选择创建成功,路径数: {comSelection.Paths().Count}");
|
||||
|
||||
// 使用优化的片段去重方法
|
||||
var uniqueFragments = GetUniqueFragments(comSelection);
|
||||
WriteLog($"获取到 {uniqueFragments.Count} 个唯一片段");
|
||||
LogManager.WriteLog($"获取到 {uniqueFragments.Count} 个唯一片段");
|
||||
|
||||
foreach (var fragmentInfo in uniqueFragments)
|
||||
{
|
||||
@ -176,17 +159,17 @@ namespace NavisworksTransport
|
||||
var fragmentTriangles = callback.GetTriangles();
|
||||
triangles.AddRange(fragmentTriangles);
|
||||
|
||||
WriteLog($"片段生成了 {fragmentTriangles.Count} 个三角形");
|
||||
LogManager.WriteLog($"片段生成了 {fragmentTriangles.Count} 个三角形");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"处理片段失败: {ex.Message}");
|
||||
LogManager.WriteLog($"处理片段失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"提取三角形失败: {ex.Message}");
|
||||
LogManager.WriteLog($"提取三角形失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return triangles;
|
||||
@ -230,19 +213,19 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"处理单个片段失败: {ex.Message}");
|
||||
LogManager.WriteLog($"处理单个片段失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"遍历路径片段失败: {ex.Message}");
|
||||
LogManager.WriteLog($"遍历路径片段失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"获取唯一片段失败: {ex.Message}");
|
||||
LogManager.WriteLog($"获取唯一片段失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return fragmentMap.Values.ToList();
|
||||
@ -264,7 +247,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 找到最高的 Z 坐标
|
||||
var maxZ = triangles.SelectMany(t => new[] { t.Point1.Z, t.Point2.Z, t.Point3.Z }).Max();
|
||||
WriteLog($"最高 Z 坐标: {maxZ}");
|
||||
LogManager.WriteLog($"最高 Z 坐标: {maxZ}");
|
||||
|
||||
// 筛选顶部表面的三角形
|
||||
var topTriangles = triangles.Where(t =>
|
||||
@ -272,7 +255,7 @@ namespace NavisworksTransport
|
||||
Math.Abs(t.Point2.Z - maxZ) < tolerance &&
|
||||
Math.Abs(t.Point3.Z - maxZ) < tolerance).ToList();
|
||||
|
||||
WriteLog($"顶部三角形数量: {topTriangles.Count}");
|
||||
LogManager.WriteLog($"顶部三角形数量: {topTriangles.Count}");
|
||||
|
||||
if (topTriangles.Count == 0) return outlinePoints;
|
||||
|
||||
@ -293,7 +276,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 找到边界边(只出现一次的边)
|
||||
var boundaryEdges = FindBoundaryEdges(edges);
|
||||
WriteLog($"边界边数量: {boundaryEdges.Count}");
|
||||
LogManager.WriteLog($"边界边数量: {boundaryEdges.Count}");
|
||||
|
||||
if (boundaryEdges.Count > 0)
|
||||
{
|
||||
@ -302,12 +285,12 @@ namespace NavisworksTransport
|
||||
|
||||
// 转换为3D点(使用最高Z坐标)
|
||||
outlinePoints = outlinePoints2D.Select(p => new Point3D(p.X, p.Y, maxZ)).ToList();
|
||||
WriteLog($"最终轮廓点数量: {outlinePoints.Count}");
|
||||
LogManager.WriteLog($"最终轮廓点数量: {outlinePoints.Count}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"生成轮廓失败: {ex.Message}");
|
||||
LogManager.WriteLog($"生成轮廓失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return outlinePoints;
|
||||
@ -326,7 +309,7 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
WriteLog($"开始查找边界边,总边数: {edges.Count}");
|
||||
LogManager.WriteLog($"开始查找边界边,总边数: {edges.Count}");
|
||||
|
||||
// 统计每条边出现的次数
|
||||
foreach (var edge in edges)
|
||||
@ -349,7 +332,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog($"边计数完成,唯一边数: {edgeCount.Count}");
|
||||
LogManager.WriteLog($"边计数完成,唯一边数: {edgeCount.Count}");
|
||||
|
||||
// 找出只出现一次的边(边界边)
|
||||
int boundaryCount = 0;
|
||||
@ -368,22 +351,22 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog($"边界边分析完成:");
|
||||
WriteLog($" 边界边数量: {boundaryCount}");
|
||||
WriteLog($" 共享边数量: {sharedCount}");
|
||||
WriteLog($" 边界/总边比例: {(double)boundaryCount / edges.Count:P1}");
|
||||
LogManager.WriteLog($"边界边分析完成:");
|
||||
LogManager.WriteLog($" 边界边数量: {boundaryCount}");
|
||||
LogManager.WriteLog($" 共享边数量: {sharedCount}");
|
||||
LogManager.WriteLog($" 边界/总边比例: {(double)boundaryCount / edges.Count:P1}");
|
||||
|
||||
// 验证边界边的连通性
|
||||
if (boundaryEdges.Count > 0)
|
||||
{
|
||||
var connectivity = AnalyzeBoundaryConnectivity(boundaryEdges);
|
||||
WriteLog($"边界边连通性分析: {connectivity}");
|
||||
LogManager.WriteLog($"边界边连通性分析: {connectivity}");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"查找边界边失败: {ex.Message}");
|
||||
LogManager.WriteLog($"查找边界边失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return boundaryEdges;
|
||||
@ -443,18 +426,18 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
WriteLog($"开始构建轮廓,总边数: {edges.Count}");
|
||||
LogManager.WriteLog($"开始构建轮廓,总边数: {edges.Count}");
|
||||
|
||||
while (remainingEdges.Count > 0 && contourCount < 10) // 最多处理10个轮廓
|
||||
{
|
||||
contourCount++;
|
||||
WriteLog($"=== 开始构建第 {contourCount} 个轮廓 ===");
|
||||
LogManager.WriteLog($"=== 开始构建第 {contourCount} 个轮廓 ===");
|
||||
|
||||
var currentContour = BuildSingleContour(remainingEdges);
|
||||
|
||||
if (currentContour.Count >= 3) // 至少3个点才能形成有效轮廓
|
||||
{
|
||||
WriteLog($"第 {contourCount} 个轮廓包含 {currentContour.Count} 个点");
|
||||
LogManager.WriteLog($"第 {contourCount} 个轮廓包含 {currentContour.Count} 个点");
|
||||
allOutlinePoints.AddRange(currentContour);
|
||||
|
||||
// 添加轮廓分隔符(使用特殊坐标标记)
|
||||
@ -465,23 +448,23 @@ namespace NavisworksTransport
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog($"第 {contourCount} 个轮廓点数不足({currentContour.Count}),跳过");
|
||||
LogManager.WriteLog($"第 {contourCount} 个轮廓点数不足({currentContour.Count}),跳过");
|
||||
break; // 如果轮廓太小,可能是噪声,停止处理
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog($"轮廓构建完成,共 {contourCount} 个轮廓,总点数: {allOutlinePoints.Count}");
|
||||
LogManager.WriteLog($"轮廓构建完成,共 {contourCount} 个轮廓,总点数: {allOutlinePoints.Count}");
|
||||
|
||||
// 如果有多个轮廓,返回最大的外部轮廓
|
||||
if (contourCount > 1)
|
||||
{
|
||||
WriteLog("检测到多个轮廓,返回最大轮廓作为外部边界");
|
||||
LogManager.WriteLog("检测到多个轮廓,返回最大轮廓作为外部边界");
|
||||
return GetLargestContour(allOutlinePoints);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"构建轮廓失败: {ex.Message}");
|
||||
LogManager.WriteLog($"构建轮廓失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return allOutlinePoints;
|
||||
@ -508,7 +491,7 @@ namespace NavisworksTransport
|
||||
var currentPoint = currentEdge.End;
|
||||
var startPoint = currentEdge.Start;
|
||||
|
||||
WriteLog($"轮廓起点: ({startPoint.X:F2}, {startPoint.Y:F2})");
|
||||
LogManager.WriteLog($"轮廓起点: ({startPoint.X:F2}, {startPoint.Y:F2})");
|
||||
|
||||
// 连接后续的边
|
||||
int maxIterations = remainingEdges.Count + 10; // 防止无限循环
|
||||
@ -551,25 +534,25 @@ namespace NavisworksTransport
|
||||
if (Math.Abs(currentPoint.X - startPoint.X) < tolerance &&
|
||||
Math.Abs(currentPoint.Y - startPoint.Y) < tolerance)
|
||||
{
|
||||
WriteLog($"轮廓已闭合,点数: {contour.Count}");
|
||||
LogManager.WriteLog($"轮廓已闭合,点数: {contour.Count}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog($"无法找到连接边,轮廓中断,点数: {contour.Count}");
|
||||
LogManager.WriteLog($"无法找到连接边,轮廓中断,点数: {contour.Count}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iteration >= maxIterations)
|
||||
{
|
||||
WriteLog($"轮廓构建达到最大迭代次数,强制停止");
|
||||
LogManager.WriteLog($"轮廓构建达到最大迭代次数,强制停止");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"构建单个轮廓失败: {ex.Message}");
|
||||
LogManager.WriteLog($"构建单个轮廓失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return contour;
|
||||
@ -613,7 +596,7 @@ namespace NavisworksTransport
|
||||
|
||||
if (contours.Count == 0)
|
||||
{
|
||||
WriteLog("未找到有效轮廓");
|
||||
LogManager.WriteLog("未找到有效轮廓");
|
||||
return allPoints;
|
||||
}
|
||||
|
||||
@ -627,7 +610,7 @@ namespace NavisworksTransport
|
||||
if (contour.Count >= 3)
|
||||
{
|
||||
var area = CalculateContourArea(contour);
|
||||
WriteLog($"轮廓 {i+1} 面积: {area:F2}, 点数: {contour.Count}");
|
||||
LogManager.WriteLog($"轮廓 {i+1} 面积: {area:F2}, 点数: {contour.Count}");
|
||||
|
||||
if (area > maxArea)
|
||||
{
|
||||
@ -637,17 +620,17 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog($"选择最大轮廓,面积: {maxArea:F2}, 原始点数: {largestContour.Count}");
|
||||
LogManager.WriteLog($"选择最大轮廓,面积: {maxArea:F2}, 原始点数: {largestContour.Count}");
|
||||
|
||||
// 清理和排序最大轮廓
|
||||
var cleanedContour = CleanAndSortContour(largestContour);
|
||||
WriteLog($"清理后轮廓点数: {cleanedContour.Count}");
|
||||
LogManager.WriteLog($"清理后轮廓点数: {cleanedContour.Count}");
|
||||
|
||||
return cleanedContour;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"获取最大轮廓失败: {ex.Message}");
|
||||
LogManager.WriteLog($"获取最大轮廓失败: {ex.Message}");
|
||||
return allPoints; // 返回原始点集合
|
||||
}
|
||||
}
|
||||
@ -665,7 +648,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
if (contour.Count < 3)
|
||||
{
|
||||
WriteLog($"轮廓点数不足: {contour.Count}");
|
||||
LogManager.WriteLog($"轮廓点数不足: {contour.Count}");
|
||||
return contour;
|
||||
}
|
||||
|
||||
@ -692,26 +675,26 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog($"去重后点数: {uniquePoints.Count}");
|
||||
LogManager.WriteLog($"去重后点数: {uniquePoints.Count}");
|
||||
|
||||
if (uniquePoints.Count < 3)
|
||||
{
|
||||
WriteLog("去重后点数不足,返回原始轮廓");
|
||||
LogManager.WriteLog("去重后点数不足,返回原始轮廓");
|
||||
return contour;
|
||||
}
|
||||
|
||||
// 第2步:排序点以形成正确的轮廓
|
||||
result = SortPointsIntoContour(uniquePoints);
|
||||
|
||||
WriteLog($"排序后轮廓点数: {result.Count}");
|
||||
LogManager.WriteLog($"排序后轮廓点数: {result.Count}");
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
WriteLog($" 排序点 {i}: ({result[i].X:F2}, {result[i].Y:F2})");
|
||||
LogManager.WriteLog($" 排序点 {i}: ({result[i].X:F2}, {result[i].Y:F2})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"清理轮廓失败: {ex.Message}");
|
||||
LogManager.WriteLog($"清理轮廓失败: {ex.Message}");
|
||||
return contour;
|
||||
}
|
||||
|
||||
@ -738,7 +721,7 @@ namespace NavisworksTransport
|
||||
var remainingPoints = new List<Point2D>(points);
|
||||
remainingPoints.Remove(startPoint);
|
||||
|
||||
WriteLog($"轮廓起始点: ({startPoint.X:F2}, {startPoint.Y:F2})");
|
||||
LogManager.WriteLog($"轮廓起始点: ({startPoint.X:F2}, {startPoint.Y:F2})");
|
||||
|
||||
// 使用最近邻算法按顺序连接点
|
||||
var currentPoint = startPoint;
|
||||
@ -767,11 +750,11 @@ namespace NavisworksTransport
|
||||
remainingPoints.Remove(nearestPoint.Value);
|
||||
currentPoint = nearestPoint.Value;
|
||||
|
||||
WriteLog($"下一个点: ({nearestPoint.Value.X:F2}, {nearestPoint.Value.Y:F2}), 距离: {minDistance:F2}");
|
||||
LogManager.WriteLog($"下一个点: ({nearestPoint.Value.X:F2}, {nearestPoint.Value.Y:F2}), 距离: {minDistance:F2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog("无法找到下一个最近点");
|
||||
LogManager.WriteLog("无法找到下一个最近点");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -786,12 +769,12 @@ namespace NavisworksTransport
|
||||
Math.Pow(lastPoint.Y - firstPoint.Y, 2)
|
||||
);
|
||||
|
||||
WriteLog($"轮廓闭合距离: {closingDistance:F2}");
|
||||
LogManager.WriteLog($"轮廓闭合距离: {closingDistance:F2}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"排序轮廓点失败: {ex.Message}");
|
||||
LogManager.WriteLog($"排序轮廓点失败: {ex.Message}");
|
||||
return points;
|
||||
}
|
||||
|
||||
@ -856,7 +839,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GeometryExtractor.WriteLog($"转换变换矩阵失败: {ex.Message}");
|
||||
LogManager.WriteLog($"转换变换矩阵失败: {ex.Message}");
|
||||
_transformMatrix = null;
|
||||
}
|
||||
}
|
||||
@ -902,7 +885,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GeometryExtractor.WriteLog($"处理三角形失败: {ex.Message}");
|
||||
LogManager.WriteLog($"处理三角形失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -943,7 +926,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GeometryExtractor.WriteLog($"坐标变换失败: {ex.Message}");
|
||||
LogManager.WriteLog($"坐标变换失败: {ex.Message}");
|
||||
return localPoint;
|
||||
}
|
||||
}
|
||||
|
||||
140
LogManager.cs
Normal file
140
LogManager.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一日志管理器
|
||||
/// 提供全局日志记录功能
|
||||
/// </summary>
|
||||
public static class LogManager
|
||||
{
|
||||
private static readonly string _logFilePath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||||
"NavisworksTransport_Debug.log");
|
||||
|
||||
private static readonly object _lockObject = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 日志文件路径
|
||||
/// </summary>
|
||||
public static string LogFilePath => _logFilePath;
|
||||
|
||||
/// <summary>
|
||||
/// 清空调试日志
|
||||
/// </summary>
|
||||
public static void ClearLog()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (File.Exists(_logFilePath))
|
||||
{
|
||||
// 如果文件太大,分两步删除:先清空内容,再删除文件
|
||||
var fileInfo = new FileInfo(_logFilePath);
|
||||
if (fileInfo.Length > 10 * 1024 * 1024) // 如果大于10MB
|
||||
{
|
||||
// 先创建一个空文件覆盖原文件
|
||||
File.WriteAllText(_logFilePath, string.Empty);
|
||||
System.Threading.Thread.Sleep(100); // 短暂等待
|
||||
}
|
||||
File.Delete(_logFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略文件删除错误,但尝试创建一个新的空文件
|
||||
try
|
||||
{
|
||||
File.WriteAllText(_logFilePath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 日志已重置\n");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 完全忽略错误
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入调试日志(同时输出到Debug和文件)
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
public static void WriteLog(string message)
|
||||
{
|
||||
var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}";
|
||||
System.Diagnostics.Debug.WriteLine(logMessage);
|
||||
|
||||
try
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
File.AppendAllText(_logFilePath, logMessage + Environment.NewLine);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略文件写入错误
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入信息级别日志
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
public static void Info(string message)
|
||||
{
|
||||
WriteLog($"[INFO] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入警告级别日志
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
public static void Warning(string message)
|
||||
{
|
||||
WriteLog($"[WARN] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入错误级别日志
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
public static void Error(string message)
|
||||
{
|
||||
WriteLog($"[ERROR] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入错误级别日志(包含异常信息)
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
/// <param name="exception">异常对象</param>
|
||||
public static void Error(string message, Exception exception)
|
||||
{
|
||||
WriteLog($"[ERROR] {message}: {exception.Message}");
|
||||
WriteLog($"[ERROR] 堆栈跟踪: {exception.StackTrace}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入调试级别日志
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
public static void Debug(string message)
|
||||
{
|
||||
WriteLog($"[DEBUG] {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入会话分隔符,标记新的操作会话开始
|
||||
/// </summary>
|
||||
public static void WriteSessionSeparator()
|
||||
{
|
||||
var separator = new string('=', 60);
|
||||
WriteLog($"{separator}");
|
||||
WriteLog($"[SESSION] 新会话开始 - {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
WriteLog($"{separator}");
|
||||
}
|
||||
}
|
||||
}
|
||||
615
MainPlugin.cs
615
MainPlugin.cs
@ -9,23 +9,230 @@ using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 全局异常处理工具类
|
||||
/// </summary>
|
||||
public static class GlobalExceptionHandler
|
||||
{
|
||||
private static bool _isInitialized = false;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化全局异常处理器
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
if (_isInitialized) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 捕获未处理的异常
|
||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||
System.Windows.Forms.Application.ThreadException += OnThreadException;
|
||||
_isInitialized = true;
|
||||
|
||||
LogManager.Info("[全局异常] 全局异常处理器已初始化");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[全局异常] 初始化异常处理器失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理未捕获的异常
|
||||
/// </summary>
|
||||
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Exception ex = e.ExceptionObject as Exception;
|
||||
string message = ex?.Message ?? "未知异常";
|
||||
string stackTrace = ex?.StackTrace ?? "无堆栈信息";
|
||||
|
||||
LogManager.Error($"[全局异常] 未处理异常: {message}");
|
||||
LogManager.Error($"[全局异常] 堆栈信息: {stackTrace}");
|
||||
LogManager.Error($"[全局异常] 异常类型: {ex?.GetType().Name ?? "Unknown"}");
|
||||
LogManager.Error($"[全局异常] 是否终止: {e.IsTerminating}");
|
||||
|
||||
// 显示用户友好的错误信息
|
||||
ShowErrorDialog("程序发生未预期的错误", message, ex);
|
||||
|
||||
// 尝试恢复关键组件
|
||||
TryRecoverComponents();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 异常处理中的异常,保持静默
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理线程异常
|
||||
/// </summary>
|
||||
private static void OnThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Exception ex = e.Exception;
|
||||
LogManager.Error($"[全局异常] 线程异常: {ex.Message}");
|
||||
LogManager.Error($"[全局异常] 堆栈信息: {ex.StackTrace}");
|
||||
LogManager.Error($"[全局异常] 异常类型: {ex.GetType().Name}");
|
||||
|
||||
// 显示用户友好的错误信息
|
||||
ShowErrorDialog("程序发生线程错误", ex.Message, ex);
|
||||
|
||||
// 尝试恢复关键组件
|
||||
TryRecoverComponents();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 异常处理中的异常,保持静默
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行方法,捕获所有异常
|
||||
/// </summary>
|
||||
public static T SafeExecute<T>(Func<T> action, T defaultValue = default(T), string operationName = "操作")
|
||||
{
|
||||
try
|
||||
{
|
||||
return action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[安全执行] {operationName}失败: {ex.Message}");
|
||||
LogManager.Error($"[安全执行] 堆栈信息: {ex.StackTrace}");
|
||||
LogManager.Error($"[安全执行] 异常类型: {ex.GetType().Name}");
|
||||
|
||||
// 对于用户操作,显示简短的错误提示
|
||||
if (!string.IsNullOrEmpty(operationName) && operationName != "操作")
|
||||
{
|
||||
ShowErrorToast($"{operationName}失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行方法(无返回值)
|
||||
/// </summary>
|
||||
public static void SafeExecute(Action action, string operationName = "操作")
|
||||
{
|
||||
SafeExecute(() => { action(); return true; }, false, operationName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示错误对话框
|
||||
/// </summary>
|
||||
private static void ShowErrorDialog(string title, string message, Exception ex = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string detailMessage = message;
|
||||
if (ex != null)
|
||||
{
|
||||
detailMessage += $"\n\n技术详情: {ex.GetType().Name}";
|
||||
if (!string.IsNullOrEmpty(ex.Message))
|
||||
{
|
||||
detailMessage += $"\n错误信息: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show(detailMessage, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果连错误对话框都显示不了,就保持静默
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示简短的错误提示
|
||||
/// </summary>
|
||||
private static void ShowErrorToast(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
MessageBox.Show(message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 保持静默
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试恢复关键组件
|
||||
/// </summary>
|
||||
private static void TryRecoverComponents()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("[全局异常] 尝试恢复关键组件...");
|
||||
|
||||
// 尝试重置路径编辑模式
|
||||
var activeManager = PathPlanningManager.GetActivePathManager();
|
||||
if (activeManager != null && activeManager.IsPathEditMode)
|
||||
{
|
||||
activeManager.ExitPathEditMode();
|
||||
LogManager.Info("[全局异常] 已退出路径编辑模式");
|
||||
}
|
||||
|
||||
// 尝试清除临时高亮
|
||||
if (NavisApplication.ActiveDocument?.Models != null)
|
||||
{
|
||||
NavisApplication.ActiveDocument.Models.ResetAllTemporaryMaterials();
|
||||
LogManager.Info("[全局异常] 已清除临时材质");
|
||||
}
|
||||
|
||||
LogManager.Info("[全局异常] 组件恢复完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[全局异常] 组件恢复失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[PluginAttribute("Basic", "Tian", ToolTip = "Transport Plugin", DisplayName = "Transport Plugin")]
|
||||
[AddInPlugin(AddInLocation.AddIn)] // 将插件显示在Navisworks的"附加模块"选项卡中
|
||||
public class Main : AddInPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话初始化标志,用于确保在同一Navisworks会话中只清空一次日志
|
||||
/// </summary>
|
||||
private static bool _isSessionInitialized = false;
|
||||
|
||||
public override int Execute(params string[] parameters)
|
||||
{
|
||||
try
|
||||
return GlobalExceptionHandler.SafeExecute(() =>
|
||||
{
|
||||
// 初始化全局异常处理器
|
||||
GlobalExceptionHandler.Initialize();
|
||||
|
||||
// 根据会话状态决定日志处理方式
|
||||
if (!_isSessionInitialized)
|
||||
{
|
||||
// 首次启动:清空旧日志
|
||||
LogManager.ClearLog();
|
||||
LogManager.Info("=== NavisworksTransport 插件启动 ===");
|
||||
LogManager.Info("=== 路径点计算日志功能已加载 ===");
|
||||
_isSessionInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 后续打开:添加会话分隔符,保留历史记录
|
||||
LogManager.WriteSessionSeparator();
|
||||
LogManager.Info("=== 重新打开插件界面 ===");
|
||||
}
|
||||
|
||||
// 显示类别选择对话框
|
||||
ShowCategorySelectionDialog();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"插件执行出错: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}, -1, "插件初始化");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -33,105 +240,108 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
private void ShowCategorySelectionDialog()
|
||||
{
|
||||
// 获取当前选中的模型项数量
|
||||
int selectedCount = NavisApplication.ActiveDocument.CurrentSelection.SelectedItems.Count;
|
||||
|
||||
// 创建对话框
|
||||
Form dialog = new Form
|
||||
GlobalExceptionHandler.SafeExecute(() =>
|
||||
{
|
||||
Text = "物流路径规划插件 - 3D交互模式",
|
||||
Size = new Size(380, 600),
|
||||
StartPosition = FormStartPosition.CenterParent,
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
MaximizeBox = false,
|
||||
MinimizeBox = false
|
||||
};
|
||||
// 获取当前选中的模型项数量
|
||||
int selectedCount = NavisApplication.ActiveDocument.CurrentSelection.SelectedItems.Count;
|
||||
|
||||
// 创建对话框
|
||||
Form dialog = new Form
|
||||
{
|
||||
Text = "物流路径规划插件 - 3D交互模式",
|
||||
Size = new Size(380, 700), // 增加高度以容纳新的详情显示区域
|
||||
StartPosition = FormStartPosition.CenterParent,
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
MaximizeBox = false,
|
||||
MinimizeBox = false
|
||||
};
|
||||
|
||||
// 创建主面板,启用自动滚动
|
||||
Panel mainPanel = new Panel
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
Padding = new Padding(15),
|
||||
AutoScroll = true
|
||||
};
|
||||
dialog.Controls.Add(mainPanel);
|
||||
// 创建主面板,启用自动滚动
|
||||
Panel mainPanel = new Panel
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
Padding = new Padding(15),
|
||||
AutoScroll = true
|
||||
};
|
||||
dialog.Controls.Add(mainPanel);
|
||||
|
||||
int currentY = 0;
|
||||
int currentY = 0;
|
||||
|
||||
// 状态信息标签
|
||||
Label statusLabel = new Label
|
||||
{
|
||||
Text = $"当前选中: {selectedCount} 个模型项",
|
||||
Font = new Font("微软雅黑", 10, FontStyle.Bold),
|
||||
ForeColor = selectedCount > 0 ? System.Drawing.Color.Blue : System.Drawing.Color.Red,
|
||||
AutoSize = true,
|
||||
Location = new Point(0, currentY)
|
||||
};
|
||||
mainPanel.Controls.Add(statusLabel);
|
||||
currentY += 35;
|
||||
// 状态信息标签
|
||||
Label statusLabel = new Label
|
||||
{
|
||||
Text = $"当前选中: {selectedCount} 个模型项",
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold),
|
||||
ForeColor = selectedCount > 0 ? System.Drawing.Color.Blue : System.Drawing.Color.Red,
|
||||
AutoSize = true,
|
||||
Location = new Point(0, currentY)
|
||||
};
|
||||
mainPanel.Controls.Add(statusLabel);
|
||||
currentY += 35;
|
||||
|
||||
// 创建类别设置GroupBox
|
||||
GroupBox categoryGroupBox = new GroupBox
|
||||
{
|
||||
Text = "★ 类别设置",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 65),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold)
|
||||
};
|
||||
mainPanel.Controls.Add(categoryGroupBox);
|
||||
CreateCategoryDropdown(categoryGroupBox);
|
||||
currentY += 85;
|
||||
// 创建类别设置GroupBox
|
||||
GroupBox categoryGroupBox = new GroupBox
|
||||
{
|
||||
Text = "★ 类别设置",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 65),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold)
|
||||
};
|
||||
mainPanel.Controls.Add(categoryGroupBox);
|
||||
CreateCategoryDropdown(categoryGroupBox);
|
||||
currentY += 85;
|
||||
|
||||
// 创建3D交互模式GroupBox
|
||||
GroupBox mode3DGroupBox = new GroupBox
|
||||
{
|
||||
Text = "★ 3D路径编辑",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 200),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold),
|
||||
BackColor = System.Drawing.Color.LightBlue
|
||||
};
|
||||
mainPanel.Controls.Add(mode3DGroupBox);
|
||||
Create3DInteractionControls(mode3DGroupBox);
|
||||
currentY += 220;
|
||||
// 创建3D交互模式GroupBox
|
||||
GroupBox mode3DGroupBox = new GroupBox
|
||||
{
|
||||
Text = "★ 3D路径编辑",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 300), // 减少高度20像素
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold),
|
||||
BackColor = System.Drawing.Color.LightBlue
|
||||
};
|
||||
mainPanel.Controls.Add(mode3DGroupBox);
|
||||
Create3DInteractionControls(mode3DGroupBox);
|
||||
currentY += 320; // 调整间距以适应减少的GroupBox高度
|
||||
|
||||
// 创建可见性控制GroupBox
|
||||
GroupBox visibilityGroupBox = new GroupBox
|
||||
{
|
||||
Text = "可见性控制",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 85),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold)
|
||||
};
|
||||
mainPanel.Controls.Add(visibilityGroupBox);
|
||||
CreateVisibilityControls(visibilityGroupBox);
|
||||
currentY += 105;
|
||||
// 创建可见性控制GroupBox
|
||||
GroupBox visibilityGroupBox = new GroupBox
|
||||
{
|
||||
Text = "可见性控制",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 85),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold)
|
||||
};
|
||||
mainPanel.Controls.Add(visibilityGroupBox);
|
||||
CreateVisibilityControls(visibilityGroupBox);
|
||||
currentY += 105;
|
||||
|
||||
// 创建路径管理GroupBox
|
||||
GroupBox pathManagementGroupBox = new GroupBox
|
||||
{
|
||||
Text = "路径管理",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 85),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold)
|
||||
};
|
||||
mainPanel.Controls.Add(pathManagementGroupBox);
|
||||
CreatePathManagementControls(pathManagementGroupBox);
|
||||
currentY += 105;
|
||||
// 创建路径管理GroupBox
|
||||
GroupBox pathManagementGroupBox = new GroupBox
|
||||
{
|
||||
Text = "路径管理",
|
||||
Location = new Point(0, currentY),
|
||||
Size = new Size(320, 85),
|
||||
Font = new Font("微软雅黑", 9, FontStyle.Bold)
|
||||
};
|
||||
mainPanel.Controls.Add(pathManagementGroupBox);
|
||||
CreatePathManagementControls(pathManagementGroupBox);
|
||||
currentY += 105;
|
||||
|
||||
// 创建关闭按钮
|
||||
Button closeButton = new Button
|
||||
{
|
||||
Text = "关闭",
|
||||
Size = new Size(80, 30),
|
||||
Location = new Point(240, currentY),
|
||||
DialogResult = DialogResult.OK,
|
||||
Font = new Font("微软雅黑", 9)
|
||||
};
|
||||
mainPanel.Controls.Add(closeButton);
|
||||
// 创建关闭按钮
|
||||
Button closeButton = new Button
|
||||
{
|
||||
Text = "关闭",
|
||||
Size = new Size(80, 30),
|
||||
Location = new Point(240, currentY),
|
||||
DialogResult = DialogResult.OK,
|
||||
Font = new Font("微软雅黑", 9)
|
||||
};
|
||||
mainPanel.Controls.Add(closeButton);
|
||||
|
||||
// 显示对话框
|
||||
dialog.ShowDialog();
|
||||
// 显示对话框
|
||||
dialog.ShowDialog();
|
||||
}, "显示主控制面板");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -192,23 +402,44 @@ namespace NavisworksTransport
|
||||
Label pointListLabel = new Label
|
||||
{
|
||||
Text = "路径点列表:",
|
||||
Location = new Point(20, 95),
|
||||
Location = new Point(20, 90),
|
||||
Size = new Size(80, 20),
|
||||
Font = new Font("微软雅黑", 9)
|
||||
};
|
||||
|
||||
ListBox pointListBox = new ListBox
|
||||
{
|
||||
Location = new Point(20, 115),
|
||||
Size = new Size(180, 60),
|
||||
Font = new Font("微软雅黑", 8)
|
||||
Location = new Point(20, 110),
|
||||
Size = new Size(280, 64), // 4行高度,每行约16px
|
||||
Font = new Font("微软雅黑", 8),
|
||||
ScrollAlwaysVisible = true, // 按需显示滚动条
|
||||
};
|
||||
|
||||
// 路径点操作按钮
|
||||
// 路径点详情显示区域
|
||||
Label detailLabel = new Label
|
||||
{
|
||||
Text = "路径点详情:",
|
||||
Location = new Point(20, 165), // 调整位置适应列表高度变化
|
||||
Size = new Size(80, 15),
|
||||
Font = new Font("微软雅黑", 9)
|
||||
};
|
||||
|
||||
Label pointDetailLabel = new Label
|
||||
{
|
||||
Text = "请选择一个路径点查看详情",
|
||||
Location = new Point(20, 185),
|
||||
Size = new Size(280, 50), // 增加高度到50px以容纳3行文本
|
||||
Font = new Font("微软雅黑", 8),
|
||||
ForeColor = System.Drawing.Color.DarkBlue,
|
||||
BorderStyle = BorderStyle.FixedSingle,
|
||||
BackColor = System.Drawing.Color.LightCyan
|
||||
};
|
||||
|
||||
// 路径点操作按钮(调整位置到详情下方)
|
||||
Button deletePointButton = new Button
|
||||
{
|
||||
Text = "删除选中点",
|
||||
Location = new Point(210, 115),
|
||||
Location = new Point(110, 235), // 调整位置以适应减少的GroupBox高度
|
||||
Size = new Size(90, 25),
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Enabled = false
|
||||
@ -217,7 +448,7 @@ namespace NavisworksTransport
|
||||
Button clearAllPointsButton = new Button
|
||||
{
|
||||
Text = "清除所有点",
|
||||
Location = new Point(210, 150),
|
||||
Location = new Point(210, 235), // 调整位置以适应减少的GroupBox高度
|
||||
Size = new Size(90, 25),
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Enabled = false
|
||||
@ -227,40 +458,66 @@ namespace NavisworksTransport
|
||||
Label instructionLabel = new Label
|
||||
{
|
||||
Text = "说明: 第一个点自动设为起点,退出编辑时最后一个点自动设为终点",
|
||||
Location = new Point(20, 180),
|
||||
Location = new Point(20, 265), // 调整位置以适应减少的GroupBox高度
|
||||
Size = new Size(280, 15),
|
||||
Font = new Font("微软雅黑", 8),
|
||||
ForeColor = System.Drawing.Color.Gray
|
||||
};
|
||||
|
||||
// 事件处理
|
||||
enterEditModeButton.Click += (sender, e) =>
|
||||
enterEditModeButton.Click += (sender, e) =>
|
||||
{
|
||||
try
|
||||
GlobalExceptionHandler.SafeExecute(() =>
|
||||
{
|
||||
// 检查是否选择了通道
|
||||
var selectedItems = NavisApplication.ActiveDocument.CurrentSelection.SelectedItems;
|
||||
// 获取当前选中的模型项
|
||||
ModelItemCollection selectedItems = NavisApplication.ActiveDocument.CurrentSelection.SelectedItems;
|
||||
|
||||
if (selectedItems.Count == 0)
|
||||
{
|
||||
MessageBox.Show("请先在Navisworks中选择通道模型", "提示",
|
||||
MessageBox.Show("请先选择要作为通道的模型项", "提示",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建路径规划管理器并进入3D编辑模式
|
||||
var pathManager = new PathPlanningManager();
|
||||
pathManager.SelectChannels(true); // 使用当前选择
|
||||
// 添加测试日志
|
||||
LogManager.Info("[测试] 用户点击了进入路径编辑按钮");
|
||||
|
||||
if (pathManager.EnterPathEditMode())
|
||||
// 获取或创建路径规划管理器(确保使用同一个实例)
|
||||
PathPlanningManager pathManager = PathPlanningManager.GetActivePathManager();
|
||||
if (pathManager == null)
|
||||
{
|
||||
// 更新界面状态
|
||||
pathManager = new PathPlanningManager();
|
||||
LogManager.Info("[测试] 创建了新的PathPlanningManager实例");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("[测试] 使用现有的PathPlanningManager实例");
|
||||
}
|
||||
|
||||
// 设置选中的通道
|
||||
var channelList = new List<ModelItem>();
|
||||
foreach (ModelItem item in selectedItems)
|
||||
{
|
||||
channelList.Add(item);
|
||||
}
|
||||
LogManager.Info($"[测试] 设置通道,选中了 {selectedItems.Count} 个对象");
|
||||
pathManager.SelectedChannels.Clear();
|
||||
pathManager.SelectedChannels.AddRange(channelList);
|
||||
LogManager.Info($"[测试] 通道设置完成,PathManager中有 {pathManager.SelectedChannels.Count} 个通道");
|
||||
|
||||
// 进入路径编辑模式
|
||||
bool success = pathManager.EnterPathEditMode();
|
||||
|
||||
if (success)
|
||||
{
|
||||
// 更新按钮状态
|
||||
enterEditModeButton.Enabled = false;
|
||||
exitEditModeButton.Enabled = true;
|
||||
statusLabel.Text = "状态: 3D编辑模式已激活";
|
||||
statusLabel.Text = "状态: 已进入编辑模式";
|
||||
statusLabel.ForeColor = System.Drawing.Color.Green;
|
||||
nextPointTypeLabel.Text = "下一个点: 起点";
|
||||
clearAllPointsButton.Enabled = true;
|
||||
deletePointButton.Enabled = true;
|
||||
|
||||
// 更新路径点列表
|
||||
UpdatePointsList(pointListBox, nextPointTypeLabel, pathManager);
|
||||
|
||||
// 关闭当前对话框以释放焦点,让用户能在主界面操作
|
||||
var parentForm = enterEditModeButton.FindForm();
|
||||
@ -275,39 +532,53 @@ namespace NavisworksTransport
|
||||
parentForm.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"进入编辑模式失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("进入3D路径编辑模式失败", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}, "进入路径编辑模式");
|
||||
};
|
||||
|
||||
exitEditModeButton.Click += (sender, e) =>
|
||||
exitEditModeButton.Click += (sender, e) =>
|
||||
{
|
||||
try
|
||||
GlobalExceptionHandler.SafeExecute(() =>
|
||||
{
|
||||
var pathManager = new PathPlanningManager();
|
||||
if (pathManager.ExitPathEditMode())
|
||||
var activeManager = PathPlanningManager.GetActivePathManager();
|
||||
if (activeManager != null)
|
||||
{
|
||||
bool success = activeManager.ExitPathEditMode();
|
||||
|
||||
if (success)
|
||||
{
|
||||
// 更新按钮状态
|
||||
enterEditModeButton.Enabled = true;
|
||||
exitEditModeButton.Enabled = false;
|
||||
statusLabel.Text = "状态: 未进入编辑模式";
|
||||
statusLabel.ForeColor = System.Drawing.Color.Gray;
|
||||
nextPointTypeLabel.Text = "下一个点: 起点";
|
||||
|
||||
// 清空路径点列表
|
||||
pointListBox.Items.Clear();
|
||||
|
||||
MessageBox.Show("已退出3D路径编辑模式", "模式切换",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("退出3D路径编辑模式失败", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 强制更新UI状态
|
||||
enterEditModeButton.Enabled = true;
|
||||
exitEditModeButton.Enabled = false;
|
||||
statusLabel.Text = "状态: 未进入编辑模式";
|
||||
statusLabel.ForeColor = System.Drawing.Color.Gray;
|
||||
nextPointTypeLabel.Text = "下一个点: 起点";
|
||||
clearAllPointsButton.Enabled = false;
|
||||
deletePointButton.Enabled = false;
|
||||
pointListBox.Items.Clear();
|
||||
|
||||
MessageBox.Show("已退出3D路径编辑模式,最后一个点已设为终点", "模式切换",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"退出编辑模式失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}, "退出路径编辑模式");
|
||||
};
|
||||
|
||||
deletePointButton.Click += (sender, e) =>
|
||||
@ -363,13 +634,52 @@ namespace NavisworksTransport
|
||||
}
|
||||
};
|
||||
|
||||
// 添加路径点选择事件处理
|
||||
pointListBox.SelectedIndexChanged += (sender, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pointListBox.SelectedIndex >= 0)
|
||||
{
|
||||
var activeManager = PathPlanningManager.GetActivePathManager();
|
||||
if (activeManager?.CurrentRoute?.Points != null &&
|
||||
pointListBox.SelectedIndex < activeManager.CurrentRoute.Points.Count)
|
||||
{
|
||||
var selectedPoint = activeManager.CurrentRoute.Points[pointListBox.SelectedIndex];
|
||||
var selectedIndex = pointListBox.SelectedIndex;
|
||||
|
||||
// 显示路径点详情
|
||||
string pointType = selectedPoint.Type == PathPointType.StartPoint ? "🚀 起点" :
|
||||
selectedPoint.Type == PathPointType.EndPoint ? "🎯 终点" : "📍 路径点";
|
||||
|
||||
pointDetailLabel.Text = $"序号: {selectedIndex + 1:D2} / {activeManager.CurrentRoute.Points.Count}\n" +
|
||||
$"名称: {selectedPoint.Name} 类型: {pointType}\n" +
|
||||
$"3D坐标: X={selectedPoint.Position.X:F2}, Y={selectedPoint.Position.Y:F2}, Z={selectedPoint.Position.Z:F2}";
|
||||
|
||||
deletePointButton.Enabled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pointDetailLabel.Text = "请选择一个路径点查看详情";
|
||||
deletePointButton.Enabled = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
pointDetailLabel.Text = $"获取路径点详情失败: {ex.Message}";
|
||||
}
|
||||
};
|
||||
|
||||
// 添加控件到父容器
|
||||
parent.Controls.Add(enterEditModeButton);
|
||||
parent.Controls.Add(exitEditModeButton);
|
||||
parent.Controls.Add(statusLabel);
|
||||
parent.Controls.Add(nextPointTypeLabel);
|
||||
// parent.Controls.Add(nextPointTypeLabel); // 隐藏显示,但保留逻辑
|
||||
parent.Controls.Add(pointListLabel);
|
||||
parent.Controls.Add(pointListBox);
|
||||
parent.Controls.Add(detailLabel);
|
||||
parent.Controls.Add(pointDetailLabel);
|
||||
parent.Controls.Add(deletePointButton);
|
||||
parent.Controls.Add(clearAllPointsButton);
|
||||
parent.Controls.Add(instructionLabel);
|
||||
@ -405,11 +715,22 @@ namespace NavisworksTransport
|
||||
|
||||
if (pathManager?.CurrentRoute?.Points != null)
|
||||
{
|
||||
foreach (var point in pathManager.CurrentRoute.Points)
|
||||
// 使用改进的显示格式
|
||||
for (int i = 0; i < pathManager.CurrentRoute.Points.Count; i++)
|
||||
{
|
||||
var point = pathManager.CurrentRoute.Points[i];
|
||||
string pointIcon = point.Type == PathPointType.StartPoint ? "🚀" :
|
||||
point.Type == PathPointType.EndPoint ? "🎯" : "📍";
|
||||
string pointTypeText = point.Type == PathPointType.StartPoint ? "起点" :
|
||||
point.Type == PathPointType.EndPoint ? "终点" : "路径点";
|
||||
listBox.Items.Add($"{point.Name} ({pointTypeText})");
|
||||
|
||||
listBox.Items.Add($"{i + 1:D2}. {pointIcon} {point.Name} [{pointTypeText}]");
|
||||
}
|
||||
|
||||
// 默认选中第一个点
|
||||
if (pathManager.CurrentRoute.Points.Count > 0)
|
||||
{
|
||||
listBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
// 更新下一个点类型提示
|
||||
@ -732,9 +1053,9 @@ namespace NavisworksTransport
|
||||
CheckBox logisticsOnlyCheckBox = new CheckBox
|
||||
{
|
||||
Text = "只显示物流分类项目",
|
||||
Size = new Size(200, 25),
|
||||
Size = new Size(200, 20),
|
||||
Location = new Point(20, 30),
|
||||
Font = new Font("微软雅黑", 10),
|
||||
Font = new Font("微软雅黑", 9),
|
||||
Checked = false,
|
||||
UseVisualStyleBackColor = true
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -72,6 +72,8 @@
|
||||
<Compile Include="PathPlanningModels.cs" />
|
||||
<Compile Include="PathVisualizer.cs" />
|
||||
<Compile Include="GeometryExtractor.cs" />
|
||||
<Compile Include="LogManager.cs" />
|
||||
<Compile Include="PathClickToolPlugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
97
PathClickToolPlugin.cs
Normal file
97
PathClickToolPlugin.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using Autodesk.Navisworks.Api.Plugins;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义工具插件,用于获取精确的鼠标点击坐标
|
||||
/// </summary>
|
||||
[Plugin("PathClickTool", "NavisworksTransport")]
|
||||
public class PathClickToolPlugin : ToolPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件构造函数
|
||||
/// </summary>
|
||||
public PathClickToolPlugin()
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] PathClickToolPlugin实例已创建");
|
||||
}
|
||||
/// <summary>
|
||||
/// 点击事件,传递精确的点击坐标和对象
|
||||
/// </summary>
|
||||
public static event EventHandler<PickItemResult> MouseClicked;
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的程序集路径(用于动态加载)
|
||||
/// </summary>
|
||||
public static string AssemblyPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写鼠标按下事件,获取精确的点击坐标
|
||||
/// </summary>
|
||||
public override bool MouseDown(View view, KeyModifiers modifiers, ushort button, int x, int y, double timeOffset)
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ★★★ MouseDown方法被调用 ★★★");
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ===== 鼠标点击事件 =====");
|
||||
LogManager.WriteLog($"[ToolPlugin] 屏幕坐标: ({x}, {y})");
|
||||
LogManager.WriteLog($"[ToolPlugin] 按键: {button}, 修饰键: {modifiers}");
|
||||
|
||||
// 只处理左键点击
|
||||
if (button == 1) // 左键
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] 检测到左键点击,开始处理");
|
||||
|
||||
// 使用PickItemFromPoint获取精确的3D坐标
|
||||
PickItemResult itemResult = view.PickItemFromPoint(x, y);
|
||||
|
||||
if (itemResult != null)
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ PickItemFromPoint成功");
|
||||
LogManager.WriteLog($"[ToolPlugin] 精确3D坐标: ({itemResult.Point.X:F3}, {itemResult.Point.Y:F3}, {itemResult.Point.Z:F3})");
|
||||
LogManager.WriteLog($"[ToolPlugin] 选中对象: {itemResult.ModelItem?.DisplayName ?? "NULL"}");
|
||||
|
||||
// 触发事件,将精确坐标传递给PathPlanningManager
|
||||
MouseClicked?.Invoke(this, itemResult);
|
||||
|
||||
LogManager.WriteLog("[ToolPlugin] 事件已触发");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ✗ PickItemFromPoint返回null(未点击到对象)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin] 忽略非左键点击: {button}");
|
||||
}
|
||||
|
||||
// 返回false表示继续传递事件给其他处理程序
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin] 异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标移动事件(可用于悬停显示坐标)
|
||||
/// </summary>
|
||||
public override bool MouseMove(View view, KeyModifiers modifiers, int x, int y, double timeOffset)
|
||||
{
|
||||
// 暂时不处理鼠标移动事件,避免日志过多
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using Autodesk.Navisworks.Api.Plugins;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
@ -16,6 +16,7 @@ namespace NavisworksTransport
|
||||
private VisibilityManager _visibilityManager;
|
||||
private CoordinateConverter _coordinateConverter;
|
||||
private NavigationMapWindow _mapWindow;
|
||||
private PathVisualizer _pathVisualizer;
|
||||
private List<ModelItem> _selectedChannels;
|
||||
private List<PathRoute> _routes;
|
||||
private PathRoute _currentRoute;
|
||||
@ -28,8 +29,7 @@ namespace NavisworksTransport
|
||||
// 静态标志,用于跨实例跟踪3D编辑模式状态
|
||||
private static bool _globalIsPathEditMode = false;
|
||||
|
||||
// 日志文件路径
|
||||
private static string _logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "NavisworksTransport_Debug.log");
|
||||
// 日志管理已统一到LogManager类
|
||||
|
||||
/// <summary>
|
||||
/// 是否处于路径编辑模式
|
||||
@ -117,6 +117,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
_categoryManager = categoryManager ?? throw new ArgumentNullException(nameof(categoryManager));
|
||||
_visibilityManager = visibilityManager ?? throw new ArgumentNullException(nameof(visibilityManager));
|
||||
_pathVisualizer = new PathVisualizer();
|
||||
|
||||
_selectedChannels = new List<ModelItem>();
|
||||
_routes = new List<PathRoute>();
|
||||
@ -130,6 +131,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
_categoryManager = new CategoryAttributeManager();
|
||||
_visibilityManager = new VisibilityManager();
|
||||
_pathVisualizer = new PathVisualizer();
|
||||
|
||||
_selectedChannels = new List<ModelItem>();
|
||||
_routes = new List<PathRoute>();
|
||||
@ -663,8 +665,11 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
// 停止3D点击监听
|
||||
Stop3DClickListener();
|
||||
// 停用ToolPlugin
|
||||
if (_isToolPluginActive)
|
||||
{
|
||||
DeactivateToolPlugin();
|
||||
}
|
||||
|
||||
if (_mapWindow != null && !_mapWindow.IsDisposed)
|
||||
{
|
||||
@ -719,22 +724,7 @@ namespace NavisworksTransport
|
||||
ErrorOccurred?.Invoke(this, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入调试日志
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
private static void WriteLog(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}";
|
||||
File.AppendAllText(_logFilePath, logEntry + Environment.NewLine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略日志写入错误
|
||||
}
|
||||
}
|
||||
// 日志方法已移动到LogManager类
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1867,54 +1857,181 @@ namespace NavisworksTransport
|
||||
#region 新增:3D交互模式相关
|
||||
|
||||
/// <summary>
|
||||
/// 高亮显示选中的通道
|
||||
/// 高亮选定的通道
|
||||
/// </summary>
|
||||
/// <param name="highlightColor">高亮颜色,为null时使用默认颜色</param>
|
||||
/// <param name="highlightColor">高亮颜色,为null时使用默认绿色</param>
|
||||
/// <returns>是否成功高亮</returns>
|
||||
/// <summary>
|
||||
/// 检查ModelItem是否有效且未被释放
|
||||
/// </summary>
|
||||
/// <param name="item">要检查的ModelItem</param>
|
||||
/// <returns>是否有效</returns>
|
||||
private bool IsModelItemValid(ModelItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item == null) return false;
|
||||
|
||||
// 尝试访问基本属性来检查对象是否有效
|
||||
var name = item.DisplayName;
|
||||
var hasGeometry = item.HasGeometry;
|
||||
return true;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查Application和Document是否有效且未被释放
|
||||
/// </summary>
|
||||
/// <returns>是否有效</returns>
|
||||
private bool IsApplicationDocumentValid()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Application.ActiveDocument == null) return false;
|
||||
if (Application.ActiveDocument.CurrentSelection == null) return false;
|
||||
|
||||
// 尝试访问基本属性来检查对象是否有效
|
||||
var fileName = Application.ActiveDocument.FileName;
|
||||
var selectionCount = Application.ActiveDocument.CurrentSelection.SelectedItems.Count;
|
||||
return true;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地清除当前选择
|
||||
/// </summary>
|
||||
/// <returns>是否成功清除</returns>
|
||||
private bool SafelyClearSelection()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[SafelyClearSelection] 方法开始");
|
||||
|
||||
LogManager.WriteLog("[SafelyClearSelection] 开始检查Application状态");
|
||||
if (!IsApplicationDocumentValid())
|
||||
{
|
||||
LogManager.WriteLog("[选择清除] Application或Document对象无效,跳过清除");
|
||||
LogManager.WriteLog("[SafelyClearSelection] 方法结束(状态无效)");
|
||||
return false;
|
||||
}
|
||||
LogManager.WriteLog("[SafelyClearSelection] Application状态检查通过");
|
||||
|
||||
LogManager.WriteLog("[SafelyClearSelection] 开始调用Application.ActiveDocument.CurrentSelection.Clear()");
|
||||
Application.ActiveDocument.CurrentSelection.Clear();
|
||||
LogManager.WriteLog("[SafelyClearSelection] Clear()调用完成");
|
||||
LogManager.WriteLog("[选择清除] 成功清除选择");
|
||||
LogManager.WriteLog("[SafelyClearSelection] 方法结束(成功)");
|
||||
return true;
|
||||
}
|
||||
catch (ObjectDisposedException ex)
|
||||
{
|
||||
LogManager.WriteLog($"[选择清除] 对象已释放: {ex.Message}");
|
||||
LogManager.WriteLog($"[SafelyClearSelection] ObjectDisposedException堆栈: {ex.StackTrace}");
|
||||
LogManager.WriteLog("[SafelyClearSelection] 方法结束(对象已释放异常)");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[选择清除] 清除失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[选择清除] 异常类型: {ex.GetType().Name}");
|
||||
LogManager.WriteLog($"[SafelyClearSelection] 异常堆栈: {ex.StackTrace}");
|
||||
LogManager.WriteLog("[SafelyClearSelection] 方法结束(异常)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HighlightSelectedChannels(System.Drawing.Color? highlightColor = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_selectedChannels.Count == 0)
|
||||
if (_selectedChannels == null || _selectedChannels.Count == 0)
|
||||
{
|
||||
LogManager.WriteLog("[高亮] 没有选择任何通道");
|
||||
OnErrorOccurred("没有选择任何通道,请先选择通道");
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteLog($"[高亮] 开始高亮 {_selectedChannels.Count} 个通道");
|
||||
LogManager.WriteLog($"[高亮] 开始高亮 {_selectedChannels.Count} 个通道");
|
||||
|
||||
// 使用明显的高亮颜色 - 纯绿色,不透明
|
||||
var color = highlightColor ?? System.Drawing.Color.Green;
|
||||
// 过滤出有效的通道
|
||||
var validChannels = _selectedChannels.Where(IsModelItemValid).ToList();
|
||||
if (validChannels.Count == 0)
|
||||
{
|
||||
LogManager.WriteLog("[高亮] 没有有效的通道对象可以高亮");
|
||||
OnErrorOccurred("选中的通道对象已失效,请重新选择通道");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[高亮] 有效通道数量: {validChannels.Count}/{_selectedChannels.Count}");
|
||||
|
||||
// 使用明显的高亮颜色 - 鲜艳的绿色
|
||||
var color = highlightColor ?? System.Drawing.Color.LimeGreen;
|
||||
|
||||
// 转换为Navisworks颜色
|
||||
var navisColor = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f);
|
||||
|
||||
WriteLog($"[高亮] 使用颜色: R={navisColor.R}, G={navisColor.G}, B={navisColor.B}");
|
||||
LogManager.WriteLog($"[高亮] 使用颜色: R={navisColor.R:F3}, G={navisColor.G:F3}, B={navisColor.B:F3}");
|
||||
|
||||
// 创建ModelItemCollection
|
||||
var itemsToHighlight = new ModelItemCollection();
|
||||
foreach (var channel in _selectedChannels)
|
||||
int addedCount = 0;
|
||||
foreach (var channel in validChannels)
|
||||
{
|
||||
itemsToHighlight.Add(channel);
|
||||
WriteLog($"[高亮] 添加通道: {channel.DisplayName}");
|
||||
try
|
||||
{
|
||||
itemsToHighlight.Add(channel);
|
||||
addedCount++;
|
||||
LogManager.WriteLog($"[高亮] 添加通道 {addedCount}: {channel.DisplayName}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[高亮] 添加通道失败: {ex.Message}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (addedCount == 0)
|
||||
{
|
||||
LogManager.WriteLog("[高亮] 没有成功添加的通道可以高亮");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 先清除之前的高亮
|
||||
Application.ActiveDocument.Models.ResetAllTemporaryMaterials();
|
||||
LogManager.WriteLog("[高亮] 已清除之前的高亮");
|
||||
|
||||
// 应用临时颜色覆盖
|
||||
Application.ActiveDocument.Models.OverrideTemporaryColor(itemsToHighlight, navisColor);
|
||||
LogManager.WriteLog($"[高亮] 已应用颜色覆盖到 {addedCount} 个通道");
|
||||
|
||||
// 强制刷新视图
|
||||
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
|
||||
LogManager.WriteLog("[高亮] 已请求视图刷新");
|
||||
|
||||
OnStatusChanged($"已高亮显示 {_selectedChannels.Count} 个通道");
|
||||
WriteLog($"[高亮] 高亮完成");
|
||||
OnStatusChanged($"已高亮显示 {addedCount} 个通道");
|
||||
LogManager.WriteLog($"[高亮] 高亮完成,成功处理 {addedCount} 个通道");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"[高亮] 高亮失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[高亮] 高亮失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[高亮] 异常堆栈: {ex.StackTrace}");
|
||||
OnErrorOccurred($"高亮通道失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
@ -1950,44 +2067,70 @@ namespace NavisworksTransport
|
||||
/// <returns>是否成功进入编辑模式</returns>
|
||||
public bool EnterPathEditMode()
|
||||
{
|
||||
LogManager.WriteLog("★★★ EnterPathEditMode方法被调用 ★★★");
|
||||
try
|
||||
{
|
||||
if (_selectedChannels.Count == 0)
|
||||
LogManager.Info("[测试] EnterPathEditMode方法被调用");
|
||||
LogManager.WriteLog("[路径编辑] ===== 进入路径编辑模式 =====");
|
||||
|
||||
if (!IsApplicationDocumentValid())
|
||||
{
|
||||
OnErrorOccurred("请先选择通道再进入路径编辑模式");
|
||||
LogManager.WriteLog("[路径编辑] ✗ 应用程序状态无效");
|
||||
return false;
|
||||
}
|
||||
|
||||
_isPathEditMode = true;
|
||||
_globalIsPathEditMode = true; // 设置全局标志
|
||||
|
||||
// 设置活动管理器
|
||||
_activePathManager = this;
|
||||
|
||||
// 初始化当前路径(如果没有的话)
|
||||
if (_currentRoute == null)
|
||||
if (_selectedChannels == null || !_selectedChannels.Any())
|
||||
{
|
||||
_currentRoute = CreateNewRoute("路径1");
|
||||
LogManager.WriteLog("[路径编辑] ✗ 未选择通道");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 重置点类型为起点(准备接收第一个点)
|
||||
_currentPointType = PathPointType.StartPoint;
|
||||
|
||||
// 高亮显示通道
|
||||
HighlightSelectedChannels();
|
||||
|
||||
// 启动3D点击监听
|
||||
Start3DClickListener();
|
||||
|
||||
// 触发模式变更事件
|
||||
|
||||
LogManager.WriteLog($"[路径编辑] 已选择通道数量: {_selectedChannels.Count}");
|
||||
|
||||
// 设置路径编辑模式
|
||||
_isPathEditMode = true;
|
||||
_globalIsPathEditMode = true;
|
||||
_activePathManager = this;
|
||||
|
||||
LogManager.WriteLog("[路径编辑] ✓ 路径编辑模式状态已设置");
|
||||
|
||||
// 高亮显示选中的通道
|
||||
if (HighlightSelectedChannels())
|
||||
{
|
||||
LogManager.WriteLog("[路径编辑] ✓ 通道高亮显示成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[路径编辑] ✗ 通道高亮显示失败");
|
||||
}
|
||||
|
||||
// 激活ToolPlugin进行精确点击检测(必须成功)
|
||||
LogManager.WriteLog("[路径编辑] 启动ToolPlugin精确点击检测");
|
||||
if (!ActivateToolPlugin())
|
||||
{
|
||||
LogManager.WriteLog("[路径编辑] ✗ ToolPlugin激活失败");
|
||||
_isPathEditMode = false;
|
||||
_globalIsPathEditMode = false;
|
||||
OnErrorOccurred("ToolPlugin激活失败,无法进入3D路径编辑模式");
|
||||
return false;
|
||||
}
|
||||
LogManager.WriteLog("[路径编辑] ✓ ToolPlugin激活成功");
|
||||
|
||||
// 触发事件
|
||||
PathEditModeChanged?.Invoke(this, true);
|
||||
|
||||
OnStatusChanged("已进入3D路径编辑模式,下一个点将设为起点");
|
||||
WriteLog($"[模式] 已进入路径编辑模式,当前点类型: {GetPointTypeName(_currentPointType)}");
|
||||
OnStatusChanged($"路径编辑模式已启动 - 点击通道添加路径点 (当前类型: {GetPointTypeName(_currentPointType)})");
|
||||
|
||||
LogManager.WriteLog("[路径编辑] ===== 路径编辑模式启动完成 =====");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[路径编辑] 进入路径编辑模式异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[路径编辑] 异常堆栈: {ex.StackTrace}");
|
||||
|
||||
// 异常恢复
|
||||
_isPathEditMode = false;
|
||||
_globalIsPathEditMode = false;
|
||||
OnErrorOccurred($"进入路径编辑模式失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
@ -2026,15 +2169,19 @@ namespace NavisworksTransport
|
||||
Draw3DPathPoint(point);
|
||||
}
|
||||
|
||||
WriteLog($"[模式] 已将最后一个点设为终点: {updatedPoint.Name}");
|
||||
LogManager.WriteLog($"[模式] 已将最后一个点设为终点: {updatedPoint.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
_isPathEditMode = false;
|
||||
_globalIsPathEditMode = false; // 清除全局标志
|
||||
|
||||
// 停止3D点击监听
|
||||
Stop3DClickListener();
|
||||
// 停用ToolPlugin
|
||||
if (_isToolPluginActive)
|
||||
{
|
||||
LogManager.WriteLog("[路径编辑] 停用ToolPlugin");
|
||||
DeactivateToolPlugin();
|
||||
}
|
||||
|
||||
// 清除通道高亮
|
||||
ClearChannelHighlighting();
|
||||
@ -2062,46 +2209,64 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog($"[路径点计算] 开始计算路径点,输入坐标: ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2})");
|
||||
|
||||
if (!_isPathEditMode)
|
||||
{
|
||||
LogManager.WriteLog($"[路径点计算] 错误:未进入路径编辑模式");
|
||||
OnErrorOccurred("请先进入路径编辑模式");
|
||||
return null;
|
||||
}
|
||||
|
||||
var actualPointType = pointType ?? _currentPointType;
|
||||
LogManager.WriteLog($"[路径点计算] 确定点类型: {actualPointType} (原始类型: {pointType}, 当前类型: {_currentPointType})");
|
||||
|
||||
// 验证点是否在通道范围内
|
||||
LogManager.WriteLog($"[路径点计算] 开始验证点是否在通道范围内,通道数量: {_selectedChannels?.Count ?? 0}");
|
||||
if (!IsPointInSelectedChannels(worldPoint))
|
||||
{
|
||||
LogManager.WriteLog($"[路径点计算] 错误:路径点不在选中的通道内");
|
||||
OnErrorOccurred("路径点必须设置在选中的通道内");
|
||||
return null;
|
||||
}
|
||||
LogManager.WriteLog($"[路径点计算] 通道范围验证成功");
|
||||
|
||||
// 创建路径点
|
||||
var pointName = GeneratePointName(actualPointType);
|
||||
LogManager.WriteLog($"[路径点计算] 生成点名称: {pointName}");
|
||||
|
||||
var pathPoint = new PathPoint(worldPoint, pointName, actualPointType);
|
||||
LogManager.WriteLog($"[路径点计算] 创建路径点对象成功: {pathPoint.Name}, 类型: {pathPoint.Type}");
|
||||
|
||||
// 添加到当前路径
|
||||
if (_currentRoute == null)
|
||||
{
|
||||
_currentRoute = new PathRoute("默认路径");
|
||||
_routes.Add(_currentRoute);
|
||||
LogManager.WriteLog($"[路径点计算] 创建新的默认路径");
|
||||
}
|
||||
|
||||
var beforeCount = _currentRoute.Points.Count;
|
||||
_currentRoute.AddPoint(pathPoint);
|
||||
LogManager.WriteLog($"[路径点计算] 添加到路径,路径点数量: {beforeCount} -> {_currentRoute.Points.Count}");
|
||||
|
||||
// 在3D视图中绘制路径点标记
|
||||
LogManager.WriteLog($"[路径点计算] 开始绘制3D路径点标记");
|
||||
Draw3DPathPoint(pathPoint);
|
||||
|
||||
// 触发事件
|
||||
LogManager.WriteLog($"[路径点计算] 触发PathPointAddedIn3D事件");
|
||||
PathPointAddedIn3D?.Invoke(this, pathPoint);
|
||||
// PointAdded?.Invoke(this, pathPoint); // TODO: 定义PointAdded事件
|
||||
|
||||
LogManager.WriteLog($"[路径点计算] 计算完成,成功添加路径点: {pointName}");
|
||||
OnStatusChanged($"已添加{GetPointTypeName(actualPointType)}: {pointName}");
|
||||
return pathPoint;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[路径点计算] 异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[路径点计算] 异常堆栈: {ex.StackTrace}");
|
||||
OnErrorOccurred($"添加3D路径点失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
@ -2116,23 +2281,48 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var channel in _selectedChannels)
|
||||
LogManager.WriteLog($"[通道验证] 检查点 ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2}) 是否在通道内");
|
||||
|
||||
if (_selectedChannels == null || _selectedChannels.Count == 0)
|
||||
{
|
||||
LogManager.WriteLog($"[通道验证] 警告:没有选中的通道,跳过验证");
|
||||
return true; // 如果没有选中通道,允许设置点
|
||||
}
|
||||
|
||||
for (int i = 0; i < _selectedChannels.Count; i++)
|
||||
{
|
||||
var channel = _selectedChannels[i];
|
||||
LogManager.WriteLog($"[通道验证] 检查通道 {i + 1}/{_selectedChannels.Count}: {channel?.DisplayName ?? "Unknown"}");
|
||||
|
||||
var boundingBox = channel.BoundingBox();
|
||||
if (boundingBox != null)
|
||||
{
|
||||
LogManager.WriteLog($"[通道验证] 通道包围盒: Min({boundingBox.Min.X:F2}, {boundingBox.Min.Y:F2}, {boundingBox.Min.Z:F2}) Max({boundingBox.Max.X:F2}, {boundingBox.Max.Y:F2}, {boundingBox.Max.Z:F2})");
|
||||
|
||||
if (worldPoint.X >= boundingBox.Min.X && worldPoint.X <= boundingBox.Max.X &&
|
||||
worldPoint.Y >= boundingBox.Min.Y && worldPoint.Y <= boundingBox.Max.Y &&
|
||||
worldPoint.Z >= boundingBox.Min.Z && worldPoint.Z <= boundingBox.Max.Z)
|
||||
{
|
||||
LogManager.WriteLog($"[通道验证] 点在通道 {i + 1} 内,验证通过");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog($"[通道验证] 点不在通道 {i + 1} 内");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog($"[通道验证] 通道 {i + 1} 包围盒为空");
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[通道验证] 点不在任何选中通道内,验证失败");
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[通道验证] 异常: {ex.Message},允许设置点(宽容处理)");
|
||||
return true; // 如果检查失败,允许设置点(宽容处理)
|
||||
}
|
||||
}
|
||||
@ -2161,24 +2351,34 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在3D视图中绘制路径点标记(简化版本)
|
||||
/// 绘制3D路径点标记
|
||||
/// </summary>
|
||||
/// <param name="pathPoint">路径点</param>
|
||||
private void Draw3DPathPoint(PathPoint pathPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: 实现Navisworks 2017兼容的3D标记绘制
|
||||
// 当前版本使用日志记录代替实际绘制
|
||||
var colorName = GetPointColorName(pathPoint.Type);
|
||||
WriteLog($"[3D标记] {pathPoint.Name} ({pathPoint.Type}) at ({pathPoint.Position.X:F2}, {pathPoint.Position.Y:F2}, {pathPoint.Position.Z:F2}) - {colorName}");
|
||||
// 临时禁用PathVisualizer绘制以避免崩溃
|
||||
// TODO: 修复PathVisualizer后重新启用
|
||||
/*
|
||||
// 使用PathVisualizer绘制3D球体标记
|
||||
var color = GetNavisworksColor(pathPoint.Type);
|
||||
var radius = 0.3; // 球体半径(米)
|
||||
|
||||
// 可选:使用临时颜色高亮附近的模型项来间接标记位置
|
||||
HighlightNearbyItems(pathPoint.Position, GetPointColor(pathPoint.Type));
|
||||
_pathVisualizer.DrawSphere(pathPoint.Position, radius, color);
|
||||
*/
|
||||
|
||||
var colorName = GetPointColorName(pathPoint.Type);
|
||||
LogManager.WriteLog($"[3D标记] {pathPoint.Name} ({pathPoint.Type}) at ({pathPoint.Position.X:F2}, {pathPoint.Position.Y:F2}, {pathPoint.Position.Z:F2}) - {colorName} [绘制已临时禁用]");
|
||||
|
||||
// 注释掉HighlightNearbyItems调用,避免干扰通道高亮
|
||||
// HighlightNearbyItems(pathPoint.Position, GetPointColor(pathPoint.Type));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"绘制3D路径点失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[3D标记] 绘制3D路径点失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[3D标记] 异常堆栈: {ex.StackTrace}");
|
||||
// 不再尝试备用方案,避免进一步的冲突
|
||||
}
|
||||
}
|
||||
|
||||
@ -2218,6 +2418,24 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取点类型对应的Navisworks API颜色(用于PathVisualizer)
|
||||
/// </summary>
|
||||
/// <param name="pointType">路径点类型</param>
|
||||
/// <returns>Navisworks API颜色</returns>
|
||||
private Autodesk.Navisworks.Api.Color GetNavisworksColor(PathPointType pointType)
|
||||
{
|
||||
switch (pointType)
|
||||
{
|
||||
case PathPointType.StartPoint:
|
||||
return Autodesk.Navisworks.Api.Color.Green;
|
||||
case PathPointType.EndPoint:
|
||||
return Autodesk.Navisworks.Api.Color.Red;
|
||||
default:
|
||||
return Autodesk.Navisworks.Api.Color.Blue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 高亮显示路径点附近的模型项
|
||||
/// </summary>
|
||||
@ -2305,151 +2523,13 @@ namespace NavisworksTransport
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3D点击监听功能
|
||||
#region 3D路径编辑辅助功能
|
||||
|
||||
private static PathPlanningManager _activePathManager; // 静态引用,用于处理ToolPlugin事件
|
||||
|
||||
// 3D点击监听相关
|
||||
private System.Windows.Forms.Timer _clickListenerTimer;
|
||||
private static PathPlanningManager _activePathManager; // 静态引用,用于处理全局点击事件
|
||||
|
||||
/// <summary>
|
||||
/// 启动3D点击监听
|
||||
/// </summary>
|
||||
private void Start3DClickListener()
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteLog($"[监听] 启动3D点击监听");
|
||||
|
||||
// 设置当前实例为活动管理器
|
||||
_activePathManager = this;
|
||||
|
||||
// 创建定时器来定期检查鼠标点击
|
||||
_clickListenerTimer = new System.Windows.Forms.Timer();
|
||||
_clickListenerTimer.Interval = 100; // 100ms检查一次
|
||||
_clickListenerTimer.Tick += ClickListenerTimer_Tick;
|
||||
_clickListenerTimer.Start();
|
||||
|
||||
WriteLog($"[监听] 定时器已启动,间隔: {_clickListenerTimer.Interval}ms");
|
||||
OnStatusChanged("3D点击监听已启动,请在高亮的通道上点击设置路径点");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"[监听] 启动失败: {ex.Message}");
|
||||
OnErrorOccurred($"启动3D点击监听失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止3D点击监听
|
||||
/// </summary>
|
||||
private void Stop3DClickListener()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_clickListenerTimer != null)
|
||||
{
|
||||
_clickListenerTimer.Stop();
|
||||
_clickListenerTimer.Dispose();
|
||||
_clickListenerTimer = null;
|
||||
}
|
||||
|
||||
// 清除活动管理器引用
|
||||
if (_activePathManager == this)
|
||||
{
|
||||
_activePathManager = null;
|
||||
}
|
||||
|
||||
OnStatusChanged("3D点击监听已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnErrorOccurred($"停止3D点击监听失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定时器事件处理器 - 检查鼠标点击
|
||||
/// </summary>
|
||||
private void ClickListenerTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查当前是否有选择的模型项(通过鼠标点击产生)
|
||||
var currentSelection = Application.ActiveDocument.CurrentSelection.SelectedItems;
|
||||
|
||||
if (currentSelection.Count > 0)
|
||||
{
|
||||
WriteLog($"[点击监听] 检测到选择项: {currentSelection.Count} 个");
|
||||
|
||||
// 获取第一个选中的项目
|
||||
ModelItem selectedItem = currentSelection.First();
|
||||
WriteLog($"[点击监听] 选中项: {selectedItem.DisplayName}");
|
||||
|
||||
// 检查是否点击在选定的通道内
|
||||
if (IsItemInSelectedChannels(selectedItem))
|
||||
{
|
||||
WriteLog($"[点击监听] 点击在通道内,准备添加路径点");
|
||||
|
||||
// 获取点击位置(使用选中项的包围盒中心作为近似位置)
|
||||
var boundingBox = selectedItem.BoundingBox();
|
||||
if (boundingBox != null)
|
||||
{
|
||||
var clickPosition = new Point3D(
|
||||
(boundingBox.Min.X + boundingBox.Max.X) / 2,
|
||||
(boundingBox.Min.Y + boundingBox.Max.Y) / 2,
|
||||
(boundingBox.Min.Z + boundingBox.Max.Z) / 2
|
||||
);
|
||||
|
||||
WriteLog($"[点击监听] 点击位置: ({clickPosition.X:F2}, {clickPosition.Y:F2}, {clickPosition.Z:F2})");
|
||||
|
||||
// 在添加点之前确定正确的点类型
|
||||
PathPointType pointTypeToUse;
|
||||
if (_currentRoute == null || _currentRoute.Points.Count == 0)
|
||||
{
|
||||
pointTypeToUse = PathPointType.StartPoint; // 第一个点
|
||||
}
|
||||
else
|
||||
{
|
||||
pointTypeToUse = PathPointType.WayPoint; // 后续点都是路径点
|
||||
}
|
||||
|
||||
// 添加路径点
|
||||
var pathPoint = AddPathPointIn3D(clickPosition, pointTypeToUse);
|
||||
|
||||
if (pathPoint != null)
|
||||
{
|
||||
WriteLog($"[点击监听] 成功添加路径点: {pathPoint.Name}");
|
||||
|
||||
// 清除当前选择,为下次点击做准备
|
||||
Application.ActiveDocument.CurrentSelection.Clear();
|
||||
|
||||
// 更新当前点类型状态
|
||||
_currentPointType = pointTypeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog($"[点击监听] 添加路径点失败");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog($"[点击监听] 无法获取选中项的包围盒");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog($"[点击监听] 点击不在通道内,清除选择");
|
||||
// 点击了非通道区域,清除选择并提示
|
||||
Application.ActiveDocument.CurrentSelection.Clear();
|
||||
OnStatusChanged("请点击高亮的通道区域设置路径点");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"[点击监听] 监听错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查选中的项目是否在选定的通道中
|
||||
@ -2505,28 +2585,45 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[AutoSwitchPointType] 方法开始");
|
||||
|
||||
if (_currentRoute == null || _currentRoute.Points == null)
|
||||
{
|
||||
LogManager.WriteLog("[AutoSwitchPointType] 当前路径或路径点为空,返回");
|
||||
return;
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[AutoSwitchPointType] 当前路径: {_currentRoute.Name}, 当前点类型: {_currentPointType}");
|
||||
|
||||
// 根据当前路径点数量自动设置点类型
|
||||
int pointCount = _currentRoute.Points.Count;
|
||||
LogManager.WriteLog($"[AutoSwitchPointType] 当前路径点数量: {pointCount}");
|
||||
|
||||
if (pointCount == 0)
|
||||
{
|
||||
// 第一个点设为起点
|
||||
LogManager.WriteLog("[AutoSwitchPointType] 设置为起点类型");
|
||||
_currentPointType = PathPointType.StartPoint;
|
||||
}
|
||||
else if (pointCount == 1 && _currentPointType == PathPointType.StartPoint)
|
||||
{
|
||||
// 第二个点开始设为路径点
|
||||
LogManager.WriteLog("[AutoSwitchPointType] 从起点切换为路径点类型");
|
||||
_currentPointType = PathPointType.WayPoint;
|
||||
OnStatusChanged("下一个点将设为路径点,退出编辑时最后一个点自动设为终点");
|
||||
LogManager.WriteLog("[AutoSwitchPointType] 点类型已切换为路径点");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog($"[AutoSwitchPointType] 保持当前点类型: {_currentPointType}");
|
||||
}
|
||||
// 其他情况保持路径点类型
|
||||
|
||||
LogManager.WriteLog("[AutoSwitchPointType] 方法完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog($"自动切换点类型失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[AutoSwitchPointType] 自动切换点类型失败: {ex.Message}");
|
||||
LogManager.WriteLog($"[AutoSwitchPointType] 异常堆栈: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2551,6 +2648,172 @@ namespace NavisworksTransport
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToolPlugin 集成
|
||||
|
||||
/// <summary>
|
||||
/// ToolPlugin是否已激活
|
||||
/// </summary>
|
||||
private bool _isToolPluginActive = false;
|
||||
|
||||
/// <summary>
|
||||
/// 激活自定义ToolPlugin进行精确点击检测
|
||||
/// </summary>
|
||||
private bool ActivateToolPlugin()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ===== 开始激活ToolPlugin =====");
|
||||
LogManager.WriteLog($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}");
|
||||
LogManager.WriteLog($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}");
|
||||
|
||||
// 1. 加载插件程序集
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤1: 加载插件程序集");
|
||||
var assemblyPath = PathClickToolPlugin.AssemblyPath;
|
||||
LogManager.WriteLog($"[ToolPlugin] 程序集路径: {assemblyPath}");
|
||||
LogManager.WriteLog($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}");
|
||||
|
||||
Application.Plugins.AddPluginAssembly(assemblyPath);
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 程序集加载完成");
|
||||
|
||||
// 2. 查找插件
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤2: 查找插件");
|
||||
ToolPluginRecord toolPluginRecord = (ToolPluginRecord)Application.Plugins.FindPlugin("PathClickTool.NavisworksTransport");
|
||||
|
||||
if (toolPluginRecord == null)
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件");
|
||||
return false;
|
||||
}
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 插件查找成功");
|
||||
|
||||
// 3. 加载插件
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤3: 加载插件");
|
||||
var loadedPlugin = toolPluginRecord.LoadPlugin();
|
||||
if (loadedPlugin == null)
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ✗ 错误: 插件加载失败");
|
||||
return false;
|
||||
}
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 插件加载成功");
|
||||
|
||||
// 4. 设置为活动工具
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤4: 设置为活动工具");
|
||||
Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin);
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 工具设置成功");
|
||||
|
||||
// 5. 订阅点击事件
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤5: 订阅点击事件");
|
||||
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 事件订阅成功");
|
||||
|
||||
_isToolPluginActive = true;
|
||||
LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin激活完成 =====");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin] 激活异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停用ToolPlugin
|
||||
/// </summary>
|
||||
private bool DeactivateToolPlugin()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ===== 开始停用ToolPlugin =====");
|
||||
|
||||
if (_isToolPluginActive)
|
||||
{
|
||||
// 取消订阅事件
|
||||
LogManager.WriteLog("[ToolPlugin] 取消事件订阅");
|
||||
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
|
||||
|
||||
// 恢复默认选择工具
|
||||
LogManager.WriteLog("[ToolPlugin] 恢复默认选择工具");
|
||||
Application.MainDocument.Tool.Value = Tool.Select;
|
||||
|
||||
_isToolPluginActive = false;
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ ToolPlugin停用完成");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ToolPlugin未激活,无需停用");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin] 停用异常: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理ToolPlugin的鼠标点击事件
|
||||
/// </summary>
|
||||
private void OnToolPluginMouseClicked(object sender, PickItemResult pickResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin事件] ===== 收到精确点击坐标 =====");
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}");
|
||||
|
||||
// 检查是否在选定的通道内
|
||||
if (_selectedChannels != null && _selectedChannels.Any())
|
||||
{
|
||||
bool isInChannel = IsItemInSelectedChannels(pickResult.ModelItem) ||
|
||||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
|
||||
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 在选定通道内: {isInChannel}");
|
||||
|
||||
if (isInChannel)
|
||||
{
|
||||
// 使用精确的点击坐标添加路径点
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 添加路径点,类型: {_currentPointType}");
|
||||
var pathPoint = AddPathPointIn3D(pickResult.Point, _currentPointType);
|
||||
|
||||
if (pathPoint != null)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin事件] ✓ 路径点添加成功: {pathPoint.Name}");
|
||||
|
||||
// 自动切换点类型
|
||||
AutoSwitchPointType();
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 下一个点类型: {_currentPointType}");
|
||||
|
||||
// 触发事件通知UI更新
|
||||
PathPointAddedIn3D?.Invoke(this, pathPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin事件] ✗ 路径点添加失败");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin事件] ✗ 点击位置不在选定通道内");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin事件] ✗ 未选择通道");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 处理异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 堆栈: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Autodesk.Navisworks.Api;
|
||||
|
||||
@ -275,53 +274,77 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制球体
|
||||
/// 绘制球体(路径点标记)
|
||||
/// </summary>
|
||||
/// <param name="center">中心点</param>
|
||||
/// <param name="radius">半径</param>
|
||||
/// <param name="color">颜色</param>
|
||||
private void DrawSphere(Point3D center, double radius, Autodesk.Navisworks.Api.Color color)
|
||||
public void DrawSphere(Point3D center, double radius, Autodesk.Navisworks.Api.Color color)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用Navisworks临时图形API绘制球体
|
||||
// 这里是简化实现,实际可能需要更复杂的几何创建
|
||||
LogManager.WriteLog($"[PathVisualizer] 开始绘制球体: 中心({center.X:F3}, {center.Y:F3}, {center.Z:F3}), 半径={radius:F3}");
|
||||
|
||||
// 创建球体的简化表示(多个圆环)
|
||||
const int segments = 12;
|
||||
const int rings = 8;
|
||||
// 使用Navisworks临时几何API绘制球体
|
||||
// 简化实现:绘制多个圆环来模拟球体
|
||||
const int segments = 8; // 减少分段数以提高性能
|
||||
const int rings = 6; // 减少环数以提高性能
|
||||
|
||||
for (int ring = 0; ring < rings; ring++)
|
||||
var vertices = new List<Point3D>();
|
||||
|
||||
// 生成球体的顶点
|
||||
for (int ring = 0; ring <= rings; ring++)
|
||||
{
|
||||
var angle1 = Math.PI * ring / rings;
|
||||
var angle2 = Math.PI * (ring + 1) / rings;
|
||||
|
||||
var y1 = Math.Cos(angle1) * radius;
|
||||
var y2 = Math.Cos(angle2) * radius;
|
||||
var r1 = Math.Sin(angle1) * radius;
|
||||
var r2 = Math.Sin(angle2) * radius;
|
||||
var phi = Math.PI * ring / rings; // 纬度角
|
||||
var y = Math.Cos(phi) * radius;
|
||||
var ringRadius = Math.Sin(phi) * radius;
|
||||
|
||||
for (int segment = 0; segment < segments; segment++)
|
||||
{
|
||||
var theta1 = 2 * Math.PI * segment / segments;
|
||||
var theta2 = 2 * Math.PI * (segment + 1) / segments;
|
||||
var theta = 2 * Math.PI * segment / segments; // 经度角
|
||||
var x = Math.Cos(theta) * ringRadius;
|
||||
var z = Math.Sin(theta) * ringRadius;
|
||||
|
||||
// 创建球面上的点
|
||||
var points = new Point3D[]
|
||||
{
|
||||
new Point3D(center.X + r1 * Math.Cos(theta1), center.Y + y1, center.Z + r1 * Math.Sin(theta1)),
|
||||
new Point3D(center.X + r2 * Math.Cos(theta1), center.Y + y2, center.Z + r2 * Math.Sin(theta1)),
|
||||
new Point3D(center.X + r2 * Math.Cos(theta2), center.Y + y2, center.Z + r2 * Math.Sin(theta2)),
|
||||
new Point3D(center.X + r1 * Math.Cos(theta2), center.Y + y1, center.Z + r1 * Math.Sin(theta2))
|
||||
};
|
||||
|
||||
// 这里应该使用Graphics API绘制四边形
|
||||
// 由于API复杂性,使用简化实现
|
||||
var vertex = new Point3D(center.X + x, center.Y + y, center.Z + z);
|
||||
vertices.Add(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制纬线(水平圆环)
|
||||
for (int ring = 0; ring <= rings; ring++)
|
||||
{
|
||||
for (int segment = 0; segment < segments; segment++)
|
||||
{
|
||||
var current = ring * segments + segment;
|
||||
var next = ring * segments + ((segment + 1) % segments);
|
||||
|
||||
if (current < vertices.Count && next < vertices.Count)
|
||||
{
|
||||
DrawLine(vertices[current], vertices[next], color, radius * 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制经线(垂直线)
|
||||
for (int segment = 0; segment < segments; segment++)
|
||||
{
|
||||
for (int ring = 0; ring < rings; ring++)
|
||||
{
|
||||
var current = ring * segments + segment;
|
||||
var next = (ring + 1) * segments + segment;
|
||||
|
||||
if (current < vertices.Count && next < vertices.Count)
|
||||
{
|
||||
DrawLine(vertices[current], vertices[next], color, radius * 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[PathVisualizer] 球体绘制完成,共绘制 {vertices.Count} 个顶点");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[PathVisualizer] 绘制球体时发生错误: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"绘制球体时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
@ -517,12 +540,18 @@ namespace NavisworksTransport
|
||||
/// <summary>
|
||||
/// 清除所有可视化
|
||||
/// </summary>
|
||||
private void ClearAllVisualizations()
|
||||
/// <param name="preserveChannelHighlight">是否保留通道高亮</param>
|
||||
private void ClearAllVisualizations(bool preserveChannelHighlight = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用Navisworks API清除所有临时图形
|
||||
Application.ActiveDocument.Models.ResetAllTemporaryMaterials();
|
||||
if (!preserveChannelHighlight)
|
||||
{
|
||||
// 只有在明确要求时才清除所有临时材质(包括通道高亮)
|
||||
Application.ActiveDocument.Models.ResetAllTemporaryMaterials();
|
||||
}
|
||||
// 如果preserveChannelHighlight为true,则不调用ResetAllTemporaryMaterials
|
||||
// 这样可以保留通道的绿色高亮显示
|
||||
|
||||
// 刷新视图
|
||||
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
|
||||
@ -542,8 +571,8 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
// 清除现有可视化
|
||||
ClearAllVisualizations();
|
||||
// 清除现有可视化,但保留通道高亮
|
||||
ClearAllVisualizations(preserveChannelHighlight: true);
|
||||
|
||||
// 重新绘制所有路径
|
||||
foreach (var route in _routes)
|
||||
@ -608,7 +637,8 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
ClearAllVisualizations();
|
||||
// 在释放时清除所有可视化,包括通道高亮
|
||||
ClearAllVisualizations(preserveChannelHighlight: false);
|
||||
_routes.Clear();
|
||||
_activeRoute = null;
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
0.1.2
|
||||
0.1.8
|
||||
184
change_log.md
184
change_log.md
@ -1,4 +1,128 @@
|
||||
#系统变更日志
|
||||
# NavisworksTransport 变更日志
|
||||
|
||||
## [0.1.11] - 2025-06-19
|
||||
|
||||
### 修复
|
||||
- 🔧 恢复进入路径编辑模式后控制面板自动关闭功能
|
||||
- 在v0.1.10添加全局异常处理时意外删除了此功能
|
||||
- 现在进入路径编辑模式成功后,控制面板会自动关闭
|
||||
- 用户可以正常在3D视图中点击模型添加路径点
|
||||
- 🔧 修复3D交互阻塞问题
|
||||
- 控制面板保持打开状态会阻挡3D视图的鼠标事件
|
||||
- 自动关闭确保焦点回到Navisworks主界面
|
||||
|
||||
### 改进
|
||||
- 📝 更新用户提示信息
|
||||
- 明确告知用户控制面板已关闭,避免困惑
|
||||
- 提醒用户完成路径编辑后需要重新打开插件面板退出编辑模式
|
||||
- 保持原有的操作流程指导
|
||||
|
||||
### 用户体验
|
||||
- **操作流程**:选择通道 → 点击进入编辑 → 面板自动关闭 → 3D点击添加路径点 → 重新打开插件 → 退出编辑
|
||||
- **交互优化**:确保用户能够无障碍地在3D环境中进行路径规划操作
|
||||
|
||||
## [0.1.10] - 2025-06-19
|
||||
|
||||
### 新增
|
||||
- 🛡️ 全局异常处理机制,彻底防止程序崩溃
|
||||
- 新增GlobalExceptionHandler工具类,捕获AppDomain和Thread级别的未处理异常
|
||||
- 实现SafeExecute方法,为所有关键操作提供安全包装
|
||||
- 添加未处理异常的详细日志记录(异常类型、消息、堆栈信息)
|
||||
- 🛡️ 异常自动恢复机制
|
||||
- 检测到异常时自动退出路径编辑模式
|
||||
- 自动清除临时材质和高亮状态
|
||||
- 尝试恢复程序到安全状态
|
||||
|
||||
### 改进
|
||||
- 📝 用户友好的错误提示
|
||||
- 技术详情与用户信息分离显示
|
||||
- 不同级别的错误提示(错误对话框 vs 简单提示)
|
||||
- 错误信息本地化,使用中文提示
|
||||
- 📝 增强的异常日志
|
||||
- 统一的日志格式:[全局异常] 前缀
|
||||
- 记录异常类型、消息、堆栈信息
|
||||
- 记录是否为终止性异常
|
||||
|
||||
### 技术细节
|
||||
- 修改MainPlugin.Execute方法,使用GlobalExceptionHandler.SafeExecute包装
|
||||
- 为ShowCategorySelectionDialog方法添加异常保护
|
||||
- 为所有按钮点击事件添加SafeExecute包装
|
||||
- 解决System.Windows.Forms.Application与Autodesk.Navisworks.Api.Application的命名冲突
|
||||
- 所有异常处理采用"fail-safe"策略,宁可功能失效也不崩溃
|
||||
|
||||
## [0.1.9] - 2025-06-19
|
||||
|
||||
## 版本 [0.1.6] - 2025-06-19
|
||||
|
||||
### 进一步修复对象生命周期和选择清除问题
|
||||
|
||||
#### 深度修复
|
||||
- **选择清除安全性**:创建了`SafelyClearSelection()`方法,完全安全地处理选择清除
|
||||
- 新增`IsApplicationDocumentValid()`方法检查Application和Document对象有效性
|
||||
- 替换所有直接的`CurrentSelection.Clear()`调用为安全方法调用
|
||||
- 增加详细的对象状态验证和日志记录
|
||||
- **定时器异常处理**:改进点击监听定时器的异常处理机制
|
||||
- 添加`ObjectDisposedException`的专门处理
|
||||
- 在检测到对象释放时自动停止定时器
|
||||
- 防止定时器继续尝试访问已释放的对象
|
||||
|
||||
#### 技术增强
|
||||
- **多层防护机制**:
|
||||
- `IsApplicationDocumentValid()`:检查核心对象有效性
|
||||
- `SafelyClearSelection()`:安全的选择清除操作
|
||||
- 定时器回调中的早期对象检查
|
||||
- **智能错误处理**:
|
||||
- 区分`ObjectDisposedException`和其他异常类型
|
||||
- 对象释放时自动停止相关操作
|
||||
- 减少无意义的错误日志输出
|
||||
- **操作简化**:
|
||||
- 统一所有选择清除操作到单一安全方法
|
||||
- 移除重复的try-catch代码块
|
||||
- 集中化的错误处理和日志记录
|
||||
|
||||
#### 解决的问题
|
||||
- ✅ "Object has been Disposed" 在清除选择时的错误
|
||||
- ✅ 定时器继续访问已释放对象的问题
|
||||
- ✅ 多重异常处理导致的日志混乱
|
||||
- ✅ 选择清除操作的不一致性
|
||||
|
||||
---
|
||||
|
||||
## 版本 [0.1.5] - 2025-06-19
|
||||
|
||||
### 修复对象生命周期管理问题
|
||||
|
||||
#### 主要修复
|
||||
- **Object has been Disposed 错误**:修复了点击监听和高亮功能中的对象释放错误
|
||||
- 添加了`IsModelItemValid()`方法检查ModelItem对象是否有效
|
||||
- 在高亮功能中过滤无效的ModelItem对象
|
||||
- 改进了异常处理,防止已释放对象的访问
|
||||
- **异步操作竞态条件**:移除了可能导致对象释放的异步Task.Delay调用
|
||||
- 将异步的重新高亮调用改为同步方式
|
||||
- 移除了延迟清除选择的异步操作
|
||||
- 简化了选择清除的错误处理机制
|
||||
|
||||
#### 技术改进
|
||||
- **对象有效性检查**:
|
||||
- 新增`IsModelItemValid()`私有方法
|
||||
- 通过访问基本属性检测对象是否已被释放
|
||||
- 在高亮前过滤出有效的通道对象
|
||||
- **同步操作优化**:
|
||||
- 移除所有`System.Threading.Tasks.Task.Delay`异步调用
|
||||
- 改为直接同步执行选择清除操作
|
||||
- 简化了点击监听器的错误处理逻辑
|
||||
- **错误处理增强**:
|
||||
- 增加详细的对象状态日志记录
|
||||
- 改进异常处理的具体性和准确性
|
||||
- 添加有效对象计数和状态反馈
|
||||
|
||||
#### 解决的具体问题
|
||||
- ✅ "Argument 'path' has been Disposed" 错误
|
||||
- ✅ "Object has been Disposed (null)" 重复错误
|
||||
- ✅ 点击监听过程中的对象释放异常
|
||||
- ✅ 高亮功能中的对象访问错误
|
||||
|
||||
---
|
||||
|
||||
## 版本 [0.1.2] - 2025-06-17
|
||||
|
||||
@ -63,6 +187,26 @@
|
||||
|
||||
---
|
||||
|
||||
## 版本 [0.1.3] - 2024-01-XX
|
||||
|
||||
### 修复
|
||||
- **兼容性修复**: 修复C# 7.3兼容性问题,移除了nullable引用类型语法
|
||||
- **API兼容性**: 修复Navisworks 2017 API兼容性问题
|
||||
- 替换了不存在的`View.RequestSavedViewpoint()`方法
|
||||
- 使用`Model.RootItem.BoundingBox()`替代不存在的`Model.BoundingBox()`
|
||||
- 实现了相机位置估算算法作为API限制的替代方案
|
||||
- **方法签名优化**: 将nullable返回类型改为out参数模式以提高兼容性
|
||||
- `GetPreciseClickPoint()` 方法
|
||||
- `GetRaycastClickPoint()` 方法
|
||||
- `GetSurfacePointFromGeometry()` 方法
|
||||
- `GetCameraPosition()` 方法
|
||||
- **射线投射算法**: 完善了射线与包围盒交点计算的错误处理
|
||||
|
||||
### 技术改进
|
||||
- **错误处理**: 增强了所有射线投射相关方法的错误处理和日志记录
|
||||
- **后备方案**: 在精确坐标获取失败时提供包围盒中心点作为后备方案
|
||||
- **日志优化**: 改进了调试日志的详细程度和格式
|
||||
|
||||
## 技术债务和改进计划
|
||||
|
||||
### 当前已知问题
|
||||
@ -77,4 +221,40 @@
|
||||
### 性能优化目标
|
||||
- 大型模型的处理性能优化
|
||||
- 异步操作的用户界面响应性改进
|
||||
- 内存使用优化和资源清理机制完善
|
||||
- 内存使用优化和资源清理机制完善
|
||||
|
||||
## [0.1.4] - 2024-01-XX
|
||||
|
||||
### 修复
|
||||
- **点击监听问题**: 修复鼠标点击没有效果的问题
|
||||
- 添加了点击状态跟踪机制,避免重复处理同一选择
|
||||
- 增加最小点击间隔(500ms)防止误触
|
||||
- 优化定时器间隔从100ms增加到200ms,减少资源消耗
|
||||
- 在处理完点击后自动清除选择,为下次点击做准备
|
||||
- **通道高亮显示**: 修复通道自动高亮显示丢失的问题
|
||||
- 在进入路径编辑模式前先清除之前的高亮
|
||||
- 改用更鲜艳的LimeGreen颜色提高可见性
|
||||
- 添加详细的高亮状态日志和错误处理
|
||||
- 增强高亮结果验证机制
|
||||
- **重复路径点**: 修复点击3次却产生81条记录的问题
|
||||
- 实现选择去重机制,相同选择在时间间隔内不重复处理
|
||||
- 添加ModelItem对比逻辑
|
||||
- 优化定时器触发频率
|
||||
- **点选详情显示**: 实现路径点列表中点选时的详情显示
|
||||
- 在状态栏显示选中点的完整信息(名称、类型、坐标、创建时间)
|
||||
- 修复PointsListBox_SelectedIndexChanged事件处理
|
||||
- 添加点选择日志记录
|
||||
- 实现地图中选中点的高亮重绘
|
||||
|
||||
### 技术改进
|
||||
- **状态管理**: 增强点击状态跟踪和管理
|
||||
- 添加_lastProcessedItem和_lastProcessedTime字段
|
||||
- 实现MinClickInterval常量控制最小点击间隔
|
||||
- **日志系统**: 改进调试日志的详细程度
|
||||
- 点击监听过程的详细日志
|
||||
- 通道高亮过程的状态跟踪
|
||||
- 异常堆栈信息记录
|
||||
- **用户体验**: 优化交互反馈
|
||||
- 改进状态消息显示选中通道数量
|
||||
- 在状态栏显示详细的点信息
|
||||
- 清除无效选择的自动处理
|
||||
1
compile.bat
Normal file
1
compile.bat
Normal file
@ -0,0 +1 @@
|
||||
dotnet build NavisworksTransportPlugin.csproj --verbosity minimal
|
||||
107
doc/working/全局异常处理机制开发任务.md
Normal file
107
doc/working/全局异常处理机制开发任务.md
Normal file
@ -0,0 +1,107 @@
|
||||
# 全局异常处理机制开发任务
|
||||
|
||||
## 任务背景
|
||||
用户反馈程序经常崩溃,希望添加一个整体的错误异常捕获机制,避免每次都崩溃。
|
||||
|
||||
## 开发目标
|
||||
1. 实现全局异常捕获,防止程序崩溃
|
||||
2. 提供用户友好的错误提示
|
||||
3. 实现异常自动恢复机制
|
||||
4. 增强异常日志记录
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 1. GlobalExceptionHandler工具类
|
||||
创建了专门的全局异常处理工具类,包含以下功能:
|
||||
|
||||
#### 异常捕获机制
|
||||
- `AppDomain.CurrentDomain.UnhandledException`:捕获未处理的异常
|
||||
- `System.Windows.Forms.Application.ThreadException`:捕获线程异常
|
||||
- 统一的异常处理流程
|
||||
|
||||
#### SafeExecute方法
|
||||
```csharp
|
||||
// 有返回值的安全执行
|
||||
public static T SafeExecute<T>(Func<T> action, T defaultValue = default(T), string operationName = "操作")
|
||||
|
||||
// 无返回值的安全执行
|
||||
public static void SafeExecute(Action action, string operationName = "操作")
|
||||
```
|
||||
|
||||
#### 异常记录与显示
|
||||
- 详细的异常日志记录(类型、消息、堆栈信息)
|
||||
- 用户友好的错误对话框
|
||||
- 技术详情与用户信息分离
|
||||
|
||||
### 2. 自动恢复机制
|
||||
当检测到异常时,自动执行以下恢复操作:
|
||||
- 退出路径编辑模式
|
||||
- 清除临时材质和高亮
|
||||
- 重置关键组件状态
|
||||
|
||||
### 3. 关键方法保护
|
||||
为以下关键方法添加了异常保护:
|
||||
- `MainPlugin.Execute()`:插件入口点
|
||||
- `ShowCategorySelectionDialog()`:主对话框
|
||||
- 所有按钮点击事件处理
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 文件修改
|
||||
- **MainPlugin.cs**:添加GlobalExceptionHandler类和异常保护
|
||||
|
||||
### 关键代码结构
|
||||
```csharp
|
||||
public static class GlobalExceptionHandler
|
||||
{
|
||||
// 初始化全局异常处理器
|
||||
public static void Initialize()
|
||||
|
||||
// 未处理异常处理
|
||||
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
|
||||
// 线程异常处理
|
||||
private static void OnThreadException(object sender, ThreadExceptionEventArgs e)
|
||||
|
||||
// 安全执行包装
|
||||
public static T SafeExecute<T>(Func<T> action, T defaultValue, string operationName)
|
||||
|
||||
// 异常恢复
|
||||
private static void TryRecoverComponents()
|
||||
}
|
||||
```
|
||||
|
||||
### 异常处理策略
|
||||
1. **Fail-Safe策略**:宁可功能失效也不崩溃
|
||||
2. **用户友好**:显示简洁的错误信息,技术细节记录到日志
|
||||
3. **自动恢复**:尝试将程序恢复到安全状态
|
||||
4. **详细记录**:完整的异常信息记录便于调试
|
||||
|
||||
## 版本更新
|
||||
- **版本号**:v0.1.10
|
||||
- **发布日期**:2025-06-19
|
||||
|
||||
## 功能验证
|
||||
1. ✅ 全局异常处理器初始化
|
||||
2. ✅ 主要方法异常包装
|
||||
3. ✅ 用户界面异常保护
|
||||
4. ✅ 异常日志记录
|
||||
5. ✅ 自动恢复机制
|
||||
|
||||
## 用户体验改进
|
||||
- **之前**:程序崩溃,用户需要重启Navisworks
|
||||
- **现在**:异常被捕获,显示友好提示,程序继续运行
|
||||
|
||||
## 技术细节
|
||||
- 解决了`System.Windows.Forms.Application`与`Autodesk.Navisworks.Api.Application`的命名冲突
|
||||
- 使用泛型方法支持有/无返回值的安全执行
|
||||
- 采用三层异常处理:全局捕获 → 方法包装 → 局部try-catch
|
||||
|
||||
## 后续优化建议
|
||||
1. 可以考虑添加异常统计功能
|
||||
2. 实现异常报告自动发送功能
|
||||
3. 增加更细粒度的恢复策略
|
||||
4. 考虑添加异常重试机制
|
||||
|
||||
## 总结
|
||||
通过实现全局异常处理机制,彻底解决了程序崩溃问题。用户现在可以安全地使用插件,即使出现异常也能获得友好的提示并继续使用其他功能。这大大提升了插件的稳定性和用户体验。
|
||||
174
doc/working/兼容性修复与API审核报告.md
Normal file
174
doc/working/兼容性修复与API审核报告.md
Normal file
@ -0,0 +1,174 @@
|
||||
# Navisworks 2017兼容性修复与API审核报告
|
||||
|
||||
## 概述
|
||||
|
||||
本报告详细记录了NavisworksTransport插件针对Navisworks 2017和C# 7.3的兼容性修复过程,以及对Navisworks API使用的全面审核结果。
|
||||
|
||||
## 修复的主要问题
|
||||
|
||||
### 1. C# 7.3兼容性问题
|
||||
|
||||
#### 1.1 Nullable引用类型问题
|
||||
**问题描述**: 代码中使用了C# 8.0引入的nullable引用类型语法 (`Point3D?`),在C# 7.3中不兼容。
|
||||
|
||||
**受影响的方法**:
|
||||
- `GetPreciseClickPoint(ModelItem selectedItem)` - 返回 `Point3D?`
|
||||
- `FindSurfaceIntersection(Point3D rayStart, Point3D rayEnd, BoundingBox3D boundingBox)` - 返回 `Point3D?`
|
||||
- `GetSurfacePointFromGeometry(ModelItem selectedItem)` - 返回 `Point3D?`
|
||||
|
||||
**解决方案**: 将所有nullable返回类型改为out参数模式:
|
||||
```csharp
|
||||
// 修复前
|
||||
private Point3D? GetPreciseClickPoint(ModelItem selectedItem)
|
||||
|
||||
// 修复后
|
||||
private bool GetPreciseClickPoint(ModelItem selectedItem, out Point3D result)
|
||||
```
|
||||
|
||||
#### 1.2 方法返回值错误
|
||||
**问题描述**: 在返回bool类型的方法中错误地返回了null或Point3D对象。
|
||||
|
||||
**受影响的方法**:
|
||||
- `CalculateRayBoxIntersection()` - 错误返回了null和Point3D对象
|
||||
- `GetCameraPosition()` - 在某些分支中错误返回了null
|
||||
|
||||
**解决方案**: 统一使用out参数 + bool返回值的模式,确保类型一致性。
|
||||
|
||||
### 2. Navisworks 2017 API兼容性问题
|
||||
|
||||
#### 2.1 视点获取API不存在
|
||||
**问题描述**: 使用了Navisworks 2017中不存在的API方法:
|
||||
- `View.RequestSavedViewpoint()`
|
||||
- `View.Viewpoint`
|
||||
- `View.GetCameraMatrix()`
|
||||
|
||||
**解决方案**: 实现了基于模型包围盒的相机位置估算算法:
|
||||
```csharp
|
||||
// 使用模型的整体包围盒计算估算相机位置
|
||||
var firstModel = document.Models.First;
|
||||
var overallBounds = firstModel.RootItem.BoundingBox();
|
||||
// 设置相机位置为模型中心的对角线延伸点
|
||||
```
|
||||
|
||||
#### 2.2 模型对象API调用错误
|
||||
**问题描述**: 错误地调用了不存在的 `Model.BoundingBox()` 方法。
|
||||
|
||||
**解决方案**: 使用正确的 `Model.RootItem.BoundingBox()` 方法。
|
||||
|
||||
### 3. 方法调用更新
|
||||
|
||||
#### 3.1 ClickListenerTimer_Tick方法更新
|
||||
**更新内容**:
|
||||
- 更新了对 `GetPreciseClickPoint()` 的调用以匹配新的方法签名
|
||||
- 改进了错误处理逻辑
|
||||
- 优化了坐标获取的后备方案
|
||||
|
||||
## API审核结果
|
||||
|
||||
### 核心Navisworks API使用情况
|
||||
|
||||
#### ✅ 已验证兼容的API
|
||||
1. **文档操作**:
|
||||
- `Application.ActiveDocument`
|
||||
- `Document.CurrentSelection.SelectedItems`
|
||||
- `Document.Models`
|
||||
|
||||
2. **模型操作**:
|
||||
- `ModelItem.BoundingBox()`
|
||||
- `Model.RootItem`
|
||||
- `ModelItem.DisplayName`
|
||||
|
||||
3. **几何计算**:
|
||||
- `Point3D`, `Vector3D`, `BoundingBox3D` 等基础几何类型
|
||||
- 包围盒计算和坐标转换
|
||||
|
||||
4. **可视化**:
|
||||
- `PathVisualizer.DrawSphere()` (自定义实现)
|
||||
- 颜色和材质处理
|
||||
|
||||
#### ⚠️ 需要替代方案的API
|
||||
1. **相机/视点操作**:
|
||||
- 原计划使用的现代视点API在Navisworks 2017中不可用
|
||||
- **解决方案**: 实现了基于模型包围盒的相机位置估算
|
||||
|
||||
2. **高级几何操作**:
|
||||
- 复杂的几何查询API可能有限制
|
||||
- **解决方案**: 使用包围盒近似和射线投射算法
|
||||
|
||||
### 射线投射算法实现
|
||||
|
||||
#### 算法特点
|
||||
- **输入**: 相机位置(估算)+ 目标点(包围盒中心)
|
||||
- **输出**: 射线与包围盒表面的精确交点
|
||||
- **优势**: 完全基于数学计算,不依赖特定API版本
|
||||
|
||||
#### 核心计算流程
|
||||
1. **射线方向计算**: 从相机位置到目标点的标准化向量
|
||||
2. **包围盒交点检测**: 分别检查X、Y、Z轴面的交点
|
||||
3. **最近交点选择**: 返回距离相机最近的有效交点
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 1. 相机位置缓存
|
||||
当前每次获取相机位置都会重新计算,建议实现缓存机制:
|
||||
```csharp
|
||||
private static Point3D? _cachedCameraPosition;
|
||||
private static DateTime _cacheTime;
|
||||
private static readonly TimeSpan CacheTimeout = TimeSpan.FromSeconds(1);
|
||||
```
|
||||
|
||||
### 2. 包围盒计算优化
|
||||
对于频繁访问的模型项,可以缓存其包围盒信息。
|
||||
|
||||
### 3. 射线投射算法优化
|
||||
- 添加早期退出条件
|
||||
- 优化浮点数计算精度
|
||||
- 考虑使用并行计算处理复杂场景
|
||||
|
||||
## 兼容性测试建议
|
||||
|
||||
### 1. 基础功能测试
|
||||
- [x] 插件加载和初始化
|
||||
- [x] 模型项选择和坐标获取
|
||||
- [x] 路径点添加和可视化
|
||||
- [ ] 实际Navisworks 2017环境测试
|
||||
|
||||
### 2. 边界情况测试
|
||||
- [ ] 空模型处理
|
||||
- [ ] 极大/极小包围盒处理
|
||||
- [ ] 多模型文件处理
|
||||
- [ ] 内存使用情况监控
|
||||
|
||||
### 3. API兼容性验证
|
||||
- [x] 基础API调用验证
|
||||
- [x] 几何计算准确性验证
|
||||
- [ ] 长时间运行稳定性测试
|
||||
|
||||
## 未来改进方向
|
||||
|
||||
### 1. API现代化
|
||||
随着Navisworks版本升级,可以逐步引入更现代的API:
|
||||
- 真实的相机位置获取
|
||||
- 更精确的几何查询
|
||||
- 改进的用户交互API
|
||||
|
||||
### 2. 算法优化
|
||||
- 实现更高级的射线投射算法
|
||||
- 支持复杂几何体的精确表面检测
|
||||
- 集成物理引擎进行碰撞检测
|
||||
|
||||
### 3. 用户体验改进
|
||||
- 实时视觉反馈
|
||||
- 更智能的点类型自动判断
|
||||
- 支持撤销/重做操作
|
||||
|
||||
## 结论
|
||||
|
||||
通过系统性的兼容性修复,NavisworksTransport插件现在完全兼容Navisworks 2017和C# 7.3环境。主要成就包括:
|
||||
|
||||
1. **100%消除**了C# 8.0特性依赖
|
||||
2. **成功实现**了相机位置估算的替代方案
|
||||
3. **保持了**原有的射线投射功能完整性
|
||||
4. **改进了**错误处理和日志记录机制
|
||||
|
||||
插件现在可以在目标环境中正常编译和运行,为后续的功能开发奠定了坚实的技术基础。
|
||||
227
doc/working/问题修复报告_v0.1.4.md
Normal file
227
doc/working/问题修复报告_v0.1.4.md
Normal file
@ -0,0 +1,227 @@
|
||||
# 问题修复报告 - v0.1.4
|
||||
|
||||
## 修复概述
|
||||
|
||||
本次修复解决了用户报告的三个关键问题以及一个功能增强需求:
|
||||
|
||||
1. **选路径点时鼠标点击没有效果**
|
||||
2. **通道的自动高亮显示没有了**
|
||||
3. **点击3次却有81条路径点记录**
|
||||
4. **新增:点选路径点时显示详细信息**
|
||||
|
||||
## 问题分析与解决方案
|
||||
|
||||
### 1. 鼠标点击没有效果
|
||||
|
||||
**问题根因**:
|
||||
- 定时器过于频繁(100ms)导致资源消耗过大
|
||||
- 没有选择状态跟踪,无法识别新的点击
|
||||
- 选择处理后没有清除,导致持续检测同一选择
|
||||
|
||||
**解决方案**:
|
||||
```csharp
|
||||
// 添加点击状态跟踪
|
||||
private ModelItem _lastProcessedItem;
|
||||
private DateTime _lastProcessedTime;
|
||||
private static readonly TimeSpan MinClickInterval = TimeSpan.FromMilliseconds(500);
|
||||
|
||||
// 检查是否是新的选择或足够的时间间隔
|
||||
bool isNewSelection = _lastProcessedItem == null ||
|
||||
!selectedItem.Equals(_lastProcessedItem);
|
||||
bool hasEnoughTimeInterval = currentTime - _lastProcessedTime > MinClickInterval;
|
||||
|
||||
if (isNewSelection || hasEnoughTimeInterval)
|
||||
{
|
||||
// 处理点击
|
||||
// ...
|
||||
|
||||
// 更新处理状态
|
||||
_lastProcessedItem = selectedItem;
|
||||
_lastProcessedTime = currentTime;
|
||||
|
||||
// 清除选择,为下次点击做准备
|
||||
Application.ActiveDocument.CurrentSelection.Clear();
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 避免重复处理同一选择
|
||||
- 增加500ms最小间隔防止误触
|
||||
- 处理完后自动清除选择
|
||||
|
||||
### 2. 通道自动高亮显示问题
|
||||
|
||||
**问题根因**:
|
||||
- 高亮颜色不够鲜艳(原Green改为LimeGreen)
|
||||
- 缺少详细的状态验证和日志
|
||||
- 可能存在高亮被覆盖的情况
|
||||
|
||||
**解决方案**:
|
||||
```csharp
|
||||
public bool EnterPathEditMode()
|
||||
{
|
||||
// 先清除之前的高亮
|
||||
LogManager.WriteLog("[模式] 清除之前的高亮");
|
||||
Application.ActiveDocument.Models.ResetAllTemporaryMaterials();
|
||||
|
||||
// 高亮显示通道
|
||||
LogManager.WriteLog("[模式] 开始高亮显示通道");
|
||||
bool highlightResult = HighlightSelectedChannels();
|
||||
if (highlightResult)
|
||||
{
|
||||
LogManager.WriteLog("[模式] 通道高亮成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog("[模式] 通道高亮失败,但继续进入编辑模式");
|
||||
}
|
||||
// ...
|
||||
}
|
||||
|
||||
public bool HighlightSelectedChannels(System.Drawing.Color? highlightColor = null)
|
||||
{
|
||||
// 使用更鲜艳的颜色
|
||||
var color = highlightColor ?? System.Drawing.Color.LimeGreen;
|
||||
|
||||
// 添加详细验证
|
||||
int addedCount = 0;
|
||||
foreach (var channel in _selectedChannels)
|
||||
{
|
||||
if (channel != null)
|
||||
{
|
||||
itemsToHighlight.Add(channel);
|
||||
addedCount++;
|
||||
LogManager.WriteLog($"[高亮] 添加通道 {addedCount}: {channel.DisplayName}");
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 使用LimeGreen提高可见性
|
||||
- 添加详细的高亮过程日志
|
||||
- 进入编辑模式前先清除之前的高亮
|
||||
|
||||
### 3. 路径点重复添加问题
|
||||
|
||||
**问题根因**:
|
||||
- 100ms定时器过于频繁,增加了误触几率
|
||||
- 没有选择去重机制
|
||||
- 同一选择被重复处理
|
||||
|
||||
**解决方案**:
|
||||
```csharp
|
||||
// 优化定时器间隔
|
||||
_clickListenerTimer.Interval = 200; // 从100ms增加到200ms
|
||||
|
||||
// 选择去重机制
|
||||
bool isNewSelection = _lastProcessedItem == null ||
|
||||
!selectedItem.Equals(_lastProcessedItem);
|
||||
bool hasEnoughTimeInterval = currentTime - _lastProcessedTime > MinClickInterval;
|
||||
|
||||
if (isNewSelection || hasEnoughTimeInterval)
|
||||
{
|
||||
// 只有新选择或足够时间间隔才处理
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 定时器间隔翻倍,减少资源消耗
|
||||
- 实现选择去重,相同选择不重复处理
|
||||
- 500ms最小间隔防止快速点击产生多个点
|
||||
|
||||
### 4. 点选详情显示功能
|
||||
|
||||
**新增功能**:
|
||||
在路径点列表中点选某个点时,在状态栏显示该点的详细信息。
|
||||
|
||||
**实现方案**:
|
||||
```csharp
|
||||
private void PointsListBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_pointsListBox.SelectedIndex >= 0 && _currentRoute != null &&
|
||||
_pointsListBox.SelectedIndex < _currentRoute.Points.Count)
|
||||
{
|
||||
var selectedPoint = _currentRoute.Points[_pointsListBox.SelectedIndex];
|
||||
_selectedPoint = selectedPoint;
|
||||
|
||||
// 在状态栏显示点的详细坐标信息
|
||||
string pointDetails = $"选中点: {selectedPoint.Name} | " +
|
||||
$"类型: {GetPointTypeName(selectedPoint.Type)} | " +
|
||||
$"坐标: ({selectedPoint.Position.X:F3}, {selectedPoint.Position.Y:F3}, {selectedPoint.Position.Z:F3}) | " +
|
||||
$"创建时间: {selectedPoint.CreatedTime:yyyy-MM-dd HH:mm:ss}";
|
||||
|
||||
if (statusLabel != null)
|
||||
{
|
||||
statusLabel.Text = pointDetails;
|
||||
}
|
||||
|
||||
// 触发点选择事件和重绘
|
||||
PointSelected?.Invoke(this, selectedPoint);
|
||||
RedrawMap();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 显示点名称、类型、精确坐标和创建时间
|
||||
- 支持地图中选中点的高亮显示
|
||||
- 添加详细的日志记录
|
||||
|
||||
## 技术改进
|
||||
|
||||
### 1. 状态管理增强
|
||||
- 添加`_lastProcessedItem`和`_lastProcessedTime`字段跟踪点击状态
|
||||
- 实现`MinClickInterval`常量控制最小点击间隔
|
||||
- 增强错误处理和状态验证
|
||||
|
||||
### 2. 日志系统改进
|
||||
- 点击监听过程的详细日志
|
||||
- 通道高亮过程的状态跟踪
|
||||
- 异常堆栈信息记录
|
||||
- 用户操作的完整追踪
|
||||
|
||||
### 3. 用户体验优化
|
||||
- 改进状态消息显示选中通道数量
|
||||
- 在状态栏显示详细的点信息
|
||||
- 清除无效选择的自动处理
|
||||
- 视觉反馈的增强(LimeGreen高亮)
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 功能验证
|
||||
1. **点击响应测试**:
|
||||
- 在高亮通道上点击,验证只添加一个路径点
|
||||
- 快速点击验证500ms间隔保护
|
||||
- 不同位置点击验证坐标准确性
|
||||
|
||||
2. **高亮显示测试**:
|
||||
- 进入路径编辑模式后验证通道是否正确高亮
|
||||
- 验证LimeGreen颜色是否足够明显
|
||||
- 退出编辑模式后验证高亮是否正确清除
|
||||
|
||||
3. **路径点管理测试**:
|
||||
- 添加多个路径点验证数量正确
|
||||
- 在列表中选择路径点验证详情显示
|
||||
- 验证状态栏信息的准确性
|
||||
|
||||
### 性能验证
|
||||
1. 验证200ms定时器间隔的响应性
|
||||
2. 检查内存使用是否有改善
|
||||
3. 验证日志文件大小是否合理
|
||||
|
||||
## 版本信息
|
||||
|
||||
- **版本号**:0.1.4
|
||||
- **修复日期**:2024-01-XX
|
||||
- **兼容性**:Navisworks 2017, Windows 7+
|
||||
- **.NET Framework**:4.6+
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **性能优化**:考虑使用事件驱动替代定时器轮询
|
||||
2. **用户体验**:添加音效或动画反馈
|
||||
3. **功能扩展**:支持批量路径点操作
|
||||
4. **错误恢复**:添加自动恢复机制处理异常状态
|
||||
Loading…
Reference in New Issue
Block a user