1use crate::animations::Instant;
12#[cfg(all(test, not(feature = "std")))]
13use num_traits::Float;
14
15#[derive(Debug)]
17enum Direction {
18 Increasing,
20 Decreasing,
22}
23
24pub trait Simulation {
27 fn step(&mut self, new_tick: Instant) -> (f32, bool);
28 fn curr_value(&self) -> f32;
29}
30
31pub trait Parameter {
34 type Output;
35 fn simulation(self, start_value: f32, limit_value: f32) -> Self::Output;
36}
37
38#[derive(Debug, Clone)]
40pub struct ConstantDecelerationParameters {
41 pub initial_velocity: f32,
42 pub deceleration: f32,
43}
44
45impl ConstantDecelerationParameters {
46 pub fn new(initial_velocity: f32, deceleration: f32) -> Self {
47 Self { initial_velocity, deceleration }
48 }
49}
50
51impl Parameter for ConstantDecelerationParameters {
52 type Output = ConstantDeceleration;
53 fn simulation(self, start_value: f32, limit_value: f32) -> Self::Output {
54 ConstantDeceleration::new(start_value, limit_value, self)
55 }
56}
57
58#[derive(Debug)]
61pub struct ConstantDeceleration {
62 limit_value: f32,
65 curr_val: f32,
66 velocity: f32,
67 data: ConstantDecelerationParameters,
68 direction: Direction,
69 start_time: Instant,
70}
71
72impl ConstantDeceleration {
73 pub fn new(start_value: f32, limit_value: f32, data: ConstantDecelerationParameters) -> Self {
80 Self::new_internal(start_value, limit_value, data, crate::animations::current_tick())
81 }
82
83 fn new_internal(
84 start_value: f32,
85 limit_value: f32,
86 mut data: ConstantDecelerationParameters,
87 start_time: Instant,
88 ) -> Self {
89 let mut initial_velocity = data.initial_velocity;
90 let direction = if start_value == limit_value {
91 if initial_velocity >= 0. {
92 data.deceleration = f32::abs(data.deceleration);
93 Direction::Increasing
94 } else {
95 data.deceleration = -f32::abs(data.deceleration);
96 Direction::Decreasing
97 }
98 } else if start_value < limit_value {
99 data.deceleration = f32::abs(data.deceleration);
100 assert!(initial_velocity >= 0.); initial_velocity = f32::abs(initial_velocity);
102 Direction::Increasing
103 } else {
104 data.deceleration = -f32::abs(data.deceleration);
105 initial_velocity = -f32::abs(initial_velocity);
106 assert!(initial_velocity <= 0.);
107 Direction::Decreasing
108 };
109
110 Self {
111 limit_value,
112 curr_val: start_value,
113 velocity: initial_velocity,
114 data,
115 direction,
116 start_time,
117 }
118 }
119
120 fn step_internal(&mut self, new_tick: Instant) -> (f32, bool) {
121 let duration = f32::min(
123 new_tick.duration_since(self.start_time).as_secs_f32(),
124 f32::abs(self.velocity / self.data.deceleration),
125 );
126
127 self.start_time = new_tick;
128
129 let new_velocity = self.velocity - duration * self.data.deceleration;
130
131 self.curr_val += duration * (self.velocity + new_velocity) / 2.; self.velocity = new_velocity;
133
134 match self.direction {
135 Direction::Increasing => {
136 if self.curr_val >= self.limit_value {
137 self.curr_val = self.limit_value;
138 self.velocity = 0.;
139 return (self.curr_val, true);
140 } else if self.velocity <= 0. {
141 return (self.curr_val, true);
142 }
143 }
144 Direction::Decreasing => {
145 if self.curr_val <= self.limit_value {
146 self.curr_val = self.limit_value;
147 self.velocity = 0.;
148 return (self.curr_val, true);
149 } else if self.velocity >= 0. {
150 return (self.curr_val, true);
151 }
152 }
153 }
154 (self.curr_val, false)
155 }
156}
157
158impl Simulation for ConstantDeceleration {
159 fn curr_value(&self) -> f32 {
160 self.curr_val
161 }
162
163 fn step(&mut self, new_tick: Instant) -> (f32, bool) {
164 self.step_internal(new_tick)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use core::time::Duration;
172
173 #[test]
174 fn constant_deceleration_start_eq_limit() {
175 const START_VALUE: f32 = 10.;
176 const LIMIT_VALUE: f32 = 10.;
177 const INITIAL_VELOCITY: f32 = 50.;
178 const DECELERATION: f32 = 20.;
179 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
180
181 let time = Instant::now();
182 let mut simulation =
183 ConstantDeceleration::new_internal(START_VALUE, LIMIT_VALUE, parameters, time.clone());
184
185 let res = simulation.step(time + Duration::from_hours(10));
186 assert_eq!(res.1, true);
187 assert_eq!(res.0, START_VALUE);
188 }
189
190 #[test]
193 fn constant_deceleration_increasing_limit_not_reached() {
194 const START_VALUE: f32 = 10.;
195 const LIMIT_VALUE: f32 = 2000.;
196 const INITIAL_VELOCITY: f32 = 50.;
197 const DECELERATION: f32 = 20.;
198 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
199
200 let mut time = Instant::now();
201 let mut simulation =
202 ConstantDeceleration::new_internal(START_VALUE, LIMIT_VALUE, parameters, time.clone());
203
204 let mut duration = Duration::from_secs(1);
206 assert!(DECELERATION * duration.as_secs_f32() < INITIAL_VELOCITY);
207 time += duration;
208 let (res, finished) = simulation.step(time);
209 assert_eq!(finished, false);
210 assert_eq!(
211 res,
212 START_VALUE + INITIAL_VELOCITY * duration.as_secs_f32()
213 - 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
214 );
215
216 duration = Duration::from_hours(10);
218 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
219 time += duration;
220 let (res, finished) = simulation.step(time);
221 assert_eq!(finished, true);
222 assert_eq!(
223 res,
224 START_VALUE + INITIAL_VELOCITY * INITIAL_VELOCITY / DECELERATION
225 - 0.5 * DECELERATION * (INITIAL_VELOCITY / DECELERATION).powi(2)
226 );
227
228 assert!(res < LIMIT_VALUE); }
230
231 #[test]
233 fn constant_deceleration_increasing_limit_reached() {
234 const START_VALUE: f32 = 10.;
235 const LIMIT_VALUE: f32 = 20.;
236 const INITIAL_VELOCITY: f32 = 50.;
237 const DECELERATION: f32 = 20.;
238 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
239
240 let mut time = Instant::now();
241 let mut simulation =
242 ConstantDeceleration::new_internal(START_VALUE, LIMIT_VALUE, parameters, time.clone());
243
244 let duration = Duration::from_secs(1);
245 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) < f32::abs(INITIAL_VELOCITY)); time += duration;
247 let (res, finished) = simulation.step(time);
248 assert_eq!(finished, true);
249 assert_eq!(res, LIMIT_VALUE); }
251
252 #[test]
255 fn constant_deceleration_decreasing_limit_not_reached() {
256 const START_VALUE: f32 = 2000.;
257 const LIMIT_VALUE: f32 = 10.;
258 const INITIAL_VELOCITY: f32 = -50.;
259 const DECELERATION: f32 = 20.;
260
261 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
262
263 let mut time = Instant::now();
264 let mut simulation =
265 ConstantDeceleration::new_internal(START_VALUE, LIMIT_VALUE, parameters, time.clone());
266
267 let mut duration = Duration::from_secs(1);
268 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) < f32::abs(INITIAL_VELOCITY));
269 time += duration;
270 let (res, finished) = simulation.step(time);
271 assert_eq!(finished, false);
272 assert_eq!(
273 res,
274 START_VALUE + INITIAL_VELOCITY * duration.as_secs_f32()
275 - INITIAL_VELOCITY.signum() * 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
276 );
277
278 duration = Duration::from_hours(10);
279 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
280 time += duration;
281 let (res, finished) = simulation.step(time);
282 assert_eq!(finished, true);
283 assert_eq!(
284 res,
285 START_VALUE + INITIAL_VELOCITY * f32::abs(INITIAL_VELOCITY / DECELERATION)
286 - 0.5
287 * INITIAL_VELOCITY.signum()
288 * DECELERATION
289 * (INITIAL_VELOCITY / DECELERATION).powi(2)
290 );
291
292 assert!(res > LIMIT_VALUE); }
294
295 #[test]
298 fn constant_deceleration_decreasing_limit_reached() {
299 const START_VALUE: f32 = 20.;
300 const LIMIT_VALUE: f32 = 10.;
301 const INITIAL_VELOCITY: f32 = -50.;
302 const DECELERATION: f32 = 20.;
303 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
304
305 let mut time = Instant::now();
306 let mut simulation =
307 ConstantDeceleration::new_internal(START_VALUE, LIMIT_VALUE, parameters, time.clone());
308
309 let duration = Duration::from_secs(3);
310 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) > f32::abs(INITIAL_VELOCITY)); time += duration;
312 let (res, finished) = simulation.step(time);
313 assert_eq!(finished, true);
314 assert_eq!(res, LIMIT_VALUE); }
316}
317
318#[cfg(test)]
321#[derive(Debug, Clone)]
322pub struct ConstantDecelerationSpringDamperParameters {
323 pub initial_velocity: f32,
324 pub deceleration: f32,
325 pub mass: f32, pub spring_constant: f32, pub damping_coefficient: f32, }
329
330#[cfg(test)]
331impl ConstantDecelerationSpringDamperParameters {
332 pub fn new(initial_velocity: f32, deceleration: f32, half_period_time: f32) -> Self {
340 let (mass, spring_constant, damping_coefficient) =
341 Self::calculate_parameters(half_period_time);
342
343 Self { initial_velocity, deceleration, mass, spring_constant, damping_coefficient }
344 }
345
346 fn calculate_parameters(half_period_time: f32) -> (f32, f32, f32) {
347 const MASS: f32 = 1.;
349 const DAMPING_COEFFICIENT: f32 = 1.;
350 let w_d = 2. * core::f32::consts::PI * 1. / (2. * half_period_time);
351 let spring_constant = w_d.powi(2) + DAMPING_COEFFICIENT.powi(2) / (4. * MASS.powi(2));
352
353 (MASS, spring_constant, DAMPING_COEFFICIENT)
354 }
355}
356
357#[cfg(test)]
358impl Parameter for ConstantDecelerationSpringDamperParameters {
359 type Output = ConstantDecelerationSpringDamper;
360 fn simulation(self, start_value: f32, limit_value: f32) -> Self::Output {
361 ConstantDecelerationSpringDamper::new(start_value, limit_value, self)
362 }
363}
364
365#[cfg(test)]
366#[derive(Debug, PartialEq)]
367enum State {
368 Deceleration,
369 SpringDamper,
370 Done,
371}
372
373#[cfg(test)]
378#[derive(Debug)]
379pub struct ConstantDecelerationSpringDamper {
380 limit_value: f32,
383 curr_val_zeroed: f32,
384 curr_val: f32,
385 velocity: f32,
386 data: ConstantDecelerationSpringDamperParameters,
387 direction: Direction,
388 start_time: Instant,
389 state: State,
390 damping_ratio: f32,
391 w_n: f32,
393 w_d: f32,
395 constant_a: f32,
396 constant_phi: f32,
397}
398
399#[cfg(test)]
400impl ConstantDecelerationSpringDamper {
401 pub fn new(
402 start_value: f32,
403 limit_value: f32,
404 data: ConstantDecelerationSpringDamperParameters,
405 ) -> Self {
406 Self::new_internal(start_value, limit_value, data, crate::animations::current_tick())
407 }
408
409 fn new_internal(
410 start_value: f32,
411 limit_value: f32,
412 mut data: ConstantDecelerationSpringDamperParameters,
413 start_time: Instant,
414 ) -> Self {
415 let mut initial_velocity = data.initial_velocity;
416 let mut state = State::Deceleration;
417 let direction = if start_value == limit_value {
418 state = State::Done;
419 if initial_velocity >= 0. {
420 data.deceleration = f32::abs(data.deceleration);
421 Direction::Increasing
422 } else {
423 data.deceleration = -f32::abs(data.deceleration);
424 Direction::Decreasing
425 }
426 } else if start_value < limit_value {
427 data.deceleration = f32::abs(data.deceleration);
428 assert!(initial_velocity >= 0.); initial_velocity = f32::abs(initial_velocity);
430 Direction::Increasing
431 } else {
432 data.deceleration = -f32::abs(data.deceleration);
433 initial_velocity = -f32::abs(initial_velocity);
434 assert!(initial_velocity <= 0.);
435 Direction::Decreasing
436 };
437
438 assert!(data.mass > 0.);
439 assert!(data.spring_constant >= 0.);
440
441 let c_cr = 2. * f32::sqrt(data.mass * data.spring_constant); let damping_ratio = data.damping_coefficient / c_cr;
443 assert!(damping_ratio > 0.);
444 assert!(damping_ratio < 1.); let w_n = c_cr / (2. * data.mass);
447 let w_d = w_n * f32::sqrt(1. - damping_ratio.powi(2));
448
449 Self {
450 limit_value,
451 curr_val: start_value,
452 curr_val_zeroed: 0.,
453 velocity: initial_velocity,
454 data,
455 direction,
456 start_time,
457 state,
458 damping_ratio,
459 w_n,
460 w_d,
461 constant_a: 0., constant_phi: 0., }
464 }
465
466 fn step_internal(&mut self, new_tick: Instant) -> (f32, bool) {
467 match self.state {
468 State::Deceleration => self.state_deceleration(new_tick),
469 State::SpringDamper => self.state_spring_damper(new_tick),
470 State::Done => (self.curr_value(), true),
471 }
472 }
473
474 fn state_deceleration(&mut self, new_tick: Instant) -> (f32, bool) {
475 let duration_unlimited = new_tick.duration_since(self.start_time);
476 let duration = f32::min(
478 duration_unlimited.as_secs_f32(),
479 f32::abs(self.velocity / self.data.deceleration),
480 );
481
482 self.start_time = new_tick;
483
484 let new_velocity = self.velocity - (duration * self.data.deceleration);
485 let new_val = self.curr_val + (duration * (self.velocity + new_velocity) / 2.); enum S {
488 LimitReached,
489 VelocityZero,
490 None,
491 }
492
493 let s = match self.direction {
494 Direction::Increasing if new_val > self.limit_value => S::LimitReached,
495 Direction::Increasing if new_velocity <= 0. => S::VelocityZero,
496 Direction::Decreasing if new_val < self.limit_value => S::LimitReached,
497 Direction::Decreasing if new_velocity >= 0. => S::VelocityZero,
498 _ => S::None,
499 };
500 match s {
501 S::LimitReached => {
502 self.state = State::SpringDamper;
503
504 let root = f32::sqrt(
507 self.velocity.powi(2)
508 - self.data.deceleration * (self.limit_value - self.curr_val) as f32,
509 );
510 let dt = f32::min(
512 (self.velocity - root) / self.data.deceleration,
513 (self.velocity + root) / self.data.deceleration,
514 );
515
516 self.velocity -= dt * self.data.deceleration; self.curr_val_zeroed = 0.;
518 self.curr_val = self.limit_value;
519
520 const X0: f32 = 0.; self.constant_a = self.velocity.signum()
522 * f32::sqrt(
523 (self.w_d.powi(2) * X0.powi(2)
524 + (self.velocity + self.damping_ratio * self.w_n * 0.).powi(2))
525 / self.w_d.powi(2),
526 );
527 self.constant_phi =
528 f32::atan(self.w_d * X0 / (self.velocity + self.damping_ratio * self.w_n * X0));
529 self.state_spring_damper(
530 new_tick
531 + (duration_unlimited
532 - core::time::Duration::from_millis((dt * 1000.) as u64)),
533 )
534 }
535 S::VelocityZero => {
536 self.velocity = 0.;
537 self.curr_val = new_val;
538 (self.curr_val, true)
539 }
540 S::None => {
541 self.velocity = new_velocity;
542 self.curr_val = new_val;
543 (self.curr_val, false)
544 }
545 }
546 }
547
548 fn state_spring_damper(&mut self, new_tick: Instant) -> (f32, bool) {
549 let t = (new_tick - self.start_time).as_secs_f32();
551 assert!(self.damping_ratio < 1.);
553 let new_val = self.constant_a
554 * f32::exp(-self.damping_ratio * self.w_n * t)
555 * f32::sin(self.w_d * t + self.constant_phi);
556 self.curr_val_zeroed = new_val; let max_time = 2. * core::f32::consts::PI / self.w_d;
559 let current_val = self.curr_value();
560 let finished = match self.direction {
561 Direction::Increasing => {
562 current_val < self.limit_value || t > max_time
564 }
565 Direction::Decreasing => {
566 current_val > self.limit_value || t > max_time
568 }
569 };
570 if finished {
571 self.velocity = 0.;
572 self.curr_val = self.limit_value;
573 self.curr_val_zeroed = 0.;
574 self.state = State::Done;
575 }
576 (current_val, finished)
577 }
578}
579
580#[cfg(test)]
581impl Simulation for ConstantDecelerationSpringDamper {
582 fn curr_value(&self) -> f32 {
583 self.curr_val + self.curr_val_zeroed
584 }
585
586 fn step(&mut self, new_tick: Instant) -> (f32, bool) {
587 self.step_internal(new_tick)
588 }
589}
590
591#[cfg(test)]
592mod tests_spring_damper {
593 use super::*;
594 use core::{f32::consts::PI, time::Duration};
595
596 #[test]
597 fn calculate_parameters() {
598 const INITIAL_VELOCITY: f32 = 50.;
599 const DECELERATION: f32 = 20.;
600 const HALF_PERIOD_TIME: f32 = 100e-3;
601 let res = super::ConstantDecelerationSpringDamperParameters::new(
602 INITIAL_VELOCITY,
603 DECELERATION,
604 HALF_PERIOD_TIME,
605 );
606
607 let w_n = f32::sqrt(res.spring_constant * res.mass) / res.mass;
608 let damping_ratio = res.damping_coefficient / (2. * res.mass * w_n);
609 let w_d = w_n * f32::sqrt(1. - damping_ratio.powi(2));
610 assert_eq!(w_d, 2. * PI * 1. / (2. * HALF_PERIOD_TIME));
611 }
612
613 #[test]
614 fn constant_deceleration_start_eq_limit() {
615 const START_VALUE: f32 = 10.;
616 const LIMIT_VALUE: f32 = 10.;
617 const INITIAL_VELOCITY: f32 = 50.;
618 const DECELERATION: f32 = 20.;
619 const HALF_PERIOD_TIME: f32 = 100e-3;
620 let parameters = ConstantDecelerationSpringDamperParameters::new(
621 INITIAL_VELOCITY,
622 DECELERATION,
623 HALF_PERIOD_TIME,
624 );
625
626 assert_eq!(START_VALUE, LIMIT_VALUE);
627 let time = Instant::now();
628 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
629 START_VALUE,
630 LIMIT_VALUE,
631 parameters,
632 time.clone(),
633 );
634 let res = simulation.step(time);
635 assert_eq!(res.0, START_VALUE);
636 assert_eq!(res.1, true);
637 assert_eq!(simulation.state, State::Done);
638 }
639
640 #[test]
643 fn constant_deceleration_increasing_limit_not_reached() {
644 const INITIAL_VELOCITY: f32 = 50.;
645 const DECELERATION: f32 = 20.;
646 const HALF_PERIOD_TIME: f32 = 100e-3;
647 let parameters = ConstantDecelerationSpringDamperParameters::new(
648 INITIAL_VELOCITY,
649 DECELERATION,
650 HALF_PERIOD_TIME,
651 );
652
653 let mut time = Instant::now();
654 let mut simulation =
655 ConstantDecelerationSpringDamper::new_internal(10., 2000., parameters, time.clone());
656
657 let mut duration = Duration::from_secs(1);
659 assert!(DECELERATION * duration.as_secs_f32() < INITIAL_VELOCITY);
660 time += duration;
661 let (res, finished) = simulation.step(time);
662 assert_eq!(finished, false);
663 assert_eq!(
664 res,
665 10. + 50. * duration.as_secs_f32()
666 - 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
667 );
668
669 duration = Duration::from_hours(10);
671 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
672 time += duration;
673 let (res, finished) = simulation.step(time);
674 assert_eq!(finished, true);
675 assert_eq!(
676 res,
677 10. + 50. * INITIAL_VELOCITY / DECELERATION
678 - 0.5 * DECELERATION * (INITIAL_VELOCITY / DECELERATION).powi(2)
679 );
680
681 assert!(res < 2000.); }
683
684 #[test]
687 fn constant_deceleration_decreasing_limit_not_reached() {
688 const START_VALUE: f32 = 2000.;
689 const LIMIT_VALUE: f32 = 10.;
690 const INITIAL_VELOCITY: f32 = -50.;
691 const DECELERATION: f32 = 20.;
692 const HALF_PERIOD_TIME: f32 = 100e-3;
693
694 let parameters = ConstantDecelerationSpringDamperParameters::new(
695 INITIAL_VELOCITY,
696 DECELERATION,
697 HALF_PERIOD_TIME,
698 );
699
700 let mut time = Instant::now();
701 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
702 START_VALUE,
703 LIMIT_VALUE,
704 parameters,
705 time.clone(),
706 );
707
708 let mut duration = Duration::from_secs(1);
709 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) < f32::abs(INITIAL_VELOCITY));
710 time += duration;
711 let (res, finished) = simulation.step(time);
712 assert_eq!(finished, false);
713 assert_eq!(
714 res,
715 START_VALUE + INITIAL_VELOCITY * duration.as_secs_f32()
716 - INITIAL_VELOCITY.signum() * 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
717 );
718
719 duration = Duration::from_hours(10);
720 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
721 time += duration;
722 let (res, finished) = simulation.step(time);
723 assert_eq!(finished, true);
724 assert_eq!(
725 res,
726 START_VALUE + INITIAL_VELOCITY * f32::abs(INITIAL_VELOCITY / DECELERATION)
727 - 0.5
728 * INITIAL_VELOCITY.signum()
729 * DECELERATION
730 * (INITIAL_VELOCITY / DECELERATION).powi(2)
731 );
732
733 assert!(res > LIMIT_VALUE); }
735
736 #[test]
739 fn constant_deceleration_spring_damper_increasing_limit_reached() {
740 const INITIAL_VELOCITY: f32 = 50.;
741 const DECELERATION: f32 = 20.;
742 const HALF_PERIOD_TIME: f32 = 10.;
743 const START_VALUE: f32 = 10.;
744 const LIMIT_VALUE: f32 = 70.;
745 let parameters = super::ConstantDecelerationSpringDamperParameters::new(
746 INITIAL_VELOCITY,
747 DECELERATION,
748 HALF_PERIOD_TIME,
749 );
750
751 let mut time = Instant::now();
752 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
753 START_VALUE,
754 LIMIT_VALUE,
755 parameters,
756 time.clone(),
757 );
758
759 let duration = Duration::from_secs(1);
760 assert!(f32::abs(DECELERATION) * duration.as_secs_f32() < f32::abs(INITIAL_VELOCITY)); time += duration;
762 let (res, finished) = simulation.step(time);
763 assert_eq!(finished, false);
764 assert_eq!(simulation.state, State::Deceleration);
765 assert!(res < LIMIT_VALUE); time += Duration::from_secs((HALF_PERIOD_TIME / 2.) as u64);
768 let (res, finished) = simulation.step(time);
769 assert_eq!(finished, false);
770 assert_eq!(simulation.state, State::SpringDamper);
771 assert!(res > LIMIT_VALUE);
772
773 time += Duration::from_hours(10);
774 let (res, finished) = simulation.step(time);
775 assert_eq!(finished, true);
776 assert_eq!(simulation.state, State::Done);
777 assert_eq!(res, LIMIT_VALUE);
778 }
779
780 #[test]
783 fn constant_deceleration_spring_damper_decreasing_limit_reached() {
784 const INITIAL_VELOCITY: f32 = -50.;
785 const DECELERATION: f32 = 20.;
786 const HALF_PERIOD_TIME: f32 = 10.;
787 const START_VALUE: f32 = 70.;
788 const LIMIT_VALUE: f32 = 10.;
789 let parameters = super::ConstantDecelerationSpringDamperParameters::new(
790 INITIAL_VELOCITY,
791 DECELERATION,
792 HALF_PERIOD_TIME,
793 );
794
795 let mut time = Instant::now();
796 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
797 START_VALUE,
798 LIMIT_VALUE,
799 parameters,
800 time.clone(),
801 );
802
803 let duration = Duration::from_secs(1);
804 assert!(f32::abs(DECELERATION) * duration.as_secs_f32() < f32::abs(INITIAL_VELOCITY)); time += duration;
806 let (res, finished) = simulation.step(time);
807 assert_eq!(finished, false);
808 assert_eq!(simulation.state, State::Deceleration);
809 assert!(res > LIMIT_VALUE); time += Duration::from_secs((HALF_PERIOD_TIME / 2.) as u64);
812 let (res, finished) = simulation.step(time);
813 assert_eq!(finished, false);
814 assert_eq!(simulation.state, State::SpringDamper);
815 assert!(res < LIMIT_VALUE);
816
817 time += Duration::from_hours(10);
818 let (res, finished) = simulation.step(time);
819 assert_eq!(finished, true);
820 assert_eq!(simulation.state, State::Done);
821 assert_eq!(res, LIMIT_VALUE);
822 }
823}