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); } } }