/* * Copyright (C) 2015 Fastboot Mobile, LLC. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses>. */ package com.fastbootmobile.encore.app.ui; import android.graphics.PointF; import android.view.animation.Animation; import android.view.animation.Transformation; // From https://gist.github.com/guohai/2293628 public class CircularPathAnimation extends Animation { private int mFromXType = ABSOLUTE; private int mToXType = ABSOLUTE; private int mFromYType = ABSOLUTE; private int mToYType = ABSOLUTE; private float mFromXValue = 0.0f; private float mToXValue = 0.0f; private float mFromYValue = 0.0f; private float mToYValue = 0.0f; private float mFromXDelta; private float mToXDelta; private float mFromYDelta; private float mToYDelta; private PointF mStart; private PointF mControl; private PointF mEnd; /** * Constructor to use when building a CircularPathAnimation from code * * @param fromXDelta * Change in X coordinate to apply at the start of the animation * @param toXDelta * Change in X coordinate to apply at the end of the animation * @param fromYDelta * Change in Y coordinate to apply at the start of the animation * @param toYDelta * Change in Y coordinate to apply at the end of the animation */ public CircularPathAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) { mFromXValue = fromXDelta; mToXValue = toXDelta; mFromYValue = fromYDelta; mToYValue = toYDelta; mFromXType = ABSOLUTE; mToXType = ABSOLUTE; mFromYType = ABSOLUTE; mToYType = ABSOLUTE; } /** * Constructor to use when building a CircularPathAnimation from code * * @param fromXType * Specifies how fromXValue should be interpreted. One of * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param fromXValue * Change in X coordinate to apply at the start of the animation. * This value can either be an absolute number if fromXType is * ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. * @param toXType * Specifies how toXValue should be interpreted. One of * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param toXValue * Change in X coordinate to apply at the end of the animation. * This value can either be an absolute number if toXType is * ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. * @param fromYType * Specifies how fromYValue should be interpreted. One of * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param fromYValue * Change in Y coordinate to apply at the start of the animation. * This value can either be an absolute number if fromYType is * ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. * @param toYType * Specifies how toYValue should be interpreted. One of * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param toYValue * Change in Y coordinate to apply at the end of the animation. * This value can either be an absolute number if toYType is * ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. */ public CircularPathAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue) { mFromXValue = fromXValue; mToXValue = toXValue; mFromYValue = fromYValue; mToYValue = toYValue; mFromXType = fromXType; mToXType = toXType; mFromYType = fromYType; mToYType = toYType; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { float dx = calcBezier(interpolatedTime, mStart.x, mControl.x, mEnd.x); float dy = calcBezier(interpolatedTime, mStart.y, mControl.y, mEnd.y); t.getMatrix().setTranslate(dx, dy); } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth); mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth); mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight); mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight); mStart = new PointF(mFromXDelta, mFromYDelta); mEnd = new PointF(mToXDelta, mToYDelta); mControl = new PointF(mFromXDelta, mToYDelta); // How to choose the // Control point(we can // use the cross of the // two tangents from p0, // p1) } /** * Calculate the position on a quadratic bezier curve by given three points * and the percentage of time passed. * * from http://en.wikipedia.org/wiki/B%C3%A9zier_curve * * @param interpolatedTime * the fraction of the duration that has passed where 0 <= time * <= 1 * @param p0 * a single dimension of the starting point * @param p1 * a single dimension of the control point * @param p2 * a single dimension of the ending point */ private long calcBezier(float interpolatedTime, float p0, float p1, float p2) { return Math.round((Math.pow((1 - interpolatedTime), 2) * p0) + (2 * (1 - interpolatedTime) * interpolatedTime * p1) + (Math.pow(interpolatedTime, 2) * p2)); } }