Skip to main content

ferroui_base/utilities/
spring_solver.rs

1// Ported from:
2// https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/SpringSolver.h
3/*
4 * Copyright (C) 2016 Apple Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 * THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28use std::f64::consts::PI;
29
30/// Solves the motion of a damped spring: maps a time to the position of a
31/// spring that is released at `0` and settles at `1`.
32#[derive(Clone, Copy, Debug, Default, PartialEq)]
33pub struct SpringSolver {
34    w0: f64,
35    zeta: f64,
36    wd: f64,
37    a: f64,
38    b: f64,
39}
40
41impl SpringSolver {
42    /// Creates a solver from the period of the undamped oscillation in
43    /// seconds, the damping ratio and the initial velocity.
44    pub fn from_period(period_seconds: f64, zeta: f64, initial_velocity: f64) -> Self {
45        // T is time period [s]
46        // T = (2*PI / sqrt(k)) * sqrt(m)
47        // wn is natural frequency of the system [Hz] [1/s]
48        // wn = 2*PI / T
49        Self::from_natural_frequency(2.0 * PI / period_seconds, zeta, initial_velocity)
50    }
51
52    /// Creates a solver from the mass `m`, the stiffness `k`, the damping
53    /// coefficient `c` and the initial velocity.
54    pub fn new(m: f64, k: f64, c: f64, initial_velocity: f64) -> Self {
55        // wn is natural frequency of the system [Hz] [1/s]
56        // wn = sqrt(k / m)
57        // Cc is critical damping coefficient
58        // Cc = 2 * sqrt(k * m)
59        // Cc = 2 * m * wn
60        // Cc = 2 * m * sqrt(k / m)
61        // zeta is damping ratio
62        // zeta = c / Cc
63        Self::from_natural_frequency((k / m).sqrt(), c / (2.0 * (k * m).sqrt()), initial_velocity)
64    }
65
66    /// Creates a solver from the natural frequency, the damping ratio and
67    /// the initial velocity.
68    pub fn from_natural_frequency(wn: f64, zeta: f64, initial_velocity: f64) -> Self {
69        let w0 = wn;
70        if zeta < 1.0 {
71            // Under-damped.
72            let wd = w0 * (1.0 - zeta * zeta).sqrt();
73            Self { w0, zeta, wd, a: 1.0, b: (zeta * w0 + -initial_velocity) / wd }
74        } else {
75            // Critically damped (ignoring over-damped case for now).
76            Self { w0, zeta, wd: 0.0, a: 1.0, b: -initial_velocity + w0 }
77        }
78    }
79
80    /// The position of the spring at time `t`.
81    pub fn solve(&self, t: f64) -> f64 {
82        let t = if self.zeta < 1.0 {
83            // Under-damped
84            (-t * self.zeta * self.w0).exp() * (self.a * (self.wd * t).cos() + self.b * (self.wd * t).sin())
85        } else {
86            // Critically damped (ignoring over-damped case for now).
87            (self.a + self.b * t) * (-t * self.w0).exp()
88        };
89
90        // Map range from [1..0] to [0..1].
91        1.0 - t
92    }
93}