用节点类型(是否只包含几何体)来进行节点包含判断,废掉包围盒的方式。
This commit is contained in:
parent
2cd3772105
commit
d75582d664
@ -242,7 +242,7 @@
|
|||||||
<!-- Utils -->
|
<!-- Utils -->
|
||||||
<Compile Include="src\Utils\CoordinateConverter.cs" />
|
<Compile Include="src\Utils\CoordinateConverter.cs" />
|
||||||
<Compile Include="src\Utils\FloorDetector.cs" />
|
<Compile Include="src\Utils\FloorDetector.cs" />
|
||||||
<Compile Include="src\Utils\GeometryAnalysisHelper.cs" />
|
<Compile Include="src\Utils\ModelItemAnalysisHelper.cs" />
|
||||||
<Compile Include="src\Utils\GeometryExtractor.cs" />
|
<Compile Include="src\Utils\GeometryExtractor.cs" />
|
||||||
<Compile Include="src\Utils\LogManager.cs" />
|
<Compile Include="src\Utils\LogManager.cs" />
|
||||||
<Compile Include="src\Utils\NavisworksApiHelper.cs" />
|
<Compile Include="src\Utils\NavisworksApiHelper.cs" />
|
||||||
|
|||||||
@ -64,7 +64,7 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
|
|
||||||
// 只构建特定于动画对象的逻辑排除列表
|
// 只构建特定于动画对象的逻辑排除列表
|
||||||
LogManager.Info("[缓存管理] 构建逻辑排除列表...");
|
LogManager.Info("[缓存管理] 构建逻辑排除列表...");
|
||||||
var exclusionList = BuildLogicalObjectExclusionList(animationObject);
|
var exclusionList = ModelItemAnalysisHelper.CollectRelatedNodes(animationObject);
|
||||||
|
|
||||||
// 更新缓存
|
// 更新缓存
|
||||||
_currentCachedAnimationObject = animationObject;
|
_currentCachedAnimationObject = animationObject;
|
||||||
@ -137,8 +137,8 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
// 检查对象是否相同
|
// 检查对象是否相同
|
||||||
if (!_currentCachedAnimationObject.Equals(animationObject))
|
if (!_currentCachedAnimationObject.Equals(animationObject))
|
||||||
{
|
{
|
||||||
string cachedObjectName = GeometryAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
|
string cachedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
|
||||||
string currentObjectName = GeometryAnalysisHelper.GetSafeDisplayName(animationObject);
|
string currentObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animationObject);
|
||||||
LogManager.Debug($"[缓存验证] 动画对象已变更: '{cachedObjectName}' -> '{currentObjectName}'");
|
LogManager.Debug($"[缓存验证] 动画对象已变更: '{cachedObjectName}' -> '{currentObjectName}'");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -171,7 +171,7 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 安全访问DisplayName,防止WeakRef已释放的警告
|
// 安全访问DisplayName,防止WeakRef已释放的警告
|
||||||
string objectName = GeometryAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
|
string objectName = ModelItemAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
|
||||||
LogManager.Debug($"[缓存管理] 清除缓存: {objectName}");
|
LogManager.Debug($"[缓存管理] 清除缓存: {objectName}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -204,7 +204,7 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
}
|
}
|
||||||
|
|
||||||
var age = DateTime.Now - _cacheCreatedTime;
|
var age = DateTime.Now - _cacheCreatedTime;
|
||||||
string objectName = GeometryAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
|
string objectName = ModelItemAnalysisHelper.GetSafeDisplayName(_currentCachedAnimationObject);
|
||||||
var count = _cachedExclusionList?.Count ?? 0;
|
var count = _cachedExclusionList?.Count ?? 0;
|
||||||
|
|
||||||
return $"缓存状态: 对象='{objectName}', 排除数={count}, 年龄={age.TotalSeconds:F1}秒";
|
return $"缓存状态: 对象='{objectName}', 排除数={count}, 年龄={age.TotalSeconds:F1}秒";
|
||||||
@ -530,83 +530,6 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 几何分析功能 (从 ClashDetectiveIntegration 移动而来)
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构建逻辑物理对象的排除列表
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="animatedObject">动画对象</param>
|
|
||||||
/// <returns>应该从碰撞检测中排除的所有对象列表</returns>
|
|
||||||
private List<ModelItem> BuildLogicalObjectExclusionList(ModelItem animatedObject)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LogManager.Debug($"[排除列表构建] 开始为动画对象构建排除列表: {animatedObject.DisplayName}");
|
|
||||||
|
|
||||||
var exclusionList = new List<ModelItem>();
|
|
||||||
|
|
||||||
// 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<ModelItem> { animatedObject };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源清理
|
/// 资源清理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -1,706 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Autodesk.Navisworks.Api;
|
|
||||||
|
|
||||||
namespace NavisworksTransport.Utils
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 几何体分析和包围盒操作工具类
|
|
||||||
/// 提供用于几何体归属分析、包围盒计算和空间关系判断的静态方法
|
|
||||||
/// </summary>
|
|
||||||
public static class GeometryAnalysisHelper
|
|
||||||
{
|
|
||||||
#region 包围盒操作方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 计算包围盒体积
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bbox">包围盒</param>
|
|
||||||
/// <returns>包围盒的体积</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查一个包围盒是否完全包含另一个包围盒
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">外层包围盒</param>
|
|
||||||
/// <param name="contained">内层包围盒</param>
|
|
||||||
/// <param name="tolerance">容差值,默认1mm</param>
|
|
||||||
/// <returns>如果container完全包含contained返回true</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查两个包围盒是否有显著重叠(用于判断是否属于同一逻辑对象)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bbox1">包围盒1</param>
|
|
||||||
/// <param name="bbox2">包围盒2</param>
|
|
||||||
/// <param name="overlapThreshold">重叠阈值(0.0-1.0),表示重叠体积占较小包围盒体积的比例</param>
|
|
||||||
/// <returns>如果重叠程度超过阈值返回true</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查两个对象是否在空间上非常接近(可能是同一逻辑对象的不同表示)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bbox1">包围盒1</param>
|
|
||||||
/// <param name="bbox2">包围盒2</param>
|
|
||||||
/// <param name="proximityThreshold">接近阈值(米),默认10cm</param>
|
|
||||||
/// <returns>如果两个对象非常接近返回true</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查两个包围盒是否相交(带容差)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="box1">包围盒1</param>
|
|
||||||
/// <param name="box2">包围盒2</param>
|
|
||||||
/// <param name="tolerance">容差值</param>
|
|
||||||
/// <returns>如果两个包围盒相交返回true</returns>
|
|
||||||
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 辅助方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 计算两个3D点之间的距离
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="point1">点1</param>
|
|
||||||
/// <param name="point2">点2</param>
|
|
||||||
/// <returns>两点间的距离</returns>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查ModelItem是否仍然有效
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">要检查的ModelItem</param>
|
|
||||||
/// <returns>如果ModelItem有效返回true</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 安全获取ModelItem的DisplayName,避免访问已释放对象的警告
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">ModelItem对象</param>
|
|
||||||
/// <returns>安全的显示名称</returns>
|
|
||||||
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 几何体归属分析方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 综合判断两个ModelItem是否属于同一个逻辑物理对象
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item1">对象1</param>
|
|
||||||
/// <param name="item2">对象2</param>
|
|
||||||
/// <returns>如果属于同一逻辑对象返回true</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 分析选中对象的完整几何边界,识别逻辑物理对象的范围
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="selectedObject">用户选中的对象</param>
|
|
||||||
/// <returns>几何边界分析结果</returns>
|
|
||||||
public static GeometricBoundaryAnalysis AnalyzeGeometricBoundary(ModelItem selectedObject)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LogManager.Debug($"[几何边界分析] 开始分析对象: {selectedObject.DisplayName}");
|
|
||||||
|
|
||||||
var analysis = new GeometricBoundaryAnalysis
|
|
||||||
{
|
|
||||||
SelectedObject = selectedObject,
|
|
||||||
RootBoundingBox = selectedObject.BoundingBox(),
|
|
||||||
RelatedNodes = new List<ModelItem>()
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收集与选中对象相关的所有节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="selectedObject">选中的对象</param>
|
|
||||||
/// <param name="analysis">分析结果对象</param>
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收集祖先节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">起始节点</param>
|
|
||||||
/// <param name="analysis">分析结果对象</param>
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收集后代节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">起始节点</param>
|
|
||||||
/// <param name="analysis">分析结果对象</param>
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收集兄弟节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">起始节点</param>
|
|
||||||
/// <param name="analysis">分析结果对象</param>
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 分析几何包含关系
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="analysis">分析结果对象</param>
|
|
||||||
public static void AnalyzeGeometricContainment(GeometricBoundaryAnalysis analysis)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
analysis.ContainmentRelations = new Dictionary<ModelItem, List<ModelItem>>();
|
|
||||||
|
|
||||||
foreach (var item in analysis.RelatedNodes)
|
|
||||||
{
|
|
||||||
analysis.ContainmentRelations[item] = new List<ModelItem>();
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 定义逻辑物理对象边界
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="analysis">分析结果对象</param>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建备用分析结果(当主分析失败时)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="selectedObject">选中的对象</param>
|
|
||||||
/// <returns>备用分析结果</returns>
|
|
||||||
public static GeometricBoundaryAnalysis CreateFallbackAnalysis(ModelItem selectedObject)
|
|
||||||
{
|
|
||||||
return new GeometricBoundaryAnalysis
|
|
||||||
{
|
|
||||||
SelectedObject = selectedObject,
|
|
||||||
RootBoundingBox = selectedObject.BoundingBox(),
|
|
||||||
RelatedNodes = new List<ModelItem> { selectedObject },
|
|
||||||
LogicalObjectScope = LogicalObjectScope.SelectedNodeOnly,
|
|
||||||
LogicalRootNode = selectedObject,
|
|
||||||
ContainmentRelations = new Dictionary<ModelItem, List<ModelItem>>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 补充空间相关的对象到排除列表
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="animatedObject">动画对象</param>
|
|
||||||
/// <param name="exclusionList">当前排除列表</param>
|
|
||||||
public static void SupplementSpatiallyRelatedObjects(ModelItem animatedObject, List<ModelItem> 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 数据结构定义
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 几何边界分析结果
|
|
||||||
/// </summary>
|
|
||||||
public class GeometricBoundaryAnalysis
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 用户选中的原始对象
|
|
||||||
/// </summary>
|
|
||||||
public ModelItem SelectedObject { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 原始对象的包围盒
|
|
||||||
/// </summary>
|
|
||||||
public BoundingBox3D RootBoundingBox { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 所有相关节点(父、子、兄弟等)
|
|
||||||
/// </summary>
|
|
||||||
public List<ModelItem> RelatedNodes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 逻辑物理对象的范围定义
|
|
||||||
/// </summary>
|
|
||||||
public LogicalObjectScope LogicalObjectScope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 逻辑对象的根节点
|
|
||||||
/// </summary>
|
|
||||||
public ModelItem LogicalRootNode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 节点间的包含关系映射
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<ModelItem, List<ModelItem>> ContainmentRelations { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 逻辑对象范围枚举
|
|
||||||
/// </summary>
|
|
||||||
public enum LogicalObjectScope
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 仅选中节点本身
|
|
||||||
/// </summary>
|
|
||||||
SelectedNodeOnly,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 选中节点及其所有后代
|
|
||||||
/// </summary>
|
|
||||||
NodeAndDescendants,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 选中节点的父节点及其所有子树
|
|
||||||
/// </summary>
|
|
||||||
ParentAndSiblings,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 整个逻辑组件(自动识别的边界)
|
|
||||||
/// </summary>
|
|
||||||
EntireLogicalComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
292
src/Utils/ModelItemAnalysisHelper.cs
Normal file
292
src/Utils/ModelItemAnalysisHelper.cs
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Autodesk.Navisworks.Api;
|
||||||
|
|
||||||
|
namespace NavisworksTransport.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 节点分析工具类
|
||||||
|
/// 提供用于节点归属分析的静态方法
|
||||||
|
/// </summary>
|
||||||
|
public static class ModelItemAnalysisHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
#region 辅助方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算两个3D点之间的距离
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point1">点1</param>
|
||||||
|
/// <param name="point2">点2</param>
|
||||||
|
/// <returns>两点间的距离</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查ModelItem是否仍然有效
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">要检查的ModelItem</param>
|
||||||
|
/// <returns>如果ModelItem有效返回true</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全获取ModelItem的DisplayName,避免访问已释放对象的警告
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">ModelItem对象</param>
|
||||||
|
/// <returns>安全的显示名称</returns>
|
||||||
|
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 节点归属分析方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 收集与选中对象相关的所有节点
|
||||||
|
/// 基于节点类型的智能收集策略:
|
||||||
|
/// - 纯几何体:找父节点,收集父节点及其所有子节点
|
||||||
|
/// - 集合节点/混合节点:收集自身及所有子节点
|
||||||
|
/// - 空节点:仅收集自身
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="selectedObject">选中的对象</param>
|
||||||
|
/// <returns>相关节点列表</returns>
|
||||||
|
public static List<ModelItem> CollectRelatedNodes(ModelItem selectedObject)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var relatedNodes = new List<ModelItem>();
|
||||||
|
|
||||||
|
// 添加自身
|
||||||
|
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<ModelItem> { selectedObject }; // 异常时返回最基本的列表
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断模型节点的类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">模型节点</param>
|
||||||
|
/// <returns>节点类型</returns>
|
||||||
|
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; // 异常时返回最保守的类型
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从父节点收集相关节点(用于纯几何体节点)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pureGeometryNode">纯几何体节点</param>
|
||||||
|
/// <param name="analysis">分析结果对象</param>
|
||||||
|
/// <summary>
|
||||||
|
/// 从父节点收集相关节点(用于纯几何体节点)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pureGeometryNode">纯几何体节点</param>
|
||||||
|
/// <param name="relatedNodes">相关节点列表</param>
|
||||||
|
private static void CollectFromParentNode(ModelItem pureGeometryNode, List<ModelItem> 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从当前节点收集相关节点(用于集合节点和混合节点)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentNode">当前节点(集合节点或混合节点)</param>
|
||||||
|
/// <param name="analysis">分析结果对象</param>
|
||||||
|
/// <summary>
|
||||||
|
/// 从当前节点收集相关节点(用于集合节点和混合节点)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentNode">当前节点(集合节点或混合节点)</param>
|
||||||
|
/// <param name="relatedNodes">相关节点列表</param>
|
||||||
|
private static void CollectFromCurrentNode(ModelItem currentNode, List<ModelItem> 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 数据结构定义
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型节点类型枚举
|
||||||
|
/// </summary>
|
||||||
|
public enum ModelItemType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 纯几何体节点:HasGeometry = true, Children.Count = 0
|
||||||
|
/// </summary>
|
||||||
|
PureGeometry,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 集合节点:HasGeometry = false, Children.Count > 0
|
||||||
|
/// </summary>
|
||||||
|
GroupNode,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 混合节点:HasGeometry = true, Children.Count > 0
|
||||||
|
/// </summary>
|
||||||
|
HybridNode,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 空节点:HasGeometry = false, Children.Count = 0
|
||||||
|
/// </summary>
|
||||||
|
EmptyNode
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user