1d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu/*
2d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Copyright (C) 2017 The Android Open Source Project
3d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu *
4d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Licensed under the Apache License, Version 2.0 (the "License");
5d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * you may not use this file except in compliance with the License.
6d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * You may obtain a copy of the License at
7d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu *
8d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu *      http://www.apache.org/licenses/LICENSE-2.0
9d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu *
10d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Unless required by applicable law or agreed to in writing, software
11d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * distributed under the License is distributed on an "AS IS" BASIS,
12d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * See the License for the specific language governing permissions and
14d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * limitations under the License.
15d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */
16d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
17d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liupackage android.support.animation;
18d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
19d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liuimport android.support.annotation.FloatRange;
20d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
21d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu/**
22d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Spring Force defines the characteristics of the spring being used in the animation.
23d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * <p>
24d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * By configuring the stiffness and damping ratio, callers can create a spring with the look and
25d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * feel suits their use case. Stiffness corresponds to the spring constant. The stiffer the spring
26d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * is, the harder it is to stretch it, the faster it undergoes dampening.
27d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * <p>
28d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * Spring damping ratio describes how oscillations in a system decay after a disturbance.
29d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * When damping ratio > 1* (i.e. over-damped), the object will quickly return to the rest position
30d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * without overshooting. If damping ratio equals to 1 (i.e. critically damped), the object will
31d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * return to equilibrium within the shortest amount of time. When damping ratio is less than 1
32d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * (i.e. under-damped), the mass tends to overshoot, and return, and overshoot again. Without any
33d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu * damping (i.e. damping ratio = 0), the mass will oscillate forever.
34d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu */
35d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liupublic final class SpringForce implements Force {
36d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
37d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Stiffness constant for extremely stiff spring.
38d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
39d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float STIFFNESS_HIGH = 10_000f;
40d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
41d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Stiffness constant for medium stiff spring. This is the default stiffness for spring force.
42d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
43d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float STIFFNESS_MEDIUM = 1500f;
44d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
45d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Stiffness constant for a spring with low stiffness.
46d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
47d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float STIFFNESS_LOW = 200f;
48d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
49d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Stiffness constant for a spring with very low stiffness.
50d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
51d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float STIFFNESS_VERY_LOW = 50f;
52d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
53d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
54d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Damping ratio for a very bouncy spring. Note for under-damped springs
55d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * (i.e. damping ratio < 1), the lower the damping ratio, the more bouncy the spring.
56d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
57d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
58d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
59d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Damping ratio for a medium bouncy spring. This is also the default damping ratio for spring
60d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * force. Note for under-damped springs (i.e. damping ratio < 1), the lower the damping ratio,
61d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * the more bouncy the spring.
62d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
63d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
64d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
65d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Damping ratio for a spring with low bounciness. Note for under-damped springs
66d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * (i.e. damping ratio < 1), the lower the damping ratio, the higher the bounciness.
67d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
68d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
69d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
70d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Damping ratio for a spring with no bounciness. This damping ratio will create a critically
71d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * damped spring that returns to equilibrium within the shortest amount of time without
72d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * oscillating.
73d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
74d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public static final float DAMPING_RATIO_NO_BOUNCY = 1f;
75d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
76d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // This multiplier is used to calculate the velocity threshold given a certain value threshold.
77d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // The idea is that if it takes >= 1 frame to move the value threshold amount, then the velocity
78d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // is a reasonable threshold.
79d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private static final double VELOCITY_THRESHOLD_MULTIPLIER = 1000.0 / 16.0;
80d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
81d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Natural frequency
82d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    double mNaturalFreq = Math.sqrt(STIFFNESS_MEDIUM);
83d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Damping ratio.
84d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    double mDampingRatio = DAMPING_RATIO_MEDIUM_BOUNCY;
85d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
86d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Value to indicate an unset state.
87d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private static final double UNSET = Double.MAX_VALUE;
88d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
89d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Indicates whether the spring has been initialized
90d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private boolean mInitialized = false;
91d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
92d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Threshold for velocity and value to determine when it's reasonable to assume that the spring
93d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // is approximately at rest.
945df257d2a2f330f8e4d37321a40431973fa9b0d1Doris Liu    private double mValueThreshold;
955df257d2a2f330f8e4d37321a40431973fa9b0d1Doris Liu    private double mVelocityThreshold;
96d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
97d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Intermediate values to simplify the spring function calculation per frame.
98d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private double mGammaPlus;
99d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private double mGammaMinus;
100d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private double mDampedFreq;
101d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
102d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Final position of the spring. This must be set before the start of the animation.
103d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private double mFinalPosition = UNSET;
104d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
105d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    // Internal state to hold a value/velocity pair.
106bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu    private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState();
107d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
108d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
109d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Creates a spring force. Note that final position of the spring must be set through
110d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * {@link #setFinalPosition(float)} before the spring animation starts.
111d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
112d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public SpringForce() {
113d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        // No op.
114d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
115d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
116d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
117d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Creates a spring with a given final rest position.
118d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
119d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @param finalPosition final position of the spring when it reaches equilibrium
120d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
121d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public SpringForce(float finalPosition) {
122d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mFinalPosition = finalPosition;
123d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
124d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
125d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
126d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Sets the stiffness of a spring. The more stiff a spring is, the more force it applies to
127d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * the object attached when the spring is not at the final position. Default stiffness is
128d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * {@link #STIFFNESS_MEDIUM}.
129d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
130d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @param stiffness non-negative stiffness constant of a spring
131d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @return the spring force that the given stiffness is set on
132bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu     * @throws IllegalArgumentException if the given spring stiffness is not positive
133d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
134bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu    public SpringForce setStiffness(
135bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu            @FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
136bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu        if (stiffness <= 0) {
137bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu            throw new IllegalArgumentException("Spring stiffness constant must be positive.");
138d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
139d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mNaturalFreq = Math.sqrt(stiffness);
140d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        // All the intermediate values need to be recalculated.
141d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mInitialized = false;
142d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return this;
143d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
144d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
145d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
146d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Gets the stiffness of the spring.
147d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
148d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @return the stiffness of the spring
149d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
150d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public float getStiffness() {
151d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return (float) (mNaturalFreq * mNaturalFreq);
152d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
153d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
154d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
155d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Spring damping ratio describes how oscillations in a system decay after a disturbance.
156d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * <p>
157d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * When damping ratio > 1 (over-damped), the object will quickly return to the rest position
158d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * without overshooting. If damping ratio equals to 1 (i.e. critically damped), the object will
159d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * return to equilibrium within the shortest amount of time. When damping ratio is less than 1
160d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * (i.e. under-damped), the mass tends to overshoot, and return, and overshoot again. Without
161d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * any damping (i.e. damping ratio = 0), the mass will oscillate forever.
162d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * <p>
163d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Default damping ratio is {@link #DAMPING_RATIO_MEDIUM_BOUNCY}.
164d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
165d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @param dampingRatio damping ratio of the spring, it should be non-negative
166d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @return the spring force that the given damping ratio is set on
167d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @throws IllegalArgumentException if the {@param dampingRatio} is negative.
168d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
169d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public SpringForce setDampingRatio(@FloatRange(from = 0.0) float dampingRatio) {
170d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        if (dampingRatio < 0) {
171d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            throw new IllegalArgumentException("Damping ratio must be non-negative");
172d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
173d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mDampingRatio = dampingRatio;
174d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        // All the intermediate values need to be recalculated.
175d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mInitialized = false;
176d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return this;
177d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
178d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
179d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
180d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Returns the damping ratio of the spring.
181d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
182d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @return damping ratio of the spring
183d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
184d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public float getDampingRatio() {
185d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return (float) mDampingRatio;
186d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
187d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
188d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
189d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Sets the rest position of the spring.
190d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
191d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @param finalPosition rest position of the spring
192d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @return the spring force that the given final position is set on
193d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
194d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public SpringForce setFinalPosition(float finalPosition) {
195d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mFinalPosition = finalPosition;
196d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return this;
197d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
198d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
199d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
200d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Returns the rest position of the spring.
201d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
202d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @return rest position of the spring
203d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
204d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public float getFinalPosition() {
205d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return (float) mFinalPosition;
206d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
207d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
208d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /*********************** Below are private APIs *********************/
209d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
210d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
211d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @hide
212d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
213d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    @Override
214d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public float getAcceleration(float lastDisplacement, float lastVelocity) {
215d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
216d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        lastDisplacement -= getFinalPosition();
217d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
218d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        double k = mNaturalFreq * mNaturalFreq;
219d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        double c = 2 * mNaturalFreq * mDampingRatio;
220d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
221d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return (float) (-k * lastDisplacement - c * lastVelocity);
222d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
223d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
224d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
225d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @hide
226d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
227d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    @Override
228d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    public boolean isAtEquilibrium(float value, float velocity) {
229d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        if (Math.abs(velocity) < mVelocityThreshold
230d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                && Math.abs(value - getFinalPosition()) < mValueThreshold) {
231d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            return true;
232d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
233d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return false;
234d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
235d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
236d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
237d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Initialize the string by doing the necessary pre-calculation as well as some sanity check
238d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * on the setup.
239d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
240d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @throws IllegalStateException if the final position is not yet set by the time the spring
241d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *                               animation has started
242d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
243d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    private void init() {
244d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        if (mInitialized) {
245d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            return;
246d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
247d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
248d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        if (mFinalPosition == UNSET) {
249d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            throw new IllegalStateException("Error: Final position of the spring must be"
250d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + " set before the animation starts");
251d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
252d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
253d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        if (mDampingRatio > 1) {
254d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            // Over damping
255d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            mGammaPlus = -mDampingRatio * mNaturalFreq
256d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + mNaturalFreq * Math.sqrt(mDampingRatio * mDampingRatio - 1);
257d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            mGammaMinus = -mDampingRatio * mNaturalFreq
258d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    - mNaturalFreq * Math.sqrt(mDampingRatio * mDampingRatio - 1);
259d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        } else if (mDampingRatio >= 0 && mDampingRatio < 1) {
260d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            // Under damping
261d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            mDampedFreq = mNaturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
262d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
263d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
264d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mInitialized = true;
265d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
266d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
267d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
268d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * Internal only call for Spring to calculate the spring position/velocity using
269d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * an analytical approach.
270d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
271bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu    DynamicAnimation.MassState updateValues(double lastDisplacement, double lastVelocity,
272bfd3aa4c1b99267c42066918cfe8347b132f4dc1Doris Liu            long timeElapsed) {
273d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        init();
274d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
275d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        double deltaT = timeElapsed / 1000d; // unit: seconds
276d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        lastDisplacement -= mFinalPosition;
277d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        double displacement;
278d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        double currentVelocity;
279d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        if (mDampingRatio > 1) {
280d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            // Overdamped
281d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            double coeffA =  lastDisplacement - (mGammaMinus * lastDisplacement - lastVelocity)
282d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    / (mGammaMinus - mGammaPlus);
283d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            double coeffB =  (mGammaMinus * lastDisplacement - lastVelocity)
284d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    / (mGammaMinus - mGammaPlus);
285d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            displacement = coeffA * Math.pow(Math.E, mGammaMinus * deltaT)
286d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + coeffB * Math.pow(Math.E, mGammaPlus * deltaT);
287d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            currentVelocity = coeffA * mGammaMinus * Math.pow(Math.E, mGammaMinus * deltaT)
288d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + coeffB * mGammaPlus * Math.pow(Math.E, mGammaPlus * deltaT);
289d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        } else if (mDampingRatio == 1) {
290d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            // Critically damped
291d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            double coeffA = lastDisplacement;
292d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            double coeffB = lastVelocity + mNaturalFreq * lastDisplacement;
293d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            displacement = (coeffA + coeffB * deltaT) * Math.pow(Math.E, -mNaturalFreq * deltaT);
294d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            currentVelocity = (coeffA + coeffB * deltaT) * Math.pow(Math.E, -mNaturalFreq * deltaT)
295d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    * (-mNaturalFreq) + coeffB * Math.pow(Math.E, -mNaturalFreq * deltaT);
296d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        } else {
297d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            // Underdamped
298d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            double cosCoeff = lastDisplacement;
299d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            double sinCoeff = (1 / mDampedFreq) * (mDampingRatio * mNaturalFreq
300d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    * lastDisplacement + lastVelocity);
301d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            displacement = Math.pow(Math.E, -mDampingRatio * mNaturalFreq * deltaT)
302d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    * (cosCoeff * Math.cos(mDampedFreq * deltaT)
303d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + sinCoeff * Math.sin(mDampedFreq * deltaT));
304d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu            currentVelocity = displacement * (-mNaturalFreq) * mDampingRatio
305d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + Math.pow(Math.E, -mDampingRatio * mNaturalFreq * deltaT)
306d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    * (-mDampedFreq * cosCoeff * Math.sin(mDampedFreq * deltaT)
307d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu                    + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
308d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        }
309d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
310d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mMassState.mValue = (float) (displacement + mFinalPosition);
311d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mMassState.mVelocity = (float) currentVelocity;
312d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        return mMassState;
313d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
314d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu
315d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    /**
316d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * This threshold defines how close the animation value needs to be before the animation can
317d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * finish. This default value is based on the property being animated, e.g. animations on alpha,
318d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * scale, translation or rotation would have different thresholds. This value should be small
319d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * enough to avoid visual glitch of "jumping to the end". But it shouldn't be so small that
320d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * animations take seconds to finish.
321d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *
322d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     * @param threshold the difference between the animation value and final spring position that
323d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     *                  is allowed to end the animation when velocity is very low
324d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu     */
3255df257d2a2f330f8e4d37321a40431973fa9b0d1Doris Liu    void setValueThreshold(double threshold) {
326d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mValueThreshold = Math.abs(threshold);
327d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu        mVelocityThreshold = mValueThreshold * VELOCITY_THRESHOLD_MULTIPLIER;
328d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu    }
329d5206a7781d95cba12ca70a20f7ee742a2e9d807Doris Liu}
330