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