1use crate::{Coord, animations::Instant};
13#[cfg(not(feature = "std"))]
14use num_traits::Float;
15
16#[derive(Debug)]
18enum Direction {
19 Increasing,
21 Decreasing,
23}
24
25pub trait Simulation {
28 fn step(&mut self, current: &mut f32, new_tick: Instant) -> bool;
29}
30
31pub trait Parameter {
34 type Output;
35 fn simulation(
36 self,
37 start_value: f32,
38 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
39 ) -> Self::Output;
40}
41
42#[derive(Debug, Clone)]
44pub struct ConstantDecelerationParameters {
45 pub initial_velocity: f32,
46 pub deceleration: f32,
47}
48
49impl ConstantDecelerationParameters {
50 pub fn new(initial_velocity: f32, deceleration: f32) -> Self {
51 Self { initial_velocity, deceleration }
52 }
53
54 pub fn new_with_distance(distance: f32, duration_secs: f32) -> Self {
62 debug_assert!(duration_secs > 0., "Duration must be greater than zero");
63
64 let d = distance;
87 let t = duration_secs;
88 let v0 = d / (0.5 * t);
89 let a = -(v0 / t);
90 Self::new(v0, -a)
92 }
93
94 pub fn remaining_distance(&self, time_elapsed: core::time::Duration) -> Coord {
96 debug_assert!(self.deceleration != 0., "deceleration must not be zero");
97 debug_assert!(
98 self.deceleration.signum() == self.initial_velocity.signum(),
99 "deceleration must actually decelerate the velocity"
100 );
101
102 let total_duration = self.initial_velocity / self.deceleration;
108
109 if time_elapsed.as_secs_f32() < total_duration {
110 (0.5 * (-self.deceleration)
112 * (total_duration.powi(2) - time_elapsed.as_secs_f32().powi(2))
113 + self.initial_velocity * (total_duration - time_elapsed.as_secs_f32()))
114 as Coord
115 } else {
116 Coord::default()
117 }
118 }
119}
120
121impl Parameter for ConstantDecelerationParameters {
122 type Output = ConstantDeceleration;
123 fn simulation(
124 self,
125 start_value: f32,
126 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
127 ) -> Self::Output {
128 ConstantDeceleration::new(start_value, limit_value, self)
129 }
130}
131
132#[derive(Debug)]
135pub struct ConstantDeceleration {
136 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
139 velocity: f32,
140 data: ConstantDecelerationParameters,
141 direction: Direction,
142 start_time: Instant,
143}
144
145impl ConstantDeceleration {
146 pub fn new(
153 start_value: f32,
154 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
155 data: ConstantDecelerationParameters,
156 ) -> Self {
157 Self::new_internal(start_value, limit_value, data, crate::animations::current_tick())
158 }
159
160 fn new_internal(
161 start_value: f32,
162 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
163 mut data: ConstantDecelerationParameters,
164 start_time: Instant,
165 ) -> Self {
166 let mut initial_velocity = data.initial_velocity;
167 let direction = if start_value == limit_value.as_ref().get() {
168 if initial_velocity >= 0. {
169 data.deceleration = f32::abs(data.deceleration);
170 Direction::Increasing
171 } else {
172 data.deceleration = -f32::abs(data.deceleration);
173 Direction::Decreasing
174 }
175 } else if start_value < limit_value.as_ref().get() {
176 data.deceleration = f32::abs(data.deceleration);
177 assert!(initial_velocity >= 0.); initial_velocity = f32::abs(initial_velocity);
179 Direction::Increasing
180 } else {
181 data.deceleration = -f32::abs(data.deceleration);
182 initial_velocity = -f32::abs(initial_velocity);
183 assert!(initial_velocity <= 0.);
184 Direction::Decreasing
185 };
186
187 Self { limit_value, velocity: initial_velocity, data, direction, start_time }
188 }
189
190 fn step_internal(&mut self, current: &mut f32, new_tick: Instant) -> bool {
191 let limit_value = self.limit_value.as_ref().get();
192
193 let duration = f32::min(
195 new_tick.duration_since(self.start_time).as_secs_f32(),
196 f32::abs(self.velocity / self.data.deceleration),
197 );
198
199 self.start_time = new_tick;
200
201 let new_velocity = self.velocity - duration * self.data.deceleration;
202
203 *current += duration * (self.velocity + new_velocity) / 2.; self.velocity = new_velocity;
205
206 match self.direction {
207 Direction::Increasing => {
208 if *current >= limit_value {
209 *current = limit_value;
210 self.velocity = 0.;
211 return true;
212 } else if self.velocity <= 0. {
213 return true;
214 }
215 }
216 Direction::Decreasing => {
217 if *current <= limit_value {
218 *current = limit_value;
219 self.velocity = 0.;
220 return true;
221 } else if self.velocity >= 0. {
222 return true;
223 }
224 }
225 }
226 false
227 }
228}
229
230impl Simulation for ConstantDeceleration {
231 fn step(&mut self, current: &mut f32, new_tick: Instant) -> bool {
232 self.step_internal(current, new_tick)
233 }
234}
235
236#[cfg(test)]
237macro_rules! assert_approx_eq {
238 ($a:expr, $b:expr) => {
239 assert!(($a - $b).abs() < 1e-4, "{} != {}", $a, $b);
240 };
241}
242#[cfg(test)]
243fn test_limit_property(value: f32) -> core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>> {
244 alloc::boxed::Box::pin(crate::Property::new(value))
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use core::time::Duration;
251
252 #[test]
253 fn constant_deceleration_start_eq_limit() {
254 const START_VALUE: f32 = 10.;
255 const LIMIT_VALUE: f32 = 10.;
256 const INITIAL_VELOCITY: f32 = 50.;
257 const DECELERATION: f32 = 20.;
258 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
259
260 let time = Instant::now();
261 let mut simulation = ConstantDeceleration::new_internal(
262 START_VALUE,
263 test_limit_property(LIMIT_VALUE),
264 parameters,
265 time,
266 );
267
268 let mut current = START_VALUE;
269 let finished = simulation.step(&mut current, time + Duration::from_hours(10));
270 assert_eq!(finished, true);
271 assert_eq!(current, START_VALUE);
272 }
273
274 #[test]
277 fn constant_deceleration_increasing_limit_not_reached() {
278 const START_VALUE: f32 = 10.;
279 const LIMIT_VALUE: f32 = 2000.;
280 const INITIAL_VELOCITY: f32 = 50.;
281 const DECELERATION: f32 = 20.;
282 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
283
284 let mut time = Instant::now();
285 let mut simulation = ConstantDeceleration::new_internal(
286 START_VALUE,
287 test_limit_property(LIMIT_VALUE),
288 parameters,
289 time,
290 );
291 let mut current = START_VALUE;
292
293 let mut duration = Duration::from_secs(1);
295 assert!(DECELERATION * duration.as_secs_f32() < INITIAL_VELOCITY);
296 time += duration;
297 let finished = simulation.step(&mut current, time);
298 assert_eq!(finished, false);
299 assert_approx_eq!(
300 current,
301 START_VALUE + INITIAL_VELOCITY * duration.as_secs_f32()
302 - 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
303 );
304
305 duration = Duration::from_hours(10);
307 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
308 time += duration;
309 let finished = simulation.step(&mut current, time);
310 assert_eq!(finished, true);
311 assert_approx_eq!(
312 current,
313 START_VALUE + INITIAL_VELOCITY * INITIAL_VELOCITY / DECELERATION
314 - 0.5 * DECELERATION * (INITIAL_VELOCITY / DECELERATION).powi(2)
315 );
316
317 assert!(current < LIMIT_VALUE); }
319
320 #[test]
322 fn constant_deceleration_increasing_limit_reached() {
323 const START_VALUE: f32 = 10.;
324 const LIMIT_VALUE: f32 = 20.;
325 const INITIAL_VELOCITY: f32 = 50.;
326 const DECELERATION: f32 = 20.;
327 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
328
329 let mut time = Instant::now();
330 let mut simulation = ConstantDeceleration::new_internal(
331 START_VALUE,
332 test_limit_property(LIMIT_VALUE),
333 parameters,
334 time,
335 );
336 let mut current = START_VALUE;
337
338 let duration = Duration::from_secs(1);
339 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) < f32::abs(INITIAL_VELOCITY)); time += duration;
341 let finished = simulation.step(&mut current, time);
342 assert_eq!(finished, true);
343 assert_eq!(current, LIMIT_VALUE); }
345
346 #[test]
349 fn constant_deceleration_decreasing_limit_not_reached() {
350 const START_VALUE: f32 = 2000.;
351 const LIMIT_VALUE: f32 = 10.;
352 const INITIAL_VELOCITY: f32 = -50.;
353 const DECELERATION: f32 = 20.;
354
355 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
356
357 let mut time = Instant::now();
358 let mut simulation = ConstantDeceleration::new_internal(
359 START_VALUE,
360 test_limit_property(LIMIT_VALUE),
361 parameters,
362 time,
363 );
364 let mut current = START_VALUE;
365
366 let mut duration = Duration::from_secs(1);
367 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) < f32::abs(INITIAL_VELOCITY));
368 time += duration;
369 let finished = simulation.step(&mut current, time);
370 assert_eq!(finished, false);
371 assert_eq!(
372 current,
373 START_VALUE + INITIAL_VELOCITY * duration.as_secs_f32()
374 - INITIAL_VELOCITY.signum() * 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
375 );
376
377 duration = Duration::from_hours(10);
378 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
379 time += duration;
380 let finished = simulation.step(&mut current, time);
381 assert_eq!(finished, true);
382 assert_eq!(
383 current,
384 START_VALUE + INITIAL_VELOCITY * f32::abs(INITIAL_VELOCITY / DECELERATION)
385 - 0.5
386 * INITIAL_VELOCITY.signum()
387 * DECELERATION
388 * (INITIAL_VELOCITY / DECELERATION).powi(2)
389 );
390
391 assert!(current > LIMIT_VALUE); }
393
394 #[test]
397 fn constant_deceleration_decreasing_limit_reached() {
398 const START_VALUE: f32 = 20.;
399 const LIMIT_VALUE: f32 = 10.;
400 const INITIAL_VELOCITY: f32 = -50.;
401 const DECELERATION: f32 = 20.;
402 let parameters = ConstantDecelerationParameters::new(INITIAL_VELOCITY, DECELERATION);
403
404 let mut time = Instant::now();
405 let mut simulation = ConstantDeceleration::new_internal(
406 START_VALUE,
407 test_limit_property(LIMIT_VALUE),
408 parameters,
409 time,
410 );
411 let mut current = START_VALUE;
412
413 let duration = Duration::from_secs(3);
414 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) > f32::abs(INITIAL_VELOCITY)); time += duration;
416 let finished = simulation.step(&mut current, time);
417 assert_eq!(finished, true);
418 assert_eq!(current, LIMIT_VALUE); }
420}
421
422#[cfg(test)]
425#[derive(Debug, Clone)]
426pub struct ConstantDecelerationSpringDamperParameters {
427 pub initial_velocity: f32,
428 pub deceleration: f32,
429 pub mass: f32, pub spring_constant: f32, pub damping_coefficient: f32, }
433
434#[cfg(test)]
435impl ConstantDecelerationSpringDamperParameters {
436 pub fn new(initial_velocity: f32, deceleration: f32, half_period_time: f32) -> Self {
444 let (mass, spring_constant, damping_coefficient) =
445 Self::calculate_parameters(half_period_time);
446
447 Self { initial_velocity, deceleration, mass, spring_constant, damping_coefficient }
448 }
449
450 fn calculate_parameters(half_period_time: f32) -> (f32, f32, f32) {
451 const MASS: f32 = 1.;
453 const DAMPING_COEFFICIENT: f32 = 1.;
454 let w_d = 2. * core::f32::consts::PI * 1. / (2. * half_period_time);
455 let spring_constant = w_d.powi(2) + DAMPING_COEFFICIENT.powi(2) / (4. * MASS.powi(2));
456
457 (MASS, spring_constant, DAMPING_COEFFICIENT)
458 }
459}
460
461#[cfg(test)]
462impl Parameter for ConstantDecelerationSpringDamperParameters {
463 type Output = ConstantDecelerationSpringDamper;
464 fn simulation(
465 self,
466 start_value: f32,
467 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
468 ) -> Self::Output {
469 ConstantDecelerationSpringDamper::new(start_value, limit_value, self)
470 }
471}
472
473#[cfg(test)]
474#[derive(Debug, PartialEq)]
475enum State {
476 Deceleration,
477 SpringDamper,
478 Done,
479}
480
481#[cfg(test)]
486#[derive(Debug)]
487pub struct ConstantDecelerationSpringDamper {
488 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
491 curr_val_zeroed: f32,
492 velocity: f32,
493 data: ConstantDecelerationSpringDamperParameters,
494 direction: Direction,
495 start_time: Instant,
496 state: State,
497 damping_ratio: f32,
498 w_n: f32,
500 w_d: f32,
502 constant_a: f32,
503 constant_phi: f32,
504}
505
506#[cfg(test)]
507impl ConstantDecelerationSpringDamper {
508 pub fn new(
509 start_value: f32,
510 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
511 data: ConstantDecelerationSpringDamperParameters,
512 ) -> Self {
513 Self::new_internal(start_value, limit_value, data, crate::animations::current_tick())
514 }
515
516 fn new_internal(
517 start_value: f32,
518 limit_value: core::pin::Pin<alloc::boxed::Box<crate::Property<f32>>>,
519 mut data: ConstantDecelerationSpringDamperParameters,
520 start_time: Instant,
521 ) -> Self {
522 let mut initial_velocity = data.initial_velocity;
523 let mut state = State::Deceleration;
524 let direction = if start_value == limit_value.as_ref().get() {
525 state = State::Done;
526 if initial_velocity >= 0. {
527 data.deceleration = f32::abs(data.deceleration);
528 Direction::Increasing
529 } else {
530 data.deceleration = -f32::abs(data.deceleration);
531 Direction::Decreasing
532 }
533 } else if start_value < limit_value.as_ref().get() {
534 data.deceleration = f32::abs(data.deceleration);
535 assert!(initial_velocity >= 0.); initial_velocity = f32::abs(initial_velocity);
537 Direction::Increasing
538 } else {
539 data.deceleration = -f32::abs(data.deceleration);
540 initial_velocity = -f32::abs(initial_velocity);
541 assert!(initial_velocity <= 0.);
542 Direction::Decreasing
543 };
544
545 assert!(data.mass > 0.);
546 assert!(data.spring_constant >= 0.);
547
548 let c_cr = 2. * f32::sqrt(data.mass * data.spring_constant); let damping_ratio = data.damping_coefficient / c_cr;
550 assert!(damping_ratio > 0.);
551 assert!(damping_ratio < 1.); let w_n = c_cr / (2. * data.mass);
554 let w_d = w_n * f32::sqrt(1. - damping_ratio.powi(2));
555
556 Self {
557 limit_value,
558 curr_val_zeroed: 0.,
559 velocity: initial_velocity,
560 data,
561 direction,
562 start_time,
563 state,
564 damping_ratio,
565 w_n,
566 w_d,
567 constant_a: 0., constant_phi: 0., }
570 }
571
572 fn new_value(&self) -> f32 {
573 self.limit_value.as_ref().get() + self.curr_val_zeroed
574 }
575
576 fn step_internal(&mut self, current: &mut f32, new_tick: Instant) -> bool {
577 match self.state {
578 State::Deceleration => self.state_deceleration(current, new_tick),
579 State::SpringDamper => self.state_spring_damper(current, new_tick),
580 State::Done => {
581 *current = self.new_value();
582 true
583 }
584 }
585 }
586
587 fn state_deceleration(&mut self, current: &mut f32, new_tick: Instant) -> bool {
588 let limit_value = self.limit_value.as_ref().get();
589 let duration_unlimited = new_tick.duration_since(self.start_time);
590 let duration = f32::min(
592 duration_unlimited.as_secs_f32(),
593 f32::abs(self.velocity / self.data.deceleration),
594 );
595
596 self.start_time = new_tick;
597
598 let new_velocity = self.velocity - (duration * self.data.deceleration);
599 let new_val = *current + (duration * (self.velocity + new_velocity) / 2.); enum S {
602 LimitReached,
603 VelocityZero,
604 None,
605 }
606
607 let s = match self.direction {
608 Direction::Increasing if new_val > limit_value => S::LimitReached,
609 Direction::Increasing if new_velocity <= 0. => S::VelocityZero,
610 Direction::Decreasing if new_val < limit_value => S::LimitReached,
611 Direction::Decreasing if new_velocity >= 0. => S::VelocityZero,
612 _ => S::None,
613 };
614 match s {
615 S::LimitReached => {
616 self.state = State::SpringDamper;
617
618 let root = f32::sqrt(
621 self.velocity.powi(2) - self.data.deceleration * (limit_value - *current),
622 );
623 let dt = f32::min(
625 (self.velocity - root) / self.data.deceleration,
626 (self.velocity + root) / self.data.deceleration,
627 );
628
629 self.velocity -= dt * self.data.deceleration; self.curr_val_zeroed = 0.;
631 *current = limit_value;
632
633 const X0: f32 = 0.; self.constant_a = self.velocity.signum()
635 * f32::sqrt(
636 (self.w_d.powi(2) * X0.powi(2)
637 + (self.velocity + self.damping_ratio * self.w_n * 0.).powi(2))
638 / self.w_d.powi(2),
639 );
640 self.constant_phi =
641 f32::atan(self.w_d * X0 / (self.velocity + self.damping_ratio * self.w_n * X0));
642 self.state_spring_damper(
643 current,
644 new_tick
645 + (duration_unlimited
646 - core::time::Duration::from_millis((dt * 1000.) as u64)),
647 )
648 }
649 S::VelocityZero => {
650 self.velocity = 0.;
651 *current = new_val;
652 true
653 }
654 S::None => {
655 self.velocity = new_velocity;
656 *current = new_val;
657 false
658 }
659 }
660 }
661
662 fn state_spring_damper(&mut self, current: &mut f32, new_tick: Instant) -> bool {
663 let t = (new_tick - self.start_time).as_secs_f32();
665 assert!(self.damping_ratio < 1.);
667 let new_val = self.constant_a
668 * f32::exp(-self.damping_ratio * self.w_n * t)
669 * f32::sin(self.w_d * t + self.constant_phi);
670 self.curr_val_zeroed = new_val; let limit_value = self.limit_value.as_ref().get();
673 let max_time = 2. * core::f32::consts::PI / self.w_d;
674 let current_val = self.new_value();
675 *current = current_val;
676 let finished = match self.direction {
677 Direction::Increasing => {
678 current_val < limit_value || t > max_time
680 }
681 Direction::Decreasing => {
682 current_val > limit_value || t > max_time
684 }
685 };
686 if finished {
687 self.velocity = 0.;
688 *current = limit_value;
689 self.curr_val_zeroed = 0.;
690 self.state = State::Done;
691 }
692 finished
693 }
694}
695
696#[cfg(test)]
697impl Simulation for ConstantDecelerationSpringDamper {
698 fn step(&mut self, current: &mut f32, new_tick: Instant) -> bool {
699 self.step_internal(current, new_tick)
700 }
701}
702
703#[cfg(test)]
704mod tests_spring_damper {
705 use super::*;
706 use core::{f32::consts::PI, time::Duration};
707
708 #[test]
709 fn calculate_parameters() {
710 const INITIAL_VELOCITY: f32 = 50.;
711 const DECELERATION: f32 = 20.;
712 const HALF_PERIOD_TIME: f32 = 100e-3;
713 let res = super::ConstantDecelerationSpringDamperParameters::new(
714 INITIAL_VELOCITY,
715 DECELERATION,
716 HALF_PERIOD_TIME,
717 );
718
719 let w_n = f32::sqrt(res.spring_constant * res.mass) / res.mass;
720 let damping_ratio = res.damping_coefficient / (2. * res.mass * w_n);
721 let w_d = w_n * f32::sqrt(1. - damping_ratio.powi(2));
722 assert_approx_eq!(w_d, 2. * PI * 1. / (2. * HALF_PERIOD_TIME));
723 }
724
725 #[test]
726 fn constant_deceleration_start_eq_limit() {
727 const START_VALUE: f32 = 10.;
728 const LIMIT_VALUE: f32 = 10.;
729 const INITIAL_VELOCITY: f32 = 50.;
730 const DECELERATION: f32 = 20.;
731 const HALF_PERIOD_TIME: f32 = 100e-3;
732 let parameters = ConstantDecelerationSpringDamperParameters::new(
733 INITIAL_VELOCITY,
734 DECELERATION,
735 HALF_PERIOD_TIME,
736 );
737
738 assert_eq!(START_VALUE, LIMIT_VALUE);
739 let time = Instant::now();
740 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
741 START_VALUE,
742 test_limit_property(LIMIT_VALUE),
743 parameters,
744 time,
745 );
746 let mut current = START_VALUE;
747 let finished = simulation.step(&mut current, time);
748 assert_eq!(current, START_VALUE);
749 assert_eq!(finished, true);
750 assert_eq!(simulation.state, State::Done);
751 }
752
753 #[test]
756 fn constant_deceleration_increasing_limit_not_reached() {
757 const INITIAL_VELOCITY: f32 = 50.;
758 const DECELERATION: f32 = 20.;
759 const HALF_PERIOD_TIME: f32 = 100e-3;
760 let parameters = ConstantDecelerationSpringDamperParameters::new(
761 INITIAL_VELOCITY,
762 DECELERATION,
763 HALF_PERIOD_TIME,
764 );
765
766 let mut time = Instant::now();
767 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
768 10.,
769 test_limit_property(2000.),
770 parameters,
771 time,
772 );
773 let mut current = 10.;
774
775 let mut duration = Duration::from_secs(1);
777 assert!(DECELERATION * duration.as_secs_f32() < INITIAL_VELOCITY);
778 time += duration;
779 let finished = simulation.step(&mut current, time);
780 assert_eq!(finished, false);
781 assert_approx_eq!(
782 current,
783 10. + 50. * duration.as_secs_f32()
784 - 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
785 );
786
787 duration = Duration::from_hours(10);
789 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
790 time += duration;
791 let finished = simulation.step(&mut current, time);
792 assert_eq!(finished, true);
793 assert_approx_eq!(
794 current,
795 10. + 50. * INITIAL_VELOCITY / DECELERATION
796 - 0.5 * DECELERATION * (INITIAL_VELOCITY / DECELERATION).powi(2)
797 );
798
799 assert!(current < 2000.); }
801
802 #[test]
805 fn constant_deceleration_decreasing_limit_not_reached() {
806 const START_VALUE: f32 = 2000.;
807 const LIMIT_VALUE: f32 = 10.;
808 const INITIAL_VELOCITY: f32 = -50.;
809 const DECELERATION: f32 = 20.;
810 const HALF_PERIOD_TIME: f32 = 100e-3;
811
812 let parameters = ConstantDecelerationSpringDamperParameters::new(
813 INITIAL_VELOCITY,
814 DECELERATION,
815 HALF_PERIOD_TIME,
816 );
817
818 let mut time = Instant::now();
819 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
820 START_VALUE,
821 test_limit_property(LIMIT_VALUE),
822 parameters,
823 time,
824 );
825 let mut current = START_VALUE;
826
827 let mut duration = Duration::from_secs(1);
828 assert!(f32::abs(DECELERATION * duration.as_secs_f32()) < f32::abs(INITIAL_VELOCITY));
829 time += duration;
830 let finished = simulation.step(&mut current, time);
831 assert_eq!(finished, false);
832 assert_eq!(
833 current,
834 START_VALUE + INITIAL_VELOCITY * duration.as_secs_f32()
835 - INITIAL_VELOCITY.signum() * 0.5 * DECELERATION * duration.as_secs_f32().powi(2)
836 );
837
838 duration = Duration::from_hours(10);
839 assert!(Duration::from_secs((INITIAL_VELOCITY / DECELERATION) as u64) < duration);
840 time += duration;
841 let finished = simulation.step(&mut current, time);
842 assert_eq!(finished, true);
843 assert_eq!(
844 current,
845 START_VALUE + INITIAL_VELOCITY * f32::abs(INITIAL_VELOCITY / DECELERATION)
846 - 0.5
847 * INITIAL_VELOCITY.signum()
848 * DECELERATION
849 * (INITIAL_VELOCITY / DECELERATION).powi(2)
850 );
851
852 assert!(current > LIMIT_VALUE); }
854
855 #[test]
858 fn constant_deceleration_spring_damper_increasing_limit_reached() {
859 const INITIAL_VELOCITY: f32 = 50.;
860 const DECELERATION: f32 = 20.;
861 const HALF_PERIOD_TIME: f32 = 10.;
862 const START_VALUE: f32 = 10.;
863 const LIMIT_VALUE: f32 = 70.;
864 let parameters = super::ConstantDecelerationSpringDamperParameters::new(
865 INITIAL_VELOCITY,
866 DECELERATION,
867 HALF_PERIOD_TIME,
868 );
869
870 let mut time = Instant::now();
871 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
872 START_VALUE,
873 test_limit_property(LIMIT_VALUE),
874 parameters,
875 time,
876 );
877 let mut current = START_VALUE;
878
879 let duration = Duration::from_secs(1);
880 assert!(f32::abs(DECELERATION) * duration.as_secs_f32() < f32::abs(INITIAL_VELOCITY)); time += duration;
882 let finished = simulation.step(&mut current, time);
883 assert_eq!(finished, false);
884 assert_eq!(simulation.state, State::Deceleration);
885 assert!(current < LIMIT_VALUE); time += Duration::from_secs((HALF_PERIOD_TIME / 2.) as u64);
888 let finished = simulation.step(&mut current, time);
889 assert_eq!(finished, false);
890 assert_eq!(simulation.state, State::SpringDamper);
891 assert!(current > LIMIT_VALUE);
892
893 time += Duration::from_hours(10);
894 let finished = simulation.step(&mut current, time);
895 assert_eq!(finished, true);
896 assert_eq!(simulation.state, State::Done);
897 assert_eq!(current, LIMIT_VALUE);
898 }
899
900 #[test]
903 fn constant_deceleration_spring_damper_decreasing_limit_reached() {
904 const INITIAL_VELOCITY: f32 = -50.;
905 const DECELERATION: f32 = 20.;
906 const HALF_PERIOD_TIME: f32 = 10.;
907 const START_VALUE: f32 = 70.;
908 const LIMIT_VALUE: f32 = 10.;
909 let parameters = super::ConstantDecelerationSpringDamperParameters::new(
910 INITIAL_VELOCITY,
911 DECELERATION,
912 HALF_PERIOD_TIME,
913 );
914
915 let mut time = Instant::now();
916 let mut simulation = ConstantDecelerationSpringDamper::new_internal(
917 START_VALUE,
918 test_limit_property(LIMIT_VALUE),
919 parameters,
920 time,
921 );
922 let mut current = START_VALUE;
923
924 let duration = Duration::from_secs(1);
925 assert!(f32::abs(DECELERATION) * duration.as_secs_f32() < f32::abs(INITIAL_VELOCITY)); time += duration;
927 let finished = simulation.step(&mut current, time);
928 assert_eq!(finished, false);
929 assert_eq!(simulation.state, State::Deceleration);
930 assert!(current > LIMIT_VALUE); time += Duration::from_secs((HALF_PERIOD_TIME / 2.) as u64);
933 let finished = simulation.step(&mut current, time);
934 assert_eq!(finished, false);
935 assert_eq!(simulation.state, State::SpringDamper);
936 assert!(current < LIMIT_VALUE);
937
938 time += Duration::from_hours(10);
939 let finished = simulation.step(&mut current, time);
940 assert_eq!(finished, true);
941 assert_eq!(simulation.state, State::Done);
942 assert_eq!(current, LIMIT_VALUE);
943 }
944}