Accept transforms list instead of matrix for transform view parameter.

Summary:
In #7916 I moved transform matrix decomposition logic from JS to java. The next step is to accept list of transforms instead oftransform matrix as a transform ReactProp. This way there is no extra processing required on JS side for the transform param (at least for android now) and this on the other hand allow us to execute transform updates (through offloaded animation) solely on the UI thread.

After this change there is a whole bunch of stuff from `Libraries/Utilities/MatrixMath.js` that can be deleted (methods like: determinant, inverse, transpose). Although astreet mentioned under one of my previous commits that the code is still being referenced internally at fb, so I decided not to delete it here.

**Test plan (required)**
Run UIExplorer Transform example before and after - compare the results
Run android unit test: com.facebook.react.uimanager.MatrixMathHelperTest
Closes https://github.com/facebook/react-native/pull/8892

Differential Revision: D3676017

Pulled By: astreet

fbshipit-source-id: 5275e30805a85c12c89bea44e8b3a2b2ec7b33fa
This commit is contained in:
Krzysztof Magiera
2016-08-05 10:48:34 -07:00
committed by Facebook Github Bot
parent eba6c379c3
commit 68b9a36858
5 changed files with 234 additions and 25 deletions

View File

@@ -8,7 +8,6 @@ import android.view.View;
import android.view.ViewGroup;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.annotations.ReactProp;
/**
@@ -53,9 +52,9 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
@ReactProp(name = PROP_TRANSFORM)
public void setTransform(T view, ReadableArray matrix) {
if (matrix == null) {
resetTransformMatrix(view);
resetTransformProperty(view);
} else {
setTransformMatrix(view, matrix);
setTransformProperty(view, matrix);
}
}
@@ -154,10 +153,8 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
}
}
private static void setTransformMatrix(View view, ReadableArray matrix) {
for (int i = 0; i < 16; i++) {
sTransformDecompositionArray[i] = matrix.getDouble(i);
}
private static void setTransformProperty(View view, ReadableArray transforms) {
TransformHelper.processTransform(transforms, sTransformDecompositionArray);
MatrixMathHelper.decomposeMatrix(sTransformDecompositionArray, sMatrixDecompositionContext);
view.setTranslationX(
PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[0]));
@@ -170,7 +167,7 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
view.setScaleY((float) sMatrixDecompositionContext.scale[1]);
}
private static void resetTransformMatrix(View view) {
private static void resetTransformProperty(View view) {
view.setTranslationX(PixelUtil.toPixelFromDIP(0));
view.setTranslationY(PixelUtil.toPixelFromDIP(0));
view.setRotation(0);

View File

@@ -3,8 +3,8 @@ package com.facebook.react.uimanager;
import com.facebook.infer.annotation.Assertions;
/**
* Provides helper methods from decomposing transform matrix into list of translate, scale and
* rotate commands.
* Provides helper methods for converting transform operations into a matrix and then into a list
* of translate, scale and rotate commands.
*/
public class MatrixMathHelper {
@@ -26,6 +26,37 @@ public class MatrixMathHelper {
return Math.abs(d) < EPSILON;
}
public static void multiplyInto(double[] out, double[] a, double[] b) {
double a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
double b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
}
/**
* @param transformMatrix 16-element array of numbers representing 4x4 transform matrix
*/
@@ -350,4 +381,79 @@ public class MatrixMathHelper {
public static double roundTo3Places(double n) {
return Math.round(n * 1000d) * 0.001;
}
public static double[] createIdentityMatrix() {
double[] res = new double[16];
resetIdentityMatrix(res);
return res;
}
public static double degreesToRadians(double degrees) {
return degrees * Math.PI / 180;
}
public static void resetIdentityMatrix(double[] matrix) {
matrix[1] = matrix[2] = matrix[3] = matrix[4] = matrix[6] = matrix[7] = matrix[8] = matrix[9] =
matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0;
matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1;
}
public static void applyPerspective(double[] m, double perspective) {
m[11] = -1 / perspective;
}
public static void applyScaleX(double[] m, double factor) {
m[0] = factor;
}
public static void applyScaleY(double[] m, double factor) {
m[5] = factor;
}
public static void applyScaleZ(double[] m, double factor) {
m[10] = factor;
}
public static void applyTranslate2D(double[] m, double x, double y) {
m[12] = x;
m[13] = y;
}
public static void applyTranslate3D(double[] m, double x, double y, double z) {
m[12] = x;
m[13] = y;
m[14] = z;
}
public static void applySkewX(double[] m, double radians) {
m[4] = Math.sin(radians);
m[5] = Math.cos(radians);
}
public static void applySkewY(double[] m, double radians) {
m[0] = Math.cos(radians);
m[1] = Math.sin(radians);
}
public static void applyRotateX(double[] m, double radians) {
m[5] = Math.cos(radians);
m[6] = Math.sin(radians);
m[9] = -Math.sin(radians);
m[10] = Math.cos(radians);
}
public static void applyRotateY(double[] m, double radians) {
m[0] = Math.cos(radians);
m[2] = -Math.sin(radians);
m[8] = Math.sin(radians);
m[10] = Math.cos(radians);
}
// http://www.w3.org/TR/css3-transforms/#recomposing-to-a-2d-matrix
public static void applyRotateZ(double[] m, double radians) {
m[0] = Math.cos(radians);
m[1] = Math.sin(radians);
m[4] = -Math.sin(radians);
m[5] = Math.cos(radians);
}
}

View File

@@ -0,0 +1,99 @@
package com.facebook.react.uimanager;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
/**
* Class providing helper methods for converting transformation list (as accepted by 'transform'
* view property) into a transformation matrix.
*/
public class TransformHelper {
private static ThreadLocal<double[]> sHelperMatrix = new ThreadLocal<double[]>() {
@Override
protected double[] initialValue() {
return new double[16];
}
};
private static double convertToRadians(ReadableMap transformMap, String key) {
double value;
boolean inRadians = false;
if (transformMap.getType(key) == ReadableType.String) {
String stringValue = transformMap.getString(key);
if (stringValue.endsWith("rad")) {
inRadians = true;
stringValue = stringValue.substring(0, stringValue.length() - 3);
} else if (stringValue.endsWith("deg")) {
stringValue = stringValue.substring(0, stringValue.length() - 3);
}
value = Float.parseFloat(stringValue);
} else {
value = transformMap.getDouble(key);
}
return inRadians ? value : MatrixMathHelper.degreesToRadians(value);
}
public static void processTransform(ReadableArray transforms, double[] result) {
double[] helperMatrix = sHelperMatrix.get();
MatrixMathHelper.resetIdentityMatrix(result);
for (int transformIdx = 0, size = transforms.size(); transformIdx < size; transformIdx++) {
ReadableMap transform = transforms.getMap(transformIdx);
String transformType = transform.keySetIterator().nextKey();
MatrixMathHelper.resetIdentityMatrix(helperMatrix);
if ("matrix".equals(transformType)) {
ReadableArray matrix = transform.getArray(transformType);
for (int i = 0; i < 16; i++) {
helperMatrix[i] = matrix.getDouble(i);
}
} else if ("perspective".equals(transformType)) {
MatrixMathHelper.applyPerspective(helperMatrix, transform.getDouble(transformType));
} else if ("rotateX".equals(transformType)) {
MatrixMathHelper.applyRotateX(
helperMatrix,
convertToRadians(transform, transformType));
} else if ("rotateY".equals(transformType)) {
MatrixMathHelper.applyRotateY(
helperMatrix,
convertToRadians(transform, transformType));
} else if ("rotate".equals(transformType) || "rotateZ".equals(transformType)) {
MatrixMathHelper.applyRotateZ(
helperMatrix,
convertToRadians(transform, transformType));
} else if ("scale".equals(transformType)) {
MatrixMathHelper.applyScaleZ(helperMatrix, transform.getDouble(transformType));
} else if ("scaleX".equals(transformType)) {
MatrixMathHelper.applyScaleX(helperMatrix, transform.getDouble(transformType));
} else if ("scaleY".equals(transformType)) {
MatrixMathHelper.applyScaleY(helperMatrix, transform.getDouble(transformType));
} else if ("translate".equals(transformType)) {
ReadableArray value = transform.getArray(transformType);
double x = value.getDouble(0);
double y = value.getDouble(1);
double z = value.size() > 2 ? value.getDouble(2) : 0d;
MatrixMathHelper.applyTranslate3D(helperMatrix, x, y, z);
} else if ("translateX".equals(transformType)) {
MatrixMathHelper.applyTranslate2D(helperMatrix, transform.getDouble(transformType), 0d);
} else if ("translateY".equals(transformType)) {
MatrixMathHelper.applyTranslate2D(helperMatrix, 0d, transform.getDouble(transformType));
} else if ("skewX".equals(transformType)) {
MatrixMathHelper.applySkewX(
helperMatrix,
convertToRadians(transform, transformType));
} else if ("skewY".equals(transformType)) {
MatrixMathHelper.applySkewY(
helperMatrix,
convertToRadians(transform, transformType));
} else {
throw new JSApplicationIllegalArgumentException("Unsupported transform type: "
+ transformType);
}
MatrixMathHelper.multiplyInto(result, result, helperMatrix);
}
}
}