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 PathInterpolatorBuilder(Path path)30 public PathInterpolatorBuilder(Path path) { 31 initPath(path); 32 } 33 PathInterpolatorBuilder(float controlX, float controlY)34 public PathInterpolatorBuilder(float controlX, float controlY) { 35 initQuad(controlX, controlY); 36 } 37 PathInterpolatorBuilder(float controlX1, float controlY1, float controlX2, float controlY2)38 public PathInterpolatorBuilder(float controlX1, float controlY1, float controlX2, 39 float controlY2) { 40 initCubic(controlX1, controlY1, controlX2, controlY2); 41 } 42 initQuad(float controlX, float controlY)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 initCubic(float x1, float y1, float x2, float y2)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 initPath(Path path)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 getXInterpolator()102 public Interpolator getXInterpolator() { 103 return new PathInterpolator(mDist, mX); 104 } 105 getYInterpolator()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 PathInterpolator(float[] xs, float[] ys)114 private PathInterpolator(float[] xs, float[] ys) { 115 mX = xs; 116 mY = ys; 117 } 118 119 @Override getInterpolation(float t)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