1. GetYawFromRotation: 通过 canonical space 转换计算 yaw,修复 YUp 下 atan2(Y,X) 用错平面 2. ApplyRotationCorrectionInPlace: 新增 CAD 原位纯旋转,评测旋转顺序与增量链一致 3. 评测器两轮优化: 先无约束找最小面积,不满足约束再带约束重搜 4. ApplyAutoAdjustedPoseAndMoveToPathStart: 确认时先校正再偏航,不读 CAD yaw 5. OnAutoAdjustClick: 去掉 Y 轴 yaw 叠加
1295 lines
58 KiB
C#
1295 lines
58 KiB
C#
using System;
|
||
using Autodesk.Navisworks.Api;
|
||
using NavisworksTransport.Utils.CoordinateSystem;
|
||
using System.Numerics;
|
||
|
||
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();
|
||
return GetYawFromRotation(components.Rotation);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 0.0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从 Rotation3D 中提取 XY 平面的 yaw 角(弧度)。
|
||
/// </summary>
|
||
public static double GetYawFromRotation(Rotation3D rotation)
|
||
{
|
||
if (rotation == null)
|
||
{
|
||
return 0.0;
|
||
}
|
||
|
||
try
|
||
{
|
||
var linear = new Transform3D(rotation).Linear;
|
||
Vector3 hostForward = new Vector3(
|
||
(float)linear.Get(0, 0),
|
||
(float)linear.Get(1, 0),
|
||
(float)linear.Get(2, 0));
|
||
|
||
// 转 canonical (ZUp),在 XY 平面算 yaw
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemManager.Instance.ResolvedType);
|
||
Vector3 canonicalForward = adapter.ToCanonicalVector3(hostForward);
|
||
canonicalForward.Z = 0f;
|
||
if (canonicalForward.LengthSquared() < 1e-9f)
|
||
{
|
||
return 0.0;
|
||
}
|
||
|
||
canonicalForward = Vector3.Normalize(canonicalForward);
|
||
return System.Math.Atan2(canonicalForward.Y, canonicalForward.X);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 0.0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取变换后局部 X 轴在世界坐标中的方向,通常可作为物体主朝向。
|
||
/// </summary>
|
||
public static Vector3D GetForwardDirectionFromTransform(Transform3D transform)
|
||
{
|
||
return GetAxisDirectionFromTransform(transform, 0, new Vector3D(1, 0, 0));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取变换后局部 Z 轴在世界坐标中的方向,可作为构件顶/底面法向。
|
||
/// </summary>
|
||
public static Vector3D GetUpDirectionFromTransform(Transform3D transform)
|
||
{
|
||
return GetAxisDirectionFromTransform(transform, 2, new Vector3D(0, 0, 1));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据模型局部轴约定,获取指定局部轴在世界坐标中的方向。
|
||
/// </summary>
|
||
public static Vector3D GetDirectionFromTransform(
|
||
Transform3D transform,
|
||
LocalAxisDirection axisDirection)
|
||
{
|
||
GetAxisIndexAndSign(axisDirection, out int axisIndex, out int sign);
|
||
Vector3D fallback = GetFallbackAxis(axisDirection);
|
||
Vector3D direction = GetAxisDirectionFromTransform(transform, axisIndex, fallback);
|
||
|
||
return sign >= 0
|
||
? direction
|
||
: new Vector3D(-direction.X, -direction.Y, -direction.Z);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取绑定在物体局部坐标中的稳定底面锚点。
|
||
/// 适用于带俯仰/侧倾的三维姿态对象,避免使用世界 AABB 底面中心导致锚点漂移。
|
||
/// </summary>
|
||
public static Point3D GetStableBottomAnchorPoint(BoundingBox3D bounds, Transform3D transform)
|
||
{
|
||
if (bounds == null)
|
||
{
|
||
return new Point3D(0, 0, 0);
|
||
}
|
||
|
||
if (transform == null)
|
||
{
|
||
return new Point3D(bounds.Center.X, bounds.Center.Y, bounds.Min.Z);
|
||
}
|
||
|
||
var up = GetUpDirectionFromTransform(transform);
|
||
var localSize = EstimateLocalBoxSize(bounds, transform);
|
||
double halfHeight = localSize.Z / 2.0;
|
||
|
||
return new Point3D(
|
||
bounds.Center.X - up.X * halfHeight,
|
||
bounds.Center.Y - up.Y * halfHeight,
|
||
bounds.Center.Z - up.Z * halfHeight);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用宿主坐标系的 up 轴,从世界 AABB 中提取“底部锚点”。
|
||
/// 适用于真实模型仍保持宿主坐标语义(如 Y-up 模型)的场景。
|
||
/// </summary>
|
||
public static Point3D GetHostBottomAnchorPoint(BoundingBox3D bounds, HostCoordinateAdapter adapter)
|
||
{
|
||
if (bounds == null)
|
||
{
|
||
return new Point3D(0, 0, 0);
|
||
}
|
||
|
||
if (adapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(adapter));
|
||
}
|
||
|
||
switch (adapter.HostUpAxisIndex)
|
||
{
|
||
case 1:
|
||
return new Point3D(bounds.Center.X, bounds.Min.Y, bounds.Center.Z);
|
||
case 2:
|
||
default:
|
||
return new Point3D(bounds.Center.X, bounds.Center.Y, bounds.Min.Z);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用宿主坐标系的 up 轴,从世界 AABB 中提取高度。
|
||
/// 适用于真实模型仍保持宿主坐标语义(如 Y-up 模型)的场景。
|
||
/// </summary>
|
||
public static double GetHostHeight(BoundingBox3D bounds, HostCoordinateAdapter adapter)
|
||
{
|
||
if (bounds == null)
|
||
{
|
||
return 0.0;
|
||
}
|
||
|
||
if (adapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(adapter));
|
||
}
|
||
|
||
switch (adapter.HostUpAxisIndex)
|
||
{
|
||
case 1:
|
||
return bounds.Max.Y - bounds.Min.Y;
|
||
case 2:
|
||
default:
|
||
return bounds.Max.Z - bounds.Min.Z;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据世界轴对齐包围盒和物体变换,估算物体在局部坐标系中的尺寸。
|
||
/// 适用于刚性箱体类对象,用于顶/底面对接点推导。
|
||
/// </summary>
|
||
public static Vector3D EstimateLocalBoxSize(BoundingBox3D bounds, Transform3D transform)
|
||
{
|
||
double halfXWorld = Math.Max(0.0, (bounds.Max.X - bounds.Min.X) / 2.0);
|
||
double halfYWorld = Math.Max(0.0, (bounds.Max.Y - bounds.Min.Y) / 2.0);
|
||
double halfZWorld = Math.Max(0.0, (bounds.Max.Z - bounds.Min.Z) / 2.0);
|
||
|
||
if (transform == null)
|
||
{
|
||
return new Vector3D(halfXWorld * 2.0, halfYWorld * 2.0, halfZWorld * 2.0);
|
||
}
|
||
|
||
try
|
||
{
|
||
var forward = GetForwardDirectionFromTransform(transform);
|
||
var localY = GetAxisDirectionFromTransform(transform, 1, new Vector3D(0, 1, 0));
|
||
var up = GetUpDirectionFromTransform(transform);
|
||
|
||
double[,] matrix =
|
||
{
|
||
{ Math.Abs(forward.X), Math.Abs(localY.X), Math.Abs(up.X) },
|
||
{ Math.Abs(forward.Y), Math.Abs(localY.Y), Math.Abs(up.Y) },
|
||
{ Math.Abs(forward.Z), Math.Abs(localY.Z), Math.Abs(up.Z) }
|
||
};
|
||
|
||
double[] worldHalfExtents = { halfXWorld, halfYWorld, halfZWorld };
|
||
double[] localHalfExtents = SolveLinearSystem3x3(matrix, worldHalfExtents);
|
||
|
||
return new Vector3D(
|
||
Math.Max(0.0, localHalfExtents[0] * 2.0),
|
||
Math.Max(0.0, localHalfExtents[1] * 2.0),
|
||
Math.Max(0.0, localHalfExtents[2] * 2.0));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return new Vector3D(halfXWorld * 2.0, halfYWorld * 2.0, halfZWorld * 2.0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据箱体局部尺寸和世界原点方向,推导“背离球心”的真实长轴方向。
|
||
/// 先选局部最长轴,再用箱体中心相对世界原点的方向决定正负。
|
||
/// </summary>
|
||
public static Vector3D GetLongestAxisDirectionAwayFromOrigin(BoundingBox3D bounds, Transform3D transform)
|
||
{
|
||
Vector3D localSize = EstimateLocalBoxSize(bounds, transform);
|
||
|
||
int longestAxisIndex = 0;
|
||
double longestAxisLength = localSize.X;
|
||
if (localSize.Y > longestAxisLength)
|
||
{
|
||
longestAxisIndex = 1;
|
||
longestAxisLength = localSize.Y;
|
||
}
|
||
|
||
if (localSize.Z > longestAxisLength)
|
||
{
|
||
longestAxisIndex = 2;
|
||
}
|
||
|
||
Vector3D axisDirection = GetAxisDirectionFromTransform(
|
||
transform,
|
||
longestAxisIndex,
|
||
longestAxisIndex == 0 ? new Vector3D(1, 0, 0) :
|
||
longestAxisIndex == 1 ? new Vector3D(0, 1, 0) :
|
||
new Vector3D(0, 0, 1));
|
||
|
||
Vector3D awayFromOrigin = new Vector3D(bounds.Center.X, bounds.Center.Y, bounds.Center.Z);
|
||
double awayLengthSquared = awayFromOrigin.X * awayFromOrigin.X + awayFromOrigin.Y * awayFromOrigin.Y + awayFromOrigin.Z * awayFromOrigin.Z;
|
||
if (awayLengthSquared < 1e-9)
|
||
{
|
||
return axisDirection;
|
||
}
|
||
|
||
double dot = axisDirection.X * awayFromOrigin.X + axisDirection.Y * awayFromOrigin.Y + axisDirection.Z * awayFromOrigin.Z;
|
||
if (dot < 0.0)
|
||
{
|
||
return new Vector3D(-axisDirection.X, -axisDirection.Y, -axisDirection.Z);
|
||
}
|
||
|
||
return axisDirection;
|
||
}
|
||
|
||
private static Vector3D GetAxisDirectionFromTransform(Transform3D transform, int axisIndex, Vector3D fallback)
|
||
{
|
||
if (transform == null)
|
||
{
|
||
return fallback;
|
||
}
|
||
|
||
try
|
||
{
|
||
var linear = transform.Linear;
|
||
var direction = new Vector3D(
|
||
linear.Get(0, axisIndex),
|
||
linear.Get(1, axisIndex),
|
||
linear.Get(2, axisIndex));
|
||
|
||
double lengthSquared = direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z;
|
||
if (lengthSquared < 1e-9)
|
||
{
|
||
return fallback;
|
||
}
|
||
|
||
double length = Math.Sqrt(lengthSquared);
|
||
return new Vector3D(direction.X / length, direction.Y / length, direction.Z / length);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return fallback;
|
||
}
|
||
}
|
||
|
||
private static void GetAxisIndexAndSign(LocalAxisDirection axisDirection, out int axisIndex, out int sign)
|
||
{
|
||
switch (axisDirection)
|
||
{
|
||
case LocalAxisDirection.PositiveX:
|
||
axisIndex = 0;
|
||
sign = 1;
|
||
break;
|
||
case LocalAxisDirection.NegativeX:
|
||
axisIndex = 0;
|
||
sign = -1;
|
||
break;
|
||
case LocalAxisDirection.PositiveY:
|
||
axisIndex = 1;
|
||
sign = 1;
|
||
break;
|
||
case LocalAxisDirection.NegativeY:
|
||
axisIndex = 1;
|
||
sign = -1;
|
||
break;
|
||
case LocalAxisDirection.PositiveZ:
|
||
axisIndex = 2;
|
||
sign = 1;
|
||
break;
|
||
case LocalAxisDirection.NegativeZ:
|
||
axisIndex = 2;
|
||
sign = -1;
|
||
break;
|
||
default:
|
||
throw new ArgumentOutOfRangeException(nameof(axisDirection), axisDirection, null);
|
||
}
|
||
}
|
||
|
||
private static Vector3D GetFallbackAxis(LocalAxisDirection axisDirection)
|
||
{
|
||
switch (axisDirection)
|
||
{
|
||
case LocalAxisDirection.PositiveX:
|
||
return new Vector3D(1, 0, 0);
|
||
case LocalAxisDirection.NegativeX:
|
||
return new Vector3D(-1, 0, 0);
|
||
case LocalAxisDirection.PositiveY:
|
||
return new Vector3D(0, 1, 0);
|
||
case LocalAxisDirection.NegativeY:
|
||
return new Vector3D(0, -1, 0);
|
||
case LocalAxisDirection.PositiveZ:
|
||
return new Vector3D(0, 0, 1);
|
||
case LocalAxisDirection.NegativeZ:
|
||
return new Vector3D(0, 0, -1);
|
||
default:
|
||
throw new ArgumentOutOfRangeException(nameof(axisDirection), axisDirection, null);
|
||
}
|
||
}
|
||
|
||
private static double[] SolveLinearSystem3x3(double[,] matrix, double[] values)
|
||
{
|
||
double determinant =
|
||
matrix[0, 0] * (matrix[1, 1] * matrix[2, 2] - matrix[1, 2] * matrix[2, 1]) -
|
||
matrix[0, 1] * (matrix[1, 0] * matrix[2, 2] - matrix[1, 2] * matrix[2, 0]) +
|
||
matrix[0, 2] * (matrix[1, 0] * matrix[2, 1] - matrix[1, 1] * matrix[2, 0]);
|
||
|
||
if (Math.Abs(determinant) < 1e-9)
|
||
{
|
||
return new[] { values[0], values[1], values[2] };
|
||
}
|
||
|
||
double inverseDeterminant = 1.0 / determinant;
|
||
double[,] inverse =
|
||
{
|
||
{
|
||
(matrix[1, 1] * matrix[2, 2] - matrix[1, 2] * matrix[2, 1]) * inverseDeterminant,
|
||
(matrix[0, 2] * matrix[2, 1] - matrix[0, 1] * matrix[2, 2]) * inverseDeterminant,
|
||
(matrix[0, 1] * matrix[1, 2] - matrix[0, 2] * matrix[1, 1]) * inverseDeterminant
|
||
},
|
||
{
|
||
(matrix[1, 2] * matrix[2, 0] - matrix[1, 0] * matrix[2, 2]) * inverseDeterminant,
|
||
(matrix[0, 0] * matrix[2, 2] - matrix[0, 2] * matrix[2, 0]) * inverseDeterminant,
|
||
(matrix[0, 2] * matrix[1, 0] - matrix[0, 0] * matrix[1, 2]) * inverseDeterminant
|
||
},
|
||
{
|
||
(matrix[1, 0] * matrix[2, 1] - matrix[1, 1] * matrix[2, 0]) * inverseDeterminant,
|
||
(matrix[0, 1] * matrix[2, 0] - matrix[0, 0] * matrix[2, 1]) * inverseDeterminant,
|
||
(matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0]) * inverseDeterminant
|
||
}
|
||
};
|
||
|
||
return new[]
|
||
{
|
||
inverse[0, 0] * values[0] + inverse[0, 1] * values[1] + inverse[0, 2] * values[2],
|
||
inverse[1, 0] * values[0] + inverse[1, 1] * values[1] + inverse[1, 2] * values[2],
|
||
inverse[2, 0] * values[0] + inverse[2, 1] * values[1] + inverse[2, 2] * values[2]
|
||
};
|
||
}
|
||
|
||
/// <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 originalCenterPos = originalBounds.Center;
|
||
var originalYaw = GetYawFromTransform(item.Transform);
|
||
|
||
// 计算从CAD原始位置到目标位置的增量
|
||
var deltaPos = new Vector3D(
|
||
targetPosition.X - originalCenterPos.X,
|
||
targetPosition.Y - originalCenterPos.Y,
|
||
targetPosition.Z - originalCenterPos.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 = originalCenterPos.X * cos - originalCenterPos.Y * sin;
|
||
double rotatedY = originalCenterPos.X * sin + originalCenterPos.Y * cos;
|
||
|
||
var compensatedTranslation = new Vector3D(
|
||
targetPosition.X - rotatedX,
|
||
targetPosition.Y - rotatedY,
|
||
targetPosition.Z - originalCenterPos.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>
|
||
/// 将物体移动到指定位置和完整三维朝向(先回到CAD原始位置,再移动)
|
||
/// </summary>
|
||
public static void MoveItemToPositionAndRotation(ModelItem item, Point3D targetPosition, Rotation3D targetRotation)
|
||
{
|
||
ApplyAbsoluteTransform(item, targetPosition, targetRotation, preserveCurrentScale: false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将物体移动到指定位置和完整三维朝向,同时保留当前缩放。
|
||
/// </summary>
|
||
public static void MoveItemToPositionAndRotationWithCurrentScale(ModelItem item, Point3D targetPosition, Rotation3D targetRotation)
|
||
{
|
||
ApplyAbsoluteTransform(item, targetPosition, targetRotation, preserveCurrentScale: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 基于物体当前实际姿态,增量移动到目标位置和完整三维朝向。
|
||
/// 适用于动画过程中的真实模型物体,避免每次回到 CAD 原始状态导致位置跑偏。
|
||
/// </summary>
|
||
public static void MoveItemIncrementallyToPositionAndRotation(
|
||
ModelItem item,
|
||
Point3D currentPosition,
|
||
Rotation3D currentRotation,
|
||
Point3D targetPosition,
|
||
Rotation3D targetRotation)
|
||
{
|
||
if (item == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(item));
|
||
}
|
||
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
Rotation3D deltaRotation = BuildDeltaRotation(currentRotation, targetRotation);
|
||
var deltaLinear = new Transform3D(deltaRotation).Linear;
|
||
var currentLinear = new Transform3D(currentRotation).Linear;
|
||
var targetLinear = new Transform3D(targetRotation).Linear;
|
||
|
||
var rotatedCurrentPosition = new Point3D(
|
||
deltaLinear.Get(0, 0) * currentPosition.X + deltaLinear.Get(0, 1) * currentPosition.Y + deltaLinear.Get(0, 2) * currentPosition.Z,
|
||
deltaLinear.Get(1, 0) * currentPosition.X + deltaLinear.Get(1, 1) * currentPosition.Y + deltaLinear.Get(1, 2) * currentPosition.Z,
|
||
deltaLinear.Get(2, 0) * currentPosition.X + deltaLinear.Get(2, 1) * currentPosition.Y + deltaLinear.Get(2, 2) * currentPosition.Z);
|
||
|
||
var translation = new Vector3D(
|
||
targetPosition.X - rotatedCurrentPosition.X,
|
||
targetPosition.Y - rotatedCurrentPosition.Y,
|
||
targetPosition.Z - rotatedCurrentPosition.Z);
|
||
|
||
LogManager.Debug(
|
||
$"[模型增量姿态] {item.DisplayName} 当前=({currentPosition.X:F3},{currentPosition.Y:F3},{currentPosition.Z:F3}), " +
|
||
$"目标=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||
$"平移=({translation.X:F3},{translation.Y:F3},{translation.Z:F3})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型增量姿态] {item.DisplayName} 当前旋转: " +
|
||
$"X=({currentLinear.Get(0, 0):F4},{currentLinear.Get(1, 0):F4},{currentLinear.Get(2, 0):F4}), " +
|
||
$"Y=({currentLinear.Get(0, 1):F4},{currentLinear.Get(1, 1):F4},{currentLinear.Get(2, 1):F4}), " +
|
||
$"Z=({currentLinear.Get(0, 2):F4},{currentLinear.Get(1, 2):F4},{currentLinear.Get(2, 2):F4})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型增量姿态] {item.DisplayName} 目标旋转: " +
|
||
$"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型增量姿态] {item.DisplayName} 增量旋转: " +
|
||
$"X=({deltaLinear.Get(0, 0):F4},{deltaLinear.Get(1, 0):F4},{deltaLinear.Get(2, 0):F4}), " +
|
||
$"Y=({deltaLinear.Get(0, 1):F4},{deltaLinear.Get(1, 1):F4},{deltaLinear.Get(2, 1):F4}), " +
|
||
$"Z=({deltaLinear.Get(0, 2):F4},{deltaLinear.Get(1, 2):F4},{deltaLinear.Get(2, 2):F4}), " +
|
||
$"旋后当前点=({rotatedCurrentPosition.X:F3},{rotatedCurrentPosition.Y:F3},{rotatedCurrentPosition.Z:F3})");
|
||
|
||
LogGeometryLevelTransforms(item, "[模型增量姿态应用前][GeometryAPI]");
|
||
|
||
// 用显式三步法应用三维增量位姿:
|
||
// 1. 把当前锚点移到原点
|
||
// 2. 绕原点旋转到目标姿态
|
||
// 3. 再把锚点移到目标位置
|
||
// 这与旧 yaw 链路的补偿语义一致,只是从二维扩展到三维。
|
||
var toOrigin = Transform3D.CreateTranslation(new Vector3D(
|
||
-currentPosition.X,
|
||
-currentPosition.Y,
|
||
-currentPosition.Z));
|
||
doc.Models.OverridePermanentTransform(modelItems, toOrigin, false);
|
||
|
||
var rotationOnlyComponents = Transform3D.CreateTranslation(new Vector3D(0, 0, 0)).Factor();
|
||
rotationOnlyComponents.Rotation = deltaRotation;
|
||
var rotationOnly = rotationOnlyComponents.Combine();
|
||
doc.Models.OverridePermanentTransform(modelItems, rotationOnly, false);
|
||
|
||
var toTarget = Transform3D.CreateTranslation(new Vector3D(
|
||
targetPosition.X,
|
||
targetPosition.Y,
|
||
targetPosition.Z));
|
||
doc.Models.OverridePermanentTransform(modelItems, toTarget, false);
|
||
|
||
LogIncrementalTransformActual(item, targetPosition, targetRotation);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合并三次绕宿主轴的旋转 + 一次平移为单次 OverridePermanentTransform 调用。
|
||
/// 等价于依次调用三次 MoveItemIncrementallyByAxisRotationAndTranslation,但 NW API 调用次数减少 66%。
|
||
/// </summary>
|
||
public static void MoveItemCombinedAxisRotationAndTranslation(
|
||
ModelItem item,
|
||
Point3D trackedPosition,
|
||
Vector3 axis1, double angle1Radians,
|
||
Vector3 axis2, double angle2Radians,
|
||
Vector3 axis3, double angle3Radians,
|
||
Point3D targetPosition)
|
||
{
|
||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||
|
||
// 用 System.Numerics 合成三个旋转
|
||
var q1 = Quaternion.CreateFromAxisAngle(Vector3.Normalize(axis1), (float)angle1Radians);
|
||
var q2 = Quaternion.CreateFromAxisAngle(Vector3.Normalize(axis2), (float)angle2Radians);
|
||
var q3 = Quaternion.CreateFromAxisAngle(Vector3.Normalize(axis3), (float)angle3Radians);
|
||
// q3 * q2 * q1: apply q1 first, then q2, then q3(与 Override 左乘积累顺序一致)
|
||
var totalQ = Quaternion.Normalize(q3 * q2 * q1);
|
||
|
||
// 计算旋后跟踪点位置
|
||
var tp = new Vector3((float)trackedPosition.X, (float)trackedPosition.Y, (float)trackedPosition.Z);
|
||
var rotatedTp = Vector3.Transform(tp, totalQ);
|
||
|
||
// 计算组合变换(调用方已在 RestoreObjectToCADPosition 中执行 ResetPermanentTransform,此处不重复)
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||
var components = identity.Factor();
|
||
components.Rotation = new Rotation3D(totalQ.X, totalQ.Y, totalQ.Z, totalQ.W);
|
||
components.Translation = new Vector3D(
|
||
targetPosition.X - rotatedTp.X,
|
||
targetPosition.Y - rotatedTp.Y,
|
||
targetPosition.Z - rotatedTp.Z);
|
||
var combined = components.Combine();
|
||
|
||
LogManager.Debug(
|
||
$"[组合旋转平移] tracked=({trackedPosition.X:F3},{trackedPosition.Y:F3},{trackedPosition.Z:F3}), " +
|
||
$"target=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||
$"translation=({components.Translation.X:F3},{components.Translation.Y:F3},{components.Translation.Z:F3})");
|
||
|
||
doc.Models.OverridePermanentTransform(modelItems, combined, false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 直接对当前显示结果施加宿主轴旋转增量,再补一段平移把业务跟踪点拉回目标点。
|
||
/// 不先构造 targetRotation,也不从 current/target 姿态反推 deltaRotation。
|
||
/// </summary>
|
||
public static void MoveItemIncrementallyByAxisRotationAndTranslation(
|
||
ModelItem item,
|
||
Point3D currentPosition,
|
||
Vector3 hostAxis,
|
||
double deltaAngleRadians,
|
||
Point3D targetPosition)
|
||
{
|
||
if (item == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(item));
|
||
}
|
||
|
||
if (hostAxis.LengthSquared() < 1e-12f)
|
||
{
|
||
throw new ArgumentException("hostAxis must be non-zero.", nameof(hostAxis));
|
||
}
|
||
|
||
Vector3 normalizedHostAxis = Vector3.Normalize(hostAxis);
|
||
Rotation3D deltaRotation = new Rotation3D(
|
||
new UnitVector3D(normalizedHostAxis.X, normalizedHostAxis.Y, normalizedHostAxis.Z),
|
||
deltaAngleRadians);
|
||
Matrix3 deltaLinear = new Transform3D(deltaRotation).Linear;
|
||
Point3D rotatedCurrentPosition = new Point3D(
|
||
deltaLinear.Get(0, 0) * currentPosition.X + deltaLinear.Get(0, 1) * currentPosition.Y + deltaLinear.Get(0, 2) * currentPosition.Z,
|
||
deltaLinear.Get(1, 0) * currentPosition.X + deltaLinear.Get(1, 1) * currentPosition.Y + deltaLinear.Get(1, 2) * currentPosition.Z,
|
||
deltaLinear.Get(2, 0) * currentPosition.X + deltaLinear.Get(2, 1) * currentPosition.Y + deltaLinear.Get(2, 2) * currentPosition.Z);
|
||
|
||
Vector3D compensatedTranslation = new Vector3D(
|
||
targetPosition.X - rotatedCurrentPosition.X,
|
||
targetPosition.Y - rotatedCurrentPosition.Y,
|
||
targetPosition.Z - rotatedCurrentPosition.Z);
|
||
|
||
LogManager.Debug(
|
||
$"[模型纯增量旋转平移] {item.DisplayName} 当前=({currentPosition.X:F3},{currentPosition.Y:F3},{currentPosition.Z:F3}), " +
|
||
$"目标=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||
$"Axis=({normalizedHostAxis.X:F4},{normalizedHostAxis.Y:F4},{normalizedHostAxis.Z:F4}), " +
|
||
$"Angle={deltaAngleRadians * 180.0 / Math.PI:F2}°, " +
|
||
$"旋后当前点=({rotatedCurrentPosition.X:F3},{rotatedCurrentPosition.Y:F3},{rotatedCurrentPosition.Z:F3}), " +
|
||
$"平移=({compensatedTranslation.X:F3},{compensatedTranslation.Y:F3},{compensatedTranslation.Z:F3})");
|
||
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
LogGeometryLevelTransforms(item, "[模型纯增量旋转平移应用前][GeometryAPI]");
|
||
|
||
var toOrigin = Transform3D.CreateTranslation(new Vector3D(
|
||
-currentPosition.X,
|
||
-currentPosition.Y,
|
||
-currentPosition.Z));
|
||
doc.Models.OverridePermanentTransform(modelItems, toOrigin, false);
|
||
|
||
var rotationOnlyComponents = Transform3D.CreateTranslation(new Vector3D(0, 0, 0)).Factor();
|
||
rotationOnlyComponents.Rotation = deltaRotation;
|
||
var rotationOnly = rotationOnlyComponents.Combine();
|
||
doc.Models.OverridePermanentTransform(modelItems, rotationOnly, false);
|
||
|
||
var toTarget = Transform3D.CreateTranslation(new Vector3D(
|
||
targetPosition.X,
|
||
targetPosition.Y,
|
||
targetPosition.Z));
|
||
doc.Models.OverridePermanentTransform(modelItems, toTarget, false);
|
||
|
||
LogGeometryLevelTransforms(item, "[模型纯增量旋转平移应用后][GeometryAPI]");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取物体当前实际几何姿态。
|
||
/// 优先使用 ModelGeometry.ActiveTransform,因为 ModelItem.Transform 只反映原始设计变换。
|
||
/// </summary>
|
||
public static bool TryGetCurrentGeometryRotation(ModelItem item, out Rotation3D rotation)
|
||
{
|
||
rotation = Rotation3D.Identity;
|
||
if (item == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||
if (geometry?.ActiveTransform == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
rotation = geometry.ActiveTransform.Factor().Rotation;
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[当前几何姿态] 读取 ActiveTransform 失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public static bool TryGetCurrentOverrideRotation(ModelItem item, out Rotation3D rotation)
|
||
{
|
||
rotation = Rotation3D.Identity;
|
||
if (item == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||
if (geometry?.PermanentOverrideTransform == null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
rotation = geometry.PermanentOverrideTransform.Factor().Rotation;
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[当前覆盖姿态] 读取 PermanentOverrideTransform 失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public static bool TryResolveOverrideRotationForFinalTarget(
|
||
ModelItem item,
|
||
Rotation3D finalTargetRotation,
|
||
out Rotation3D overrideRotation)
|
||
{
|
||
overrideRotation = Rotation3D.Identity;
|
||
if (item == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||
Transform3D originalTransform = geometry?.OriginalTransform ?? item.Transform;
|
||
Rotation3D originalRotation = originalTransform.Factor().Rotation;
|
||
var originalInverse = originalRotation.Invert();
|
||
var overrideTransform = Transform3D.Multiply(
|
||
new Transform3D(originalInverse),
|
||
new Transform3D(finalTargetRotation));
|
||
overrideRotation = overrideTransform.Factor().Rotation;
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[覆盖姿态换算] 计算 override 姿态失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public static void LogCurrentGeometryTransformsForDebug(ModelItem item, string prefix)
|
||
{
|
||
LogGeometryLevelTransforms(item, prefix);
|
||
}
|
||
|
||
private static void LogIncrementalTransformActual(ModelItem item, Point3D targetPosition, Rotation3D targetRotation)
|
||
{
|
||
try
|
||
{
|
||
var actualBounds = item.BoundingBox();
|
||
var actualTransform = item.Transform;
|
||
var actualRotation = actualTransform.Factor().Rotation;
|
||
var actualLinear = new Transform3D(actualRotation).Linear;
|
||
var targetLinear = new Transform3D(targetRotation).Linear;
|
||
var actualPosition = actualBounds.Center;
|
||
|
||
LogManager.Debug(
|
||
$"[模型增量姿态] {item.DisplayName} 立即读回位置(可能滞后): " +
|
||
$"实际=({actualPosition.X:F3},{actualPosition.Y:F3},{actualPosition.Z:F3}), " +
|
||
$"期望=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||
$"偏差=({actualPosition.X - targetPosition.X:F3},{actualPosition.Y - targetPosition.Y:F3},{actualPosition.Z - targetPosition.Z:F3})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型增量姿态] {item.DisplayName} 立即读回旋转(可能滞后/不反映override): " +
|
||
$"实际X=({actualLinear.Get(0, 0):F4},{actualLinear.Get(1, 0):F4},{actualLinear.Get(2, 0):F4}), " +
|
||
$"实际Y=({actualLinear.Get(0, 1):F4},{actualLinear.Get(1, 1):F4},{actualLinear.Get(2, 1):F4}), " +
|
||
$"实际Z=({actualLinear.Get(0, 2):F4},{actualLinear.Get(1, 2):F4},{actualLinear.Get(2, 2):F4}), " +
|
||
$"期望X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||
$"期望Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||
$"期望Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||
|
||
LogGeometryLevelTransforms(item, "[模型增量姿态应用后][GeometryAPI]");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[模型增量姿态] 输出应用后日志失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将物体移动到指定中心点和完整三维朝向,同时保留当前缩放。
|
||
/// 仅适用于“几何中心就是业务定位点”的实体,例如参考杆、辅助几何、虚拟包围盒。
|
||
/// 不适用于真实模型物体的动画、碰撞验证或场景恢复;真实模型应使用增量三步法,
|
||
/// 即先将固定参考点移到原点,再旋转,再移动到目标位置。
|
||
/// </summary>
|
||
public static void MoveItemToCenterAndRotationWithCurrentScale(ModelItem item, Point3D targetCenter, Rotation3D targetRotation)
|
||
{
|
||
if (item == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(item));
|
||
}
|
||
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
var currentComponents = item.Transform.Factor();
|
||
Vector3D currentScale = currentComponents.Scale;
|
||
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
var originalBounds = item.BoundingBox();
|
||
Point3D originalCenter = originalBounds.Center;
|
||
|
||
var rotationTransform = new Transform3D(targetRotation);
|
||
var linear = rotationTransform.Linear;
|
||
|
||
var rotatedCenter = new Point3D(
|
||
linear.Get(0, 0) * originalCenter.X + linear.Get(0, 1) * originalCenter.Y + linear.Get(0, 2) * originalCenter.Z,
|
||
linear.Get(1, 0) * originalCenter.X + linear.Get(1, 1) * originalCenter.Y + linear.Get(1, 2) * originalCenter.Z,
|
||
linear.Get(2, 0) * originalCenter.X + linear.Get(2, 1) * originalCenter.Y + linear.Get(2, 2) * originalCenter.Z);
|
||
|
||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||
var components = identity.Factor();
|
||
components.Scale = currentScale;
|
||
components.Rotation = targetRotation;
|
||
components.Translation = new Vector3D(
|
||
targetCenter.X - rotatedCenter.X,
|
||
targetCenter.Y - rotatedCenter.Y,
|
||
targetCenter.Z - rotatedCenter.Z);
|
||
|
||
doc.Models.OverridePermanentTransform(modelItems, components.Combine(), false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将物体移动到指定中心点和完整三维朝向。
|
||
/// 仅应用位置和旋转,不保留当前缩放。
|
||
/// 仅适用于“几何中心就是业务定位点”的实体,例如缩放已经在 Model 层完成的参考杆。
|
||
/// 不适用于真实模型物体的动画、碰撞验证或场景恢复;真实模型应使用增量三步法,
|
||
/// 即先将固定参考点移到原点,再旋转,再移动到目标位置。
|
||
/// </summary>
|
||
public static void MoveItemToCenterAndRotation(ModelItem item, Point3D targetCenter, Rotation3D targetRotation)
|
||
{
|
||
if (item == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(item));
|
||
}
|
||
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
var originalBounds = item.BoundingBox();
|
||
Point3D originalCenter = originalBounds.Center;
|
||
|
||
var rotationTransform = new Transform3D(targetRotation);
|
||
var linear = rotationTransform.Linear;
|
||
|
||
var rotatedCenter = new Point3D(
|
||
linear.Get(0, 0) * originalCenter.X + linear.Get(0, 1) * originalCenter.Y + linear.Get(0, 2) * originalCenter.Z,
|
||
linear.Get(1, 0) * originalCenter.X + linear.Get(1, 1) * originalCenter.Y + linear.Get(1, 2) * originalCenter.Z,
|
||
linear.Get(2, 0) * originalCenter.X + linear.Get(2, 1) * originalCenter.Y + linear.Get(2, 2) * originalCenter.Z);
|
||
|
||
var translation = new Vector3D(
|
||
targetCenter.X - rotatedCenter.X,
|
||
targetCenter.Y - rotatedCenter.Y,
|
||
targetCenter.Z - rotatedCenter.Z);
|
||
|
||
doc.Models.OverridePermanentTransform(modelItems, new Transform3D(targetRotation, translation), false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将物体的局部正 X 端面中心对齐到目标点,并保留当前缩放。
|
||
/// 适用于单位立方体/单位圆柱体沿 +X 定义长度方向的参考杆资源。
|
||
/// </summary>
|
||
public static void MoveItemPositiveXEndToPointAndRotationWithCurrentScale(ModelItem item, Point3D targetPoint, Rotation3D targetRotation)
|
||
{
|
||
if (item == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(item));
|
||
}
|
||
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
var currentComponents = item.Transform.Factor();
|
||
Vector3D currentScale = currentComponents.Scale;
|
||
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
var originalBounds = item.BoundingBox();
|
||
Point3D originalPositiveXEndCenter = new Point3D(
|
||
originalBounds.Max.X,
|
||
originalBounds.Center.Y,
|
||
originalBounds.Center.Z);
|
||
|
||
var rotationTransform = new Transform3D(targetRotation);
|
||
var linear = rotationTransform.Linear;
|
||
|
||
var rotatedPositiveXEndCenter = new Point3D(
|
||
linear.Get(0, 0) * originalPositiveXEndCenter.X + linear.Get(0, 1) * originalPositiveXEndCenter.Y + linear.Get(0, 2) * originalPositiveXEndCenter.Z,
|
||
linear.Get(1, 0) * originalPositiveXEndCenter.X + linear.Get(1, 1) * originalPositiveXEndCenter.Y + linear.Get(1, 2) * originalPositiveXEndCenter.Z,
|
||
linear.Get(2, 0) * originalPositiveXEndCenter.X + linear.Get(2, 1) * originalPositiveXEndCenter.Y + linear.Get(2, 2) * originalPositiveXEndCenter.Z);
|
||
|
||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||
var components = identity.Factor();
|
||
components.Scale = currentScale;
|
||
components.Rotation = targetRotation;
|
||
components.Translation = new Vector3D(
|
||
targetPoint.X - rotatedPositiveXEndCenter.X,
|
||
targetPoint.Y - rotatedPositiveXEndCenter.Y,
|
||
targetPoint.Z - rotatedPositiveXEndCenter.Z);
|
||
|
||
doc.Models.OverridePermanentTransform(modelItems, components.Combine(), false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将物体的局部正 X 端面中心对齐到目标点。
|
||
/// 仅应用位置和旋转,不保留当前缩放。
|
||
/// 适用于缩放已经在 Model 层完成、Item 层只负责定位的场景。
|
||
/// </summary>
|
||
public static void MoveItemPositiveXEndToPointAndRotation(ModelItem item, Point3D targetPoint, Rotation3D targetRotation)
|
||
{
|
||
if (item == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(item));
|
||
}
|
||
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
var originalBounds = item.BoundingBox();
|
||
Point3D originalPositiveXEndCenter = new Point3D(
|
||
originalBounds.Max.X,
|
||
originalBounds.Center.Y,
|
||
originalBounds.Center.Z);
|
||
|
||
var rotationTransform = new Transform3D(targetRotation);
|
||
var linear = rotationTransform.Linear;
|
||
|
||
var rotatedPositiveXEndCenter = new Point3D(
|
||
linear.Get(0, 0) * originalPositiveXEndCenter.X + linear.Get(0, 1) * originalPositiveXEndCenter.Y + linear.Get(0, 2) * originalPositiveXEndCenter.Z,
|
||
linear.Get(1, 0) * originalPositiveXEndCenter.X + linear.Get(1, 1) * originalPositiveXEndCenter.Y + linear.Get(1, 2) * originalPositiveXEndCenter.Z,
|
||
linear.Get(2, 0) * originalPositiveXEndCenter.X + linear.Get(2, 1) * originalPositiveXEndCenter.Y + linear.Get(2, 2) * originalPositiveXEndCenter.Z);
|
||
|
||
var translation = new Vector3D(
|
||
targetPoint.X - rotatedPositiveXEndCenter.X,
|
||
targetPoint.Y - rotatedPositiveXEndCenter.Y,
|
||
targetPoint.Z - rotatedPositiveXEndCenter.Z);
|
||
|
||
doc.Models.OverridePermanentTransform(modelItems, new Transform3D(targetRotation, translation), false);
|
||
}
|
||
|
||
private static void ApplyAbsoluteTransform(ModelItem item, Point3D targetPosition, Rotation3D targetRotation, bool preserveCurrentScale)
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
Vector3D currentScale = new Vector3D(1, 1, 1);
|
||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||
|
||
if (preserveCurrentScale)
|
||
{
|
||
var currentBaseTransform = geometry?.PermanentOverrideTransform ?? item.Transform;
|
||
var currentComponents = currentBaseTransform.Factor();
|
||
currentScale = currentComponents.Scale;
|
||
}
|
||
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
var originalTransform = geometry?.OriginalTransform ?? item.Transform;
|
||
var originalComponents = originalTransform.Factor();
|
||
var originalRotation = originalComponents.Rotation;
|
||
var originalBounds = geometry?.BoundingBox ?? item.BoundingBox();
|
||
var originalCenterPos = originalBounds.Center;
|
||
|
||
Rotation3D deltaRotation = BuildDeltaRotation(originalRotation, targetRotation);
|
||
var rotationTransform = new Transform3D(deltaRotation);
|
||
var linear = rotationTransform.Linear;
|
||
|
||
var rotatedCenterPos = new Point3D(
|
||
linear.Get(0, 0) * originalCenterPos.X + linear.Get(0, 1) * originalCenterPos.Y + linear.Get(0, 2) * originalCenterPos.Z,
|
||
linear.Get(1, 0) * originalCenterPos.X + linear.Get(1, 1) * originalCenterPos.Y + linear.Get(1, 2) * originalCenterPos.Z,
|
||
linear.Get(2, 0) * originalCenterPos.X + linear.Get(2, 1) * originalCenterPos.Y + linear.Get(2, 2) * originalCenterPos.Z
|
||
);
|
||
|
||
var translation = new Vector3D(
|
||
targetPosition.X - rotatedCenterPos.X,
|
||
targetPosition.Y - rotatedCenterPos.Y,
|
||
targetPosition.Z - rotatedCenterPos.Z
|
||
);
|
||
|
||
Transform3D transform;
|
||
if (preserveCurrentScale)
|
||
{
|
||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||
var components = identity.Factor();
|
||
components.Scale = currentScale;
|
||
components.Rotation = deltaRotation;
|
||
components.Translation = translation;
|
||
transform = components.Combine();
|
||
}
|
||
else
|
||
{
|
||
transform = new Transform3D(deltaRotation, translation);
|
||
}
|
||
|
||
LogAbsoluteTransformDiagnostics(item, originalRotation, targetRotation, deltaRotation, originalCenterPos, targetPosition, translation);
|
||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||
LogAbsoluteTransformActual(item, preserveCurrentScale, currentScale, targetPosition, targetRotation);
|
||
}
|
||
|
||
private static Rotation3D BuildDeltaRotation(Rotation3D originalRotation, Rotation3D targetRotation)
|
||
{
|
||
var originalInverse = originalRotation.Invert();
|
||
var deltaTransform = Transform3D.Multiply(
|
||
new Transform3D(targetRotation),
|
||
new Transform3D(originalInverse));
|
||
return deltaTransform.Factor().Rotation;
|
||
}
|
||
|
||
private static void LogAbsoluteTransformDiagnostics(
|
||
ModelItem item,
|
||
Rotation3D originalRotation,
|
||
Rotation3D targetRotation,
|
||
Rotation3D deltaRotation,
|
||
Point3D originalTrackedPosition,
|
||
Point3D targetPosition,
|
||
Vector3D translation)
|
||
{
|
||
try
|
||
{
|
||
var originalLinear = new Transform3D(originalRotation).Linear;
|
||
var targetLinear = new Transform3D(targetRotation).Linear;
|
||
var deltaLinear = new Transform3D(deltaRotation).Linear;
|
||
|
||
LogManager.Debug(
|
||
$"[模型姿态] {item.DisplayName} 原始: " +
|
||
$"X=({originalLinear.Get(0, 0):F4},{originalLinear.Get(1, 0):F4},{originalLinear.Get(2, 0):F4}), " +
|
||
$"Y=({originalLinear.Get(0, 1):F4},{originalLinear.Get(1, 1):F4},{originalLinear.Get(2, 1):F4}), " +
|
||
$"Z=({originalLinear.Get(0, 2):F4},{originalLinear.Get(1, 2):F4},{originalLinear.Get(2, 2):F4})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型姿态] {item.DisplayName} 目标: " +
|
||
$"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型姿态] {item.DisplayName} 增量: " +
|
||
$"X=({deltaLinear.Get(0, 0):F4},{deltaLinear.Get(1, 0):F4},{deltaLinear.Get(2, 0):F4}), " +
|
||
$"Y=({deltaLinear.Get(0, 1):F4},{deltaLinear.Get(1, 1):F4},{deltaLinear.Get(2, 1):F4}), " +
|
||
$"Z=({deltaLinear.Get(0, 2):F4},{deltaLinear.Get(1, 2):F4},{deltaLinear.Get(2, 2):F4}), " +
|
||
$"原始跟踪点=({originalTrackedPosition.X:F3},{originalTrackedPosition.Y:F3},{originalTrackedPosition.Z:F3}), " +
|
||
$"目标跟踪点=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||
$"平移=({translation.X:F3},{translation.Y:F3},{translation.Z:F3})");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[模型姿态] 输出诊断日志失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private static void LogAbsoluteTransformActual(
|
||
ModelItem item,
|
||
bool preserveCurrentScale,
|
||
Vector3D currentScale,
|
||
Point3D targetPosition,
|
||
Rotation3D targetRotation)
|
||
{
|
||
try
|
||
{
|
||
Transform3D actualTransform = item.Transform;
|
||
var actualComponents = actualTransform.Factor();
|
||
var actualRotation = actualComponents.Rotation;
|
||
var actualScale = actualComponents.Scale;
|
||
var actualLinear = new Transform3D(actualRotation).Linear;
|
||
var targetLinear = new Transform3D(targetRotation).Linear;
|
||
var actualBounds = item.BoundingBox();
|
||
Point3D actualCenter = actualBounds?.Center ?? new Point3D(0, 0, 0);
|
||
|
||
LogManager.Debug(
|
||
$"[模型姿态应用后] {item.DisplayName} PreserveScale={preserveCurrentScale}, " +
|
||
$"输入Scale=({currentScale.X:F4},{currentScale.Y:F4},{currentScale.Z:F4}), " +
|
||
$"实际Scale=({actualScale.X:F4},{actualScale.Y:F4},{actualScale.Z:F4}), " +
|
||
$"目标中心=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||
$"实际中心=({actualCenter.X:F3},{actualCenter.Y:F3},{actualCenter.Z:F3})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型姿态应用后] {item.DisplayName} 目标Transform轴: " +
|
||
$"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||
|
||
LogManager.Debug(
|
||
$"[模型姿态应用后] {item.DisplayName} 实际Transform轴: " +
|
||
$"X=({actualLinear.Get(0, 0):F4},{actualLinear.Get(1, 0):F4},{actualLinear.Get(2, 0):F4}), " +
|
||
$"Y=({actualLinear.Get(0, 1):F4},{actualLinear.Get(1, 1):F4},{actualLinear.Get(2, 1):F4}), " +
|
||
$"Z=({actualLinear.Get(0, 2):F4},{actualLinear.Get(1, 2):F4},{actualLinear.Get(2, 2):F4})");
|
||
|
||
LogGeometryLevelTransforms(item, "[模型姿态应用后][GeometryAPI]");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[模型姿态应用后] 输出诊断日志失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private static void LogGeometryLevelTransforms(ModelItem item, string prefix)
|
||
{
|
||
if (item == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||
if (geometry == null)
|
||
{
|
||
LogManager.Debug($"{prefix} Geometry=null");
|
||
return;
|
||
}
|
||
|
||
LogManager.Debug(
|
||
$"{prefix} GeometryType={geometry.GetType().FullName}, " +
|
||
$"FragmentCount={geometry.FragmentCount}, " +
|
||
$"BoundsCenter=({geometry.BoundingBox.Center.X:F3},{geometry.BoundingBox.Center.Y:F3},{geometry.BoundingBox.Center.Z:F3})");
|
||
|
||
LogTransformProperty(geometry.OriginalTransform, $"{prefix} OriginalTransform");
|
||
LogTransformProperty(geometry.PermanentOverrideTransform, $"{prefix} PermanentOverrideTransform");
|
||
LogTransformProperty(geometry.PermanentTransform, $"{prefix} PermanentTransform");
|
||
LogTransformProperty(geometry.ActiveTransform, $"{prefix} ActiveTransform");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"{prefix} 读取几何层当前变换失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private static void LogTransformProperty(Transform3D transform, string prefix)
|
||
{
|
||
if (transform == null)
|
||
{
|
||
LogManager.Debug($"{prefix}=null");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
var linear = transform.Linear;
|
||
var components = transform.Factor();
|
||
LogManager.Debug(
|
||
$"{prefix}: " +
|
||
$"X=({linear.Get(0, 0):F4},{linear.Get(1, 0):F4},{linear.Get(2, 0):F4}), " +
|
||
$"Y=({linear.Get(0, 1):F4},{linear.Get(1, 1):F4},{linear.Get(2, 1):F4}), " +
|
||
$"Z=({linear.Get(0, 2):F4},{linear.Get(1, 2):F4},{linear.Get(2, 2):F4}), " +
|
||
$"T=({components.Translation.X:F3},{components.Translation.Y:F3},{components.Translation.Z:F3})");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"{prefix} 读取失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将物体移动到指定位置和朝向,同时保持缩放比例
|
||
/// 专为虚拟物体设计,避免缩放被覆盖
|
||
/// </summary>
|
||
/// <param name="item">要移动的物体</param>
|
||
/// <param name="targetPosition">目标位置(地面位置)</param>
|
||
/// <param name="targetYaw">目标朝向(弧度)</param>
|
||
/// <param name="scaleX">X方向缩放</param>
|
||
/// <param name="scaleY">Y方向缩放</param>
|
||
/// <param name="scaleZ">Z方向缩放</param>
|
||
public static void MoveItemToPositionAndYawWithScale(ModelItem item, Point3D targetPosition, double targetYaw,
|
||
double scaleX, double scaleY, double scaleZ)
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { item };
|
||
|
||
// 重置到CAD原始状态
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
// 获取CAD原始状态
|
||
var originalBounds = item.BoundingBox();
|
||
var originalCenterPos = originalBounds.Center;
|
||
var originalYaw = GetYawFromTransform(item.Transform);
|
||
|
||
// 计算从CAD原始位置到目标位置的增量
|
||
var deltaPos = new Vector3D(
|
||
targetPosition.X - originalCenterPos.X,
|
||
targetPosition.Y - originalCenterPos.Y,
|
||
targetPosition.Z - originalCenterPos.Z
|
||
);
|
||
double deltaYaw = targetYaw - originalYaw;
|
||
|
||
// 构建变换组件
|
||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||
var components = identity.Factor();
|
||
|
||
// 应用缩放
|
||
components.Scale = new Vector3D(scaleX, scaleY, scaleZ);
|
||
|
||
// 应用旋转
|
||
if (Math.Abs(deltaYaw) > 0.001)
|
||
{
|
||
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
|
||
}
|
||
|
||
// 计算平移(考虑旋转带来的位置偏移)
|
||
if (Math.Abs(deltaYaw) > 0.001)
|
||
{
|
||
double cos = Math.Cos(deltaYaw);
|
||
double sin = Math.Sin(deltaYaw);
|
||
double rotatedX = originalCenterPos.X * cos - originalCenterPos.Y * sin;
|
||
double rotatedY = originalCenterPos.X * sin + originalCenterPos.Y * cos;
|
||
|
||
components.Translation = new Vector3D(
|
||
targetPosition.X - rotatedX,
|
||
targetPosition.Y - rotatedY,
|
||
targetPosition.Z - originalCenterPos.Z
|
||
);
|
||
}
|
||
else
|
||
{
|
||
components.Translation = deltaPos;
|
||
}
|
||
|
||
// 应用组合变换
|
||
Transform3D transform = components.Combine();
|
||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 物体状态快照,用于保存和恢复
|
||
/// </summary>
|
||
public class ObjectStateSnapshot
|
||
{
|
||
public Point3D Position { get; set; }
|
||
public Rotation3D Rotation { get; set; }
|
||
public bool HasCustomRotation { get; set; }
|
||
}
|
||
}
|
||
}
|