重构了一些重复的几何计算
This commit is contained in:
parent
101c929f15
commit
2955bfd38b
@ -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;
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@ -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}] 几何类型设置完成");
|
||||
}
|
||||
|
||||
@ -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>
|
||||
/// 创建空的通道覆盖
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user