1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.util; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.animation.Interpolator; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Class that acts as an interpolator to match the cubic-bezier css timing function where p0 is 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * fixed at 0,0 and p3 is fixed at 1,1 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class CubicBezierInterpolator implements Interpolator { 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final float mX1; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final float mY1; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final float mX2; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final float mY2; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public CubicBezierInterpolator(final float x1, final float y1, final float x2, final float y2) { 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mX1 = x1; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mY1 = y1; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mX2 = x2; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mY2 = y2; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public float getInterpolation(float v) { 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getY(getTForXValue(v)); 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private float getX(final float t) { 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getCoordinate(t, mX1, mX2); 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private float getY(final float t) { 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return getCoordinate(t, mY1, mY2); 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private float getCoordinate(float t, float p1, float p2) { 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Special case start and end. 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (t == 0.0f || t == 1.0f) { 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return t; 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Step one - from 4 points to 3. 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float ip0 = linearInterpolate(0, p1, t); 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float ip1 = linearInterpolate(p1, p2, t); 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float ip2 = linearInterpolate(p2, 1, t); 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Step two - from 3 points to 2. 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ip0 = linearInterpolate(ip0, ip1, t); 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ip1 = linearInterpolate(ip1, ip2, t); 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Final step - last point. 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return linearInterpolate(ip0, ip1, t); 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private float linearInterpolate(float a, float b, float progress) { 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return a + (b - a) * progress; 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private float getTForXValue(final float x) { 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final float epsilon = 1e-6f; 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int iterations = 8; 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (x <= 0.0f) { 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0.0f; 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (x >= 1.0f) { 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 1.0f; 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Try gradient descent to solve for t. If it works, it is very fast. 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float t = x; 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float minT = 0.0f; 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float maxT = 1.0f; 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd float value = 0.0f; 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < iterations; i++) { 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd value = getX(t); 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd double derivative = (getX(t + epsilon) - value) / epsilon; 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (Math.abs(value - x) < epsilon) { 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return t; 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (Math.abs(derivative) < epsilon) { 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd break; 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (value < x) { 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd minT = t; 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd maxT = t; 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd t -= (value - x) / derivative; 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the gradient descent got stuck in a local minimum, e.g. because the 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // derivative was close to 0, use an interval bisection instead. 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; Math.abs(value - x) > epsilon && i < iterations; i++) { 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (value < x) { 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd minT = t; 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd t = (t + maxT) / 2.0f; 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd maxT = t; 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd t = (t + minT) / 2.0f; 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd value = getX(t); 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return t; 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 121