namespace Yw.WpfUI.Hydro
|
{
|
/// <summary>
|
/// 抽象缩放辅助类
|
/// </summary>
|
internal class LogicalZoomHelper
|
{
|
/// <summary>
|
///
|
/// </summary>
|
public LogicalZoomHelper(HelixViewport3D viewport)
|
{
|
_viewport = viewport;
|
}
|
|
private readonly HelixViewport3D _viewport;//三维组件
|
private readonly double _animationDuration = 0.5; // 动画持续时间(秒)
|
private readonly double _zoomFactor = 1.2; // 缩放因子(在边界框外留些空间)
|
|
/// <summary>
|
/// 缩放至边界框
|
/// </summary>
|
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);
|
}
|
|
/// <summary>
|
/// 缩放至Visual
|
/// </summary>
|
public void ZoomToVisual(Visual3D visual)
|
{
|
if (visual == null)
|
return;
|
|
// 获取模型的边界框
|
var bounds = Visual3DHelper.FindBounds(visual, Transform3D.Identity);
|
ZoomToBounds(bounds);
|
}
|
|
/// <summary>
|
/// 缩放至Visuals
|
/// </summary>
|
public void ZoomToVisuals(List<Visual3D> visuals)
|
{
|
if (visuals == null || visuals.Count < 1)
|
{
|
return;
|
}
|
|
// 计算所有模型的联合边界框
|
var bounds = Rect3D.Empty;
|
foreach (var visual in visuals)
|
{
|
bounds.Union(Visual3DHelper.FindBounds(visual, Transform3D.Identity));
|
}
|
|
ZoomToBounds(bounds);
|
}
|
|
/// <summary>
|
/// 缩放至边界框(无动画)
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 缩放至Visual(无动画)
|
/// </summary>
|
public void ZoomDirectToVisual(Visual3D visual)
|
{
|
if (visual == null)
|
return;
|
|
// 获取模型的边界框
|
var bounds = Visual3DHelper.FindBounds(visual, Transform3D.Identity);
|
ZoomDirectToBounds(bounds);
|
}
|
|
/// <summary>
|
/// 缩放至Visuals(无动画)
|
/// </summary>
|
public void ZoomDirectToVisuals(List<Visual3D> 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);
|
}
|
|
|
|
|
}
|
}
|