fix: move face-to-ground mate logic from modal dialog to ViewModel
- Modal ShowDialog blocks Navisworks ToolPlugin message loop - Now: '贴地面' sets MateToGroundRequested flag, closes dialog - AnimationControlViewModel handles FaceInferToolPlugin activation - OnFaceToGroundInferred computes rotation and applies directly
This commit is contained in:
parent
2f16ec0d43
commit
10a0ad754c
@ -12,6 +12,8 @@ using System.Windows.Input;
|
|||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using Autodesk.Navisworks.Api;
|
using Autodesk.Navisworks.Api;
|
||||||
using Autodesk.Navisworks.Api.Clash;
|
using Autodesk.Navisworks.Api.Clash;
|
||||||
|
using Autodesk.Navisworks.Api.Plugins;
|
||||||
|
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||||||
using NavisworksTransport.Core;
|
using NavisworksTransport.Core;
|
||||||
using NavisworksTransport.Core.Models;
|
using NavisworksTransport.Core.Models;
|
||||||
using NavisworksTransport.Core.Config;
|
using NavisworksTransport.Core.Config;
|
||||||
@ -2187,6 +2189,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
|||||||
if (dialog.ShowDialog() == true)
|
if (dialog.ShowDialog() == true)
|
||||||
{
|
{
|
||||||
var placementRequest = dialog.AdjustmentRequest;
|
var placementRequest = dialog.AdjustmentRequest;
|
||||||
|
|
||||||
|
if (placementRequest.MateToGroundRequested)
|
||||||
|
{
|
||||||
|
BeginFaceToGroundMate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_objectGroundLiftHeightInMeters = placementRequest.VerticalLiftInMeters;
|
_objectGroundLiftHeightInMeters = placementRequest.VerticalLiftInMeters;
|
||||||
bool isAutoAdjusted = dialog.AutoAdjustAabbSizes.HasValue;
|
bool isAutoAdjusted = dialog.AutoAdjustAabbSizes.HasValue;
|
||||||
if (isAutoAdjusted)
|
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(
|
private ObjectPassageProjectionOptimizationResult OptimizeObjectPassageProjectionByMeasuredAabb(
|
||||||
ObjectPassageProjectionOptimizationRequest request)
|
ObjectPassageProjectionOptimizationRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -402,87 +402,14 @@ namespace NavisworksTransport.UI.WPF.Views
|
|||||||
|
|
||||||
private void OnMateToGroundClick(object sender, RoutedEventArgs e)
|
private void OnMateToGroundClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
LogManager.Debug("[贴地面] 按钮被点击");
|
if (!TryCommitInputValues())
|
||||||
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);
|
|
||||||
|
|
||||||
EventHandler<FaceInferResult> 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;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var doc = NavisApplication.ActiveDocument;
|
_adjustmentRequest = ObjectStartPlacementRequest.CreateRotationCorrection(
|
||||||
var worldUp = doc.CurrentViewpoint.Value.WorldUpVector;
|
RotationCorrection, GroundPathLiftHeightInMeters);
|
||||||
var hostUp = new Vector3((float)worldUp.X, (float)worldUp.Y, (float)worldUp.Z);
|
_adjustmentRequest.MateToGroundRequested = true;
|
||||||
hostUp = Vector3.Normalize(hostUp);
|
DialogResult = true;
|
||||||
var groundDir = -hostUp;
|
Close();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExtractZyxEuler(Quaternion q, out double xDeg, out double yDeg, out double zDeg)
|
private static void ExtractZyxEuler(Quaternion q, out double xDeg, out double yDeg, out double zDeg)
|
||||||
|
|||||||
@ -21,6 +21,7 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
RotationCorrection = rotationCorrection;
|
RotationCorrection = rotationCorrection;
|
||||||
PlacementMode = placementMode;
|
PlacementMode = placementMode;
|
||||||
VerticalLiftInMeters = verticalLiftInMeters;
|
VerticalLiftInMeters = verticalLiftInMeters;
|
||||||
|
MateToGroundRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalEulerRotationCorrection RotationCorrection { get; }
|
public LocalEulerRotationCorrection RotationCorrection { get; }
|
||||||
@ -29,6 +30,8 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
|
|
||||||
public double VerticalLiftInMeters { get; }
|
public double VerticalLiftInMeters { get; }
|
||||||
|
|
||||||
|
public bool MateToGroundRequested { get; set; }
|
||||||
|
|
||||||
public bool PreserveInitialPose => PlacementMode == ObjectStartPlacementMode.PreserveInitialPose;
|
public bool PreserveInitialPose => PlacementMode == ObjectStartPlacementMode.PreserveInitialPose;
|
||||||
|
|
||||||
public static ObjectStartPlacementRequest CreateRotationCorrection(
|
public static ObjectStartPlacementRequest CreateRotationCorrection(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user