Home | History | Annotate | Download | only in util
      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 
     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
     39     public float getInterpolation(float v) {
     40         return getY(getTForXValue(v));
     41     }
     42 
     43     private float getX(final float t) {
     44         return getCoordinate(t, mX1, mX2);
     45     }
     46 
     47     private float getY(final float t) {
     48         return getCoordinate(t, mY1, mY2);
     49     }
     50 
     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 
     70     private float linearInterpolate(float a, float b, float progress) {
     71         return a + (b - a) * progress;
     72     }
     73 
     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