namespace Yw.WpfUI.Hydro
{
///
/// 抽象缩放辅助类
///
internal class LogicalZoomHelper
{
///
///
///
public LogicalZoomHelper(HelixViewport3D viewport)
{
_viewport = viewport;
}
private readonly HelixViewport3D _viewport;// 三维组件
private readonly double _animationDuration = 0.5; // 动画持续时间(秒)
private readonly double _zoomFactor = 1.2; // 缩放因子(在边界框外留些空间)
///
/// 缩放至边界框
///
public void ZoomToBounds(Rect3D bounds)
{
if (bounds.IsEmpty)
{
return;
}
if (!(_viewport.Camera is PerspectiveCamera camera))
{
return;
}
// 计算目标点(边界框中心)
var target = new Point3D(
bounds.X + bounds.SizeX / 2,
bounds.Y + bounds.SizeY / 2,
bounds.Z + bounds.SizeZ / 2);
// 计算相机到目标点的距离
double distance = CalculateOptimalDistance(bounds, camera);
// 计算新相机位置
Vector3D lookDirection = camera.LookDirection;
lookDirection.Normalize();
var newPosition = target - lookDirection * distance;
// 创建动画
AnimateCamera(camera, newPosition, target, _animationDuration);
}
///
/// 缩放至Visual
///
public void ZoomToVisual(LogicalVisual3D visual3d)
{
if (visual3d == null)
{
return;
}
// 获取模型的边界框
var bounds = Visual3DHelper.FindBounds(visual3d, Transform3D.Identity);
ZoomToBounds(bounds);
}
///
/// 缩放至Visual
///
public void ZoomToVisual(List visual3ds)
{
if (visual3ds == null || visual3ds.Count < 1)
{
return;
}
// 计算所有模型的联合边界框
var bounds = Rect3D.Empty;
foreach (var visual in visual3ds)
{
bounds.Union(Visual3DHelper.FindBounds(visual, Transform3D.Identity));
}
ZoomToBounds(bounds);
}
///
/// 缩放至边界框(无动画)
///
public void ZoomDirectToBounds(Rect3D bounds)
{
if (bounds.IsEmpty)
{
return;
}
if (!(_viewport.Camera is PerspectiveCamera camera))
{
return;
}
var target = new Point3D(
bounds.X + bounds.SizeX / 2,
bounds.Y + bounds.SizeY / 2,
bounds.Z + bounds.SizeZ / 2);
double distance = CalculateOptimalDistance(bounds, camera);
Vector3D lookDirection = camera.LookDirection;
lookDirection.Normalize();
camera.Position = target - lookDirection * distance;
camera.LookDirection = target - camera.Position;
}
///
/// 缩放至Visual(无动画)
///
public void ZoomDirectToVisual(Visual3D visual)
{
if (visual == null)
return;
// 获取模型的边界框
var bounds = Visual3DHelper.FindBounds(visual, Transform3D.Identity);
ZoomDirectToBounds(bounds);
}
///
/// 缩放至Visuals(无动画)
///
public void ZoomDirectToVisuals(List visuals)
{
if (visuals == null || visuals.Count < 1)
{
return;
}
// 计算所有模型的联合边界框
var bounds = Rect3D.Empty;
foreach (var visual in visuals)
{
bounds.Union(Visual3DHelper.FindBounds(visual, Transform3D.Identity));
}
ZoomDirectToBounds(bounds);
}
//计算最佳观察距离
private double CalculateOptimalDistance(Rect3D bounds, PerspectiveCamera camera)
{
// 计算边界框的对角线长度
double diagonal = Math.Sqrt(
bounds.SizeX * bounds.SizeX +
bounds.SizeY * bounds.SizeY +
bounds.SizeZ * bounds.SizeZ);
// 根据视野角度计算所需距离
double fov = Math.PI * camera.FieldOfView / 180.0;
double distance = _zoomFactor * diagonal / (2 * Math.Tan(fov / 2));
// 确保距离不为零且不小于最小距离
return Math.Max(distance, diagonal * 0.5);
}
//执行相机动画
private void AnimateCamera(PerspectiveCamera camera, Point3D newPosition, Point3D target, double duration)
{
// 创建位置动画
var posAnim = new Point3DAnimation(
camera.Position,
newPosition,
new Duration(TimeSpan.FromSeconds(duration)))
{
AccelerationRatio = 0.3,
DecelerationRatio = 0.5,
FillBehavior = FillBehavior.Stop
};
// 创建观察方向动画
var lookAnim = new Vector3DAnimation(
camera.LookDirection,
target - newPosition,
new Duration(TimeSpan.FromSeconds(duration)))
{
AccelerationRatio = 0.3,
DecelerationRatio = 0.5,
FillBehavior = FillBehavior.Stop
};
// 开始动画
camera.BeginAnimation(ProjectionCamera.PositionProperty, posAnim);
camera.BeginAnimation(ProjectionCamera.LookDirectionProperty, lookAnim);
}
}
}