Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timob 9493 Android animation regression when animation contains multiple simultaneous transformations. #2416

Merged
merged 4 commits into from
Jun 18, 2012
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LinearInterpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.view.animation.TranslateAnimation;

Expand Down Expand Up @@ -257,80 +256,17 @@ public AnimationSet render(TiViewProxy viewProxy, View view, int x, int y, int w

TiUIView tiView = viewProxy.peekView();

// For scale animations, rely on the Android-provided ScaleAnimation rather than our
// TiMatrixAnimation and Operation.
Animation anim;
if (tdm.isScaleOperation()) {
// fromX, toX, fromY, toY, anchorX, anchorY
float[] params = tdm.getScaleOperationParameters();
float fromX = params[0];
float fromY = params[2];

if (fromX == Ti2DMatrix.VALUE_UNSPECIFIED) {
if (tiView != null) {
fromX = tiView.getAnimatedXScale();
} else {
fromX = 1.0f; // i.e., regular size.
}
}

if (fromY == Ti2DMatrix.VALUE_UNSPECIFIED) {
if (tiView != null) {
fromY = tiView.getAnimatedYScale();
} else {
fromY = 1.0f; // i.e., regular size.
}
}

anim = new ScaleAnimation(fromX, params[1], fromY, params[3],
Animation.RELATIVE_TO_SELF, params[4],
Animation.RELATIVE_TO_SELF, params[5]);

// Remember the toX, toY
if (tiView != null) {
float rememberX = params[1];
float rememberY = params[3];

// But if autoreverse is in effect, then
// set back to the fromX, fromY
if (autoreverse != null && autoreverse.booleanValue()) {
rememberX = fromX;
rememberY = fromY;
}

tiView.setAnimatedXScale(rememberX);
tiView.setAnimatedYScale(rememberY);
}

} else {

if (tdm.isRotateOperation()) {
// Make sure we rotate from the right starting position,
// in case a rotation has already occurred.
float[] params = tdm.getRotateOperationParameters();
float fromDegrees = params[0];
float toDegrees = params[1];

if (fromDegrees == Ti2DMatrix.VALUE_UNSPECIFIED) {
fromDegrees = tiView.getAnimatedRotationDegrees();
tdm.setRotationFromDegrees(fromDegrees);
}

// And remember for next time.
if (autoreverse == null || !autoreverse.booleanValue()) {
tiView.setAnimatedRotationDegrees(toDegrees);
} else {
// Because the animation will autoreverse, we
// want to save the original degrees.
tiView.setAnimatedRotationDegrees(fromDegrees);
}

}

anim = new TiMatrixAnimation(tdm, anchorX, anchorY);
if (tdm.hasScaleOperation() && tiView != null) {
tiView.setAnimatedScaleValues(tdm.verifyScaleValues(tiView, (autoreverse != null && autoreverse.booleanValue())));
}

if (tdm.hasRotateOperation() && tiView != null) {
tiView.setAnimatedRotationDegrees(tdm.verifyRotationValues(tiView, (autoreverse != null && autoreverse.booleanValue())));
}

anim = new TiMatrixAnimation(tdm, anchorX, anchorY);

anim.setFillAfter(true);

if (duration != null) {
Expand Down
150 changes: 125 additions & 25 deletions android/titanium/src/java/org/appcelerator/titanium/view/Ti2DMatrix.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.kroll.common.Log;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.TiConvert;

import android.graphics.Matrix;
import android.util.Pair;

@Kroll.proxy
public class Ti2DMatrix extends KrollProxy
{
private static final String TAG = "Ti2DMatrix";

public static final float DEFAULT_ANCHOR_VALUE = -1f;
public static final float VALUE_UNSPECIFIED = Float.MIN_VALUE;

Expand All @@ -41,6 +39,8 @@ protected static class Operation
protected float anchorX = 0.5f, anchorY = 0.5f;
protected Ti2DMatrix multiplyWith;
protected int type;
protected boolean scaleFromValuesSpecified = false;
protected boolean rotationFromValueSpecified = false;

public Operation(int type)
{
Expand Down Expand Up @@ -132,17 +132,20 @@ public Ti2DMatrix scale(Object args[])
// varargs for API backwards compatibility
if (args.length == 4) {
// scale(fromX, fromY, toX, toY)
newMatrix.op.scaleFromValuesSpecified = true;
newMatrix.op.scaleFromX = TiConvert.toFloat(args[0]);
newMatrix.op.scaleFromY = TiConvert.toFloat(args[1]);
newMatrix.op.scaleToX = TiConvert.toFloat(args[2]);
newMatrix.op.scaleToY = TiConvert.toFloat(args[3]);
}
if (args.length == 2) {
// scale(toX, toY)
newMatrix.op.scaleFromValuesSpecified = false;
newMatrix.op.scaleToX = TiConvert.toFloat(args[0]);
newMatrix.op.scaleToY = TiConvert.toFloat(args[1]);
} else if (args.length == 1) {
// scale(scaleFactor)
newMatrix.op.scaleFromValuesSpecified = false;
newMatrix.op.scaleToX = newMatrix.op.scaleToY = TiConvert.toFloat(args[0]);
}
// TODO newMatrix.handleAnchorPoint(newMatrix.getProperties());
Expand All @@ -155,9 +158,11 @@ public Ti2DMatrix rotate(Object[] args)
Ti2DMatrix newMatrix = new Ti2DMatrix(this, Operation.TYPE_ROTATE);

if (args.length == 1) {
newMatrix.op.rotationFromValueSpecified = false;
newMatrix.op.rotateFrom = VALUE_UNSPECIFIED;
newMatrix.op.rotateTo = TiConvert.toFloat(args[0]);
} else if (args.length == 2) {
newMatrix.op.rotationFromValueSpecified = true;
newMatrix.op.rotateFrom = TiConvert.toFloat(args[0]);
newMatrix.op.rotateTo = TiConvert.toFloat(args[1]);
}
Expand Down Expand Up @@ -213,43 +218,138 @@ public Matrix interpolate(float interpolatedTime, int childWidth, int childHeigh
return matrix;
}

public boolean isScaleOperation()
/**
* Check if this matrix has an operation of a particular type, or if any
* in the chain of operations preceding it does.
* @param operationType Operation.TYPE_SCALE, etc.
* @return true if this matrix or any of the "prev" matrices is of the given type, false otherwise
*/
private boolean containsOperationOfType(int operationType)
{
if (this.op == null) {
return false;
Ti2DMatrix check = this;
while (check != null) {
if (check.op != null && check.op.type == operationType) {
return true;
}
check = check.prev;
}
return (this.op.type == Operation.TYPE_SCALE);
return false;
}

public boolean isRotateOperation()
public boolean hasScaleOperation()
{
if (this.op == null) {
return false;
return containsOperationOfType(Operation.TYPE_SCALE);
}

public boolean hasRotateOperation()
{
return containsOperationOfType(Operation.TYPE_ROTATE);
}

/**
* Checks all of the scale operations in the sequence and sets the appropriate
* scale "from" values for them all (in case they aren't specified), then gives
* back the final scale values that will be in effect when the animation has completed.
* @param view
* @param autoreverse
* @return Final scale values after the animation has finished.
*/
public Pair<Float, Float> verifyScaleValues(TiUIView view, boolean autoreverse)
{
ArrayList<Operation> scaleOps = new ArrayList<Operation>();

Ti2DMatrix check = this;
while (check != null) {

if (check.op != null && check.op.type == Operation.TYPE_SCALE) {
scaleOps.add(0, check.op);
}
check = check.prev;
}

Pair<Float, Float> viewCurrentScale = (view == null ?
Pair.create(Float.valueOf(1f), Float.valueOf(1f)) :
view.getAnimatedScaleValues());

if (scaleOps.size() == 0) {
return viewCurrentScale;
}

float lastToX = viewCurrentScale.first;
float lastToY = viewCurrentScale.second;

for (Operation op : scaleOps) {
if (!op.scaleFromValuesSpecified) {
// The "from" values were not specified,
// so they should be whatever the last "to" values were.
op.scaleFromX = lastToX;
op.scaleFromY = lastToY;
}
lastToX = op.scaleToX;
lastToY = op.scaleToY;
}

// If autoreversing, then the final scale values for the view will be
// whatever they are at the start. Else they are whatever the last "to" scale
// values are in the sequence.
if (autoreverse) {
return viewCurrentScale;
} else {
return Pair.create(Float.valueOf(lastToX), Float.valueOf(lastToY));
}
return (this.op.type == Operation.TYPE_ROTATE);
}

public float[] getScaleOperationParameters()
/**
* Checks all of the rotate operations in the sequence and sets the appropriate
* "from" value for them all (in case they aren't specified), then gives
* back the final value that will be in effect when the animation has completed.
* @param view
* @param autoreverse
* @return Final rotation value after the animation has finished.
*/
public float verifyRotationValues(TiUIView view, boolean autoreverse)
{
if (!isScaleOperation()) {
Log.w(TAG, "getScaleOperationParameters called though matrix is not for a scale operation.");
return new float[6];
ArrayList<Operation> rotationOps = new ArrayList<Operation>();

Ti2DMatrix check = this;
while (check != null) {

if (check.op != null && check.op.type == Operation.TYPE_ROTATE) {
rotationOps.add(0, check.op);
}
check = check.prev;
}

return new float[] {
this.op.scaleFromX,
this.op.scaleToX,
this.op.scaleFromY,
this.op.scaleToY,
this.op.anchorX,
this.op.anchorY
};
float viewCurrentRotation = (view == null ? 0f : view.getAnimatedRotationDegrees());

if (rotationOps.size() == 0) {
return viewCurrentRotation;
}

float lastRotation = viewCurrentRotation;

for (Operation op : rotationOps) {
if (!op.rotationFromValueSpecified) {
// The "from" value was not specified,
// so it should be whatever the last "to" value was.
op.rotateFrom = lastRotation;
}
lastRotation = op.rotateTo;
}

// If autoreversing, then the final rotation value for the view will be
// whatever it was at the start. Else it's whatever the last "to" rotation
// value is in the sequence.
if (autoreverse) {
return viewCurrentRotation;
} else {
return lastRotation;
}
}

public float[] getRotateOperationParameters()
{
if (!isRotateOperation()) {
Log.w(TAG, "getRotateOperationParameters called though matrix is not for a scale operation.");
if (this.op == null) {
return new float[4];
}

Expand Down