Fix rail assembly normals and tighten rail camera
This commit is contained in:
parent
2e0b26e032
commit
e71ea81cdf
@ -63,6 +63,28 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem
|
|||||||
Assert.IsFalse(result.IsReliable);
|
Assert.IsFalse(result.IsReliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void OrientNormalTowardTarget_ShouldFlip_WhenNormalOpposesTargetDirection()
|
||||||
|
{
|
||||||
|
Vector3 orientedNormal = AssemblyEndFaceAnalyzer.OrientNormalTowardTarget(
|
||||||
|
new Vector3(-1f, 0f, 0f),
|
||||||
|
Vector3.Zero,
|
||||||
|
new Vector3(10f, 0f, 0f));
|
||||||
|
|
||||||
|
AssertPoint(orientedNormal, 1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void OrientNormalTowardTarget_ShouldKeepDirection_WhenNormalAlreadyFacesTarget()
|
||||||
|
{
|
||||||
|
Vector3 orientedNormal = AssemblyEndFaceAnalyzer.OrientNormalTowardTarget(
|
||||||
|
new Vector3(0f, 1f, 0f),
|
||||||
|
new Vector3(1f, 2f, 3f),
|
||||||
|
new Vector3(1f, 5f, 3f));
|
||||||
|
|
||||||
|
AssertPoint(orientedNormal, 0.0, 1.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<AnalysisTriangle3> CreateRectangleFace(double minX, double maxX, double minY, double maxY, double z)
|
private static IEnumerable<AnalysisTriangle3> CreateRectangleFace(double minX, double maxX, double minY, double maxY, double z)
|
||||||
{
|
{
|
||||||
yield return new AnalysisTriangle3(
|
yield return new AnalysisTriangle3(
|
||||||
|
|||||||
36
UnitTests/CoordinateSystem/RailPathPoseHelperTests.cs
Normal file
36
UnitTests/CoordinateSystem/RailPathPoseHelperTests.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using NavisworksTransport.Utils;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class RailPathPoseHelperTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void NormalizePreferredNormalToHostUpHemisphere_ShouldFlip_WhenPreferredNormalOpposesHostUp()
|
||||||
|
{
|
||||||
|
Vector3 hostPreferredNormal = new Vector3(-0.1222f, -0.9676f, 0.2211f);
|
||||||
|
Vector3 normalized = RailPathPoseHelper.NormalizePreferredNormalToHostUpHemisphere(
|
||||||
|
hostPreferredNormal,
|
||||||
|
Vector3.UnitY);
|
||||||
|
|
||||||
|
Assert.IsTrue(Vector3.Dot(normalized, Vector3.UnitY) > 0f);
|
||||||
|
Assert.AreEqual(0.1222 / 1.0008249, normalized.X, 1e-4);
|
||||||
|
Assert.AreEqual(0.9676 / 1.0008249, normalized.Y, 1e-4);
|
||||||
|
Assert.AreEqual(-0.2211 / 1.0008249, normalized.Z, 1e-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void NormalizePreferredNormalToHostUpHemisphere_ShouldKeepDirection_WhenPreferredNormalAlreadyMatchesHostUp()
|
||||||
|
{
|
||||||
|
Vector3 hostPreferredNormal = new Vector3(0.139f, 0.954f, 0.266f);
|
||||||
|
Vector3 normalized = RailPathPoseHelper.NormalizePreferredNormalToHostUpHemisphere(
|
||||||
|
hostPreferredNormal,
|
||||||
|
Vector3.UnitY);
|
||||||
|
|
||||||
|
Assert.IsTrue(Vector3.Dot(normalized, Vector3.UnitY) > 0f);
|
||||||
|
Assert.IsTrue(Vector3.Dot(Vector3.Normalize(hostPreferredNormal), normalized) > 0.9999f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,8 +21,8 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem
|
|||||||
Assert.AreEqual(8.0, hoisting.MinDistanceMeters, 1e-9);
|
Assert.AreEqual(8.0, hoisting.MinDistanceMeters, 1e-9);
|
||||||
Assert.AreEqual(22.0, hoisting.ElevationDegrees, 1e-9);
|
Assert.AreEqual(22.0, hoisting.ElevationDegrees, 1e-9);
|
||||||
|
|
||||||
Assert.AreEqual(0.9, rail.DistanceScale, 1e-9);
|
Assert.AreEqual(0.75, rail.DistanceScale, 1e-9);
|
||||||
Assert.AreEqual(6.0, rail.MinDistanceMeters, 1e-9);
|
Assert.AreEqual(4.5, rail.MinDistanceMeters, 1e-9);
|
||||||
Assert.AreEqual(12.0, rail.ElevationDegrees, 1e-9);
|
Assert.AreEqual(12.0, rail.ElevationDegrees, 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1775,10 +1775,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
|||||||
throw new InvalidOperationException("更新 Rail 路径起点失败。");
|
throw new InvalidOperationException("更新 Rail 路径起点失败。");
|
||||||
}
|
}
|
||||||
|
|
||||||
route.RailPreferredNormal = new Point3D(
|
route.RailPreferredNormal = CreatePersistedAssemblyPreferredNormal();
|
||||||
_assemblyInstallationPlaneNormal.X,
|
|
||||||
_assemblyInstallationPlaneNormal.Y,
|
|
||||||
_assemblyInstallationPlaneNormal.Z);
|
|
||||||
route.RailNormalOffset = UnitsConverter.ConvertFromMeters(AssemblyAnchorVerticalOffsetInMeters);
|
route.RailNormalOffset = UnitsConverter.ConvertFromMeters(AssemblyAnchorVerticalOffsetInMeters);
|
||||||
route.LastModified = DateTime.Now;
|
route.LastModified = DateTime.Now;
|
||||||
_assemblyInstallationReferenceRouteId = route.Id;
|
_assemblyInstallationReferenceRouteId = route.Id;
|
||||||
@ -2435,10 +2432,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
|||||||
|
|
||||||
if (_hasAssemblyInstallationReference)
|
if (_hasAssemblyInstallationReference)
|
||||||
{
|
{
|
||||||
route.RailPreferredNormal = new Point3D(
|
route.RailPreferredNormal = CreatePersistedAssemblyPreferredNormal();
|
||||||
_assemblyInstallationPlaneNormal.X,
|
|
||||||
_assemblyInstallationPlaneNormal.Y,
|
|
||||||
_assemblyInstallationPlaneNormal.Z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
route.AddPoint(new PathPoint(startPoint, "起点", PathPointType.StartPoint));
|
route.AddPoint(new PathPoint(startPoint, "起点", PathPointType.StartPoint));
|
||||||
@ -2678,6 +2672,19 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Point3D CreatePersistedAssemblyPreferredNormal()
|
||||||
|
{
|
||||||
|
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||||
|
Vector3 normalizedPreferredNormal = RailPathPoseHelper.NormalizePreferredNormalToHostUpHemisphere(
|
||||||
|
_assemblyInstallationPlaneNormal,
|
||||||
|
adapter.HostUpVector3);
|
||||||
|
|
||||||
|
return new Point3D(
|
||||||
|
normalizedPreferredNormal.X,
|
||||||
|
normalizedPreferredNormal.Y,
|
||||||
|
normalizedPreferredNormal.Z);
|
||||||
|
}
|
||||||
|
|
||||||
private Vector3 GetCurrentAssemblyOpticalAxisDirection()
|
private Vector3 GetCurrentAssemblyOpticalAxisDirection()
|
||||||
{
|
{
|
||||||
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
|
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
|
||||||
@ -3439,18 +3446,26 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
Point3D centerPoint = AssemblyEndFaceAnalyzer.ToPoint3D(result.Center);
|
Point3D centerPoint = AssemblyEndFaceAnalyzer.ToPoint3D(result.Center);
|
||||||
|
Point3D sphereCenterPoint = new Point3D(
|
||||||
|
AssemblySphereCenterX,
|
||||||
|
AssemblySphereCenterY,
|
||||||
|
AssemblySphereCenterZ);
|
||||||
|
Vector3 orientedNormal = AssemblyEndFaceAnalyzer.OrientNormalTowardTarget(
|
||||||
|
result.Normal,
|
||||||
|
new Vector3((float)centerPoint.X, (float)centerPoint.Y, (float)centerPoint.Z),
|
||||||
|
new Vector3((float)sphereCenterPoint.X, (float)sphereCenterPoint.Y, (float)sphereCenterPoint.Z));
|
||||||
_hasAssemblyEndFaceAnalysis = true;
|
_hasAssemblyEndFaceAnalysis = true;
|
||||||
_assemblyEndFaceCenterPoint = centerPoint;
|
_assemblyEndFaceCenterPoint = centerPoint;
|
||||||
_assemblyEndFaceNormal = result.Normal;
|
_assemblyEndFaceNormal = orientedNormal;
|
||||||
RenderAssemblyEndFaceSeedPoints();
|
RenderAssemblyEndFaceSeedPoints();
|
||||||
RenderAssemblyEndFaceCenter(centerPoint);
|
RenderAssemblyEndFaceCenter(centerPoint);
|
||||||
RenderAssemblyEndFaceNormal(centerPoint, result.Normal);
|
RenderAssemblyEndFaceNormal(centerPoint, orientedNormal);
|
||||||
RefreshAssemblyTerminalObjectInfo();
|
RefreshAssemblyTerminalObjectInfo();
|
||||||
RefreshAssemblyReferenceRodIfNeeded();
|
RefreshAssemblyReferenceRodIfNeeded();
|
||||||
UpdateMainStatus($"端面分析完成:中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}),候选三角形={result.CandidateTriangleCount}");
|
UpdateMainStatus($"端面分析完成:中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}),候选三角形={result.CandidateTriangleCount}");
|
||||||
LogManager.Info(
|
LogManager.Info(
|
||||||
$"[直线装配] 端面分析完成: 中心=({centerPoint.X:F3}, {centerPoint.Y:F3}, {centerPoint.Z:F3}), " +
|
$"[直线装配] 端面分析完成: 中心=({centerPoint.X:F3}, {centerPoint.Y:F3}, {centerPoint.Z:F3}), " +
|
||||||
$"法向=({result.Normal.X:F4}, {result.Normal.Y:F4}, {result.Normal.Z:F4}), " +
|
$"法向=({orientedNormal.X:F4}, {orientedNormal.Y:F4}, {orientedNormal.Z:F4}), " +
|
||||||
$"三角形={result.CandidateTriangleCount}, 顶点={result.CandidateVertexCount}, 偏差={result.MaxPlaneDeviation:F6}");
|
$"三角形={result.CandidateTriangleCount}, 顶点={result.CandidateVertexCount}, 偏差={result.MaxPlaneDeviation:F6}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -133,6 +133,29 @@ namespace NavisworksTransport.Utils.GeometryAnalysis
|
|||||||
return new Point3D(point.X, point.Y, point.Z);
|
return new Point3D(point.X, point.Y, point.Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector3 OrientNormalTowardTarget(Vector3 normal, Vector3 originPoint, Vector3 targetPoint)
|
||||||
|
{
|
||||||
|
if (normal.LengthSquared() < 1e-12f)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("端面法向长度过小,无法定向。", nameof(normal));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 normalizedNormal = Vector3.Normalize(normal);
|
||||||
|
Vector3 targetDirection = targetPoint - originPoint;
|
||||||
|
if (targetDirection.LengthSquared() < 1e-12f)
|
||||||
|
{
|
||||||
|
return normalizedNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 normalizedTargetDirection = Vector3.Normalize(targetDirection);
|
||||||
|
if (Vector3.Dot(normalizedNormal, normalizedTargetDirection) < 0f)
|
||||||
|
{
|
||||||
|
normalizedNormal = -normalizedNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedNormal;
|
||||||
|
}
|
||||||
|
|
||||||
private static AnalysisTriangle3 ConvertTriangle(Triangle3D triangle)
|
private static AnalysisTriangle3 ConvertTriangle(Triangle3D triangle)
|
||||||
{
|
{
|
||||||
return new AnalysisTriangle3(ToVector3(triangle.Point1), ToVector3(triangle.Point2), ToVector3(triangle.Point3));
|
return new AnalysisTriangle3(ToVector3(triangle.Point1), ToVector3(triangle.Point2), ToVector3(triangle.Point3));
|
||||||
|
|||||||
@ -15,6 +15,31 @@ namespace NavisworksTransport.Utils
|
|||||||
private const double TangentEpsilon = 1e-9;
|
private const double TangentEpsilon = 1e-9;
|
||||||
private static bool _rotationConstructorConventionLogged;
|
private static bool _rotationConstructorConventionLogged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将宿主坐标系中的 Rail 首选法向统一到宿主 up 半球,避免新建装配路径把“轨上侧”写反。
|
||||||
|
/// </summary>
|
||||||
|
public static Vector3 NormalizePreferredNormalToHostUpHemisphere(Vector3 hostPreferredNormal, Vector3 hostUpVector)
|
||||||
|
{
|
||||||
|
if (hostPreferredNormal.LengthSquared() < 1e-9f)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Rail 首选法向长度过小,无法归一化。", nameof(hostPreferredNormal));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostUpVector.LengthSquared() < 1e-9f)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("宿主 up 向量长度过小,无法归一化。", nameof(hostUpVector));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 normalizedPreferredNormal = Vector3.Normalize(hostPreferredNormal);
|
||||||
|
Vector3 normalizedHostUp = Vector3.Normalize(hostUpVector);
|
||||||
|
if (Vector3.Dot(normalizedPreferredNormal, normalizedHostUp) < 0f)
|
||||||
|
{
|
||||||
|
normalizedPreferredNormal = -normalizedPreferredNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedPreferredNormal;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 计算构件底面中心相对于 Rail 参考点的 Z 偏移(模型单位)。
|
/// 计算构件底面中心相对于 Rail 参考点的 Z 偏移(模型单位)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -301,7 +301,7 @@ namespace NavisworksTransport.Utils
|
|||||||
case ViewpointStrategy.PathHoistingSelection:
|
case ViewpointStrategy.PathHoistingSelection:
|
||||||
return new PathViewpointProfile(distanceScale: 1.2, minDistanceMeters: 8.0, elevationDegrees: 22.0);
|
return new PathViewpointProfile(distanceScale: 1.2, minDistanceMeters: 8.0, elevationDegrees: 22.0);
|
||||||
case ViewpointStrategy.PathRailSelection:
|
case ViewpointStrategy.PathRailSelection:
|
||||||
return new PathViewpointProfile(distanceScale: 0.9, minDistanceMeters: 6.0, elevationDegrees: 12.0);
|
return new PathViewpointProfile(distanceScale: 0.75, minDistanceMeters: 4.5, elevationDegrees: 12.0);
|
||||||
case ViewpointStrategy.PathGroundSelection:
|
case ViewpointStrategy.PathGroundSelection:
|
||||||
default:
|
default:
|
||||||
return new PathViewpointProfile(distanceScale: 1.0, minDistanceMeters: 12.0, elevationDegrees: 90.0);
|
return new PathViewpointProfile(distanceScale: 1.0, minDistanceMeters: 12.0, elevationDegrees: 90.0);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user