diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj
index 2985205..9a63776 100644
--- a/NavisworksTransportPlugin.csproj
+++ b/NavisworksTransportPlugin.csproj
@@ -242,7 +242,7 @@
-
+
diff --git a/src/Core/Animation/LogisticsAnimationManager.cs b/src/Core/Animation/LogisticsAnimationManager.cs
index fbebed1..ad8c480 100644
--- a/src/Core/Animation/LogisticsAnimationManager.cs
+++ b/src/Core/Animation/LogisticsAnimationManager.cs
@@ -64,7 +64,7 @@ namespace NavisworksTransport.Core.Animation
// 只构建特定于动画对象的逻辑排除列表
LogManager.Info("[缓存管理] 构建逻辑排除列表...");
- var exclusionList = BuildLogicalObjectExclusionList(animationObject);
+ var exclusionList = ModelItemAnalysisHelper.CollectRelatedNodes(animationObject);
// 更新缓存
_currentCachedAnimationObject = animationObject;
@@ -137,8 +137,8 @@ namespace NavisworksTransport.Core.Animation
// 检查对象是否相同
if (!_currentCachedAnimationObject.Equals(animationObject))
{
- string cachedObjectName = GeometryAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
- string currentObjectName = GeometryAnalysisHelper.GetSafeDisplayName(animationObject);
+ string cachedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
+ string currentObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animationObject);
LogManager.Debug($"[缓存验证] 动画对象已变更: '{cachedObjectName}' -> '{currentObjectName}'");
return false;
}
@@ -171,7 +171,7 @@ namespace NavisworksTransport.Core.Animation
try
{
// 安全访问DisplayName,防止WeakRef已释放的警告
- string objectName = GeometryAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
+ string objectName = ModelItemAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
LogManager.Debug($"[缓存管理] 清除缓存: {objectName}");
}
catch (Exception ex)
@@ -204,7 +204,7 @@ namespace NavisworksTransport.Core.Animation
}
var age = DateTime.Now - _cacheCreatedTime;
- string objectName = GeometryAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
+ string objectName = ModelItemAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
var count = _cachedExclusionList?.Count ?? 0;
return $"缓存状态: 对象='{objectName}', 排除数={count}, 年龄={age.TotalSeconds:F1}秒";
@@ -530,83 +530,6 @@ namespace NavisworksTransport.Core.Animation
}
}
- #region 几何分析功能 (从 ClashDetectiveIntegration 移动而来)
-
- ///
- /// 构建逻辑物理对象的排除列表
- ///
- /// 动画对象
- /// 应该从碰撞检测中排除的所有对象列表
- private List BuildLogicalObjectExclusionList(ModelItem animatedObject)
- {
- try
- {
- LogManager.Debug($"[排除列表构建] 开始为动画对象构建排除列表: {animatedObject.DisplayName}");
-
- var exclusionList = new List();
-
- // 1. 首先进行几何边界分析
- var geometryAnalysis = GeometryAnalysisHelper.AnalyzeGeometricBoundary(animatedObject);
-
- // 2. 根据分析结果确定排除范围
- switch (geometryAnalysis.LogicalObjectScope)
- {
- case LogicalObjectScope.SelectedNodeOnly:
- exclusionList.Add(animatedObject);
- LogManager.Debug($"[排除列表构建] 使用仅选中节点策略");
- break;
-
- case LogicalObjectScope.NodeAndDescendants:
- exclusionList.AddRange(animatedObject.DescendantsAndSelf.Where(d => d.HasGeometry));
- LogManager.Debug($"[排除列表构建] 使用节点及后代策略,排除 {exclusionList.Count} 个对象");
- break;
-
- case LogicalObjectScope.ParentAndSiblings:
- if (animatedObject.Parent != null)
- {
- exclusionList.AddRange(animatedObject.Parent.DescendantsAndSelf.Where(d => d.HasGeometry));
- }
- else
- {
- exclusionList.AddRange(animatedObject.DescendantsAndSelf.Where(d => d.HasGeometry));
- }
- LogManager.Debug($"[排除列表构建] 使用父节点及兄弟策略,排除 {exclusionList.Count} 个对象");
- break;
-
- case LogicalObjectScope.EntireLogicalComponent:
- exclusionList.AddRange(geometryAnalysis.RelatedNodes.Where(n => n.HasGeometry));
- LogManager.Debug($"[排除列表构建] 使用整个逻辑组件策略,排除 {exclusionList.Count} 个对象");
- break;
- }
-
- // 3. 补充几何空间分析的相关对象
- GeometryAnalysisHelper.SupplementSpatiallyRelatedObjects(animatedObject, exclusionList);
-
- // 4. 去重并验证
- exclusionList = exclusionList.Distinct().Where(item => GeometryAnalysisHelper.IsModelItemValid(item)).ToList();
-
- LogManager.Info($"[排除列表构建] 最终排除列表包含 {exclusionList.Count} 个对象:");
- foreach (var item in exclusionList.Take(10)) // 只记录前10个,避免日志过长
- {
- LogManager.Debug($" - 排除对象: {item.DisplayName ?? ""}");
- }
- if (exclusionList.Count > 10)
- {
- LogManager.Debug($" - ... 以及其他 {exclusionList.Count - 10} 个对象");
- }
-
- return exclusionList;
- }
- catch (Exception ex)
- {
- LogManager.Error($"[排除列表构建] 构建失败: {ex.Message}", ex);
- // 返回最基本的排除列表(仅动画对象本身)
- return new List { animatedObject };
- }
- }
-
- #endregion
-
///
/// 资源清理
///
diff --git a/src/Utils/GeometryAnalysisHelper.cs b/src/Utils/GeometryAnalysisHelper.cs
deleted file mode 100644
index 1f85447..0000000
--- a/src/Utils/GeometryAnalysisHelper.cs
+++ /dev/null
@@ -1,706 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Autodesk.Navisworks.Api;
-
-namespace NavisworksTransport.Utils
-{
- ///
- /// 几何体分析和包围盒操作工具类
- /// 提供用于几何体归属分析、包围盒计算和空间关系判断的静态方法
- ///
- public static class GeometryAnalysisHelper
- {
- #region 包围盒操作方法
-
- ///
- /// 计算包围盒体积
- ///
- /// 包围盒
- /// 包围盒的体积
- public static double CalculateBoundingBoxVolume(BoundingBox3D bbox)
- {
- try
- {
- var width = Math.Abs(bbox.Max.X - bbox.Min.X);
- var height = Math.Abs(bbox.Max.Y - bbox.Min.Y);
- var depth = Math.Abs(bbox.Max.Z - bbox.Min.Z);
- return width * height * depth;
- }
- catch
- {
- return 0.0;
- }
- }
-
- ///
- /// 检查一个包围盒是否完全包含另一个包围盒
- ///
- /// 外层包围盒
- /// 内层包围盒
- /// 容差值,默认1mm
- /// 如果container完全包含contained返回true
- public static bool BoundingBoxContains(BoundingBox3D container, BoundingBox3D contained, double tolerance = 0.001)
- {
- try
- {
- // 检查六个面是否都在容差范围内包含
- var containsMinX = container.Min.X <= contained.Min.X + tolerance;
- var containsMinY = container.Min.Y <= contained.Min.Y + tolerance;
- var containsMinZ = container.Min.Z <= contained.Min.Z + tolerance;
-
- var containsMaxX = container.Max.X >= contained.Max.X - tolerance;
- var containsMaxY = container.Max.Y >= contained.Max.Y - tolerance;
- var containsMaxZ = container.Max.Z >= contained.Max.Z - tolerance;
-
- var result = containsMinX && containsMinY && containsMinZ &&
- containsMaxX && containsMaxY && containsMaxZ;
-
- if (result)
- {
- LogManager.Debug($"[包围盒包含] 检测到包含关系: " +
- $"外层[{container.Min.X:F2},{container.Min.Y:F2},{container.Min.Z:F2}] - [{container.Max.X:F2},{container.Max.Y:F2},{container.Max.Z:F2}] " +
- $"包含 内层[{contained.Min.X:F2},{contained.Min.Y:F2},{contained.Min.Z:F2}] - [{contained.Max.X:F2},{contained.Max.Y:F2},{contained.Max.Z:F2}]");
- }
-
- return result;
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[包围盒包含] 检测异常: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 检查两个包围盒是否有显著重叠(用于判断是否属于同一逻辑对象)
- ///
- /// 包围盒1
- /// 包围盒2
- /// 重叠阈值(0.0-1.0),表示重叠体积占较小包围盒体积的比例
- /// 如果重叠程度超过阈值返回true
- public static bool BoundingBoxesHaveSignificantOverlap(BoundingBox3D bbox1, BoundingBox3D bbox2, double overlapThreshold = 0.5)
- {
- try
- {
- // 计算重叠区域
- var overlapMin = new Point3D(
- Math.Max(bbox1.Min.X, bbox2.Min.X),
- Math.Max(bbox1.Min.Y, bbox2.Min.Y),
- Math.Max(bbox1.Min.Z, bbox2.Min.Z)
- );
-
- var overlapMax = new Point3D(
- Math.Min(bbox1.Max.X, bbox2.Max.X),
- Math.Min(bbox1.Max.Y, bbox2.Max.Y),
- Math.Min(bbox1.Max.Z, bbox2.Max.Z)
- );
-
- // 检查是否有重叠
- if (overlapMin.X >= overlapMax.X || overlapMin.Y >= overlapMax.Y || overlapMin.Z >= overlapMax.Z)
- {
- return false; // 没有重叠
- }
-
- // 计算重叠体积
- var overlapVolume = (overlapMax.X - overlapMin.X) *
- (overlapMax.Y - overlapMin.Y) *
- (overlapMax.Z - overlapMin.Z);
-
- // 计算两个包围盒的体积
- var volume1 = CalculateBoundingBoxVolume(bbox1);
- var volume2 = CalculateBoundingBoxVolume(bbox2);
- var smallerVolume = Math.Min(volume1, volume2);
-
- if (smallerVolume <= 0) return false;
-
- var overlapRatio = overlapVolume / smallerVolume;
- var hasSignificantOverlap = overlapRatio >= overlapThreshold;
-
- if (hasSignificantOverlap)
- {
- LogManager.Debug($"[包围盒重叠] 检测到显著重叠: 重叠比例 {overlapRatio:F3} >= {overlapThreshold:F3}");
- }
-
- return hasSignificantOverlap;
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[包围盒重叠] 检测异常: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 检查两个对象是否在空间上非常接近(可能是同一逻辑对象的不同表示)
- ///
- /// 包围盒1
- /// 包围盒2
- /// 接近阈值(米),默认10cm
- /// 如果两个对象非常接近返回true
- public static bool BoundingBoxesAreVeryClose(BoundingBox3D bbox1, BoundingBox3D bbox2, double proximityThreshold = 0.1)
- {
- try
- {
- // 计算两个包围盒中心点的距离
- var center1 = new Point3D(
- (bbox1.Min.X + bbox1.Max.X) / 2,
- (bbox1.Min.Y + bbox1.Max.Y) / 2,
- (bbox1.Min.Z + bbox1.Max.Z) / 2
- );
-
- var center2 = new Point3D(
- (bbox2.Min.X + bbox2.Max.X) / 2,
- (bbox2.Min.Y + bbox2.Max.Y) / 2,
- (bbox2.Min.Z + bbox2.Max.Z) / 2
- );
-
- var centerDistance = CalculatePointDistance(center1, center2);
- var areClose = centerDistance <= proximityThreshold;
-
- if (areClose)
- {
- LogManager.Debug($"[包围盒接近] 检测到接近对象: 中心距离 {centerDistance:F3}m <= {proximityThreshold:F3}m");
- }
-
- return areClose;
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[包围盒接近] 检测异常: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 检查两个包围盒是否相交(带容差)
- ///
- /// 包围盒1
- /// 包围盒2
- /// 容差值
- /// 如果两个包围盒相交返回true
- public static bool BoundingBoxesIntersectWithTolerance(BoundingBox3D box1, BoundingBox3D box2, double tolerance)
- {
- return box1.Min.X <= box2.Max.X + tolerance && box1.Max.X >= box2.Min.X - tolerance &&
- box1.Min.Y <= box2.Max.Y + tolerance && box1.Max.Y >= box2.Min.Y - tolerance &&
- box1.Min.Z <= box2.Max.Z + tolerance && box1.Max.Z >= box2.Min.Z - tolerance;
- }
-
- #endregion
-
- #region 辅助方法
-
- ///
- /// 计算两个3D点之间的距离
- ///
- /// 点1
- /// 点2
- /// 两点间的距离
- public static double CalculatePointDistance(Point3D point1, Point3D point2)
- {
- var dx = point1.X - point2.X;
- var dy = point1.Y - point2.Y;
- var dz = point1.Z - point2.Z;
- return Math.Sqrt(dx * dx + dy * dy + dz * dz);
- }
-
- ///
- /// 检查ModelItem是否仍然有效
- ///
- /// 要检查的ModelItem
- /// 如果ModelItem有效返回true
- public static bool IsModelItemValid(ModelItem item)
- {
- try
- {
- if (item == null)
- return false;
-
- // 尝试访问对象的属性来检查是否有效
- var displayName = item.DisplayName;
- var hasGeometry = item.HasGeometry;
-
- // 额外检查:确保对象没有被释放
- var boundingBox = item.BoundingBox();
- return true;
- }
- catch (Exception ex)
- {
- LogManager.Debug($"ModelItem无效: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 安全获取ModelItem的DisplayName,避免访问已释放对象的警告
- ///
- /// ModelItem对象
- /// 安全的显示名称
- public static string GetSafeDisplayName(ModelItem item)
- {
- try
- {
- if (item == null)
- {
- return "NULL";
- }
-
- // 尝试访问DisplayName,如果对象已被释放会抛出异常
- return item.DisplayName ?? "未命名对象";
- }
- catch (System.ObjectDisposedException)
- {
- return "已释放对象";
- }
- catch (System.Runtime.InteropServices.COMException)
- {
- return "COM对象已释放";
- }
- catch (Exception ex)
- {
- return $"访问失败({ex.GetType().Name})";
- }
- }
-
- #endregion
-
- #region 几何体归属分析方法
-
- ///
- /// 综合判断两个ModelItem是否属于同一个逻辑物理对象
- ///
- /// 对象1
- /// 对象2
- /// 如果属于同一逻辑对象返回true
- public static bool BelongsToSameLogicalObject(ModelItem item1, ModelItem item2)
- {
- try
- {
- // 1. 引用相等检查
- if (ReferenceEquals(item1, item2) || item1.Equals(item2))
- {
- LogManager.Debug($"[逻辑对象判断] 引用相等: {item1.DisplayName}");
- return true;
- }
-
- // 2. 获取包围盒
- var bbox1 = item1.BoundingBox();
- var bbox2 = item2.BoundingBox();
-
- // 3. 包含关系检查
- if (BoundingBoxContains(bbox1, bbox2) || BoundingBoxContains(bbox2, bbox1))
- {
- LogManager.Debug($"[逻辑对象判断] 包含关系: {item1.DisplayName} <-> {item2.DisplayName}");
- return true;
- }
-
- // 4. 显著重叠检查
- if (BoundingBoxesHaveSignificantOverlap(bbox1, bbox2, 0.7)) // 70%重叠阈值
- {
- LogManager.Debug($"[逻辑对象判断] 显著重叠: {item1.DisplayName} <-> {item2.DisplayName}");
- return true;
- }
-
- // 5. 空间接近检查(对于小对象)
- var volume1 = CalculateBoundingBoxVolume(bbox1);
- var volume2 = CalculateBoundingBoxVolume(bbox2);
- var maxVolume = Math.Max(volume1, volume2);
-
- // 如果是相对较小的对象(体积小于1立方米),使用更严格的接近检查
- if (maxVolume < 1.0 && BoundingBoxesAreVeryClose(bbox1, bbox2, 0.05)) // 5cm阈值
- {
- LogManager.Debug($"[逻辑对象判断] 小对象接近: {item1.DisplayName} <-> {item2.DisplayName}");
- return true;
- }
-
- return false;
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[逻辑对象判断] 判断异常: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 分析选中对象的完整几何边界,识别逻辑物理对象的范围
- ///
- /// 用户选中的对象
- /// 几何边界分析结果
- public static GeometricBoundaryAnalysis AnalyzeGeometricBoundary(ModelItem selectedObject)
- {
- try
- {
- LogManager.Debug($"[几何边界分析] 开始分析对象: {selectedObject.DisplayName}");
-
- var analysis = new GeometricBoundaryAnalysis
- {
- SelectedObject = selectedObject,
- RootBoundingBox = selectedObject.BoundingBox(),
- RelatedNodes = new List()
- };
-
- // 1. 收集所有相关节点(向上和向下遍历)
- CollectRelatedNodes(selectedObject, analysis);
-
- // 2. 分析几何包含关系
- AnalyzeGeometricContainment(analysis);
-
- // 3. 建立逻辑物理对象边界
- DefineLogicalObjectBoundary(analysis);
-
- LogManager.Debug($"[几何边界分析] 完成,发现 {analysis.RelatedNodes.Count} 个相关节点");
- LogManager.Debug($"[几何边界分析] 逻辑对象范围: {analysis.LogicalObjectScope}");
-
- return analysis;
- }
- catch (Exception ex)
- {
- LogManager.Error($"[几何边界分析] 分析失败: {ex.Message}", ex);
- return CreateFallbackAnalysis(selectedObject);
- }
- }
-
- ///
- /// 收集与选中对象相关的所有节点
- ///
- /// 选中的对象
- /// 分析结果对象
- public static void CollectRelatedNodes(ModelItem selectedObject, GeometricBoundaryAnalysis analysis)
- {
- try
- {
- // 添加自身
- analysis.RelatedNodes.Add(selectedObject);
-
- // 1. 向上收集:父节点、祖父节点等
- CollectAncestorNodes(selectedObject, analysis);
-
- // 2. 向下收集:子节点、孙节点等
- CollectDescendantNodes(selectedObject, analysis);
-
- // 3. 收集兄弟节点(同级节点)
- CollectSiblingNodes(selectedObject, analysis);
-
- // 去重
- analysis.RelatedNodes = analysis.RelatedNodes.Distinct().ToList();
-
- LogManager.Debug($"[收集相关节点] 总共收集到 {analysis.RelatedNodes.Count} 个节点");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[收集相关节点] 收集过程异常: {ex.Message}");
- }
- }
-
- ///
- /// 收集祖先节点
- ///
- /// 起始节点
- /// 分析结果对象
- public static void CollectAncestorNodes(ModelItem item, GeometricBoundaryAnalysis analysis)
- {
- try
- {
- var current = item.Parent;
- int levels = 0;
-
- while (current != null && levels < 10) // 最多向上10层,避免无限循环
- {
- analysis.RelatedNodes.Add(current);
-
- // 如果当前节点的包围盒显著大于原始对象,可能已经到了更大的组件
- var currentBbox = current.BoundingBox();
- var originalBbox = analysis.RootBoundingBox;
-
- // 如果包围盒体积增大超过5倍,可能已经超出了逻辑对象范围
- var currentVolume = CalculateBoundingBoxVolume(currentBbox);
- var originalVolume = CalculateBoundingBoxVolume(originalBbox);
-
- if (currentVolume > originalVolume * 5.0)
- {
- LogManager.Debug($"[祖先节点收集] 在第{levels}层停止,包围盒体积增大过多");
- break;
- }
-
- current = current.Parent;
- levels++;
- }
-
- LogManager.Debug($"[祖先节点收集] 向上收集了 {levels} 层节点");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[祖先节点收集] 收集异常: {ex.Message}");
- }
- }
-
- ///
- /// 收集后代节点
- ///
- /// 起始节点
- /// 分析结果对象
- public static void CollectDescendantNodes(ModelItem item, GeometricBoundaryAnalysis analysis)
- {
- try
- {
- var descendants = item.DescendantsAndSelf.Where(d => d.HasGeometry).ToList();
- analysis.RelatedNodes.AddRange(descendants);
-
- LogManager.Debug($"[后代节点收集] 收集了 {descendants.Count} 个有几何体的后代节点");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[后代节点收集] 收集异常: {ex.Message}");
- }
- }
-
- ///
- /// 收集兄弟节点
- ///
- /// 起始节点
- /// 分析结果对象
- public static void CollectSiblingNodes(ModelItem item, GeometricBoundaryAnalysis analysis)
- {
- try
- {
- if (item.Parent != null)
- {
- var siblings = item.Parent.Children.Where(child =>
- child.HasGeometry && !child.Equals(item)).ToList();
-
- // 只收集包围盒有重叠或非常接近的兄弟节点
- var itemBbox = item.BoundingBox();
- var tolerance = 0.1; // 10cm容差
-
- foreach (var sibling in siblings)
- {
- var siblingBbox = sibling.BoundingBox();
- if (BoundingBoxesIntersectWithTolerance(itemBbox, siblingBbox, tolerance))
- {
- analysis.RelatedNodes.Add(sibling);
- }
- }
-
- LogManager.Debug($"[兄弟节点收集] 收集了 {siblings.Count} 个相关兄弟节点");
- }
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[兄弟节点收集] 收集异常: {ex.Message}");
- }
- }
-
- ///
- /// 分析几何包含关系
- ///
- /// 分析结果对象
- public static void AnalyzeGeometricContainment(GeometricBoundaryAnalysis analysis)
- {
- try
- {
- analysis.ContainmentRelations = new Dictionary>();
-
- foreach (var item in analysis.RelatedNodes)
- {
- analysis.ContainmentRelations[item] = new List();
- var itemBbox = item.BoundingBox();
-
- foreach (var other in analysis.RelatedNodes)
- {
- if (item.Equals(other)) continue;
-
- var otherBbox = other.BoundingBox();
- if (BoundingBoxContains(itemBbox, otherBbox))
- {
- analysis.ContainmentRelations[item].Add(other);
- }
- }
- }
-
- LogManager.Debug($"[几何包含分析] 分析了 {analysis.RelatedNodes.Count} 个节点的包含关系");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[几何包含分析] 分析异常: {ex.Message}");
- }
- }
-
- ///
- /// 定义逻辑物理对象边界
- ///
- /// 分析结果对象
- public static void DefineLogicalObjectBoundary(GeometricBoundaryAnalysis analysis)
- {
- try
- {
- // 策略:找到一个合适的"逻辑根节点"
- // 优先级:1. 有明确名称的节点 2. 包含关系合理的节点 3. 包围盒大小适中的节点
-
- var candidates = analysis.RelatedNodes.Where(node =>
- !string.IsNullOrEmpty(node.DisplayName) &&
- node.HasGeometry).ToList();
-
- if (candidates.Any())
- {
- // 选择包围盒最接近原始选择对象的有名称节点
- var originalVolume = CalculateBoundingBoxVolume(analysis.RootBoundingBox);
-
- var bestCandidate = candidates
- .OrderBy(c => Math.Abs(CalculateBoundingBoxVolume(c.BoundingBox()) - originalVolume))
- .First();
-
- analysis.LogicalObjectScope = LogicalObjectScope.NodeAndDescendants;
- analysis.LogicalRootNode = bestCandidate;
-
- LogManager.Debug($"[逻辑边界定义] 选择逻辑根节点: {bestCandidate.DisplayName}");
- }
- else
- {
- // 没有合适的候选,使用原始选择
- analysis.LogicalObjectScope = LogicalObjectScope.SelectedNodeOnly;
- analysis.LogicalRootNode = analysis.SelectedObject;
-
- LogManager.Debug($"[逻辑边界定义] 使用原始选择作为逻辑边界");
- }
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[逻辑边界定义] 定义异常: {ex.Message}");
- analysis.LogicalObjectScope = LogicalObjectScope.SelectedNodeOnly;
- analysis.LogicalRootNode = analysis.SelectedObject;
- }
- }
-
- ///
- /// 创建备用分析结果(当主分析失败时)
- ///
- /// 选中的对象
- /// 备用分析结果
- public static GeometricBoundaryAnalysis CreateFallbackAnalysis(ModelItem selectedObject)
- {
- return new GeometricBoundaryAnalysis
- {
- SelectedObject = selectedObject,
- RootBoundingBox = selectedObject.BoundingBox(),
- RelatedNodes = new List { selectedObject },
- LogicalObjectScope = LogicalObjectScope.SelectedNodeOnly,
- LogicalRootNode = selectedObject,
- ContainmentRelations = new Dictionary>()
- };
- }
-
- ///
- /// 补充空间相关的对象到排除列表
- ///
- /// 动画对象
- /// 当前排除列表
- public static void SupplementSpatiallyRelatedObjects(ModelItem animatedObject, List exclusionList)
- {
- try
- {
- LogManager.Debug($"[空间关联补充] 开始补充空间相关对象");
-
- var animatedBBox = animatedObject.BoundingBox();
- var originalCount = exclusionList.Count;
-
- // 获取所有有几何体的对象进行空间分析
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry && !exclusionList.Contains(item))
- .ToList();
-
- foreach (var item in allItems)
- {
- try
- {
- // 使用 BelongsToSameLogicalObject 方法进行综合判断
- if (BelongsToSameLogicalObject(animatedObject, item))
- {
- exclusionList.Add(item);
- LogManager.Debug($"[空间关联补充] 添加空间相关对象: {item.DisplayName}");
- }
- }
- catch (Exception itemEx)
- {
- LogManager.Warning($"[空间关联补充] 分析对象时异常 {item.DisplayName}: {itemEx.Message}");
- }
- }
-
- var addedCount = exclusionList.Count - originalCount;
- LogManager.Debug($"[空间关联补充] 补充了 {addedCount} 个空间相关对象");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[空间关联补充] 补充过程异常: {ex.Message}");
- }
- }
-
- #endregion
- }
-
- #region 数据结构定义
-
- ///
- /// 几何边界分析结果
- ///
- public class GeometricBoundaryAnalysis
- {
- ///
- /// 用户选中的原始对象
- ///
- public ModelItem SelectedObject { get; set; }
-
- ///
- /// 原始对象的包围盒
- ///
- public BoundingBox3D RootBoundingBox { get; set; }
-
- ///
- /// 所有相关节点(父、子、兄弟等)
- ///
- public List RelatedNodes { get; set; }
-
- ///
- /// 逻辑物理对象的范围定义
- ///
- public LogicalObjectScope LogicalObjectScope { get; set; }
-
- ///
- /// 逻辑对象的根节点
- ///
- public ModelItem LogicalRootNode { get; set; }
-
- ///
- /// 节点间的包含关系映射
- ///
- public Dictionary> ContainmentRelations { get; set; }
- }
-
- ///
- /// 逻辑对象范围枚举
- ///
- public enum LogicalObjectScope
- {
- ///
- /// 仅选中节点本身
- ///
- SelectedNodeOnly,
-
- ///
- /// 选中节点及其所有后代
- ///
- NodeAndDescendants,
-
- ///
- /// 选中节点的父节点及其所有子树
- ///
- ParentAndSiblings,
-
- ///
- /// 整个逻辑组件(自动识别的边界)
- ///
- EntireLogicalComponent
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/src/Utils/ModelItemAnalysisHelper.cs b/src/Utils/ModelItemAnalysisHelper.cs
new file mode 100644
index 0000000..e642360
--- /dev/null
+++ b/src/Utils/ModelItemAnalysisHelper.cs
@@ -0,0 +1,292 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Autodesk.Navisworks.Api;
+
+namespace NavisworksTransport.Utils
+{
+ ///
+ /// 节点分析工具类
+ /// 提供用于节点归属分析的静态方法
+ ///
+ public static class ModelItemAnalysisHelper
+ {
+
+ #region 辅助方法
+
+ ///
+ /// 计算两个3D点之间的距离
+ ///
+ /// 点1
+ /// 点2
+ /// 两点间的距离
+ public static double CalculatePointDistance(Point3D point1, Point3D point2)
+ {
+ var dx = point1.X - point2.X;
+ var dy = point1.Y - point2.Y;
+ var dz = point1.Z - point2.Z;
+ return Math.Sqrt(dx * dx + dy * dy + dz * dz);
+ }
+
+ ///
+ /// 检查ModelItem是否仍然有效
+ ///
+ /// 要检查的ModelItem
+ /// 如果ModelItem有效返回true
+ public static bool IsModelItemValid(ModelItem item)
+ {
+ try
+ {
+ if (item == null)
+ return false;
+
+ // 尝试访问对象的属性来检查是否有效
+ var displayName = item.DisplayName;
+ var hasGeometry = item.HasGeometry;
+
+ // 额外检查:确保对象没有被释放
+ var boundingBox = item.BoundingBox();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Debug($"ModelItem无效: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 安全获取ModelItem的DisplayName,避免访问已释放对象的警告
+ ///
+ /// ModelItem对象
+ /// 安全的显示名称
+ public static string GetSafeDisplayName(ModelItem item)
+ {
+ try
+ {
+ if (item == null)
+ {
+ return "NULL";
+ }
+
+ // 尝试访问DisplayName,如果对象已被释放会抛出异常
+ return item.DisplayName ?? "未命名对象";
+ }
+ catch (System.ObjectDisposedException)
+ {
+ return "已释放对象";
+ }
+ catch (System.Runtime.InteropServices.COMException)
+ {
+ return "COM对象已释放";
+ }
+ catch (Exception ex)
+ {
+ return $"访问失败({ex.GetType().Name})";
+ }
+ }
+
+ #endregion
+
+ #region 节点归属分析方法
+
+ ///
+ /// 收集与选中对象相关的所有节点
+ /// 基于节点类型的智能收集策略:
+ /// - 纯几何体:找父节点,收集父节点及其所有子节点
+ /// - 集合节点/混合节点:收集自身及所有子节点
+ /// - 空节点:仅收集自身
+ ///
+ /// 选中的对象
+ /// 相关节点列表
+ public static List CollectRelatedNodes(ModelItem selectedObject)
+ {
+ try
+ {
+ var relatedNodes = new List();
+
+ // 添加自身
+ relatedNodes.Add(selectedObject);
+
+ // 判断节点类型
+ var itemType = GetModelItemType(selectedObject);
+ LogManager.Debug($"[收集相关节点] 开始收集,节点类型: {itemType}");
+
+ // 根据节点类型执行不同的收集策略
+ switch (itemType)
+ {
+ case ModelItemType.PureGeometry:
+ // 纯几何体:找父节点,收集父节点及其所有子节点
+ CollectFromParentNode(selectedObject, relatedNodes);
+ break;
+
+ case ModelItemType.GroupNode:
+ case ModelItemType.HybridNode:
+ // 集合节点或混合节点:收集自身及所有子节点
+ CollectFromCurrentNode(selectedObject, relatedNodes);
+ break;
+
+ case ModelItemType.EmptyNode:
+ // 空节点:仅收集自身(已在开头添加)
+ LogManager.Debug($"[收集相关节点] 空节点,仅收集自身");
+ break;
+ }
+
+ // 去重
+ relatedNodes = relatedNodes.Distinct().ToList();
+
+ LogManager.Debug($"[收集相关节点] 总共收集到 {relatedNodes.Count} 个节点");
+ return relatedNodes;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[收集相关节点] 收集过程异常: {ex.Message}");
+ return new List { selectedObject }; // 异常时返回最基本的列表
+ }
+ }
+
+ ///
+ /// 判断模型节点的类型
+ ///
+ /// 模型节点
+ /// 节点类型
+ private static ModelItemType GetModelItemType(ModelItem item)
+ {
+ try
+ {
+ bool hasGeometry = item.HasGeometry;
+ int childCount = item.Children?.Count() ?? 0;
+
+ if (hasGeometry && childCount == 0)
+ {
+ LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 纯几何体节点");
+ return ModelItemType.PureGeometry;
+ }
+ else if (!hasGeometry && childCount > 0)
+ {
+ LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 集合节点 (子节点数: {childCount})");
+ return ModelItemType.GroupNode;
+ }
+ else if (hasGeometry && childCount > 0)
+ {
+ LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 混合节点 (子节点数: {childCount})");
+ return ModelItemType.HybridNode;
+ }
+ else
+ {
+ LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 空节点");
+ return ModelItemType.EmptyNode;
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[节点类型判断] 判断节点类型时异常 '{item.DisplayName}': {ex.Message}");
+ return ModelItemType.EmptyNode; // 异常时返回最保守的类型
+ }
+ }
+
+
+ ///
+ /// 从父节点收集相关节点(用于纯几何体节点)
+ ///
+ /// 纯几何体节点
+ /// 分析结果对象
+ ///
+ /// 从父节点收集相关节点(用于纯几何体节点)
+ ///
+ /// 纯几何体节点
+ /// 相关节点列表
+ private static void CollectFromParentNode(ModelItem pureGeometryNode, List relatedNodes)
+ {
+ try
+ {
+ if (pureGeometryNode.Parent != null)
+ {
+ var parent = pureGeometryNode.Parent;
+ LogManager.Debug($"[父节点收集] 找到父节点: '{parent.DisplayName}'");
+
+ // 添加父节点
+ relatedNodes.Add(parent);
+
+ // 收集父节点的所有子节点(包含几何体的)
+ if (parent.Children != null)
+ {
+ var siblings = parent.Children.Where(child => child.HasGeometry).ToList();
+ relatedNodes.AddRange(siblings);
+
+ LogManager.Debug($"[父节点收集] 从父节点 '{parent.DisplayName}' 收集了 {siblings.Count} 个有几何体的子节点");
+ }
+ }
+ else
+ {
+ // 没有父节点,可能是根节点,只收集自己的后代
+ LogManager.Debug($"[父节点收集] '{pureGeometryNode.DisplayName}' 没有父节点,收集自身后代");
+ CollectFromCurrentNode(pureGeometryNode, relatedNodes);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[父节点收集] 收集过程异常: {ex.Message}");
+ }
+ }
+
+
+ ///
+ /// 从当前节点收集相关节点(用于集合节点和混合节点)
+ ///
+ /// 当前节点(集合节点或混合节点)
+ /// 分析结果对象
+ ///
+ /// 从当前节点收集相关节点(用于集合节点和混合节点)
+ ///
+ /// 当前节点(集合节点或混合节点)
+ /// 相关节点列表
+ private static void CollectFromCurrentNode(ModelItem currentNode, List relatedNodes)
+ {
+ try
+ {
+ // 收集当前节点的所有后代节点(包含几何体的)
+ var descendants = currentNode.DescendantsAndSelf.Where(d => d.HasGeometry).ToList();
+ relatedNodes.AddRange(descendants);
+
+ LogManager.Debug($"[当前节点收集] 从节点 '{currentNode.DisplayName}' 收集了 {descendants.Count} 个有几何体的后代节点");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[当前节点收集] 收集过程异常: {ex.Message}");
+ }
+ }
+
+ #endregion
+ }
+
+ #region 数据结构定义
+
+ ///
+ /// 模型节点类型枚举
+ ///
+ public enum ModelItemType
+ {
+ ///
+ /// 纯几何体节点:HasGeometry = true, Children.Count = 0
+ ///
+ PureGeometry,
+
+ ///
+ /// 集合节点:HasGeometry = false, Children.Count > 0
+ ///
+ GroupNode,
+
+ ///
+ /// 混合节点:HasGeometry = true, Children.Count > 0
+ ///
+ HybridNode,
+
+ ///
+ /// 空节点:HasGeometry = false, Children.Count = 0
+ ///
+ EmptyNode
+ }
+
+ #endregion
+}
\ No newline at end of file