1376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen/*
2376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * Copyright (C) 2015 The Android Open Source Project
3376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen *
4376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * Licensed under the Apache License, Version 2.0 (the "License");
5376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * you may not use this file except in compliance with the License.
6376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * You may obtain a copy of the License at
7376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen *
8150417cd66a89ffb27fd7edb4b7ff3e66dd377ccAurimas Liutikas *      http://www.apache.org/licenses/LICENSE-2.0
9376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen *
10376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * Unless required by applicable law or agreed to in writing, software
11376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * distributed under the License is distributed on an "AS IS" BASIS,
12376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * See the License for the specific language governing permissions and
14376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen * limitations under the License.
15376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen */
16376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
17376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassenpackage android.support.v4.view.animation;
18376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
19376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassenimport android.graphics.Path;
20376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassenimport android.graphics.PathMeasure;
21376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassenimport android.view.animation.Interpolator;
22376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
23376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen/**
24150417cd66a89ffb27fd7edb4b7ff3e66dd377ccAurimas Liutikas * A path interpolator implementation compatible with API 14+.
25376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen */
26150417cd66a89ffb27fd7edb4b7ff3e66dd377ccAurimas Liutikasclass PathInterpolatorApi14 implements Interpolator {
27376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
28376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    /**
29376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen     * Governs the accuracy of the approximation of the {@link Path}.
30376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen     */
31376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    private static final float PRECISION = 0.002f;
32376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
33376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    private final float[] mX;
34376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    private final float[] mY;
35376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
36150417cd66a89ffb27fd7edb4b7ff3e66dd377ccAurimas Liutikas    PathInterpolatorApi14(Path path) {
37376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
38376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
39376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float pathLength = pathMeasure.getLength();
40376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final int numPoints = (int) (pathLength / PRECISION) + 1;
41376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
42376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        mX = new float[numPoints];
43376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        mY = new float[numPoints];
44376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
45376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float[] position = new float[2];
46376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        for (int i = 0; i < numPoints; ++i) {
47376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            final float distance = (i * pathLength) / (numPoints - 1);
48376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            pathMeasure.getPosTan(distance, position, null /* tangent */);
49376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
50376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            mX[i] = position[0];
51376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            mY[i] = position[1];
52376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        }
53376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    }
54376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
55150417cd66a89ffb27fd7edb4b7ff3e66dd377ccAurimas Liutikas    PathInterpolatorApi14(float controlX, float controlY) {
56376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        this(createQuad(controlX, controlY));
57376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    }
58376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
59150417cd66a89ffb27fd7edb4b7ff3e66dd377ccAurimas Liutikas    PathInterpolatorApi14(float controlX1, float controlY1,
60376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            float controlX2, float controlY2) {
61376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        this(createCubic(controlX1, controlY1, controlX2, controlY2));
62376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    }
63376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
64376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    @Override
65376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    public float getInterpolation(float t) {
66376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        if (t <= 0.0f) {
67376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            return 0.0f;
68376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        } else if (t >= 1.0f) {
69376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            return 1.0f;
70376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        }
71376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
72376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        // Do a binary search for the correct x to interpolate between.
73376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        int startIndex = 0;
74376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        int endIndex = mX.length - 1;
75376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        while (endIndex - startIndex > 1) {
76376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            int midIndex = (startIndex + endIndex) / 2;
77376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            if (t < mX[midIndex]) {
78376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen                endIndex = midIndex;
79376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            } else {
80376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen                startIndex = midIndex;
81376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            }
82376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        }
83376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
84376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float xRange = mX[endIndex] - mX[startIndex];
85376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        if (xRange == 0) {
86376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            return mY[startIndex];
87376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        }
88376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
89376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float tInRange = t - mX[startIndex];
90376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float fraction = tInRange / xRange;
91376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
92376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float startY = mY[startIndex];
93376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final float endY = mY[endIndex];
94376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
95376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        return startY + (fraction * (endY - startY));
96376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    }
97376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
98376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    private static Path createQuad(float controlX, float controlY) {
99376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final Path path = new Path();
100376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        path.moveTo(0.0f, 0.0f);
101376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        path.quadTo(controlX, controlY, 1.0f, 1.0f);
102376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        return path;
103376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    }
104376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen
105376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    private static Path createCubic(float controlX1, float controlY1,
106376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen            float controlX2, float controlY2) {
107376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        final Path path = new Path();
108376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        path.moveTo(0.0f, 0.0f);
109376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
110376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen        return path;
111376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen    }
112376f90c159c74a267a5b5e13a5d71273980a72dfJustin Klaassen}
113