1use crate::{Delta, Frame, Plane, Scope};
2use core::fmt::Debug;
3use nalgebra::{Point3, RealField, UnitQuaternion};
4
5pub trait Clamp<N: Copy + RealField>: Send + Sync + Debug + 'static {
20 #[must_use]
26 fn loops(&self) -> usize {
27 100
28 }
29
30 #[must_use]
34 fn target(&self, frame: &Frame<N>) -> Option<Plane<N>>;
35 #[must_use]
39 fn eye(&self, frame: &Frame<N>) -> Option<Plane<N>>;
40 #[must_use]
44 fn up(&self, frame: &Frame<N>) -> Option<Plane<N>>;
45
46 #[allow(clippy::too_many_lines)]
50 #[must_use]
51 fn compute(
52 &self,
53 frame: &Frame<N>,
54 scope: &Scope<N>,
55 delta: &Delta<N>,
56 ) -> Option<(Delta<N>, usize)> {
57 match delta {
58 Delta::Frame => None,
59 &Delta::First {
60 pitch: _,
61 yaw: _,
62 yaw_axis,
63 } => {
64 let eye = frame.eye();
65 let distance = frame.distance();
66 let pitch_axis = frame.pitch_axis();
67 let old_target = frame.target() - eye;
69 let mut min_delta = *delta;
70 let mut loops = 0;
71 loop {
72 let frame = min_delta.transform(frame);
73 let mut bound = false;
74 if let Some(plane) = self.target(&frame) {
75 bound = true;
76 let center = plane.project_point(&eye);
78 let height = distance - (center - eye).norm();
80 let radius = (height * (distance * (N::one() + N::one()) - height)).sqrt();
82 let new_target = (plane.project_point(frame.target()) - center)
84 .normalize()
85 .scale(radius);
86 let new_target = center + new_target;
88 let new_target = new_target - eye;
90
91 let pitch_plane = Plane::with_point(pitch_axis, &eye);
93 let old_pitch_target = pitch_plane.project_vector(&old_target);
94 let new_pitch_target = pitch_plane.project_vector(&new_target);
95 let pitch = pitch_plane.angle_between(&old_pitch_target, &new_pitch_target);
96 let pitch_rot = UnitQuaternion::from_axis_angle(&pitch_axis, pitch);
98 let old_target = pitch_rot * old_target;
99 let yaw_plane = Plane::with_point(yaw_axis, &eye);
101 let old_yaw_target = yaw_plane.project_vector(&old_target);
102 let new_yaw_target = yaw_plane.project_vector(&new_target);
103 let yaw = yaw_plane.angle_between(&old_yaw_target, &new_yaw_target);
104
105 #[allow(clippy::no_effect_underscore_binding)]
107 let _min_delta = Delta::First {
108 pitch,
109 yaw,
110 yaw_axis,
111 };
112 min_delta = Delta::Frame;
113 }
114 if bound {
115 if loops == self.loops() {
116 break;
117 }
118 loops += 1;
119 } else {
120 break;
121 }
122 }
123 (min_delta != *delta).then_some((min_delta, loops))
124 }
125 Delta::Track { vec: _ } => {
126 let old_frame = frame;
127 let old_target = frame.target();
128 let old_rot_inverse = frame.view().rotation.inverse();
129 let mut min_delta = *delta;
130 let mut loops = 0;
131 loop {
132 let frame = min_delta.transform(old_frame);
133 let mut bound = false;
134 if let Some(plane) = self.target(&frame) {
135 bound = true;
136 let new_target = plane.project_point(frame.target());
137 let vec = old_rot_inverse * (new_target - old_target);
138 min_delta = Delta::Track { vec };
139 }
140 let frame = min_delta.transform(old_frame);
141 if let Some(_plane) = self.up(&frame) {
142 bound = true;
143 min_delta = Delta::Frame;
145 }
146 if bound {
147 if loops == self.loops() {
148 break;
149 }
150 loops += 1;
151 } else {
152 break;
153 }
154 }
155 (min_delta != *delta).then_some((min_delta, loops))
156 }
157 &Delta::Orbit { rot: _, pos } => {
158 if pos != Point3::origin() {
159 return Some((Delta::Frame, 0));
160 }
161 let old_frame = frame;
162 let distance = frame.distance();
163 let target = frame.target();
164 let old_rot_inverse = frame.view().rotation.inverse();
166 let old_eye = old_rot_inverse * (frame.eye() - target);
168 let mut min_delta = *delta;
169 let mut loops = 0;
170 loop {
171 let mut bound = false;
172 let frame = min_delta.transform(old_frame);
173 if let Some(plane) = self.eye(&frame) {
174 bound = true;
175 let center = plane.project_point(target);
177 let height = distance - (center - target).norm();
179 let radius = (height * (distance * (N::one() + N::one()) - height)).sqrt();
181 let new_eye = (plane.project_point(&frame.eye()) - center)
183 .normalize()
184 .scale(radius);
185 let new_eye = center + new_eye;
187 let new_eye = old_rot_inverse * (new_eye - target);
189 let rot = UnitQuaternion::rotation_between(&old_eye, &new_eye)
191 .unwrap_or_default();
192 min_delta = Delta::Orbit { rot, pos };
193 }
194 let frame = min_delta.transform(old_frame);
195 if let Some(_plane) = self.up(&frame) {
196 bound = true;
197 min_delta = Delta::Frame;
199 }
200 if bound {
201 if loops == self.loops() {
202 break;
203 }
204 loops += 1;
205 } else {
206 break;
207 }
208 }
209 (min_delta != *delta).then_some((min_delta, loops))
210 }
211 Delta::Slide { vec: _ } => {
212 let old_frame = frame;
213 let old_target = frame.target();
214 let old_rot_inverse = frame.view().rotation.inverse();
215 let old_eye = frame.eye();
216 let mut min_delta = *delta;
217 let mut loops = 0;
218 loop {
219 let frame = min_delta.transform(old_frame);
220 let mut bound = false;
221 if let Some(plane) = self.target(&frame) {
222 bound = true;
223 let new_target = plane.project_point(frame.target());
224 let vec = old_rot_inverse * (new_target - old_target);
225 min_delta = Delta::Slide { vec };
226 }
227 let frame = min_delta.transform(old_frame);
228 if let Some(plane) = self.eye(&frame) {
229 bound = true;
230 let new_eye = plane.project_point(&frame.eye());
231 let vec = old_rot_inverse * (new_eye - old_eye);
232 min_delta = Delta::Slide { vec };
233 }
234 if bound {
235 if loops == self.loops() {
236 break;
237 }
238 loops += 1;
239 } else {
240 break;
241 }
242 }
243 (min_delta != *delta).then_some((min_delta, loops))
244 }
245 &Delta::Scale { mut rat, pos } => {
246 let old_zat = frame.distance();
247 let mut min_delta = *delta;
248 let mut loops = 0;
249 loop {
250 let frame = min_delta.transform(frame);
251 let mut bound = false;
252 if let Some(_plane) = self.eye(&frame) {
253 bound = true;
254 min_delta = Delta::Frame;
256 }
257 if scope.scale() {
258 let (znear, _zfar) = scope.clip_planes(N::zero());
259 let min_zat = -znear * (N::one() + N::default_epsilon().sqrt());
260 let new_zat = old_zat * rat;
261 if new_zat < min_zat {
262 bound = true;
263 rat = min_zat / old_zat;
265 min_delta = Delta::Scale { rat, pos };
266 }
267 }
268 if bound {
269 if loops == self.loops() {
270 break;
271 }
272 loops += 1;
273 } else {
274 break;
275 }
276 }
277 (min_delta != *delta).then_some((min_delta, loops))
278 }
279 }
280 }
281}