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