1 /* 2 * Copyright (C) 2020 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 androidx.constraintlayout.core.motion.utils; 18 19 import androidx.constraintlayout.core.motion.CustomAttribute; 20 import androidx.constraintlayout.core.motion.CustomVariable; 21 import androidx.constraintlayout.core.motion.MotionWidget; 22 import androidx.constraintlayout.core.state.WidgetFrame; 23 24 import java.text.DecimalFormat; 25 import java.util.Arrays; 26 27 /** 28 * This engine allows manipulation of attributes by Curves 29 * 30 * 31 */ 32 33 public abstract class SplineSet { 34 private static final String TAG = "SplineSet"; 35 protected CurveFit mCurveFit; 36 protected int[] mTimePoints = new int[10]; 37 protected float[] mValues = new float[10]; 38 private int mCount; 39 private String mType; 40 41 // @TODO: add description setProperty(TypedValues widget, float t)42 public void setProperty(TypedValues widget, float t) { 43 widget.setValue(TypedValues.AttributesType.getId(mType), get(t)); 44 } 45 46 @Override toString()47 public String toString() { 48 String str = mType; 49 DecimalFormat df = new DecimalFormat("##.##"); 50 for (int i = 0; i < mCount; i++) { 51 str += "[" + mTimePoints[i] + " , " + df.format(mValues[i]) + "] "; 52 53 } 54 return str; 55 } 56 setType(String type)57 public void setType(String type) { 58 mType = type; 59 } 60 61 // @TODO: add description get(float t)62 public float get(float t) { 63 return (float) mCurveFit.getPos(t, 0); 64 } 65 66 // @TODO: add description getSlope(float t)67 public float getSlope(float t) { 68 return (float) mCurveFit.getSlope(t, 0); 69 } 70 getCurveFit()71 public CurveFit getCurveFit() { 72 return mCurveFit; 73 } 74 75 // @TODO: add description setPoint(int position, float value)76 public void setPoint(int position, float value) { 77 if (mTimePoints.length < mCount + 1) { 78 mTimePoints = Arrays.copyOf(mTimePoints, mTimePoints.length * 2); 79 mValues = Arrays.copyOf(mValues, mValues.length * 2); 80 } 81 mTimePoints[mCount] = position; 82 mValues[mCount] = value; 83 mCount++; 84 } 85 86 // @TODO: add description setup(int curveType)87 public void setup(int curveType) { 88 if (mCount == 0) { 89 return; 90 } 91 92 Sort.doubleQuickSort(mTimePoints, mValues, 0, mCount - 1); 93 94 int unique = 1; 95 96 for (int i = 1; i < mCount; i++) { 97 if (mTimePoints[i - 1] != mTimePoints[i]) { 98 unique++; 99 } 100 } 101 102 double[] time = new double[unique]; 103 double[][] values = new double[unique][1]; 104 int k = 0; 105 for (int i = 0; i < mCount; i++) { 106 if (i > 0 && mTimePoints[i] == mTimePoints[i - 1]) { 107 continue; 108 } 109 110 time[k] = mTimePoints[i] * 1E-2; 111 values[k][0] = mValues[i]; 112 k++; 113 } 114 mCurveFit = CurveFit.get(curveType, time, values); 115 } 116 117 // @TODO: add description makeCustomSpline(String str, KeyFrameArray.CustomArray attrList)118 public static SplineSet makeCustomSpline(String str, KeyFrameArray.CustomArray attrList) { 119 return new CustomSet(str, attrList); 120 } 121 122 // @TODO: add description makeCustomSplineSet(String str, KeyFrameArray.CustomVar attrList)123 public static SplineSet makeCustomSplineSet(String str, KeyFrameArray.CustomVar attrList) { 124 return new CustomSpline(str, attrList); 125 } 126 127 // @TODO: add description makeSpline(String str, long currentTime)128 public static SplineSet makeSpline(String str, long currentTime) { 129 130 return new CoreSpline(str, currentTime); 131 } 132 133 private static class Sort { 134 doubleQuickSort(int[] key, float[] value, int low, int hi)135 static void doubleQuickSort(int[] key, float[] value, int low, int hi) { 136 int[] stack = new int[key.length + 10]; 137 int count = 0; 138 stack[count++] = hi; 139 stack[count++] = low; 140 while (count > 0) { 141 low = stack[--count]; 142 hi = stack[--count]; 143 if (low < hi) { 144 int p = partition(key, value, low, hi); 145 stack[count++] = p - 1; 146 stack[count++] = low; 147 stack[count++] = hi; 148 stack[count++] = p + 1; 149 } 150 } 151 } 152 partition(int[] array, float[] value, int low, int hi)153 private static int partition(int[] array, float[] value, int low, int hi) { 154 int pivot = array[hi]; 155 int i = low; 156 for (int j = low; j < hi; j++) { 157 if (array[j] <= pivot) { 158 swap(array, value, i, j); 159 i++; 160 } 161 } 162 swap(array, value, i, hi); 163 return i; 164 } 165 swap(int[] array, float[] value, int a, int b)166 private static void swap(int[] array, float[] value, int a, int b) { 167 int tmp = array[a]; 168 array[a] = array[b]; 169 array[b] = tmp; 170 float tmpv = value[a]; 171 value[a] = value[b]; 172 value[b] = tmpv; 173 } 174 } 175 176 177 public static class CustomSet extends SplineSet { 178 String mAttributeName; 179 KeyFrameArray.CustomArray mConstraintAttributeList; 180 float[] mTempValues; 181 182 @SuppressWarnings("StringSplitter") CustomSet(String attribute, KeyFrameArray.CustomArray attrList)183 public CustomSet(String attribute, KeyFrameArray.CustomArray attrList) { 184 mAttributeName = attribute.split(",")[1]; 185 mConstraintAttributeList = attrList; 186 } 187 188 // @TODO: add description 189 @Override setup(int curveType)190 public void setup(int curveType) { 191 int size = mConstraintAttributeList.size(); 192 int dimensionality = mConstraintAttributeList.valueAt(0).numberOfInterpolatedValues(); 193 double[] time = new double[size]; 194 mTempValues = new float[dimensionality]; 195 double[][] values = new double[size][dimensionality]; 196 for (int i = 0; i < size; i++) { 197 198 int key = mConstraintAttributeList.keyAt(i); 199 CustomAttribute ca = mConstraintAttributeList.valueAt(i); 200 201 time[i] = key * 1E-2; 202 ca.getValuesToInterpolate(mTempValues); 203 for (int k = 0; k < mTempValues.length; k++) { 204 values[i][k] = mTempValues[k]; 205 } 206 207 } 208 mCurveFit = CurveFit.get(curveType, time, values); 209 } 210 211 // @TODO: add description 212 @Override setPoint(int position, float value)213 public void setPoint(int position, float value) { 214 throw new RuntimeException("don't call for custom " 215 + "attribute call setPoint(pos, ConstraintAttribute)"); 216 } 217 218 // @TODO: add description setPoint(int position, CustomAttribute value)219 public void setPoint(int position, CustomAttribute value) { 220 mConstraintAttributeList.append(position, value); 221 } 222 223 // @TODO: add description setProperty(WidgetFrame view, float t)224 public void setProperty(WidgetFrame view, float t) { 225 mCurveFit.getPos(t, mTempValues); 226 view.setCustomValue(mConstraintAttributeList.valueAt(0), mTempValues); 227 } 228 } 229 230 231 private static class CoreSpline extends SplineSet { 232 String mType; 233 @SuppressWarnings("unused") long mStart; 234 CoreSpline(String str, long currentTime)235 CoreSpline(String str, long currentTime) { 236 mType = str; 237 mStart = currentTime; 238 } 239 240 @Override setProperty(TypedValues widget, float t)241 public void setProperty(TypedValues widget, float t) { 242 int id = widget.getId(mType); 243 widget.setValue(id, get(t)); 244 } 245 } 246 247 public static class CustomSpline extends SplineSet { 248 String mAttributeName; 249 KeyFrameArray.CustomVar mConstraintAttributeList; 250 float[] mTempValues; 251 252 @SuppressWarnings("StringSplitter") CustomSpline(String attribute, KeyFrameArray.CustomVar attrList)253 public CustomSpline(String attribute, KeyFrameArray.CustomVar attrList) { 254 mAttributeName = attribute.split(",")[1]; 255 mConstraintAttributeList = attrList; 256 } 257 258 // @TODO: add description 259 @Override setup(int curveType)260 public void setup(int curveType) { 261 int size = mConstraintAttributeList.size(); 262 int dimensionality = mConstraintAttributeList.valueAt(0).numberOfInterpolatedValues(); 263 double[] time = new double[size]; 264 mTempValues = new float[dimensionality]; 265 double[][] values = new double[size][dimensionality]; 266 for (int i = 0; i < size; i++) { 267 268 int key = mConstraintAttributeList.keyAt(i); 269 CustomVariable ca = mConstraintAttributeList.valueAt(i); 270 271 time[i] = key * 1E-2; 272 ca.getValuesToInterpolate(mTempValues); 273 for (int k = 0; k < mTempValues.length; k++) { 274 values[i][k] = mTempValues[k]; 275 } 276 277 } 278 mCurveFit = CurveFit.get(curveType, time, values); 279 } 280 281 // @TODO: add description 282 @Override setPoint(int position, float value)283 public void setPoint(int position, float value) { 284 throw new RuntimeException("don't call for custom attribute" 285 + " call setPoint(pos, ConstraintAttribute)"); 286 } 287 288 // @TODO: add description 289 @Override setProperty(TypedValues widget, float t)290 public void setProperty(TypedValues widget, float t) { 291 setProperty((MotionWidget) widget, t); 292 } 293 294 // @TODO: add description setPoint(int position, CustomVariable value)295 public void setPoint(int position, CustomVariable value) { 296 mConstraintAttributeList.append(position, value); 297 } 298 299 // @TODO: add description setProperty(MotionWidget view, float t)300 public void setProperty(MotionWidget view, float t) { 301 mCurveFit.getPos(t, mTempValues); 302 mConstraintAttributeList.valueAt(0).setInterpolatedValue(view, mTempValues); 303 } 304 } 305 306 } 307