lixiaojun
2025-04-12 19e6eb0b172154f9b44323a4174ff9b61628439d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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);
        }
 
 
 
 
    }
}