feat: add single-point+normal overload to AssemblyEndFaceAnalyzer

- New Analyze(triangles, hitPoint, normal) bypasses 3-point seed plane construction
- New Analyze(modelItem, hitPoint, normal) for Navisworks types
- Same triangle extraction and centroid computation as 3-point version
- Enables single-click end face detection via FaceInferToolPlugin
This commit is contained in:
tian 2026-06-09 01:51:05 +08:00
parent 1e45546aed
commit e7b5206198

View File

@ -128,6 +128,108 @@ namespace NavisworksTransport.Utils.GeometryAnalysis
$"端面识别完成: 三角形={candidateTriangles.Count}, 顶点={candidatePoints.Count}, 偏差={maxPlaneDeviation:F6}");
}
/// <summary>
/// 单点+法向量分析端面。用于取面工具得到法向量后替代手工三点拾取。
/// </summary>
/// <param name="triangles">模型三角面片</param>
/// <param name="hitPoint">命中点</param>
/// <param name="normal">面法向量(需归一化)</param>
public static EndFaceAnalysisResult Analyze(
IEnumerable<AnalysisTriangle3> triangles,
Vector3 hitPoint,
Vector3 normal)
{
if (triangles == null)
throw new ArgumentNullException(nameof(triangles));
if (normal.LengthSquared() < 1e-12f)
return EndFaceAnalysisResult.Failure("法向量长度过小。");
var triangleList = triangles.ToList();
if (triangleList.Count == 0)
return EndFaceAnalysisResult.Failure("没有可用的三角几何数据。");
var seedPlane = new PlaneDefinition(Vector3.Normalize(normal), Vector3.Dot(normal, hitPoint));
var candidateTriangles = new List<AnalysisTriangle3>();
var candidatePoints = new HashSet<Vector3>(new Vector3EqualityComparer());
double maxPlaneDeviation = 0.0;
foreach (var triangle in triangleList)
{
if (!TryGetTriangleNormal(triangle, out Vector3 triangleNormal))
continue;
if (Math.Abs(Vector3.Dot(triangleNormal, seedPlane.Normal)) < NormalAlignmentCosineThreshold)
continue;
double d1 = Math.Abs(GetSignedDistanceToPlane(seedPlane, triangle.Point1));
double d2 = Math.Abs(GetSignedDistanceToPlane(seedPlane, triangle.Point2));
double d3 = Math.Abs(GetSignedDistanceToPlane(seedPlane, triangle.Point3));
double triangleDeviation = Math.Max(d1, Math.Max(d2, d3));
if (triangleDeviation > PlaneDistanceTolerance)
continue;
candidateTriangles.Add(triangle);
candidatePoints.Add(ProjectPointToPlane(seedPlane, triangle.Point1));
candidatePoints.Add(ProjectPointToPlane(seedPlane, triangle.Point2));
candidatePoints.Add(ProjectPointToPlane(seedPlane, triangle.Point3));
maxPlaneDeviation = Math.Max(maxPlaneDeviation, triangleDeviation);
}
if (candidateTriangles.Count == 0 || candidatePoints.Count < 3)
return EndFaceAnalysisResult.Failure("没有识别到足够的端面共面三角形。");
PlaneDefinition refinedPlane = RefinePlane(seedPlane, candidatePoints);
PlaneBasis planeBasis = CreatePlaneBasis(refinedPlane);
double minU = double.MaxValue;
double maxU = double.MinValue;
double minV = double.MaxValue;
double maxV = double.MinValue;
foreach (Vector3 candidatePoint in candidatePoints)
{
Vector2 projected = ProjectToPlane2D(candidatePoint, planeBasis);
minU = Math.Min(minU, projected.X);
maxU = Math.Max(maxU, projected.X);
minV = Math.Min(minV, projected.Y);
maxV = Math.Max(maxV, projected.Y);
}
double extentU = maxU - minU;
double extentV = maxV - minV;
if (extentU < MinimumProjectedExtent || extentV < MinimumProjectedExtent)
{
return EndFaceAnalysisResult.Failure("识别到的端面分布过窄,无法稳定求中心。");
}
Vector3 center = ProjectFromPlane2D((minU + maxU) / 2.0, (minV + maxV) / 2.0, planeBasis);
return EndFaceAnalysisResult.Success(
center,
refinedPlane.Normal,
candidateTriangles.Count,
candidatePoints.Count,
maxPlaneDeviation,
$"端面识别完成(单点法向): 三角形={candidateTriangles.Count}, 顶点={candidatePoints.Count}, 偏差={maxPlaneDeviation:F6}");
}
/// <summary>
/// 单点+法向量分析端面的 Navisworks 兼容重载。
/// </summary>
public static EndFaceAnalysisResult Analyze(ModelItem modelItem, Point3D hitPoint, Vector3D normal)
{
if (modelItem == null)
throw new ArgumentNullException(nameof(modelItem));
var triangles = GeometryHelper.ExtractTriangles(new[] { modelItem })
.Select(ConvertTriangle)
.ToList();
return Analyze(triangles, ToVector3(hitPoint), new Vector3((float)normal.X, (float)normal.Y, (float)normal.Z));
}
public static Point3D ToPoint3D(Vector3 point)
{
return new Point3D(point.X, point.Y, point.Z);