• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.messaging.util;
18 
19 import android.view.animation.Interpolator;
20 
21 /**
22  * Class that acts as an interpolator to match the cubic-bezier css timing function where p0 is
23  * fixed at 0,0 and p3 is fixed at 1,1
24  */
25 public class CubicBezierInterpolator implements Interpolator {
26     private final float mX1;
27     private final float mY1;
28     private final float mX2;
29     private final float mY2;
30 
CubicBezierInterpolator(final float x1, final float y1, final float x2, final float y2)31     public CubicBezierInterpolator(final float x1, final float y1, final float x2, final float y2) {
32         mX1 = x1;
33         mY1 = y1;
34         mX2 = x2;
35         mY2 = y2;
36     }
37 
38     @Override
getInterpolation(float v)39     public float getInterpolation(float v) {
40         return getY(getTForXValue(v));
41     }
42 
getX(final float t)43     private float getX(final float t) {
44         return getCoordinate(t, mX1, mX2);
45     }
46 
getY(final float t)47     private float getY(final float t) {
48         return getCoordinate(t, mY1, mY2);
49     }
50 
getCoordinate(float t, float p1, float p2)51     private float getCoordinate(float t, float p1, float p2) {
52         // Special case start and end.
53         if (t == 0.0f || t == 1.0f) {
54             return t;
55         }
56 
57         // Step one - from 4 points to 3.
58         float ip0 = linearInterpolate(0, p1, t);
59         float ip1 = linearInterpolate(p1, p2, t);
60         float ip2 = linearInterpolate(p2, 1, t);
61 
62         // Step two - from 3 points to 2.
63         ip0 = linearInterpolate(ip0, ip1, t);
64         ip1 = linearInterpolate(ip1, ip2, t);
65 
66         // Final step - last point.
67         return linearInterpolate(ip0, ip1, t);
68     }
69 
linearInterpolate(float a, float b, float progress)70     private float linearInterpolate(float a, float b, float progress) {
71         return a + (b - a) * progress;
72     }
73 
getTForXValue(final float x)74     private float getTForXValue(final float x) {
75         final float epsilon = 1e-6f;
76         final int iterations = 8;
77 
78         if (x <= 0.0f) {
79             return 0.0f;
80         } else if (x >= 1.0f) {
81             return 1.0f;
82         }
83 
84         // Try gradient descent to solve for t. If it works, it is very fast.
85         float t = x;
86         float minT = 0.0f;
87         float maxT = 1.0f;
88         float value = 0.0f;
89         for (int i = 0; i < iterations; i++) {
90             value = getX(t);
91             double derivative = (getX(t + epsilon) - value) / epsilon;
92             if (Math.abs(value - x) < epsilon) {
93                 return t;
94             } else if (Math.abs(derivative) < epsilon) {
95                 break;
96             } else {
97                 if (value < x) {
98                     minT = t;
99                 } else {
100                     maxT = t;
101                 }
102                 t -= (value - x) / derivative;
103             }
104         }
105 
106         // If the gradient descent got stuck in a local minimum, e.g. because the
107         // derivative was close to 0, use an interval bisection instead.
108         for (int i = 0; Math.abs(value - x) > epsilon && i < iterations; i++) {
109             if (value < x) {
110                 minT = t;
111                 t = (t + maxT) / 2.0f;
112             } else {
113                 maxT = t;
114                 t = (t + minT) / 2.0f;
115             }
116             value = getX(t);
117         }
118         return t;
119     }
120 }
121