diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs index 9332368..5459efa 100644 --- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs +++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs @@ -12,6 +12,8 @@ using System.Windows.Input; using System.Windows.Threading; using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Api.Clash; +using Autodesk.Navisworks.Api.Plugins; +using NavisApplication = Autodesk.Navisworks.Api.Application; using NavisworksTransport.Core; using NavisworksTransport.Core.Models; using NavisworksTransport.Core.Config; @@ -2187,6 +2189,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (dialog.ShowDialog() == true) { var placementRequest = dialog.AdjustmentRequest; + + if (placementRequest.MateToGroundRequested) + { + BeginFaceToGroundMate(); + return; + } + _objectGroundLiftHeightInMeters = placementRequest.VerticalLiftInMeters; bool isAutoAdjusted = dialog.AutoAdjustAabbSizes.HasValue; if (isAutoAdjusted) @@ -2234,6 +2243,112 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } + private void BeginFaceToGroundMate() + { + try + { + _pathPlanningManager?.DisableMouseHandling(); + + var assemblyPath = FaceInferToolPlugin.AssemblyPath; + NavisApplication.Plugins.AddPluginAssembly(assemblyPath); + var record = (Autodesk.Navisworks.Api.Plugins.ToolPluginRecord) + NavisApplication.Plugins.FindPlugin("FaceInferTool.NavisworksTransport"); + var loadedPlugin = record.LoadPlugin(); + NavisApplication.MainDocument.Tool.Value = Autodesk.Navisworks.Api.Tool.None; + NavisApplication.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin); + + FaceInferToolPlugin.FaceInferred += OnFaceToGroundInferred; + UpdateMainStatus("请点击物体上需要贴地的那一面。"); + } + catch (Exception ex) + { + LogManager.Error($"[贴地面] 激活失败: {ex.Message}", ex); + UpdateMainStatus($"激活取面工具失败: {ex.Message}"); + } + } + + private void OnFaceToGroundInferred(object sender, FaceInferResult result) + { + FaceInferToolPlugin.FaceInferred -= OnFaceToGroundInferred; + try { NavisApplication.MainDocument.Tool.Value = Autodesk.Navisworks.Api.Tool.Select; } catch { } + _pathPlanningManager?.EnableMouseHandling(); + + if (!result.IsValid) + { + var reason = result.FailureReason ?? "取面失败。"; + DialogHelper.ShowStatusAndDialog(reason, "贴地面", reason, UpdateMainStatus, System.Windows.MessageBoxImage.Warning); + return; + } + + var doc = NavisApplication.ActiveDocument; + var worldUp = doc.CurrentViewpoint.Value.WorldUpVector; + var hostUp = new System.Numerics.Vector3((float)worldUp.X, (float)worldUp.Y, (float)worldUp.Z); + hostUp = System.Numerics.Vector3.Normalize(hostUp); + var groundDir = -hostUp; + var faceNormal = new System.Numerics.Vector3((float)result.Normal.X, (float)result.Normal.Y, (float)result.Normal.Z); + faceNormal = System.Numerics.Vector3.Normalize(faceNormal); + + float dot = System.Numerics.Vector3.Dot(faceNormal, groundDir); + System.Numerics.Quaternion rotation; + if (dot > 0.9999f) + { + DialogHelper.ShowStatusAndDialog("选中面已朝向地面,无需旋转。", "贴地面", "选中面已朝向地面,无需旋转。", UpdateMainStatus, System.Windows.MessageBoxImage.Information); + return; + } + if (dot < -0.9999f) + { + var perp = Math.Abs(hostUp.X) < 0.9f + ? System.Numerics.Vector3.Normalize(System.Numerics.Vector3.Cross(hostUp, new System.Numerics.Vector3(1, 0, 0))) + : System.Numerics.Vector3.Normalize(System.Numerics.Vector3.Cross(hostUp, new System.Numerics.Vector3(0, 0, 1))); + rotation = System.Numerics.Quaternion.CreateFromAxisAngle(perp, (float)Math.PI); + } + else + { + var axis = System.Numerics.Vector3.Normalize(System.Numerics.Vector3.Cross(faceNormal, groundDir)); + float angle = (float)Math.Acos(dot); + rotation = System.Numerics.Quaternion.CreateFromAxisAngle(axis, angle); + } + + // 分解为 ZYX Euler 角度 + double xDeg, yDeg, zDeg; + double sinY = -2.0 * (rotation.X * rotation.Z - rotation.W * rotation.Y); + sinY = Math.Max(-1.0, Math.Min(1.0, sinY)); + yDeg = Math.Asin(sinY) * (180.0 / Math.PI); + if (Math.Abs(Math.Abs(sinY) - 1.0) < 1e-9) + { + zDeg = 0.0; + xDeg = Math.Atan2(2.0 * (rotation.X * rotation.Y + rotation.W * rotation.Z), + rotation.W * rotation.W - rotation.Y * rotation.Y - rotation.Z * rotation.Z + rotation.X * rotation.X) * (180.0 / Math.PI); + } + else + { + xDeg = Math.Atan2(2.0 * (rotation.Y * rotation.Z + rotation.W * rotation.X), + rotation.W * rotation.W - rotation.X * rotation.X - rotation.Y * rotation.Y + rotation.Z * rotation.Z) * (180.0 / Math.PI); + zDeg = Math.Atan2(2.0 * (rotation.X * rotation.Y + rotation.W * rotation.Z), + rotation.W * rotation.W - rotation.X * rotation.X + rotation.Y * rotation.Y - rotation.Z * rotation.Z) * (180.0 / Math.PI); + } + + var correction = new LocalEulerRotationCorrection( + NormalizeDegrees(xDeg), NormalizeDegrees(yDeg), NormalizeDegrees(zDeg)); + + var placementRequest = ObjectStartPlacementRequest.CreateRotationCorrection( + correction, _objectGroundLiftHeightInMeters); + _pathAnimationManager?.ApplyObjectStartPlacementRequest(placementRequest); + ObjectRotationCorrection = correction; + UpdatePassageSpaceVisualization(); + + LogManager.Info($"[贴地面] faceNormal=({faceNormal.X:F3},{faceNormal.Y:F3},{faceNormal.Z:F3}), Euler=({correction.XDegrees:F2},{correction.YDegrees:F2},{correction.ZDegrees:F2})"); + UpdateMainStatus($"贴地面完成: {correction}"); + } + + private static double NormalizeDegrees(double degrees) + { + double normalized = degrees % 360.0; + if (normalized < -180.0) normalized += 360.0; + if (normalized > 180.0) normalized -= 360.0; + return Math.Abs(normalized) < 1e-9 ? 0.0 : normalized; + } + private ObjectPassageProjectionOptimizationResult OptimizeObjectPassageProjectionByMeasuredAabb( ObjectPassageProjectionOptimizationRequest request) { diff --git a/src/UI/WPF/Views/EditRotationWindow.xaml.cs b/src/UI/WPF/Views/EditRotationWindow.xaml.cs index bbd95d8..cd801c6 100644 --- a/src/UI/WPF/Views/EditRotationWindow.xaml.cs +++ b/src/UI/WPF/Views/EditRotationWindow.xaml.cs @@ -402,87 +402,14 @@ namespace NavisworksTransport.UI.WPF.Views private void OnMateToGroundClick(object sender, RoutedEventArgs e) { - LogManager.Debug("[贴地面] 按钮被点击"); - try - { - var assemblyPath = FaceInferToolPlugin.AssemblyPath; - NavisApplication.Plugins.AddPluginAssembly(assemblyPath); - var record = (Autodesk.Navisworks.Api.Plugins.ToolPluginRecord) - NavisApplication.Plugins.FindPlugin("FaceInferTool.NavisworksTransport"); - var loadedPlugin = record.LoadPlugin(); - NavisApplication.MainDocument.Tool.Value = Autodesk.Navisworks.Api.Tool.None; - NavisApplication.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin); + if (!TryCommitInputValues()) + return; - EventHandler handler = null; - handler = (s, result) => - { - FaceInferToolPlugin.FaceInferred -= handler; - - Dispatcher.Invoke(() => - { - try - { - NavisApplication.MainDocument.Tool.Value = Autodesk.Navisworks.Api.Tool.Select; - } - catch { } - - if (!result.IsValid) - { - var reason = result.FailureReason ?? "取面失败。"; - MessageBox.Show(reason, "贴地面", MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - - var doc = NavisApplication.ActiveDocument; - var worldUp = doc.CurrentViewpoint.Value.WorldUpVector; - var hostUp = new Vector3((float)worldUp.X, (float)worldUp.Y, (float)worldUp.Z); - hostUp = Vector3.Normalize(hostUp); - var groundDir = -hostUp; - - var faceNormal = new Vector3( - (float)result.Normal.X, (float)result.Normal.Y, (float)result.Normal.Z); - faceNormal = Vector3.Normalize(faceNormal); - - float dot = Vector3.Dot(faceNormal, groundDir); - - Quaternion rotation; - if (dot > 0.9999f) - { - MessageBox.Show("选中面已朝向地面,无需旋转。", "贴地面", MessageBoxButton.OK, MessageBoxImage.Information); - return; - } - if (dot < -0.9999f) - { - var perp = Math.Abs(hostUp.X) < 0.9f - ? Vector3.Normalize(Vector3.Cross(hostUp, new Vector3(1, 0, 0))) - : Vector3.Normalize(Vector3.Cross(hostUp, new Vector3(0, 0, 1))); - rotation = Quaternion.CreateFromAxisAngle(perp, (float)Math.PI); - } - else - { - var axis = Vector3.Normalize(Vector3.Cross(faceNormal, groundDir)); - float angle = (float)Math.Acos(dot); - rotation = Quaternion.CreateFromAxisAngle(axis, angle); - } - - ExtractZyxEuler(rotation, out double xDeg, out double yDeg, out double zDeg); - - RotationXDegrees = NormalizeDisplayDegrees(xDeg); - RotationYDegrees = NormalizeDisplayDegrees(yDeg); - RotationZDegrees = NormalizeDisplayDegrees(zDeg); - - LogManager.Info($"[贴地面] {faceNormal} -> {groundDir}, Euler=({RotationXDegrees:F2},{RotationYDegrees:F2},{RotationZDegrees:F2})"); - }); - }; - - FaceInferToolPlugin.FaceInferred += handler; - LogManager.Debug("[贴地面] 取面工具已激活"); - } - catch (Exception ex) - { - LogManager.Error($"[贴地面] 失败: {ex.Message}", ex); - MessageBox.Show($"激活取面工具失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); - } + _adjustmentRequest = ObjectStartPlacementRequest.CreateRotationCorrection( + RotationCorrection, GroundPathLiftHeightInMeters); + _adjustmentRequest.MateToGroundRequested = true; + DialogResult = true; + Close(); } private static void ExtractZyxEuler(Quaternion q, out double xDeg, out double yDeg, out double zDeg) diff --git a/src/Utils/CoordinateSystem/ObjectStartPlacementRequest.cs b/src/Utils/CoordinateSystem/ObjectStartPlacementRequest.cs index ffeb517..1a3273a 100644 --- a/src/Utils/CoordinateSystem/ObjectStartPlacementRequest.cs +++ b/src/Utils/CoordinateSystem/ObjectStartPlacementRequest.cs @@ -21,6 +21,7 @@ namespace NavisworksTransport.Utils.CoordinateSystem RotationCorrection = rotationCorrection; PlacementMode = placementMode; VerticalLiftInMeters = verticalLiftInMeters; + MateToGroundRequested = false; } public LocalEulerRotationCorrection RotationCorrection { get; } @@ -29,6 +30,8 @@ namespace NavisworksTransport.Utils.CoordinateSystem public double VerticalLiftInMeters { get; } + public bool MateToGroundRequested { get; set; } + public bool PreserveInitialPose => PlacementMode == ObjectStartPlacementMode.PreserveInitialPose; public static ObjectStartPlacementRequest CreateRotationCorrection(