NavisworksTransport/src/Utils/ModelItemTransformHelper.cs

256 lines
9.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Utils
{
/// <summary>
/// ModelItem变换辅助工具
/// 提供临时移动和恢复物体位置的通用方法
/// </summary>
public static class ModelItemTransformHelper
{
/// <summary>
/// 临时移动物体到指定位置
/// </summary>
/// <param name="item">要移动的物体</param>
/// <param name="targetPosition">目标位置(物体中心)</param>
/// <returns>用于恢复的偏移向量</returns>
public static Vector3D MoveItemToPosition(ModelItem item, Point3D targetPosition)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 计算当前中心位置
var currentBounds = item.BoundingBox();
var currentPos = new Point3D(
(currentBounds.Min.X + currentBounds.Max.X) / 2,
(currentBounds.Min.Y + currentBounds.Max.Y) / 2,
(currentBounds.Min.Z + currentBounds.Max.Z) / 2
);
// 计算偏移
var offset = new Vector3D(
targetPosition.X - currentPos.X,
targetPosition.Y - currentPos.Y,
targetPosition.Z - currentPos.Z
);
// 应用变换
var transform = Transform3D.CreateTranslation(offset);
doc.Models.OverridePermanentTransform(modelItems, transform, false);
return offset;
}
/// <summary>
/// 从Transform3D中提取Yaw角度绕Z轴旋转单位弧度
/// </summary>
/// <param name="transform">变换矩阵</param>
/// <returns>Yaw角度弧度</returns>
public static double GetYawFromTransform(Transform3D transform)
{
if (transform == null) return 0.0;
try
{
// 获取变换的组件
var components = transform.Factor();
var rotation = components.Rotation;
// 使用ToAxisAndAngle()提取轴和角度
var rotationResult = rotation.ToAxisAndAngle();
double angle = rotationResult.Angle;
double ux = rotationResult.Axis.X;
double uy = rotationResult.Axis.Y;
double uz = rotationResult.Axis.Z;
// 旋转公式罗德里格旋转公式简化版提取局部X轴(1,0,0)旋转后的方向)
// 旋转后的向量 v' = v*cosθ + (u×v)*sinθ + u*(u·v)*(1-cosθ)
// 当 v = (1,0,0) 时:
// v'.x = cosθ + ux*ux*(1-cosθ)
// v'.y = uz*sinθ + ux*uy*(1-cosθ)
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
// 计算旋转后的X轴在世界坐标系XY平面上的分量
double vx = cos + ux * ux * (1 - cos);
double vy = uz * sin + ux * uy * (1 - cos);
// 在XY平面上通过Atan2计算偏航角
return Math.Atan2(vy, vx);
}
catch (Exception)
{
return 0.0;
}
}
/// <summary>
/// 恢复物体到原位置
/// </summary>
/// <param name="item">要恢复的物体</param>
/// <param name="originalOffset">原始移动偏移向量</param>
public static void RestoreItemPosition(ModelItem item, Vector3D originalOffset)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 反向偏移
var restoreOffset = new Vector3D(-originalOffset.X, -originalOffset.Y, -originalOffset.Z);
var restoreTransform = Transform3D.CreateTranslation(restoreOffset);
doc.Models.OverridePermanentTransform(modelItems, restoreTransform, false);
}
/// <summary>
/// 将物体移动到指定位置和朝向先回到CAD原始位置再移动
/// 适用于碰撞位置还原等场景
/// </summary>
/// <param name="item">要移动的物体</param>
/// <param name="targetPosition">目标位置(地面位置,即包围盒底面)</param>
/// <param name="targetYaw">目标朝向弧度绕Z轴</param>
public static void MoveItemToPositionAndYaw(ModelItem item, Point3D targetPosition, double targetYaw)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 🔥 关键先回到CAD原始位置确保从已知状态开始计算
doc.Models.ResetPermanentTransform(modelItems);
// 获取CAD原始状态
var originalBounds = item.BoundingBox();
var originalGroundPos = new Point3D(
originalBounds.Center.X,
originalBounds.Center.Y,
originalBounds.Min.Z
);
var originalYaw = GetYawFromTransform(item.Transform);
// 计算从CAD原始位置到目标位置的增量
var deltaPos = new Vector3D(
targetPosition.X - originalGroundPos.X,
targetPosition.Y - originalGroundPos.Y,
targetPosition.Z - originalGroundPos.Z
);
double deltaYaw = targetYaw - originalYaw;
// 应用增量变换
Transform3D transform;
if (Math.Abs(deltaYaw) > 0.001)
{
// 有旋转:需要补偿绕原点旋转带来的位置偏移
double cos = Math.Cos(deltaYaw);
double sin = Math.Sin(deltaYaw);
double rotatedX = originalGroundPos.X * cos - originalGroundPos.Y * sin;
double rotatedY = originalGroundPos.X * sin + originalGroundPos.Y * cos;
var compensatedTranslation = new Vector3D(
targetPosition.X - rotatedX,
targetPosition.Y - rotatedY,
deltaPos.Z
);
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
var components = identity.Factor();
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
components.Translation = compensatedTranslation;
transform = components.Combine();
}
else
{
// 纯平移
transform = Transform3D.CreateTranslation(deltaPos);
}
doc.Models.OverridePermanentTransform(modelItems, transform, false);
}
/// <summary>
/// 物体状态快照,用于保存和恢复
/// </summary>
public class ObjectStateSnapshot
{
public Point3D Position { get; set; }
public double YawRadians { get; set; }
public Transform3D Transform { get; set; }
}
/// <summary>
/// 保存物体当前状态
/// </summary>
public static ObjectStateSnapshot SaveObjectState(ModelItem item)
{
if (item == null) return null;
var bounds = item.BoundingBox();
return new ObjectStateSnapshot
{
Position = new Point3D(bounds.Center.X, bounds.Center.Y, bounds.Min.Z),
YawRadians = GetYawFromTransform(item.Transform),
Transform = item.Transform
};
}
/// <summary>
/// 从状态快照恢复物体位置
/// 先回到CAD原始位置再移动到保存的位置
/// </summary>
public static void RestoreObjectState(ModelItem item, ObjectStateSnapshot state)
{
if (item == null || state == null) return;
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 1. 先回到CAD原始位置
doc.Models.ResetPermanentTransform(modelItems);
// 2. 从CAD原始位置移动到保存的位置
// 获取CAD原始状态
var originalBounds = item.BoundingBox();
var originalGroundPos = new Point3D(
originalBounds.Center.X,
originalBounds.Center.Y,
originalBounds.Min.Z
);
var originalYaw = GetYawFromTransform(item.Transform);
// 计算到保存位置的增量
var deltaPos = new Vector3D(
state.Position.X - originalGroundPos.X,
state.Position.Y - originalGroundPos.Y,
state.Position.Z - originalGroundPos.Z
);
double deltaYaw = state.YawRadians - originalYaw;
// 应用增量变换
Transform3D transform;
if (Math.Abs(deltaYaw) > 0.001)
{
double cos = Math.Cos(deltaYaw);
double sin = Math.Sin(deltaYaw);
double rotatedX = originalGroundPos.X * cos - originalGroundPos.Y * sin;
double rotatedY = originalGroundPos.X * sin + originalGroundPos.Y * cos;
var compensatedTranslation = new Vector3D(
state.Position.X - rotatedX,
state.Position.Y - rotatedY,
deltaPos.Z
);
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
var components = identity.Factor();
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
components.Translation = compensatedTranslation;
transform = components.Combine();
}
else
{
transform = Transform3D.CreateTranslation(deltaPos);
}
doc.Models.OverridePermanentTransform(modelItems, transform, false);
}
}
}