Home | History | Annotate | Download | only in qs
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
      5  * except in compliance with the License. You may obtain a copy of the License at
      6  *
      7  *      http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the
     10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     11  * KIND, either express or implied. See the License for the specific language governing
     12  * permissions and limitations under the License.
     13  */
     14 
     15 package com.android.systemui.qs;
     16 
     17 import android.graphics.Path;
     18 import android.view.animation.BaseInterpolator;
     19 import android.view.animation.Interpolator;
     20 
     21 public class PathInterpolatorBuilder {
     22 
     23     // This governs how accurate the approximation of the Path is.
     24     private static final float PRECISION = 0.002f;
     25 
     26     private float[] mX; // x coordinates in the line
     27     private float[] mY; // y coordinates in the line
     28     private float[] mDist; // Cumulative percentage length of the line
     29 
     30     public PathInterpolatorBuilder(Path path) {
     31         initPath(path);
     32     }
     33 
     34     public PathInterpolatorBuilder(float controlX, float controlY) {
     35         initQuad(controlX, controlY);
     36     }
     37 
     38     public PathInterpolatorBuilder(float controlX1, float controlY1, float controlX2,
     39             float controlY2) {
     40         initCubic(controlX1, controlY1, controlX2, controlY2);
     41     }
     42 
     43     private void initQuad(float controlX, float controlY) {
     44         Path path = new Path();
     45         path.moveTo(0, 0);
     46         path.quadTo(controlX, controlY, 1f, 1f);
     47         initPath(path);
     48     }
     49 
     50     private void initCubic(float x1, float y1, float x2, float y2) {
     51         Path path = new Path();
     52         path.moveTo(0, 0);
     53         path.cubicTo(x1, y1, x2, y2, 1f, 1f);
     54         initPath(path);
     55     }
     56 
     57     private void initPath(Path path) {
     58         float[] pointComponents = path.approximate(PRECISION);
     59 
     60         int numPoints = pointComponents.length / 3;
     61         if (pointComponents[1] != 0 || pointComponents[2] != 0
     62                 || pointComponents[pointComponents.length - 2] != 1
     63                 || pointComponents[pointComponents.length - 1] != 1) {
     64             throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)");
     65         }
     66 
     67         mX = new float[numPoints];
     68         mY = new float[numPoints];
     69         mDist = new float[numPoints];
     70         float prevX = 0;
     71         float prevFraction = 0;
     72         int componentIndex = 0;
     73         for (int i = 0; i < numPoints; i++) {
     74             float fraction = pointComponents[componentIndex++];
     75             float x = pointComponents[componentIndex++];
     76             float y = pointComponents[componentIndex++];
     77             if (fraction == prevFraction && x != prevX) {
     78                 throw new IllegalArgumentException(
     79                         "The Path cannot have discontinuity in the X axis.");
     80             }
     81             if (x < prevX) {
     82                 throw new IllegalArgumentException("The Path cannot loop back on itself.");
     83             }
     84             mX[i] = x;
     85             mY[i] = y;
     86             if (i > 0) {
     87                 float dx = mX[i] - mX[i - 1];
     88                 float dy = mY[i] - mY[i - 1];
     89                 float dist = (float) Math.sqrt(dx * dx + dy * dy);
     90                 mDist[i] = mDist[i - 1] + dist;
     91             }
     92             prevX = x;
     93             prevFraction = fraction;
     94         }
     95         // Scale down dist to 0-1.
     96         float max = mDist[mDist.length - 1];
     97         for (int i = 0; i < numPoints; i++) {
     98             mDist[i] /= max;
     99         }
    100     }
    101 
    102     public Interpolator getXInterpolator() {
    103         return new PathInterpolator(mDist, mX);
    104     }
    105 
    106     public Interpolator getYInterpolator() {
    107         return new PathInterpolator(mDist, mY);
    108     }
    109 
    110     private static class PathInterpolator extends BaseInterpolator {
    111         private final float[] mX; // x coordinates in the line
    112         private final float[] mY; // y coordinates in the line
    113 
    114         private PathInterpolator(float[] xs, float[] ys) {
    115             mX = xs;
    116             mY = ys;
    117         }
    118 
    119         @Override
    120         public float getInterpolation(float t) {
    121             if (t <= 0) {
    122                 return 0;
    123             } else if (t >= 1) {
    124                 return 1;
    125             }
    126             // Do a binary search for the correct x to interpolate between.
    127             int startIndex = 0;
    128             int endIndex = mX.length - 1;
    129 
    130             while (endIndex - startIndex > 1) {
    131                 int midIndex = (startIndex + endIndex) / 2;
    132                 if (t < mX[midIndex]) {
    133                     endIndex = midIndex;
    134                 } else {
    135                     startIndex = midIndex;
    136                 }
    137             }
    138 
    139             float xRange = mX[endIndex] - mX[startIndex];
    140             if (xRange == 0) {
    141                 return mY[startIndex];
    142             }
    143 
    144             float tInRange = t - mX[startIndex];
    145             float fraction = tInRange / xRange;
    146 
    147             float startY = mY[startIndex];
    148             float endY = mY[endIndex];
    149             return startY + (fraction * (endY - startY));
    150         }
    151     }
    152 
    153 }
    154