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