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