• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.mechanics.spring
18 
19 import androidx.compose.ui.util.fastCoerceIn
20 import androidx.compose.ui.util.lerp
21 import androidx.compose.ui.util.packFloats
22 import androidx.compose.ui.util.unpackFloat1
23 import androidx.compose.ui.util.unpackFloat2
24 import kotlin.math.pow
25 
26 /**
27  * Describes the parameters of a spring.
28  *
29  * Note: This is conceptually compatible with the Compose [SpringSpec]. In contrast to the compose
30  * implementation, these [SpringParameters] are intended to be continuously updated.
31  *
32  * @see SpringParameters function to create this value.
33  */
34 @JvmInline
35 value class SpringParameters(val packedValue: Long) {
36     val stiffness: Float
37         get() = unpackFloat1(packedValue)
38 
39     val dampingRatio: Float
40         get() = unpackFloat2(packedValue)
41 
42     /** Whether the spring is expected to immediately end movement. */
43     val isSnapSpring: Boolean
44         get() = stiffness >= snapStiffness && dampingRatio == snapDamping
45 
toStringnull46     override fun toString(): String {
47         return "MechanicsSpringSpec(stiffness=$stiffness, dampingRatio=$dampingRatio)"
48     }
49 
50     companion object {
51         private val snapStiffness = 100_000f
52         private val snapDamping = 1f
53 
54         /** A spring so stiff it completes the motion almost immediately. */
55         val Snap = SpringParameters(snapStiffness, snapDamping)
56     }
57 }
58 
59 /** Creates a [SpringParameters] with the given [stiffness] and [dampingRatio]. */
SpringParametersnull60 fun SpringParameters(stiffness: Float, dampingRatio: Float): SpringParameters {
61     require(stiffness > 0) { "Spring stiffness constant must be positive." }
62     require(dampingRatio >= 0) { "Spring damping constant must be positive." }
63     return SpringParameters(packFloats(stiffness, dampingRatio))
64 }
65 
66 /**
67  * Return interpolated [SpringParameters], based on the [fraction] between [start] and [stop].
68  *
69  * The [SpringParameters.dampingRatio] is interpolated linearly, the [SpringParameters.stiffness] is
70  * interpolated logarithmically.
71  *
72  * The [fraction] is clamped to a `0..1` range.
73  */
lerpnull74 fun lerp(start: SpringParameters, stop: SpringParameters, fraction: Float): SpringParameters {
75     val f = fraction.fastCoerceIn(0f, 1f)
76     val stiffness = start.stiffness.pow(1 - f) * stop.stiffness.pow(f)
77     val dampingRatio = lerp(start.dampingRatio, stop.dampingRatio, f)
78     return SpringParameters(packFloats(stiffness, dampingRatio))
79 }
80