用节点类型(是否只包含几何体)来进行节点包含判断,废掉包围盒的方式。
This commit is contained in:
parent
2cd3772105
commit
d75582d664
@ -242,7 +242,7 @@
|
||||
<!-- Utils -->
|
||||
<Compile Include="src\Utils\CoordinateConverter.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\LogManager.cs" />
|
||||
<Compile Include="src\Utils\NavisworksApiHelper.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 移动而来)
|
||||
|
||||
/// <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>
|
||||
|
||||
@ -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