新增指定对象聚焦功能

This commit is contained in:
tian 2026-02-10 13:49:43 +08:00
parent 9a7a2da3c6
commit 78158c1cb1
4 changed files with 281 additions and 1 deletions

View File

@ -2212,6 +2212,32 @@ namespace NavisworksTransport.UI.WPF.ViewModels
UpdateMainStatus("已清除手工对象高亮");
}
/// <summary>
/// 聚焦到手工指定的对象
/// </summary>
public void FocusOnManualTarget(ManualCollisionTargetViewModel target)
{
if (target?.ModelItem == null) return;
try
{
// 先高亮该对象
var items = new List<ModelItem> { target.ModelItem };
ModelHighlightHelper.HighlightItems(ManualTargetsHighlightCategory, items);
// 聚焦到对象斜上方45度视角
ViewpointHelper.FocusOnModelItem(target.ModelItem, viewAngleDegrees: 45.0, targetViewRatio: 0.33);
UpdateMainStatus($"已聚焦到: {target.DisplayName}");
LogManager.Info($"聚焦到手工指定对象: {target.DisplayName}");
}
catch (Exception ex)
{
LogManager.Error($"聚焦到手工指定对象失败: {ex.Message}", ex);
UpdateMainStatus($"聚焦失败: {ex.Message}");
}
}
#endregion
#region

View File

@ -186,7 +186,8 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
MinHeight="60"
MaxHeight="200"
BorderBrush="#FFE2E8F0"
BorderThickness="1">
BorderThickness="1"
MouseDoubleClick="OnManualTargetDoubleClick">
<ListView.View>
<GridView>
<GridViewColumn Header="序号" Width="45">

View File

@ -154,5 +154,28 @@ namespace NavisworksTransport.UI.WPF.Views
LogManager.Error($"[AnimationControlView] 处理排除对象选择变化失败: {ex.Message}", ex);
}
}
/// <summary>
/// 手工指定对象列表双击事件处理 - 聚焦到对象
/// </summary>
private void OnManualTargetDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
try
{
if (ViewModel == null) return;
// 获取双击的对象
var listView = sender as ListView;
if (listView?.SelectedItem is ManualCollisionTargetViewModel selectedTarget)
{
// 聚焦到对象
ViewModel.FocusOnManualTarget(selectedTarget);
}
}
catch (Exception ex)
{
LogManager.Error($"[AnimationControlView] 处理手工指定对象双击失败: {ex.Message}", ex);
}
}
}
}

View File

