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 package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
17 
18 /**
19  * Support Animation of the FloatExpression
20  */
21 public class FloatAnimation extends Easing {
22     float[] mSpec;
23     // mSpec[0] = duration
24     // int(mSpec[1]) = num_of_param << 16 | type
25     // mSpec[2..1+num_of_param] params
26     // mSpec[2+num_of_param] starting Value
27     Easing mEasingCurve;
28     private int mType = CUBIC_STANDARD;
29     private float mDuration = 1;
30     private float mWrap = Float.NaN;
31     private float mInitialValue = Float.NaN;
32     private float mTargetValue = Float.NaN;
33     private float mScale = 1;
34     float mOffset = 0;
35 
36     @Override
toString()37     public String toString() {
38 
39         String str = "type " + mType;
40         if (!Float.isNaN(mInitialValue)) {
41             str += " " + mInitialValue;
42         }
43         if (!Float.isNaN(mTargetValue)) {
44             str += " -> " + mTargetValue;
45         }
46         if (!Float.isNaN(mWrap)) {
47             str += "  % " + mWrap;
48         }
49 
50         return str;
51     }
52 
FloatAnimation()53     public FloatAnimation() {
54     }
55 
FloatAnimation(float... description)56     public FloatAnimation(float... description) {
57         setAnimationDescription(description);
58     }
59 
FloatAnimation(int type, float duration, float[] description, float initialValue, float wrap)60     public FloatAnimation(int type,
61                           float duration,
62                           float[] description,
63                           float initialValue,
64                           float wrap) {
65         setAnimationDescription(packToFloatArray(duration,
66                 type, description, initialValue, wrap));
67     }
68 
69     /**
70      * packs spec into a float array
71      *
72      * @param duration
73      * @param type
74      * @param spec
75      * @param initialValue
76      * @return
77      */
packToFloatArray(float duration, int type, float[] spec, float initialValue, float wrap)78     public static float[] packToFloatArray(float duration,
79                                            int type,
80                                            float[] spec,
81                                            float initialValue,
82                                            float wrap) {
83         int count = 0;
84 
85         if (!Float.isNaN(initialValue)) {
86             count++;
87         }
88         if (spec != null) {
89             count++;
90         }
91         if (spec != null || type != CUBIC_STANDARD) {
92             count++;
93             count += (spec == null) ? 0 : spec.length;
94         }
95         if (duration != 1 || count > 0) {
96             count++;
97         }
98         if (!Float.isNaN(initialValue)) {
99             count++;
100         }
101         if (!Float.isNaN(wrap)) {
102             count++;
103         }
104         float[] ret = new float[count];
105         int pos = 0;
106         int specLen = (spec == null) ? 0 : spec.length;
107 
108         if (ret.length > 0) {
109             ret[pos++] = duration;
110 
111         }
112         if (ret.length > 1) {
113             int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
114             int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
115             int bits = type | ((wrapBit | initBit) << 8);
116             ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
117         }
118 
119         if (specLen > 0) {
120             System.arraycopy(spec, 0, ret, pos, spec.length);
121             pos += spec.length;
122         }
123         if (!Float.isNaN(initialValue)) {
124             ret[pos++] = initialValue;
125         }
126         if (!Float.isNaN(wrap)) {
127             ret[pos] = wrap;
128         }
129         return ret;
130     }
131 
132     /**
133      * Create an animation based on a float encoding of the animation
134      * @param description
135      */
setAnimationDescription(float[] description)136     public void setAnimationDescription(float[] description) {
137         mSpec = description;
138         mDuration = (mSpec.length == 0) ? 1 : mSpec[0];
139         int len = 0;
140         if (mSpec.length > 1) {
141             int num_type = Float.floatToRawIntBits(mSpec[1]);
142             mType = num_type & 0xFF;
143             boolean wrap = ((num_type >> 8) & 0x1) > 0;
144             boolean init = ((num_type >> 8) & 0x2) > 0;
145             len = (num_type >> 16) & 0xFFFF;
146             int off = 2 + len;
147             if (init) {
148                 mInitialValue = mSpec[off++];
149             }
150             if (wrap) {
151                 mWrap = mSpec[off];
152             }
153         }
154         create(mType, description, 2, len);
155     }
156 
create(int type, float[] params, int offset, int len)157     private void create(int type, float[] params, int offset, int len) {
158         switch (type) {
159             case CUBIC_STANDARD:
160             case CUBIC_ACCELERATE:
161             case CUBIC_DECELERATE:
162             case CUBIC_LINEAR:
163             case CUBIC_ANTICIPATE:
164             case CUBIC_OVERSHOOT:
165                 mEasingCurve = new CubicEasing(type);
166                 break;
167             case CUBIC_CUSTOM:
168                 mEasingCurve = new CubicEasing(params[offset + 0],
169                         params[offset + 1],
170                         params[offset + 2],
171                         params[offset + 3]
172                 );
173                 break;
174             case EASE_OUT_BOUNCE:
175                 mEasingCurve = new BounceCurve(type);
176                 break;
177             case EASE_OUT_ELASTIC:
178                 mEasingCurve = new ElasticOutCurve();
179                 break;
180             case SPLINE_CUSTOM:
181                 mEasingCurve = new StepCurve(params, offset, len);
182                 break;
183         }
184     }
185 
186     /**
187      * Get the duration the interpolate is to take
188      * @return duration in seconds
189      */
getDuration()190     public float getDuration() {
191         return mDuration;
192     }
193 
194     /**
195      * Set the initial Value
196      * @param value
197      */
setInitialValue(float value)198     public void setInitialValue(float value) {
199 
200         if (Float.isNaN(mWrap)) {
201             mInitialValue = value;
202         } else {
203             mInitialValue = value % mWrap;
204         }
205         setScaleOffset();
206     }
207 
208     /**
209      * Set the target value to interpolate to
210      * @param value
211      */
setTargetValue(float value)212     public void setTargetValue(float value) {
213         if (Float.isNaN(mWrap)) {
214             mTargetValue = value;
215         } else {
216             if (Math.abs((value % mWrap) + mWrap - mInitialValue)
217                     < Math.abs((value % mWrap) - mInitialValue)) {
218                 mTargetValue = (value % mWrap) + mWrap;
219 
220             } else {
221                 mTargetValue = value % mWrap;
222             }
223         }
224         setScaleOffset();
225     }
226 
getTargetValue()227     public float getTargetValue() {
228         return mTargetValue;
229     }
230 
setScaleOffset()231     private void setScaleOffset() {
232         if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
233             mScale = (mTargetValue - mInitialValue);
234             mOffset = mInitialValue;
235         } else {
236             mScale = 1;
237             mOffset = 0;
238         }
239     }
240 
241     /**
242      * get the value at time t in seconds since start
243      */
get(float t)244     public float get(float t) {
245         return mEasingCurve.get(t / mDuration)
246                 * (mTargetValue - mInitialValue) + mInitialValue;
247     }
248 
249     /**
250      * get the slope of the easing function at at x
251      */
getDiff(float t)252     public float getDiff(float t) {
253         return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
254     }
255 
getInitialValue()256     public float getInitialValue() {
257         return mInitialValue;
258     }
259 }
260