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