重构了一些重复的几何计算

This commit is contained in:
tian 2025-09-06 14:06:05 +08:00
parent 101c929f15
commit 2955bfd38b
8 changed files with 285 additions and 1455 deletions

View File

@ -1646,61 +1646,66 @@ using Autodesk.Navisworks.Api.Controls;
}
```
### Tool Enumeration
### Tool Enumeration
Member name Description
None No active tool
Select Select
SelectBox Select Box
RedlineFreehand Redline Freehand
RedlineLine Redline Line
RedlineEllipse Redline Ellipse
RedlineCloud Redline Cloud
RedlineLineString Redline Line String
RedlineTag Redline Tag
RedlineText Redline Text
RedlineErase Redline Erase
RedlineArrow Redline Arrow
MeasurePointToPoint Measure Point To Point
MeasurePointToMultiplePoints Measure Point To Multiple Points
MeasurePointLine Measure Point Line
MeasureAccumulate Measure Accumulate
MeasureAngle Measure Angle
MeasureArea Measure Area
MeasureSingle
BasicViewObjectWheel Basic View Object Wheel
BasicTourBuildingWheel Basic Tour Building Wheel
FullNavigationWheel Full Navigation Wheel
MiniViewObjectWheel Mini View Object Wheel
MiniTourBuildingWheel Mini Tour Building Wheel
MiniFullNavigationWheel Mini Full Navigation Wheel
Full2DNavigationWheel Full 2D Navigation Wheel
CommonPan Pan common across all Autodesk products
CommonZoom Zoom common across all Autodesk products
CommonZoomWindow Zoom Window common across all Autodesk products
CommonOrbit Orbit common across all Autodesk products
CommonFreeOrbit Free Orbit common across all Autodesk products
CommonConstrainedOrbit Constrained Orbit common across all Autodesk products
CommonLookAt Look At common across all Autodesk products
CommonLookAround Look Around common across all Autodesk products
CommonWalk Walk common across all Autodesk products
CommonCenter Center common across all Autodesk products
NavigateFixed Camera fixed in place
NavigateFreeLookAround Classic Navisworks Free Look Around (Swivel)
NavigateFreeOrbit Classic Navisworks Free Orbit (Examine)
NavigateWalk Classic Navisworks Walk
NavigateFly Classic Navisworks Fly
NavigateConstrainedOrbit Classic Navisworks Constrained Orbit (Turntable)
NavigateZoom Classic Navisworks Zoom
NavigatePan Classic Navisworks Pan
NavigateConstrainedPan Classic Navisworks Constrained Pan
NavigateLookAround Clasic Navisworks Look Around (Swivel)
NavigateOrbit Classic Navisworks Orbit
NavigateZoomWindow Classic Navisworks Zoom Window (Zoom Box)
CustomToolPlugin Functionality is provided by a ToolPlugin
● Tool 枚举值
| 成员名称 | 描述 |
|------------------------------|------------------------|
| None | 无活动工具 |
| Select | 选择工具 |
| SelectBox | 框选工具 |
| RedlineFreehand | 红线手绘工具 |
| RedlineLine | 红线直线工具 |
| RedlineEllipse | 红线椭圆工具 |
| RedlineCloud | 红线云形标注工具 |
| RedlineLineString | 红线连续线工具 |
| RedlineTag | 红线标签工具 |
| RedlineText | 红线文本工具 |
| RedlineErase | 红线擦除工具 |
| RedlineArrow | 红线箭头工具 |
| MeasurePointToPoint | 点到点测量工具 |
| MeasurePointToMultiplePoints | 点到多点测量工具 |
| MeasurePointLine | 点线测量工具 |
| MeasureAccumulate | 累积测量工具 |
| MeasureAngle | 角度测量工具 |
| MeasureArea | 面积测量工具 |
| MeasureSingle | 单点测量工具 |
| BasicViewObjectWheel | 基础视图对象导航轮 |
| BasicTourBuildingWheel | 基础建筑巡游导航轮 |
| FullNavigationWheel | 完整导航轮 |
| MiniViewObjectWheel | 迷你视图对象导航轮 |
| MiniTourBuildingWheel | 迷你建筑巡游导航轮 |
| MiniFullNavigationWheel | 迷你完整导航轮 |
| Full2DNavigationWheel | 完整2D导航轮 |
| CommonPan | 通用平移所有Autodesk产品通用 |
| CommonZoom | 通用缩放所有Autodesk产品通用 |
| CommonZoomWindow | 通用窗口缩放所有Autodesk产品通用 |
| CommonOrbit | 通用轨道所有Autodesk产品通用 |
| CommonFreeOrbit | 通用自由轨道所有Autodesk产品通用 |
| CommonConstrainedOrbit | 通用约束轨道所有Autodesk产品通用 |
| CommonLookAt | 通用看向所有Autodesk产品通用 |
| CommonLookAround | 通用环视所有Autodesk产品通用 |
| CommonWalk | 通用漫游所有Autodesk产品通用 |
| CommonCenter | 通用居中所有Autodesk产品通用 |
| NavigateFixed | 固定相机位置 |
| NavigateFreeLookAround | 经典Navisworks自由环视旋转 |
| NavigateFreeOrbit | 经典Navisworks自由轨道检查 |
| NavigateWalk | 经典Navisworks漫游 |
| NavigateFly | 经典Navisworks飞行 |
| NavigateConstrainedOrbit | 经典Navisworks约束轨道转盘 |
| NavigateZoom | 经典Navisworks缩放 |
| NavigatePan | 经典Navisworks平移 |
| NavigateConstrainedPan | 经典Navisworks约束平移 |
| NavigateLookAround | 经典Navisworks环视旋转 |
| NavigateOrbit | 经典Navisworks轨道 |
| NavigateZoomWindow | 经典Navisworks窗口缩放缩放框 |
| CustomToolPlugin | 由ToolPlugin提供的自定义功能 |
### Cursor枚举成员
这个枚举列出了Navisworks中所有可用的工具类型包括导航工具、测量工具、标注工具等。其中CustomToolPlugin是用于自定
义工具插件的特殊值。
### Cursor枚举成员
| 成员名称 | 描述 |
|-----------------|----------------------------|
@ -1753,4 +1758,4 @@ public class PathClickToolPlugin : ToolPlugin
return Cursor.MarkupSnapping;
}
}
```
```

View File

@ -675,8 +675,8 @@ namespace NavisworksTransport
try
{
var copyTest = addedTempTest.CreateCopy() as ClashTest;
copyTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
copyTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
copyTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines ;
copyTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines ;
_documentClash.TestsData.TestsEditTestFromCopy(addedTempTest, copyTest);
LogManager.Debug($"[分组测试-{resultCount:00}] 几何类型设置完成");
}

View File

@ -3,13 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Core;
using NavisworksTransport.Utils;
// COM API引用
using ComBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
using COMApi = Autodesk.Navisworks.Api.Interop.ComApi;
namespace NavisworksTransport.PathPlanning
{
/// <summary>
@ -61,7 +56,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[通道网格构建器] 找到 {channelItems.Count} 个通道物品");
// 2. 计算通道总边界
var totalBounds = CalculateTotalBounds(channelItems);
var totalBounds = BoundingBoxGeometryUtils.CalculateTotalBounds(channelItems);
LogManager.Info($"[通道网格构建器] 通道总边界: {FormatBounds(totalBounds)}");
// 3. 创建网格地图
@ -107,7 +102,7 @@ namespace NavisworksTransport.PathPlanning
_categoryManager = new CategoryAttributeManager();
}
// 优先使用CategoryAttributeManager获取标记为"通道"的物品
// 使用CategoryAttributeManager获取标记为"通道"的物品
var document = Application.ActiveDocument;
if (document?.Models != null)
{
@ -125,20 +120,9 @@ namespace NavisworksTransport.PathPlanning
}
}
// 如果没有用户标记的通道,回退到自动检测
LogManager.Warning("[通道网格构建器] 未找到用户标记的通道物品,使用自动检测");
var autoDetectedChannels = new List<ModelItem>();
foreach (ModelItem item in allItems)
{
if (IsChannelItem(item))
{
autoDetectedChannels.Add(item);
}
}
LogManager.Info($"[通道网格构建器] 自动检测到 {autoDetectedChannels.Count} 个通道物品");
return autoDetectedChannels;
// 如果没有用户标记的通道,直接返回空列表
LogManager.Warning("[通道网格构建器] 未找到用户标记的通道物品,返回空列表");
return new List<ModelItem>();
}
catch (Exception ex)
{
@ -147,32 +131,6 @@ namespace NavisworksTransport.PathPlanning
}
}
/// <summary>
/// 判断物品是否为通道
/// </summary>
/// <param name="item">模型物品</param>
/// <returns>是否为通道</returns>
private bool IsChannelItem(ModelItem item)
{
try
{
// 优先使用物流属性判断
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
var elementType = CategoryAttributeManager.GetLogisticsElementType(item);
return elementType == CategoryAttributeManager.LogisticsElementType.;
}
// 备用通过AutoPathObstacleDetector判断是否可通行
return AutoPathObstacleDetector.IsPassable(item);
}
catch (Exception ex)
{
LogManager.Warning($"[通道网格构建器] 判断通道时发生错误 '{item.DisplayName}': {ex.Message}");
return false;
}
}
/// <summary>
/// 将通道投影到网格
/// 核心创新:智能投影策略 - 薄通道优先使用顶面提取,复杂通道使用射线扫描
@ -189,7 +147,7 @@ namespace NavisworksTransport.PathPlanning
// 智能投影策略选择
if (thickness < 1.5) // 薄通道优先使用顶面提取
{
var triangles = ExtractTrianglesFromModelItem(channel);
var triangles = GeometryExtractor.ExtractTrianglesOptimized(channel);
var topFaceCount = CountTopFaces(triangles);
var topFaceRatio = triangles.Count > 0 ? (double)topFaceCount / triangles.Count : 0;
@ -217,7 +175,7 @@ namespace NavisworksTransport.PathPlanning
{
if (triangles == null)
{
triangles = ExtractTrianglesFromModelItem(item);
triangles = GeometryExtractor.ExtractTrianglesOptimized(item);
}
int projectedTriangles = 0;
@ -245,17 +203,13 @@ namespace NavisworksTransport.PathPlanning
/// <param name="gridMap">网格地图</param>
private void ProjectUsingRayCasting(ModelItem item, GridMap gridMap)
{
var triangles = ExtractTrianglesFromModelItem(item);
var triangles = GeometryExtractor.ExtractTrianglesOptimized(item);
if (triangles.Count == 0)
{
LogManager.Warning($"[通道网格构建器] 未能从 '{item.DisplayName}' 提取到三角形");
return;
}
// 使用空间哈希加速稍后实现TriangleSpatialHash时启用
// var spatialHash = new TriangleSpatialHash();
// spatialHash.Build(triangles, gridMap.CellSize);
var bbox = item.BoundingBox();
var minGrid = gridMap.WorldToGrid(new Point3D(bbox.Min.X, bbox.Min.Y, bbox.Min.Z));
var maxGrid = gridMap.WorldToGrid(new Point3D(bbox.Max.X, bbox.Max.Y, bbox.Max.Z));
@ -284,7 +238,7 @@ namespace NavisworksTransport.PathPlanning
// 检查射线是否击中任何三角形
foreach (var triangle in triangles)
{
if (RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
if (GeometryExtractor.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
{
if (intersectionZ >= bbox.Min.Z && intersectionZ <= bbox.Max.Z)
{
@ -299,243 +253,10 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[通道网格构建器] 射线投影:{hitCount} 个网格单元");
}
/// <summary>
/// 提取模型物品的三角形复用ChannelHeightDetector的现有功能
/// </summary>
/// <param name="modelItem">模型物品</param>
/// <returns>三角形列表</returns>
private List<Triangle3D> ExtractTrianglesFromModelItem(ModelItem modelItem)
{
try
{
LogManager.Info($"[通道网格构建器] 提取三角形: {modelItem.DisplayName}");
// 直接调用已有的三角形提取功能复用ChannelHeightDetector的代码逻辑
var allTriangles = new List<Triangle3D>();
// 创建几何提取回调
var callback = new GeometryExtractorCallback();
// 递归处理模型项及其子项
ExtractTrianglesRecursive(modelItem, callback, allTriangles);
LogManager.Info($"[通道网格构建器] 从ModelItem {modelItem.DisplayName} 总共提取到 {allTriangles.Count} 个三角形");
return allTriangles;
}
catch (Exception ex)
{
LogManager.Error($"[通道网格构建器] 提取三角形失败: {ex.Message}");
return new List<Triangle3D>();
}
}
/// <summary>
/// 递归提取ModelItem及其子项的几何数据
/// </summary>
private void ExtractTrianglesRecursive(ModelItem modelItem, GeometryExtractorCallback callback, List<Triangle3D> allTriangles)
{
try
{
// 如果当前项有几何数据,提取它
if (modelItem.HasGeometry)
{
ExtractTrianglesFromGeometry(modelItem, callback, allTriangles);
}
// 递归处理子项
foreach (ModelItem child in modelItem.Children)
{
ExtractTrianglesRecursive(child, callback, allTriangles);
}
}
catch (Exception ex)
{
LogManager.Warning($"[通道网格构建器] 处理ModelItem {modelItem.DisplayName} 时出错: {ex.Message}");
}
}
/// <summary>
/// 从单个有几何数据的ModelItem中提取三角形
/// </summary>
private void ExtractTrianglesFromGeometry(ModelItem modelItem, GeometryExtractorCallback callback, List<Triangle3D> allTriangles)
{
try
{
// 基于ChannelHeightDetector的方法直接从COM状态获取节点并遍历fragments
var comState = ComBridge.State;
var comObject = ComBridge.ToInwOaPath(modelItem);
if (comObject == null)
{
return;
}
// 检查路径是否有效并获取节点
var nodesColl = comObject.Nodes();
if (nodesColl.Count > 0)
{
var comNode = nodesColl[nodesColl.Count] as COMApi.InwOaNode; // 获取最后一个节点并转换类型
if (comNode != null && comNode.IsGeometry)
{
var fragCount = comNode.Fragments().Count;
for (long fragIndex = 1; fragIndex <= fragCount; fragIndex++)
{
var fragsColl = comNode.Fragments();
var fragment = fragsColl[fragIndex] as COMApi.InwOaFragment3;
if (fragment != null)
{
// 获取变换矩阵
var transformMatrix = GetTransformMatrix(fragment);
callback.SetTransformMatrix(transformMatrix);
// 清空回调中的三角形
callback.Clear();
// 生成几何图元 - 使用正确的枚举值
fragment.GenerateSimplePrimitives(COMApi.nwEVertexProperty.eNORMAL, callback);
// 将提取的三角形添加到总列表
allTriangles.AddRange(callback.Triangles);
}
}
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[通道网格构建器] 从 {modelItem.DisplayName} 提取几何时出错: {ex.Message}");
}
}
/// <summary>
/// 获取Fragment的变换矩阵
/// </summary>
private Matrix4 GetTransformMatrix(COMApi.InwOaFragment3 fragment)
{
try
{
var transform = fragment.GetLocalToWorldMatrix();
if (transform != null)
{
object matrixArrayObj = transform.Matrix;
if (matrixArrayObj is Array matrixArray && matrixArray.Length >= 16)
{
var matrix = new double[16];
matrixArray.CopyTo(matrix, 0);
// Navisworks使用列主序矩阵需要转置
return new Matrix4(
matrix[0], matrix[4], matrix[8], matrix[12],
matrix[1], matrix[5], matrix[9], matrix[13],
matrix[2], matrix[6], matrix[10], matrix[14],
matrix[3], matrix[7], matrix[11], matrix[15]
);
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[通道网格构建器] 获取变换矩阵失败: {ex.Message}");
}
return Matrix4.Identity; // 使用静态属性创建单位矩阵
}
/// <summary>
/// 射线-三角形相交测试复用ChannelHeightDetector的现有功能
/// </summary>
/// <param name="rayOrigin">射线起点</param>
/// <param name="rayDirection">射线方向</param>
/// <param name="triangle">三角形</param>
/// <param name="intersectionZ">交点Z坐标</param>
/// <returns>是否相交</returns>
private bool RayTriangleIntersect(Point3D rayOrigin, Point3D rayDirection, Triangle3D triangle, out double intersectionZ)
{
intersectionZ = 0.0;
const double EPSILON = 0.0000001;
try
{
// 使用Möller-Trumbore算法进行射线-三角形相交测试
// 计算三角形的两条边
var edge1 = SubtractPoints(triangle.V2, triangle.V1);
var edge2 = SubtractPoints(triangle.V3, triangle.V1);
// 计算射线方向与edge2的叉积
var h = CrossProduct(rayDirection, edge2);
var a = DotProduct(edge1, h);
// 如果a接近0射线与三角形平行
if (a > -EPSILON && a < EPSILON)
{
return false;
}
var f = 1.0 / a;
var s = SubtractPoints(rayOrigin, triangle.V1);
var u = f * DotProduct(s, h);
if (u < 0.0 || u > 1.0)
{
return false;
}
var q = CrossProduct(s, edge1);
var v = f * DotProduct(rayDirection, q);
if (v < 0.0 || u + v > 1.0)
{
return false;
}
// 计算t值射线参数
var t = f * DotProduct(edge2, q);
if (t > EPSILON) // 射线相交
{
// 计算交点
intersectionZ = rayOrigin.Z + t * rayDirection.Z;
return true;
}
return false; // 线段相交但射线不相交
}
catch (Exception ex)
{
LogManager.Warning($"[通道网格构建器] 射线-三角形交点计算出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 计算两点之间的向量
/// </summary>
private Point3D SubtractPoints(Point3D a, Point3D b)
{
return new Point3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
/// <summary>
/// 计算两个向量的叉积
/// </summary>
private Point3D CrossProduct(Point3D a, Point3D b)
{
return new Point3D(
a.Y * b.Z - a.Z * b.Y,
a.Z * b.X - a.X * b.Z,
a.X * b.Y - a.Y * b.X
);
}
/// <summary>
/// 计算两个向量的点积
/// </summary>
private double DotProduct(Point3D a, Point3D b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
}
/// <summary>
/// 计算三角形法向量
@ -545,14 +266,14 @@ namespace NavisworksTransport.PathPlanning
private Point3D ComputeTriangleNormal(Triangle3D triangle)
{
var edge1 = new Point3D(
triangle.V2.X - triangle.V1.X,
triangle.V2.Y - triangle.V1.Y,
triangle.V2.Z - triangle.V1.Z);
triangle.Point2.X - triangle.Point1.X,
triangle.Point2.Y - triangle.Point1.Y,
triangle.Point2.Z - triangle.Point1.Z);
var edge2 = new Point3D(
triangle.V3.X - triangle.V1.X,
triangle.V3.Y - triangle.V1.Y,
triangle.V3.Z - triangle.V1.Z);
triangle.Point3.X - triangle.Point1.X,
triangle.Point3.Y - triangle.Point1.Y,
triangle.Point3.Z - triangle.Point1.Z);
// 计算叉积
var normal = new Point3D(
@ -597,10 +318,10 @@ namespace NavisworksTransport.PathPlanning
private void RasterizeTriangleToGrid(GridMap gridMap, Triangle3D triangle)
{
// 获取三角形的2D投影边界框
var minX = Math.Min(triangle.V1.X, Math.Min(triangle.V2.X, triangle.V3.X));
var maxX = Math.Max(triangle.V1.X, Math.Max(triangle.V2.X, triangle.V3.X));
var minY = Math.Min(triangle.V1.Y, Math.Min(triangle.V2.Y, triangle.V3.Y));
var maxY = Math.Max(triangle.V1.Y, Math.Max(triangle.V2.Y, triangle.V3.Y));
var minX = Math.Min(triangle.Point1.X, Math.Min(triangle.Point2.X, triangle.Point3.X));
var maxX = Math.Max(triangle.Point1.X, Math.Max(triangle.Point2.X, triangle.Point3.X));
var minY = Math.Min(triangle.Point1.Y, Math.Min(triangle.Point2.Y, triangle.Point3.Y));
var maxY = Math.Max(triangle.Point1.Y, Math.Max(triangle.Point2.Y, triangle.Point3.Y));
var minGrid = gridMap.WorldToGrid(new Point3D(minX, minY, 0));
var maxGrid = gridMap.WorldToGrid(new Point3D(maxX, maxY, 0));
@ -616,7 +337,7 @@ namespace NavisworksTransport.PathPlanning
var worldPos = gridMap.GridToWorld(gridPos);
// 检查点是否在三角形内部
if (IsPointInTriangle2D(worldPos, triangle))
if (GeometryExtractor.IsPointInTriangle2D(worldPos, triangle))
{
SetCellAsChannel(gridMap, gridPos, null);
}
@ -625,34 +346,6 @@ namespace NavisworksTransport.PathPlanning
}
}
/// <summary>
/// 检查2D点是否在三角形内部
/// </summary>
/// <param name="point">测试点</param>
/// <param name="triangle">三角形</param>
/// <returns>是否在内部</returns>
private bool IsPointInTriangle2D(Point3D point, Triangle3D triangle)
{
// 使用重心坐标法判断点是否在三角形内
var v0x = triangle.V3.X - triangle.V1.X;
var v0y = triangle.V3.Y - triangle.V1.Y;
var v1x = triangle.V2.X - triangle.V1.X;
var v1y = triangle.V2.Y - triangle.V1.Y;
var v2x = point.X - triangle.V1.X;
var v2y = point.Y - triangle.V1.Y;
var dot00 = v0x * v0x + v0y * v0y;
var dot01 = v0x * v1x + v0y * v1y;
var dot02 = v0x * v2x + v0y * v2y;
var dot11 = v1x * v1x + v1y * v1y;
var dot12 = v1x * v2x + v1y * v2y;
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return (u >= 0) && (v >= 0) && (u + v <= 1);
}
/// <summary>
/// 设置网格单元为通道类型
@ -666,41 +359,6 @@ namespace NavisworksTransport.PathPlanning
gridMap.SetCellAsChannel(gridPos, ChannelType.Corridor, relatedItem);
}
/// <summary>
/// 计算通道总边界
/// </summary>
/// <param name="channelItems">通道物品列表</param>
/// <returns>总边界框</returns>
private BoundingBox3D CalculateTotalBounds(List<ModelItem> channelItems)
{
if (!channelItems.Any())
{
return new BoundingBox3D(new Point3D(0, 0, 0), new Point3D(0, 0, 0));
}
var firstBounds = channelItems.First().BoundingBox();
double minX = firstBounds.Min.X;
double minY = firstBounds.Min.Y;
double minZ = firstBounds.Min.Z;
double maxX = firstBounds.Max.X;
double maxY = firstBounds.Max.Y;
double maxZ = firstBounds.Max.Z;
foreach (var item in channelItems.Skip(1))
{
var bounds = item.BoundingBox();
minX = Math.Min(minX, bounds.Min.X);
minY = Math.Min(minY, bounds.Min.Y);
minZ = Math.Min(minZ, bounds.Min.Z);
maxX = Math.Max(maxX, bounds.Max.X);
maxY = Math.Max(maxY, bounds.Max.Y);
maxZ = Math.Max(maxZ, bounds.Max.Z);
}
return new BoundingBox3D(
new Point3D(minX, minY, minZ),
new Point3D(maxX, maxY, maxZ));
}
/// <summary>
/// 创建空的通道覆盖

View File

@ -252,37 +252,6 @@ namespace NavisworksTransport.PathPlanning
}
}
/// <summary>
/// 确定通道类型
/// </summary>
/// <param name="channel">通道模型项</param>
/// <returns>通道类型</returns>
private ChannelType DetermineChannelType(ModelItem channel)
{
var displayName = channel.DisplayName?.ToLower() ?? "";
var className = channel.ClassDisplayName?.ToLower() ?? "";
if (displayName.Contains("楼梯") || displayName.Contains("stair") ||
className.Contains("楼梯") || className.Contains("stair"))
{
return ChannelType.Stairs;
}
if (displayName.Contains("坡道") || displayName.Contains("ramp") ||
className.Contains("坡道") || className.Contains("ramp"))
{
return ChannelType.Ramp;
}
if (displayName.Contains("电梯") || displayName.Contains("elevator") ||
className.Contains("电梯") || className.Contains("elevator"))
{
return ChannelType.Elevator;
}
return ChannelType.Corridor; // 默认为走廊
}
/// <summary>
/// 采样通道高度剖面
/// </summary>
@ -503,7 +472,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Debug($"[几何分析] PickItemFromPoint结果: 模型={pickResult.ModelItem?.DisplayName ?? "NULL"}, 3D点=({pickResult.Point.X:F2}, {pickResult.Point.Y:F2}, {pickResult.Point.Z:F2})");
// 验证拾取到的模型是否为目标通道(或其子项)
if (IsModelItemRelatedToChannel(pickResult.ModelItem, channel))
if (pickResult.ModelItem.Equals(channel) || ModelItemAnalysisHelper.IsChildOf(pickResult.ModelItem, channel))
{
height = pickResult.Point.Z;
LogManager.Info($"[几何分析] ✅ 使用投影法获得精确表面高度: {height:F2},通道: {channel.DisplayName}");
@ -579,50 +548,6 @@ namespace NavisworksTransport.PathPlanning
return new Point3D(relativeX, relativeY, relativeZ);
}
/// <summary>
/// 检查模型项是否与目标通道相关(同一模型或父子关系)
/// </summary>
private bool IsModelItemRelatedToChannel(ModelItem pickedItem, ModelItem targetChannel)
{
if (pickedItem == null || targetChannel == null)
return false;
// 直接匹配
if (pickedItem.InstanceGuid == targetChannel.InstanceGuid)
return true;
// 检查是否为同一父对象下的子项(简化的层次关系检查)
try
{
// 比较显示名称(对于复合模型的情况)
var pickedName = pickedItem.DisplayName?.ToLower() ?? "";
var targetName = targetChannel.DisplayName?.ToLower() ?? "";
if (!string.IsNullOrEmpty(pickedName) && !string.IsNullOrEmpty(targetName))
{
// 如果名称相似或包含关系,认为是相关的
if (pickedName.Contains(targetName) || targetName.Contains(pickedName))
{
return true;
}
}
// 检查几何边界是否重叠(作为相关性的另一个指标)
var pickedBounds = pickedItem.BoundingBox();
var targetBounds = targetChannel.BoundingBox();
if (pickedBounds != null && targetBounds != null)
{
return BoundingBoxesOverlap(pickedBounds, targetBounds);
}
}
catch (Exception ex)
{
LogManager.Debug($"[模型关系检查] 检查模型关系时发生错误: {ex.Message}");
}
return false;
}
/// <summary>
/// 检查两个边界框是否重叠
@ -634,17 +559,6 @@ namespace NavisworksTransport.PathPlanning
bbox1.Max.Z < bbox2.Min.Z || bbox2.Max.Z < bbox1.Min.Z);
}
/// <summary>
/// 根据相对位置估算高度(作为备用方法)
/// </summary>
private double EstimateHeightFromRelativePosition(Point3D relativePos, BoundingBox3D bbox)
{
// 保守估算:对于大多数通道,表面高度接近顶面
// 优先使用精确方法,这里作为最后的备用方案
var estimatedRatio = 0.95; // 假设表面在95%高度位置(更保守的估算)
return bbox.Min.Z + (bbox.Max.Z - bbox.Min.Z) * estimatedRatio;
}
/// <summary>
/// 生成缓存键
/// </summary>
@ -714,7 +628,7 @@ namespace NavisworksTransport.PathPlanning
{
// 使用PickItemFromPoint获取该屏幕位置的实际表面点
var pickResult = activeView.PickItemFromPoint((int)screenPoint.X, (int)screenPoint.Y);
if (pickResult != null && IsModelItemRelatedToChannel(pickResult.ModelItem, channel))
if (pickResult != null && (pickResult.ModelItem.Equals(channel) || ModelItemAnalysisHelper.IsChildOf(pickResult.ModelItem, channel)))
{
LogManager.Info($"[射线投射] ✅ 成功检测到表面点: Z={pickResult.Point.Z:F2}");
return pickResult.Point.Z;
@ -748,7 +662,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[COM几何射线] 开始从ModelItem提取三角形几何数据: {channel.DisplayName}");
// 使用COM API提取三角形几何数据
var triangles = ExtractTrianglesFromModelItem(channel);
var triangles = GeometryExtractor.ExtractTrianglesOptimized(channel);
if (triangles.Count == 0)
{
LogManager.Warning($"[COM几何射线] 未能从ModelItem提取到三角形数据: {channel.DisplayName}");
@ -780,153 +694,6 @@ namespace NavisworksTransport.PathPlanning
}
}
/// <summary>
/// 使用COM API从ModelItem中提取三角形几何数据
/// </summary>
/// <param name="modelItem">模型项</param>
/// <returns>三角形列表</returns>
private List<Triangle3D> ExtractTrianglesFromModelItem(ModelItem modelItem)
{
var allTriangles = new List<Triangle3D>();
try
{
// 创建几何提取回调
var callback = new GeometryExtractorCallback();
// 递归处理模型项及其子项
ExtractTrianglesRecursive(modelItem, callback, allTriangles);
//LogManager.Info($"[COM几何提取] 从ModelItem {modelItem.DisplayName} 总共提取到 {allTriangles.Count} 个三角形");
return allTriangles;
}
catch (Exception ex)
{
LogManager.Error($"[COM几何提取] 提取三角形几何数据失败: {ex.Message}");
return allTriangles;
}
}
/// <summary>
/// 递归提取ModelItem及其子项的几何数据
/// </summary>
private void ExtractTrianglesRecursive(ModelItem modelItem, GeometryExtractorCallback callback, List<Triangle3D> allTriangles)
{
try
{
// 如果当前项有几何数据,提取它
if (modelItem.HasGeometry)
{
ExtractTrianglesFromGeometry(modelItem, callback, allTriangles);
}
// 递归处理子项
foreach (ModelItem child in modelItem.Children)
{
ExtractTrianglesRecursive(child, callback, allTriangles);
}
}
catch (Exception ex)
{
LogManager.Warning($"[COM几何提取] 处理ModelItem {modelItem.DisplayName} 时出错: {ex.Message}");
}
}
/// <summary>
/// 从单个有几何数据的ModelItem中提取三角形
/// </summary>
private void ExtractTrianglesFromGeometry(ModelItem modelItem, GeometryExtractorCallback callback, List<Triangle3D> allTriangles)
{
try
{
// 基于示例代码的方法直接从COM状态获取节点并遍历fragments
var comState = ComBridge.State;
var comObject = ComBridge.ToInwOaPath(modelItem);
if (comObject == null)
{
//LogManager.Debug($"[COM几何提取] 无法转换ModelItem为COM对象: {modelItem.DisplayName}");
return;
}
// 检查路径是否有效并获取节点
var nodesColl = comObject.Nodes();
if (nodesColl.Count > 0)
{
var comNode = nodesColl[nodesColl.Count] as COMApi.InwOaNode; // 获取最后一个节点并转换类型
if (comNode != null && comNode.IsGeometry)
{
var fragCount = comNode.Fragments().Count;
//LogManager.Debug($"[COM几何提取] 找到 {fragCount} 个fragments在 {modelItem.DisplayName}");
for (long fragIndex = 1; fragIndex <= fragCount; fragIndex++)
{
var fragsColl = comNode.Fragments();
var fragment = fragsColl[fragIndex] as COMApi.InwOaFragment3;
if (fragment != null)
{
// 获取变换矩阵
var transformMatrix = GetTransformMatrix(fragment);
callback.SetTransformMatrix(transformMatrix);
// 清空回调中的三角形
callback.Clear();
// 生成几何图元 - 使用正确的枚举值
fragment.GenerateSimplePrimitives(COMApi.nwEVertexProperty.eNORMAL, callback);
// 将提取的三角形添加到总列表
allTriangles.AddRange(callback.Triangles);
//LogManager.Debug($"[COM几何提取] 从fragment {fragIndex} 提取到 {callback.Triangles.Count} 个三角形");
}
}
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[COM几何提取] 从 {modelItem.DisplayName} 提取几何时出错: {ex.Message}");
}
}
/// <summary>
/// 获取Fragment的变换矩阵
/// </summary>
private Matrix4 GetTransformMatrix(COMApi.InwOaFragment3 fragment)
{
try
{
var transform = fragment.GetLocalToWorldMatrix();
if (transform != null)
{
object matrixArrayObj = transform.Matrix;
if (matrixArrayObj is Array matrixArray && matrixArray.Length >= 16)
{
var matrix = new double[16];
matrixArray.CopyTo(matrix, 0);
//LogManager.Debug($"[COM几何提取] 获取到变换矩阵: [{matrix[0]:F2},{matrix[1]:F2},{matrix[2]:F2},{matrix[3]:F2}] [{matrix[4]:F2},{matrix[5]:F2},{matrix[6]:F2},{matrix[7]:F2}] [{matrix[8]:F2},{matrix[9]:F2},{matrix[10]:F2},{matrix[11]:F2}] [{matrix[12]:F2},{matrix[13]:F2},{matrix[14]:F2},{matrix[15]:F2}]");
// Navisworks使用列主序矩阵需要转置
return new Matrix4(
matrix[0], matrix[4], matrix[8], matrix[12],
matrix[1], matrix[5], matrix[9], matrix[13],
matrix[2], matrix[6], matrix[10], matrix[14],
matrix[3], matrix[7], matrix[11], matrix[15]
);
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[COM几何提取] 获取变换矩阵失败: {ex.Message}");
}
return Matrix4.Identity; // 使用静态属性创建单位矩阵
}
/// <summary>
/// 执行射线与三角形的交点检测
/// </summary>
@ -940,35 +707,20 @@ namespace NavisworksTransport.PathPlanning
try
{
// 创建垂直向下的射线(从通道上方合理高度开始)
var maxZ = triangles.Count > 0 ? triangles.Max(t => Math.Max(Math.Max(t.V1.Z, t.V2.Z), t.V3.Z)) : 0;
var maxZ = triangles.Count > 0 ? triangles.Max(t => Math.Max(Math.Max(t.Point1.Z, t.Point2.Z), t.Point3.Z)) : 0;
var rayOrigin = new Point3D(position.X, position.Y, maxZ + 1000.0); // 从最高点上方1km开始
var rayDirection = new Point3D(0, 0, -1); // 向下
//LogManager.Debug($"[射线-三角形] 射线起点: ({rayOrigin.X:F2}, {rayOrigin.Y:F2}, {rayOrigin.Z:F2}), 方向: (0, 0, -1), 三角形最高点: {maxZ:F2}");
int intersectionCount = 0;
foreach (var triangle in triangles)
{
if (RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
if (GeometryExtractor.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
{
intersectionPoints.Add(intersectionZ);
intersectionCount++;
//LogManager.Debug($"[射线-三角形] 第{intersectionCount}个交点: Z={intersectionZ:F2}, 三角形V1({triangle.V1.X:F2},{triangle.V1.Y:F2},{triangle.V1.Z:F2})");
}
}
if (intersectionCount == 0)
{
LogManager.Warning($"[射线-三角形] 详细分析: 射线({rayOrigin.X:F2},{rayOrigin.Y:F2},{rayOrigin.Z:F2})→(0,0,-1) 与{triangles.Count}个三角形均无交点");
// 输出前3个三角形的详细信息用于调试
for (int i = 0; i < Math.Min(3, triangles.Count); i++)
{
var tri = triangles[i];
//LogManager.Debug($"[射线-三角形] 三角形{i}: V1({tri.V1.X:F2},{tri.V1.Y:F2},{tri.V1.Z:F2}) V2({tri.V2.X:F2},{tri.V2.Y:F2},{tri.V2.Z:F2}) V3({tri.V3.X:F2},{tri.V3.Y:F2},{tri.V3.Z:F2})");
}
}
//LogManager.Debug($"[射线-三角形] 位置({position.X:F2}, {position.Y:F2}) 检测到 {intersectionPoints.Count} 个交点");
return intersectionPoints;
}
catch (Exception ex)
@ -978,213 +730,6 @@ namespace NavisworksTransport.PathPlanning
}
}
/// <summary>
/// 射线与三角形的交点检测算法Möller-Trumbore算法
/// </summary>
/// <param name="rayOrigin">射线起点</param>
/// <param name="rayDirection">射线方向</param>
/// <param name="triangle">三角形</param>
/// <param name="intersectionZ">交点的Z坐标</param>
/// <returns>是否相交</returns>
private bool RayTriangleIntersect(Point3D rayOrigin, Point3D rayDirection, Triangle3D triangle, out double intersectionZ)
{
intersectionZ = 0.0;
const double EPSILON = 0.0000001;
try
{
// 计算三角形的两条边
var edge1 = SubtractPoints(triangle.V2, triangle.V1);
var edge2 = SubtractPoints(triangle.V3, triangle.V1);
// 计算射线方向与edge2的叉积
var h = CrossProduct(rayDirection, edge2);
var a = DotProduct(edge1, h);
// 如果a接近0射线与三角形平行
if (a > -EPSILON && a < EPSILON)
{
//LogManager.Debug($"[Möller-Trumbore] 射线与三角形平行a={a:F8}");
return false;
}
var f = 1.0 / a;
var s = SubtractPoints(rayOrigin, triangle.V1);
var u = f * DotProduct(s, h);
if (u < 0.0 || u > 1.0)
{
//LogManager.Debug($"[Möller-Trumbore] u值超出范围u={u:F6}");
return false;
}
var q = CrossProduct(s, edge1);
var v = f * DotProduct(rayDirection, q);
if (v < 0.0 || u + v > 1.0)
{
//LogManager.Debug($"[Möller-Trumbore] v值超出范围v={v:F6}, u+v={u + v:F6}");
return false;
}
// 计算t值射线参数
var t = f * DotProduct(edge2, q);
if (t > EPSILON) // 射线相交
{
// 计算交点
intersectionZ = rayOrigin.Z + t * rayDirection.Z;
//LogManager.Debug($"[Möller-Trumbore] ✅ 找到交点: t={t:F6}, intersectionZ={intersectionZ:F2}, 三角形V1.Z={triangle.V1.Z:F2}");
return true;
}
else
{
LogManager.Debug($"[Möller-Trumbore] t值无效t={t:F6}");
}
return false; // 线段相交但射线不相交
}
catch (Exception ex)
{
LogManager.Warning($"[射线-三角形] 交点计算出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 计算两点之间的向量
/// </summary>
private Point3D SubtractPoints(Point3D a, Point3D b)
{
return new Point3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
/// <summary>
/// 计算两个向量的叉积
/// </summary>
private Point3D CrossProduct(Point3D a, Point3D b)
{
return new Point3D(
a.Y * b.Z - a.Z * b.Y,
a.Z * b.X - a.X * b.Z,
a.X * b.Y - a.Y * b.X
);
}
/// <summary>
/// 计算两个向量的点积
/// </summary>
private double DotProduct(Point3D a, Point3D b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
}
private List<double> FindRayModelSurfaceIntersections(Point3D position, ModelItem channel)
{
var intersections = new List<double>();
var bbox = channel.BoundingBox();
try
{
var view = Autodesk.Navisworks.Api.Application.ActiveDocument.ActiveView;
if (view == null)
{
LogManager.Warning("无法获取当前视图,射线投射失败");
return intersections;
}
// 在通道包围盒范围内的多个Z层采样点
int zSamples = 10;
double zStep = (bbox.Max.Z - bbox.Min.Z) / zSamples;
for (int i = 0; i <= zSamples; i++)
{
double testZ = bbox.Min.Z + i * zStep;
var testPoint = new Point3D(position.X, position.Y, testZ);
// 将3D点投影到屏幕坐标
var projectionResult = view.ProjectPoint(testPoint, false, false);
if (projectionResult != null)
{
// 使用屏幕坐标进行pick操作获取实际表面点
var pickResult = view.PickItemFromPoint((int)projectionResult.X, (int)projectionResult.Y);
if (pickResult != null && pickResult.ModelItem != null)
{
// 检查是否命中目标通道或其子项
if (IsChannelOrChildItem(pickResult.ModelItem, channel))
{
// 获取实际表面点的高度
var surfacePoint = pickResult.Point;
LogManager.Debug($"射线投射命中通道表面: Z={surfacePoint.Z:F2}, 原测试点Z={testZ:F2}");
intersections.Add(surfacePoint.Z);
}
}
}
}
// 去重并排序
intersections = intersections.Distinct().OrderBy(z => z).ToList();
LogManager.Debug($"模型表面射线投射找到 {intersections.Count} 个交点");
return intersections;
}
catch (Exception ex)
{
LogManager.Error($"模型表面射线投射出错: {ex.Message}");
return intersections;
}
}
private bool IsChannelOrChildItem(ModelItem pickedItem, ModelItem targetChannel)
{
if (pickedItem == null || targetChannel == null)
return false;
// 检查是否是目标通道本身
if (pickedItem.InstanceGuid == targetChannel.InstanceGuid)
return true;
// 检查是否是目标通道的子项
var parent = pickedItem.Parent;
while (parent != null)
{
if (parent.InstanceGuid == targetChannel.InstanceGuid)
return true;
parent = parent.Parent;
}
return false;
}
/// <summary>
/// 从边界框估算表面高度
/// </summary>
private double? EstimateSurfaceHeightFromBounds(Point3D position, BoundingBox3D bbox)
{
try
{
// 检查位置是否在通道的XY范围内
if (position.X >= bbox.Min.X && position.X <= bbox.Max.X &&
position.Y >= bbox.Min.Y && position.Y <= bbox.Max.Y)
{
// 对于大多数通道类型,表面高度接近顶部
// 使用90%的高度作为保守估算
var estimatedHeight = bbox.Min.Z + (bbox.Max.Z - bbox.Min.Z) * 0.9;
LogManager.Debug($"[边界框估算] 位置在通道范围内,估算表面高度: {estimatedHeight:F2}");
return estimatedHeight;
}
LogManager.Debug($"[边界框估算] 位置不在通道XY范围内");
return null;
}
catch (Exception ex)
{
LogManager.Warning($"[边界框估算] 估算失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 尝试多点采样获取表面高度
/// </summary>
@ -1223,7 +768,7 @@ namespace NavisworksTransport.PathPlanning
screenPoint.Y >= 0 && screenPoint.Y < activeView.Height)
{
var pickResult = activeView.PickItemFromPoint((int)screenPoint.X, (int)screenPoint.Y);
if (pickResult != null && IsModelItemRelatedToChannel(pickResult.ModelItem, channel))
if (pickResult != null && (pickResult.ModelItem.Equals(channel) || ModelItemAnalysisHelper.IsChildOf(pickResult.ModelItem, channel)))
{
validHeights.Add(pickResult.Point.Z);
LogManager.Debug($"[多点采样] 采样点({samplePoint.X:F1}, {samplePoint.Y:F1})高度: {pickResult.Point.Z:F2}");
@ -1330,166 +875,4 @@ namespace NavisworksTransport.PathPlanning
Other
}
/// <summary>
/// 三角形数据结构
/// </summary>
public class Triangle3D
{
public Point3D V1 { get; set; }
public Point3D V2 { get; set; }
public Point3D V3 { get; set; }
public Triangle3D(Point3D v1, Point3D v2, Point3D v3)
{
V1 = v1;
V2 = v2;
V3 = v3;
}
}
/// <summary>
/// COM API几何提取回调类
/// 用于从ModelItem中提取真实的三角形几何数据
/// </summary>
public class GeometryExtractorCallback : COMApi.InwSimplePrimitivesCB
{
private readonly List<Triangle3D> _triangles;
private Matrix4 _transformMatrix;
public List<Triangle3D> Triangles => _triangles;
public GeometryExtractorCallback()
{
_triangles = new List<Triangle3D>();
_transformMatrix = Matrix4.Identity; // 使用静态属性创建单位矩阵
}
public void SetTransformMatrix(Matrix4 matrix)
{
_transformMatrix = matrix;
}
public void Line(COMApi.InwSimpleVertex v1, COMApi.InwSimpleVertex v2)
{
// 我们只关心三角形,忽略线段
}
public void Point(COMApi.InwSimpleVertex v1)
{
// 我们只关心三角形,忽略点
}
public void SnapPoint(COMApi.InwSimpleVertex v1)
{
// 我们只关心三角形,忽略捕捉点
}
public void Triangle(COMApi.InwSimpleVertex v1, COMApi.InwSimpleVertex v2, COMApi.InwSimpleVertex v3)
{
try
{
// 提取顶点坐标
var vertex1 = ConvertVertex(v1);
var vertex2 = ConvertVertex(v2);
var vertex3 = ConvertVertex(v3);
// 应用变换矩阵到顶点坐标
var originalVertex1 = vertex1;
vertex1 = _transformMatrix.Transform(vertex1);
vertex2 = _transformMatrix.Transform(vertex2);
vertex3 = _transformMatrix.Transform(vertex3);
//LogManager.Debug($"[COM几何提取] 局部坐标: V1({originalVertex1.X:F2},{originalVertex1.Y:F2},{originalVertex1.Z:F2})");
//LogManager.Debug($"[COM几何提取] 变换后世界坐标: V1({vertex1.X:F2},{vertex1.Y:F2},{vertex1.Z:F2}) V2({vertex2.X:F2},{vertex2.Y:F2},{vertex2.Z:F2}) V3({vertex3.X:F2},{vertex3.Y:F2},{vertex3.Z:F2})");
// 创建三角形并添加到列表
var triangle = new Triangle3D(vertex1, vertex2, vertex3);
_triangles.Add(triangle);
}
catch (Exception ex)
{
LogManager.Warning($"[COM几何提取] 处理三角形时出错: {ex.Message}");
}
}
private Point3D ConvertVertex(COMApi.InwSimpleVertex vertex)
{
// 从COM API顶点获取坐标
object coordObj = vertex.coord;
if (coordObj is Array coordinates && coordinates.Length >= 3)
{
var coords = new double[3];
coordinates.CopyTo(coords, 0);
return new Point3D(coords[0], coords[1], coords[2]);
}
return new Point3D(0, 0, 0);
}
public void Clear()
{
_triangles.Clear();
}
}
/// <summary>
/// 4x4变换矩阵结构
/// </summary>
public struct Matrix4
{
public double m11, m12, m13, m14;
public double m21, m22, m23, m24;
public double m31, m32, m33, m34;
public double m41, m42, m43, m44;
public Matrix4(double _m11, double _m12, double _m13, double _m14,
double _m21, double _m22, double _m23, double _m24,
double _m31, double _m32, double _m33, double _m34,
double _m41, double _m42, double _m43, double _m44)
{
m11 = _m11; m12 = _m12; m13 = _m13; m14 = _m14;
m21 = _m21; m22 = _m22; m23 = _m23; m24 = _m24;
m31 = _m31; m32 = _m32; m33 = _m33; m34 = _m34;
m41 = _m41; m42 = _m42; m43 = _m43; m44 = _m44;
}
/// <summary>
/// 创建单位矩阵
/// </summary>
public static Matrix4 Identity
{
get
{
return new Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
}
/// <summary>
/// 应用变换到点坐标
/// </summary>
public Point3D Transform(Point3D point)
{
// 4x4矩阵变换假设w=1
var x = m11 * point.X + m12 * point.Y + m13 * point.Z + m14;
var y = m21 * point.X + m22 * point.Y + m23 * point.Z + m24;
var z = m31 * point.X + m32 * point.Y + m33 * point.Z + m34;
var w = m41 * point.X + m42 * point.Y + m43 * point.Z + m44;
// 透视除法如果w不是1
if (Math.Abs(w - 1.0) > 0.0001)
{
x /= w;
y /= w;
z /= w;
}
return new Point3D(x, y, z);
}
}
}

View File

@ -843,43 +843,11 @@ namespace NavisworksTransport.PathPlanning
{
try
{
// 使用包围盒进行快速垂直相交检测
var bbox = item.BoundingBox();
if (bbox == null)
return null;
// 检查XY平面投影是否包含扫描点
bool intersectsXY = (basePoint.X >= bbox.Min.X && basePoint.X <= bbox.Max.X &&
basePoint.Y >= bbox.Min.Y && basePoint.Y <= bbox.Max.Y);
if (intersectsXY)
{
// 计算Z轴相交区间
double scanMinZ = basePoint.Z;
double scanMaxZ = basePoint.Z + scanHeight;
double intersectionMinZ = Math.Max(bbox.Min.Z, scanMinZ);
double intersectionMaxZ = Math.Min(bbox.Max.Z, scanMaxZ);
if (intersectionMaxZ > intersectionMinZ)
{
return new IntersectionData
{
MinZ = intersectionMinZ,
MaxZ = intersectionMaxZ,
IntersectionPoint = new Point3D(basePoint.X, basePoint.Y, (intersectionMinZ + intersectionMaxZ) / 2)
};
}
}
return null;
/* 线 -
if (!item.HasGeometry)
return null;
// 直接提取模型项的三角形几何数据,不做包围盒预筛选
var triangles = ExtractTrianglesFromModelItem(item);
// 直接提取模型项的三角形几何数据
var triangles = GeometryExtractor.ExtractTrianglesOptimized(item);
if (triangles == null || triangles.Count == 0)
{
return null;
@ -914,174 +882,14 @@ namespace NavisworksTransport.PathPlanning
}
return null;
*/
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 包围盒相交测试异常: {item.DisplayName}, {ex.Message}");
LogManager.Debug($"【垂直扫描处理器】 射线相交测试异常: {item.DisplayName}, {ex.Message}");
return null;
}
}
/// <summary>
/// 提取模型项的三角形几何数据复用自ChannelHeightDetector
/// </summary>
/// <param name="modelItem">模型项</param>
/// <returns>三角形列表</returns>
private List<Triangle3D> ExtractTrianglesFromModelItem(ModelItem modelItem)
{
LogManager.Info($"[几何提取] 开始处理: {modelItem.DisplayName}");
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var allTriangles = new List<Triangle3D>();
try
{
// 创建几何提取回调
var callback = new GeometryExtractorCallback();
// 递归处理模型项及其子项
ExtractTrianglesRecursive(modelItem, callback, allTriangles);
stopwatch.Stop();
LogManager.Info($"[几何提取] 完成处理: {modelItem.DisplayName}, 耗时: {stopwatch.ElapsedMilliseconds}ms, 三角形数量: {allTriangles.Count}");
return allTriangles;
}
catch (Exception ex)
{
stopwatch.Stop();
LogManager.Error($"[几何提取] 处理失败: {modelItem.DisplayName}, 耗时: {stopwatch.ElapsedMilliseconds}ms, 错误: {ex.Message}");
return allTriangles;
}
}
/// <summary>
/// 递归提取三角形复用自ChannelHeightDetector
/// </summary>
private void ExtractTrianglesRecursive(ModelItem modelItem, GeometryExtractorCallback callback, List<Triangle3D> allTriangles)
{
try
{
// 如果当前项有几何数据,尝试提取
if (modelItem.HasGeometry)
{
ExtractTrianglesFromGeometry(modelItem, callback, allTriangles);
}
// 递归处理子项
if (modelItem.Children != null)
{
foreach (var child in modelItem.Children)
{
ExtractTrianglesRecursive(child, callback, allTriangles);
}
}
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 递归提取三角形异常: {ex.Message}");
}
}
/// <summary>
/// 从单个有几何数据的ModelItem中提取三角形复用自ChannelHeightDetector
/// </summary>
private void ExtractTrianglesFromGeometry(ModelItem modelItem, GeometryExtractorCallback callback, List<Triangle3D> allTriangles)
{
try
{
// 基于示例代码的方法直接从COM状态获取节点并遍历fragments
var comState = ComBridge.State;
var comObject = ComBridge.ToInwOaPath(modelItem);
if (comObject == null)
{
return;
}
// 检查路径是否有效并获取节点
var nodesColl = comObject.Nodes();
if (nodesColl.Count > 0)
{
var comNode = nodesColl[nodesColl.Count] as COMApi.InwOaNode; // 获取最后一个节点并转换类型
if (comNode != null && comNode.IsGeometry)
{
var fragCount = comNode.Fragments().Count;
LogManager.Info($"[COM API] 开始片段处理: {modelItem.DisplayName}, 片段数量: {fragCount}");
for (long fragIndex = 1; fragIndex <= fragCount; fragIndex++)
{
var fragStopwatch = System.Diagnostics.Stopwatch.StartNew();
var fragsColl = comNode.Fragments();
var fragment = fragsColl[fragIndex] as COMApi.InwOaFragment3;
if (fragment != null)
{
// 获取变换矩阵
var transformMatrix = GetTransformMatrix(fragment);
callback.SetTransformMatrix(transformMatrix);
// 清空回调中的三角形
callback.ClearTriangles();
// 记录GenerateSimplePrimitives调用前的三角形数量
int trianglesBeforeGeneration = allTriangles.Count;
// 生成几何图元 - 使用正确的枚举值
fragment.GenerateSimplePrimitives(COMApi.nwEVertexProperty.eNORMAL, callback);
fragStopwatch.Stop();
int newTriangles = callback.Triangles.Count;
LogManager.Info($"[COM API] 片段 {fragIndex}/{fragCount} 处理完成, 耗时: {fragStopwatch.ElapsedMilliseconds}ms, 新增三角形: {newTriangles}");
// 将提取的三角形添加到总列表
allTriangles.AddRange(callback.Triangles);
}
}
}
}
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 从 {modelItem.DisplayName} 提取几何时出错: {ex.Message}");
}
}
/// <summary>
/// 获取Fragment的变换矩阵复用自ChannelHeightDetector
/// </summary>
private Matrix4 GetTransformMatrix(COMApi.InwOaFragment3 fragment)
{
try
{
var transform = fragment.GetLocalToWorldMatrix();
if (transform != null)
{
object matrixArrayObj = transform.Matrix;
if (matrixArrayObj is Array matrixArray && matrixArray.Length >= 16)
{
var matrix = new double[16];
matrixArray.CopyTo(matrix, 0);
// Navisworks使用列主序矩阵需要转置
return new Matrix4(
matrix[0], matrix[4], matrix[8], matrix[12],
matrix[1], matrix[5], matrix[9], matrix[13],
matrix[2], matrix[6], matrix[10], matrix[14],
matrix[3], matrix[7], matrix[11], matrix[15]
);
}
}
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 获取变换矩阵失败: {ex.Message}");
}
return Matrix4.Identity;
}
/// <summary>
/// 执行垂直射线与三角形相交检测
/// </summary>
@ -1101,7 +909,7 @@ namespace NavisworksTransport.PathPlanning
foreach (var triangle in triangles)
{
if (RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
if (GeometryExtractor.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
{
// 检查相交点是否在扫描范围内
if (intersectionZ >= basePoint.Z && intersectionZ <= basePoint.Z + scanHeight)
@ -1120,98 +928,6 @@ namespace NavisworksTransport.PathPlanning
}
}
/// <summary>
/// 射线-三角形相交算法Möller-Trumbore算法复用自ChannelHeightDetector
/// </summary>
/// <param name="rayOrigin">射线起点</param>
/// <param name="rayDirection">射线方向</param>
/// <param name="triangle">三角形</param>
/// <param name="intersectionZ">输出相交点Z坐标</param>
/// <returns>是否相交</returns>
private bool RayTriangleIntersect(Point3D rayOrigin, Point3D rayDirection, Triangle3D triangle, out double intersectionZ)
{
intersectionZ = 0.0;
const double EPSILON = 0.0000001;
try
{
// 计算三角形的两条边
var edge1 = SubtractPoints(triangle.V2, triangle.V1);
var edge2 = SubtractPoints(triangle.V3, triangle.V1);
// 计算射线方向与edge2的叉积
var h = CrossProduct(rayDirection, edge2);
var a = DotProduct(edge1, h);
// 如果a接近0射线与三角形平行
if (a > -EPSILON && a < EPSILON)
{
return false;
}
var f = 1.0 / a;
var s = SubtractPoints(rayOrigin, triangle.V1);
var u = f * DotProduct(s, h);
if (u < 0.0 || u > 1.0)
{
return false;
}
var q = CrossProduct(s, edge1);
var v = f * DotProduct(rayDirection, q);
if (v < 0.0 || u + v > 1.0)
{
return false;
}
// 计算t值射线参数
var t = f * DotProduct(edge2, q);
if (t > EPSILON) // 射线相交
{
// 计算交点
intersectionZ = rayOrigin.Z + t * rayDirection.Z;
return true;
}
return false; // 线段相交但射线不相交
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 射线相交计算出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 向量减法
/// </summary>
private Point3D SubtractPoints(Point3D p1, Point3D p2)
{
return new Point3D(p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.Z);
}
/// <summary>
/// 向量叉积
/// </summary>
private Point3D CrossProduct(Point3D v1, Point3D v2)
{
return new Point3D(
v1.Y * v2.Z - v1.Z * v2.Y,
v1.Z * v2.X - v1.X * v2.Z,
v1.X * v2.Y - v1.Y * v2.X
);
}
/// <summary>
/// 向量点积
/// </summary>
private double DotProduct(Point3D v1, Point3D v2)
{
return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;
}
/// <summary>
/// 检查模型项是否类似地面/楼板
@ -1334,108 +1050,6 @@ namespace NavisworksTransport.PathPlanning
public bool IsPassable { get; set; }
}
/// <summary>
/// 三角形数据结构复用自ChannelHeightDetector
/// </summary>
public class Triangle3D
{
public Point3D V1 { get; set; }
public Point3D V2 { get; set; }
public Point3D V3 { get; set; }
public Triangle3D(Point3D v1, Point3D v2, Point3D v3)
{
V1 = v1;
V2 = v2;
V3 = v3;
}
}
/// <summary>
/// 几何提取回调类复用自ChannelHeightDetector
/// </summary>
public class GeometryExtractorCallback : COMApi.InwSimplePrimitivesCB
{
private readonly List<Triangle3D> _triangles;
private Matrix4 _transformMatrix;
public List<Triangle3D> Triangles => _triangles;
public GeometryExtractorCallback()
{
_triangles = new List<Triangle3D>();
_transformMatrix = Matrix4.Identity;
}
public void SetTransformMatrix(Matrix4 matrix)
{
_transformMatrix = matrix;
}
public void Line(COMApi.InwSimpleVertex v1, COMApi.InwSimpleVertex v2)
{
// 我们只关心三角形,忽略线段
}
public void Point(COMApi.InwSimpleVertex v1)
{
// 我们只关心三角形,忽略点
}
public void SnapPoint(COMApi.InwSimpleVertex v1)
{
// 我们只关心三角形,忽略捕捉点
}
public void Triangle(COMApi.InwSimpleVertex v1, COMApi.InwSimpleVertex v2, COMApi.InwSimpleVertex v3)
{
try
{
// 提取顶点坐标
var vertex1 = ConvertVertex(v1);
var vertex2 = ConvertVertex(v2);
var vertex3 = ConvertVertex(v3);
// 应用变换矩阵到顶点坐标
vertex1 = _transformMatrix.Transform(vertex1);
vertex2 = _transformMatrix.Transform(vertex2);
vertex3 = _transformMatrix.Transform(vertex3);
// 创建三角形并添加到列表
var triangle = new Triangle3D(vertex1, vertex2, vertex3);
_triangles.Add(triangle);
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 处理三角形时出错: {ex.Message}");
}
}
private Point3D ConvertVertex(COMApi.InwSimpleVertex vertex)
{
// 从COM API顶点获取坐标
object coordObj = vertex.coord;
if (coordObj is Array coordinates && coordinates.Length >= 3)
{
var coords = new double[3];
coordinates.CopyTo(coords, 0);
return new Point3D(coords[0], coords[1], coords[2]);
}
return new Point3D(0, 0, 0);
}
public void ClearTriangles()
{
_triangles.Clear();
}
public List<Triangle3D> GetTriangles()
{
return new List<Triangle3D>(_triangles);
}
}
#endregion
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Utils
@ -97,19 +99,6 @@ namespace NavisworksTransport.Utils
box1.Min.Z <= box2.Max.Z && box1.Max.Z >= box2.Min.Z;
}
/// <summary>
/// 计算两个3D点之间的距离
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</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>
/// 获取包围盒的中心点
@ -173,5 +162,42 @@ namespace NavisworksTransport.Utils
point.Y >= boundingBox.Min.Y - tolerance && point.Y <= boundingBox.Max.Y + tolerance &&
point.Z >= boundingBox.Min.Z - tolerance && point.Z <= boundingBox.Max.Z + tolerance;
}
/// <summary>
/// 计算模型项列表的总边界
/// </summary>
/// <param name="modelItems">模型项列表</param>
/// <returns>总边界框</returns>
public static BoundingBox3D CalculateTotalBounds(IEnumerable<ModelItem> modelItems)
{
var itemList = modelItems?.ToList();
if (itemList == null || !itemList.Any())
{
return new BoundingBox3D(new Point3D(0, 0, 0), new Point3D(0, 0, 0));
}
var firstBounds = itemList.First().BoundingBox();
double minX = firstBounds.Min.X;
double minY = firstBounds.Min.Y;
double minZ = firstBounds.Min.Z;
double maxX = firstBounds.Max.X;
double maxY = firstBounds.Max.Y;
double maxZ = firstBounds.Max.Z;
foreach (var item in itemList.Skip(1))
{
var bounds = item.BoundingBox();
minX = Math.Min(minX, bounds.Min.X);
minY = Math.Min(minY, bounds.Min.Y);
minZ = Math.Min(minZ, bounds.Min.Z);
maxX = Math.Max(maxX, bounds.Max.X);
maxY = Math.Max(maxY, bounds.Max.Y);
maxZ = Math.Max(maxZ, bounds.Max.Z);
}
return new BoundingBox3D(
new Point3D(minX, minY, minZ),
new Point3D(maxX, maxY, maxZ));
}
}
}

View File

@ -127,7 +127,7 @@ namespace NavisworksTransport
/// </summary>
/// <param name="modelItem">模型项</param>
/// <returns>三角形集合</returns>
private static List<Triangle3D> ExtractTrianglesOptimized(ModelItem modelItem)
public static List<Triangle3D> ExtractTrianglesOptimized(ModelItem modelItem)
{
var triangles = new List<Triangle3D>();
@ -179,7 +179,7 @@ namespace NavisworksTransport
/// </summary>
/// <param name="selection">COM 选择</param>
/// <returns>唯一片段信息集合</returns>
private static List<FragmentInfo> GetUniqueFragments(ComApi.InwOpSelection selection)
public static List<FragmentInfo> GetUniqueFragments(ComApi.InwOpSelection selection)
{
var fragmentMap = new Dictionary<string, FragmentInfo>();
@ -803,6 +803,142 @@ namespace NavisworksTransport
return 0;
}
}
/// <summary>
/// 射线与三角形相交检测Möller-Trumbore算法
/// </summary>
/// <param name="rayOrigin">射线起点</param>
/// <param name="rayDirection">射线方向</param>
/// <param name="triangle">三角形</param>
/// <param name="intersectionZ">相交点的Z坐标</param>
/// <returns>是否相交</returns>
public static bool RayTriangleIntersect(Point3D rayOrigin, Point3D rayDirection, Triangle3D triangle, out double intersectionZ)
{
intersectionZ = 0.0;
const double EPSILON = 0.0000001;
try
{
// 计算三角形的两条边
var edge1 = SubtractPoints(triangle.Point2, triangle.Point1);
var edge2 = SubtractPoints(triangle.Point3, triangle.Point1);
// 计算射线方向与edge2的叉积
var h = CrossProduct(rayDirection, edge2);
var a = DotProduct(edge1, h);
// 如果a接近0射线与三角形平行
if (a > -EPSILON && a < EPSILON)
{
return false;
}
var f = 1.0 / a;
var s = SubtractPoints(rayOrigin, triangle.Point1);
var u = f * DotProduct(s, h);
if (u < 0.0 || u > 1.0)
{
return false;
}
var q = CrossProduct(s, edge1);
var v = f * DotProduct(rayDirection, q);
if (v < 0.0 || u + v > 1.0)
{
return false;
}
// 计算t值射线参数
var t = f * DotProduct(edge2, q);
if (t > EPSILON) // 射线相交
{
// 计算交点
intersectionZ = rayOrigin.Z + t * rayDirection.Z;
return true;
}
return false; // 线段相交但射线不相交
}
catch (Exception ex)
{
LogManager.WriteLog($"射线-三角形相交计算出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 点减法运算
/// </summary>
public static Point3D SubtractPoints(Point3D a, Point3D b)
{
return new Point3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
/// <summary>
/// 向量叉积运算
/// </summary>
public static Point3D CrossProduct(Point3D a, Point3D b)
{
return new Point3D(
a.Y * b.Z - a.Z * b.Y,
a.Z * b.X - a.X * b.Z,
a.X * b.Y - a.Y * b.X
);
}
/// <summary>
/// 向量点积运算
/// </summary>
public static double DotProduct(Point3D a, Point3D b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
}
/// <summary>
/// 检查2D点是否在三角形内部使用重心坐标法
/// </summary>
/// <param name="point">测试点</param>
/// <param name="triangle">三角形</param>
/// <returns>是否在内部</returns>
public static bool IsPointInTriangle2D(Point3D point, Triangle3D triangle)
{
// 使用重心坐标法判断点是否在三角形内
var v0x = triangle.Point3.X - triangle.Point1.X;
var v0y = triangle.Point3.Y - triangle.Point1.Y;
var v1x = triangle.Point2.X - triangle.Point1.X;
var v1y = triangle.Point2.Y - triangle.Point1.Y;
var v2x = point.X - triangle.Point1.X;
var v2y = point.Y - triangle.Point1.Y;
var dot00 = v0x * v0x + v0y * v0y;
var dot01 = v0x * v1x + v0y * v1y;
var dot02 = v0x * v2x + v0y * v2y;
var dot11 = v1x * v1x + v1y * v1y;
var dot12 = v1x * v2x + v1y * v2y;
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return (u >= 0) && (v >= 0) && (u + v <= 1);
}
/// <summary>
/// 计算两个3D点之间的距离
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</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>

View File

@ -15,19 +15,27 @@ namespace NavisworksTransport.Utils
#region
/// <summary>
/// 计算两个3D点之间的距离
/// 检查一个ModelItem是否是另一个ModelItem的子节点
/// </summary>
/// <param name="point1">点1</param>
/// <param name="point2">点2</param>
/// <returns>两点间的距离</returns>
public static double CalculatePointDistance(Point3D point1, Point3D point2)
/// <param name="child">子节点</param>
/// <param name="parent">父节点</param>
/// <returns>如果child是parent的子节点返回true</returns>
public static bool IsChildOf(ModelItem child, ModelItem parent)
{
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);
if (child == null || parent == null)
return false;
var currentParent = child.Parent;
while (currentParent != null)
{
if (currentParent.Equals(parent))
return true;
currentParent = currentParent.Parent;
}
return false;
}
/// <summary>
/// 检查ModelItem是否仍然有效
/// </summary>