@ -276,5 +276,235 @@ namespace NavisworksTransport.Utils
{
System.Threading.Thread.Sleep(VIEW_UPDATE_DELAY_MS);
}
#region
/// <summary>
/// 聚焦到指定模型元素使用斜上方45度视角
/// 相机会 positioned 在元素斜上方使元素占据视图约1/3宽度
/// </summary>
/// <param name="modelItem">要聚焦的模型元素</param>
/// <param name="viewAngleDegrees">视角角度默认45度斜上方</param>
/// <param name="targetViewRatio">目标占据视图比例默认1/3</param>
public static void FocusOnModelItem(ModelItem modelItem, double viewAngleDegrees = 45.0, double targetViewRatio = 0.25)
{
if (modelItem == null)
{
throw new ArgumentNullException(nameof(modelItem));
}
Document doc = Application.ActiveDocument;
if (doc == null || doc.IsClear)
{
throw new InvalidOperationException("没有活动的文档");
}
try
{
// 1. 获取模型元素的包围盒
BoundingBox3D boundingBox = modelItem.BoundingBox();
if (boundingBox == null || boundingBox.Size == new Vector3D(0, 0, 0))
{
LogManager.Warning($"模型元素 {modelItem.DisplayName} 没有有效的包围盒,使用默认视角");
return;
}
// 2. 计算包围盒中心和尺寸
Point3D center = new Point3D(
(boundingBox.Min.X + boundingBox.Max.X) / 2,
(boundingBox.Min.Y + boundingBox.Max.Y) / 2,
(boundingBox.Min.Z + boundingBox.Max.Z) / 2
);
double maxDimension = Math.Max(
boundingBox.Max.X - boundingBox.Min.X,
Math.Max(boundingBox.Max.Y - boundingBox.Min.Y, boundingBox.Max.Z - boundingBox.Min.Z)
);
LogManager.Info($"聚焦到模型元素: {modelItem.DisplayName}, 最大边: {maxDimension:F2}");
// 3. 计算相机位置(使用模型标准视角方向)
Point3D cameraPosition = CalculateCameraPosition(center, maxDimension, viewAngleDegrees, targetViewRatio);
// 4. 创建视角并设置相机
Viewpoint newViewpoint = doc.CurrentViewpoint.Value.CreateCopy();
newViewpoint.Position = cameraPosition;
// 获取模型的标准视角向量
Vector3D viewDirection = doc.FrontRightTopViewVector;
viewDirection.Normalize();
// 使用 AlignDirection 设置相机朝向(看向目标方向)
// 方向是从相机指向目标
Vector3D lookDirection = new Vector3D(
center.X - cameraPosition.X,
center.Y - cameraPosition.Y,
center.Z - cameraPosition.Z
);
lookDirection.Normalize();
newViewpoint.AlignDirection(lookDirection);
// 使用 AlignUp 设置相机的上方向(基于模型的标准上方向)
Vector3D upVector = doc.FrontRightTopViewUpVector;
upVector.Normalize();
newViewpoint.AlignUp(upVector);
// 5. 应用视角(不使用 ZoomBox让相机距离决定视野比例
doc.CurrentViewpoint.CopyFrom(newViewpoint);
LogManager.Info($"视角已调整: 标准前右上视角, 目标占据视图{targetViewRatio:P0}");
}
catch (Exception ex)
{
LogManager.Error($"聚焦到模型元素失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 聚焦到多个模型元素(如碰撞中的两个对象)
/// </summary>
/// <param name="item1">第一个模型元素(运动物体)</param>
/// <param name="item2">第二个模型元素(被撞对象)</param>
/// <param name="viewAngleDegrees">视角角度默认45度斜上方</param>
public static void FocusOnCollision(ModelItem item1, ModelItem item2, double viewAngleDegrees = 45.0)
{
if (item1 == null && item2 == null)
{
throw new ArgumentException("至少需要一个模型元素");
}
try
{
// 计算两个元素的联合包围盒
BoundingBox3D combinedBounds = null;
if (item1 != null)
{
combinedBounds = item1.BoundingBox();
}
if (item2 != null)
{
if (combinedBounds == null)
{
combinedBounds = item2.BoundingBox();
}
else
{
BoundingBox3D item2Box = item2.BoundingBox();
combinedBounds.Extend(item2Box.Min);
combinedBounds.Extend(item2Box.Max);
}
}
if (combinedBounds == null || combinedBounds.Size == new Vector3D(0, 0, 0))
{
LogManager.Warning("碰撞对象没有有效的包围盒");
return;
}
// 使用联合包围盒的中心点和尺寸
Point3D center = new Point3D(
(combinedBounds.Min.X + combinedBounds.Max.X) / 2,
(combinedBounds.Min.Y + combinedBounds.Max.Y) / 2,
(combinedBounds.Min.Z + combinedBounds.Max.Z) / 2
);
double maxDimension = Math.Max(
combinedBounds.Max.X - combinedBounds.Min.X,
Math.Max(combinedBounds.Max.Y - combinedBounds.Min.Y, combinedBounds.Max.Z - combinedBounds.Min.Z)
);
FocusOnPosition(center, maxDimension, viewAngleDegrees);
LogManager.Info($"已聚焦到碰撞位置: {item1?.DisplayName} ↔ {item2?.DisplayName}");
}
catch (Exception ex)
{
LogManager.Error($"聚焦到碰撞位置失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 聚焦到指定位置和尺寸
/// </summary>
/// <param name="center">目标中心点</param>
/// <param name="targetSize">目标尺寸(最大边)</param>
/// <param name="viewAngleDegrees">视角角度</param>
public static void FocusOnPosition(Point3D center, double targetSize, double viewAngleDegrees = 45.0)
{
Document doc = Application.ActiveDocument;
if (doc == null || doc.IsClear)
{
throw new InvalidOperationException("没有活动的文档");
}
try
{
// 计算相机位置(使用模型标准视角方向)
Point3D cameraPosition = CalculateCameraPosition(center, targetSize, viewAngleDegrees, 0.25);
// 创建视角并设置相机
Viewpoint newViewpoint = doc.CurrentViewpoint.Value.CreateCopy();
newViewpoint.Position = cameraPosition;
// 获取模型的标准视角向量
Vector3D viewDirection = doc.FrontRightTopViewVector;
viewDirection.Normalize();
// 使用 AlignDirection 设置相机朝向
Vector3D lookDirection = new Vector3D(
center.X - cameraPosition.X,
center.Y - cameraPosition.Y,
center.Z - cameraPosition.Z
);
lookDirection.Normalize();
newViewpoint.AlignDirection(lookDirection);
// 使用 AlignUp 设置相机的上方向
Vector3D upVector = doc.FrontRightTopViewUpVector;
upVector.Normalize();
newViewpoint.AlignUp(upVector);
// 应用视角(不使用 ZoomBox让相机距离决定视野比例
doc.CurrentViewpoint.CopyFrom(newViewpoint);
}
catch (Exception ex)
{
LogManager.Error($"聚焦到位置失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 计算相机位置(使用模型标准前右上视角)
/// 基于 Navisworks 的标准 FrontRightTopViewVector 确定相机方向
/// </summary>
private static Point3D CalculateCameraPosition(Point3D center, double targetSize, double viewAngleDegrees, double targetViewRatio)
{
// 计算相机距离(目标占据视图 targetViewRatio
double fovRadians = 0.785398; // Navisworks 默认 FOV 45度
double cameraDistance = (targetSize / 2.0) / Math.Tan(fovRadians / 2.0) / targetViewRatio;
cameraDistance *= 1.2; // 添加边距
// 获取模型的标准前右上视角向量
// 这个向量定义了模型的"正方向",基于模型坐标系
Vector3D viewDirection = Application.ActiveDocument.FrontRightTopViewVector;
// 归一化方向向量
viewDirection.Normalize();
// 沿标准视角方向偏移相机位置
// 相机位置 = 目标中心 - 方向向量 * 距离
return new Point3D(
center.X - viewDirection.X * cameraDistance,
center.Y - viewDirection.Y * cameraDistance,
center.Z - viewDirection.Z * cameraDistance
);
}
#endregion
}
}