/**
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.keyframes.model.keyframedmodels;
import android.graphics.Matrix;
import com.facebook.keyframes.model.KFAnimation;
import com.facebook.keyframes.model.KFAnimationFrame;
import java.util.List;
/**
* A {@link KeyFramedObject} which houses information for a matrix based animation. This includes
* rotation, scale, and translation (position) information which can be applied to other animation
* layers or feature layers. This is a post-process object used for {@link KFAnimation}.
*/
public class KeyFramedMatrixAnimation extends KeyFramedObject<KFAnimationFrame, Matrix> {
/**
* Constructs a KeyFramedMatrixAnimation from a {@link KFAnimation}.
*/
public static KeyFramedMatrixAnimation fromAnimation(KFAnimation animation) {
if (!animation.getPropertyType().isMatrixBased()) {
throw new IllegalArgumentException(
"Cannot create a KeyFramedMatrixAnimation from a non matrix based KFAnimation.");
}
return new KeyFramedMatrixAnimation(
animation.getAnimationFrames(),
animation.getTimingCurves(),
animation.getPropertyType(),
animation.getAnchor());
}
/**
* The property type that is animated by this animation.
*/
private final KFAnimation.PropertyType mPropertyType;
/**
* The origin for this matrix animation.
* Deprecated in favor of the ANCHOR_POINT animation.
*/
@Deprecated
private final float[] mAnchor;
private KeyFramedMatrixAnimation(
List<KFAnimationFrame> objects,
float[][][] timingCurves,
KFAnimation.PropertyType propertyType,
float[] anchor) {
super(objects, timingCurves);
mPropertyType = propertyType;
// Deprecated anchor behavior below
mAnchor = anchor != null ? anchor : new float[2];
if (propertyType == KFAnimation.PropertyType.POSITION) {
// Translations are special cased relative transforms.
mAnchor[0] = objects.get(0).getData()[0];
mAnchor[1] = objects.get(0).getData()[1];
}
}
/**
* Applies the current state, given by interpolationValue, to the Matrix object. Implementation
* of the application method depends on the {@link KFAnimation.PropertyType} for this
* animation.
* @param stateA Initial state
* @param stateB End state
* @param interpolationValue Progress [0..1] between stateA and stateB
* @param modifiable The matrix to apply the values to
*/
@Override
protected void applyImpl(
KFAnimationFrame stateA,
KFAnimationFrame stateB,
float interpolationValue,
Matrix modifiable) {
switch (mPropertyType) {
case ROTATION:
applyRotation(stateA, stateB, interpolationValue, modifiable);
break;
case SCALE:
applyScale(stateA, stateB, interpolationValue, modifiable);
break;
case POSITION:
applyPosition(stateA, stateB, interpolationValue, modifiable);
break;
case X_POSITION:
applyXPosition(stateA, stateB, interpolationValue, modifiable);
break;
case Y_POSITION:
applyYPosition(stateA, stateB, interpolationValue, modifiable);
break;
default:
throw new UnsupportedOperationException(
"Cannot apply matrix transformation to " + mPropertyType);
}
}
/**
* This method applies a rotational transform to the matrix, interpolated between two states.
*/
private void applyRotation(
KFAnimationFrame stateA,
KFAnimationFrame stateB,
float interpolationValue,
Matrix modifiable) {
if (stateB == null) {
modifiable.postRotate(stateA.getData()[0],
mAnchor != null ? mAnchor[0] : 0,
mAnchor != null ? mAnchor[1] : 0);
return;
}
float rotationStart = stateA.getData()[0];
float rotationEnd = stateB.getData()[0];
modifiable.postRotate(
interpolateValue(rotationStart, rotationEnd, interpolationValue),
mAnchor != null ? mAnchor[0] : 0,
mAnchor != null ? mAnchor[1] : 0);
}
/**
* This method applies a scale transformation to the matrix, interpolated between two states.
*/
private void applyScale(
KFAnimationFrame stateA,
KFAnimationFrame stateB,
float interpolationValue,
Matrix modifiable) {
if (stateB == null) {
modifiable.postScale(
stateA.getData()[0] / 100f,
stateA.getData()[1] / 100f,
mAnchor != null ? mAnchor[0] : 0,
mAnchor != null ? mAnchor[1] : 0);
return;
}
float scaleStartX = stateA.getData()[0];
float scaleEndX = stateB.getData()[0];
float scaleStartY = stateA.getData()[1];
float scaleEndY = stateB.getData()[1];
modifiable.postScale(
interpolateValue(scaleStartX, scaleEndX, interpolationValue) / 100f,
interpolateValue(scaleStartY, scaleEndY, interpolationValue) / 100f,
mAnchor != null ? mAnchor[0] : 0,
mAnchor != null ? mAnchor[1] : 0);
}
/**
* This method applies an X translation transformation to the matrix, interpolated between two
* states.
*/
private void applyXPosition(
KFAnimationFrame stateA,
KFAnimationFrame stateB,
float interpolationValue,
Matrix modifiable) {
if (stateB == null) {
modifiable.postTranslate(stateA.getData()[0], 0);
return;
}
float translationStartX = stateA.getData()[0];
float translationEndX = stateB.getData()[0];
modifiable.postTranslate(
interpolateValue(translationStartX, translationEndX, interpolationValue),
0);
}
/**
* This method applies a Y translation transformation to the matrix, interpolated between two
* states.
*/
private void applyYPosition(
KFAnimationFrame stateA,
KFAnimationFrame stateB,
float interpolationValue,
Matrix modifiable) {
if (stateB == null) {
modifiable.postTranslate(0, stateA.getData()[0]);
return;
}
float translationStartY = stateA.getData()[0];
float translationEndY = stateB.getData()[0];
modifiable.postTranslate(
0,
interpolateValue(translationStartY, translationEndY, interpolationValue));
}
/**
* This method applies a translation transformation to the matrix. Anchor points for a
* translation animation are special cased to be relative to the position of the first frame
* of this animation. This means that if the translation for the first frame is 80x, 80y, and the
* translation for the second key frame is 90x, 70y, the resulting translation is 10x, -10y.
*
* Deprecated in favor of X_POSITION and Y_POSITION transforms.
*/
@Deprecated
private void applyPosition(
KFAnimationFrame stateA,
KFAnimationFrame stateB,
float interpolationValue,
Matrix modifiable) {
if (stateB == null) {
return;
}
float translationStartX = stateA.getData()[0];
float translationEndX = stateB.getData()[0];
float translationStartY = stateA.getData()[1];
float translationEndY = stateB.getData()[1];
modifiable.postTranslate(
interpolateValue(translationStartX, translationEndX, interpolationValue) - mAnchor[0],
interpolateValue(translationStartY, translationEndY, interpolationValue) - mAnchor[1]);
}
}