Skip to content

Commit

Permalink
fix(android): fix transform order and convert perspective same as iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
iPel committed Nov 24, 2023
1 parent bb9b7db commit 403221c
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public abstract class HippyViewController<T extends View & HippyViewBase> implem
private static final String GET_BOUNDING_CLIENT_RECT = "getBoundingClientRect";
public static final String KEY_REL_TO_CONTAINER = "relToContainer";
public static final String KEY_ERR_MSG = "errMsg";
private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = (float) Math.sqrt(5);
private static final MatrixUtil.MatrixDecompositionContext sMatrixDecompositionContext = new MatrixUtil.MatrixDecompositionContext();
private static final double[] sTransformDecompositionArray = new double[16];
private boolean bUserChangeFocus = false;
Expand Down Expand Up @@ -412,6 +414,27 @@ private void applyTransform(T view, ArrayList<Object> transformArray) {
view.setRotationY((float) sMatrixDecompositionContext.rotationDegrees[1]);
view.setScaleX((float) sMatrixDecompositionContext.scale[0]);
view.setScaleY((float) sMatrixDecompositionContext.scale[1]);

double[] perspectiveArray = sMatrixDecompositionContext.perspective;

if (perspectiveArray.length > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
float invertedCameraDistance = (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX];
if (invertedCameraDistance == 0) {
// Default camera distance, before scale multiplier (1280)
invertedCameraDistance = 0.00078125f;
}
float cameraDistance = -1 / invertedCameraDistance;
float scale = PixelUtil.getDensity();

// The following converts the matrix's perspective to a camera distance
// such that the camera perspective looks the same on Android and iOS.
// The native Android implementation removed the screen density from the
// calculation, so squaring and a normalization value of
// sqrt(5) produces an exact replica with iOS.
// For more information, see https://github.com/facebook/react-native/pull/18302
float normalizedCameraDistance = scale * scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER;
view.setCameraDistance(normalizedCameraDistance);
}
}

public static void resetTransform(View view) {
Expand All @@ -422,6 +445,7 @@ public static void resetTransform(View view) {
view.setRotationY(0);
view.setScaleX(1);
view.setScaleY(1);
view.setCameraDistance(0);
}

@SuppressWarnings("deprecation")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public class MatrixUtil {
public static class MatrixDecompositionContext {

final double[] perspective = new double[4];
final double[] quaternion = new double[4];
final double[] scale = new double[3];
final double[] skew = new double[3];
final double[] translation = new double[3];
Expand All @@ -34,11 +33,6 @@ public void reset() {
perspective[2] = 0;
perspective[3] = 0;

quaternion[0] = 0;
quaternion[1] = 0;
quaternion[2] = 0;
quaternion[3] = 0;

scale[0] = 0;
scale[1] = 0;
scale[2] = 0;
Expand Down Expand Up @@ -68,43 +62,55 @@ private static boolean isZero(double d) {
// 输入:matrixA,长度16的一维数组,代表四维矩阵
// 输入:matrixB,长度16的一维数组,代表四维矩阵
public static void multiplyInto(double[] out, double[] matrixA, double[] matrixB) {
double b00 = matrixB[0], b01 = matrixB[1], b02 = matrixB[2], b03 = matrixB[3],
b10 = matrixB[4], b11 = matrixB[5], b12 = matrixB[6], b13 = matrixB[7],
b20 = matrixB[8], b21 = matrixB[9], b22 = matrixB[10], b23 = matrixB[11],
b30 = matrixB[12], b31 = matrixB[13], b32 = matrixB[14], b33 = matrixB[15];

double a0 = matrixA[0], a1 = matrixA[1], a2 = matrixA[2], a3 = matrixA[3];
out[0] = a0 * b00 + a1 * b10 + a2 * b20 + a3 * b30;
out[1] = a0 * b01 + a1 * b11 + a2 * b21 + a3 * b31;
out[2] = a0 * b02 + a1 * b12 + a2 * b22 + a3 * b32;
out[3] = a0 * b03 + a1 * b13 + a2 * b23 + a3 * b33;

a0 = matrixA[4];
a1 = matrixA[5];
a2 = matrixA[6];
a3 = matrixA[7];
out[4] = a0 * b00 + a1 * b10 + a2 * b20 + a3 * b30;
out[5] = a0 * b01 + a1 * b11 + a2 * b21 + a3 * b31;
out[6] = a0 * b02 + a1 * b12 + a2 * b22 + a3 * b32;
out[7] = a0 * b03 + a1 * b13 + a2 * b23 + a3 * b33;

a0 = matrixA[8];
a1 = matrixA[9];
a2 = matrixA[10];
a3 = matrixA[11];
out[8] = a0 * b00 + a1 * b10 + a2 * b20 + a3 * b30;
out[9] = a0 * b01 + a1 * b11 + a2 * b21 + a3 * b31;
out[10] = a0 * b02 + a1 * b12 + a2 * b22 + a3 * b32;
out[11] = a0 * b03 + a1 * b13 + a2 * b23 + a3 * b33;

a0 = matrixA[12];
a1 = matrixA[13];
a2 = matrixA[14];
a3 = matrixA[15];
out[12] = a0 * b00 + a1 * b10 + a2 * b20 + a3 * b30;
out[13] = a0 * b01 + a1 * b11 + a2 * b21 + a3 * b31;
out[14] = a0 * b02 + a1 * b12 + a2 * b22 + a3 * b32;
out[15] = a0 * b03 + a1 * b13 + a2 * b23 + a3 * b33;
double a00 = matrixA[0],
a01 = matrixA[1],
a02 = matrixA[2],
a03 = matrixA[3],
a10 = matrixA[4],
a11 = matrixA[5],
a12 = matrixA[6],
a13 = matrixA[7],
a20 = matrixA[8],
a21 = matrixA[9],
a22 = matrixA[10],
a23 = matrixA[11],
a30 = matrixA[12],
a31 = matrixA[13],
a32 = matrixA[14],
a33 = matrixA[15];

double b0 = matrixB[0], b1 = matrixB[1], b2 = matrixB[2], b3 = matrixB[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 = matrixB[4];
b1 = matrixB[5];
b2 = matrixB[6];
b3 = matrixB[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 = matrixB[8];
b1 = matrixB[9];
b2 = matrixB[10];
b3 = matrixB[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 = matrixB[12];
b1 = matrixB[13];
b2 = matrixB[14];
b3 = matrixB[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;
}

/**
Expand All @@ -113,7 +119,6 @@ public static void multiplyInto(double[] out, double[] matrixA, double[] matrixB
public static void decomposeMatrix(double[] transformMatrix, MatrixDecompositionContext ctx) {
// output values
final double[] perspective = ctx.perspective;
final double[] quaternion = ctx.quaternion;
final double[] scale = ctx.scale;
final double[] skew = ctx.skew;
final double[] translation = ctx.translation;
Expand Down Expand Up @@ -177,10 +182,6 @@ public static void decomposeMatrix(double[] transformMatrix, MatrixDecomposition
skew[0] = v3Dot(row[0], row[1]);
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);

// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = v3Dot(row[0], row[1]);
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);

// Now, compute Y scale and normalize 2nd row.
scale[1] = v3Length(row[1]);
row[1] = v3Normalize(row[1], scale[1]);
Expand Down Expand Up @@ -212,31 +213,12 @@ public static void decomposeMatrix(double[] transformMatrix, MatrixDecomposition
}

// Now, get the rotations out
quaternion[0] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] - row[1][1] - row[2][2], 0));
quaternion[1] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] + row[1][1] - row[2][2], 0));
quaternion[2] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] - row[1][1] + row[2][2], 0));
quaternion[3] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] + row[1][1] + row[2][2], 0));

if (row[2][1] > row[1][2]) {
quaternion[0] = -quaternion[0];
}
if (row[0][2] > row[2][0]) {
quaternion[1] = -quaternion[1];
}
if (row[1][0] > row[0][1]) {
quaternion[2] = -quaternion[2];
}

// correct for occasional, weird Euler synonyms for 2d rotation

if (quaternion[0] < 0.001 && quaternion[0] >= 0 && quaternion[1] < 0.001
&& quaternion[1] >= 0) {
// this is a 2d rotation on the z-axis
rotationDegrees[0] = rotationDegrees[1] = 0d;
rotationDegrees[2] = roundTo3Places(Math.atan2(row[0][1], row[0][0]) * 180 / Math.PI);
} else {
quaternionToDegreesXYZ(quaternion, rotationDegrees);
}
// Based on: http://nghiaho.com/?page_id=846
double conv = 180 / Math.PI;
rotationDegrees[0] = roundTo3Places(-Math.atan2(row[2][1], row[2][2]) * conv);
rotationDegrees[1] =
roundTo3Places(-Math.atan2(-row[2][0], Math.sqrt(row[2][1] * row[2][1] + row[2][2] * row[2][2])) * conv);
rotationDegrees[2] = roundTo3Places(-Math.atan2(row[1][0], row[0][0]) * conv);
}

public static double determinant(double[] matrix) {
Expand Down Expand Up @@ -360,46 +342,6 @@ public static double[] v3Cross(double[] a, double[] b) {
a[0] * b[1] - a[1] * b[0]};
}

/**
* Based on: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
* and: http://quat.zachbennett.com/
* <p>
* Note that this rounds degrees to the thousandth of a degree, due to floating point errors in
* the creation of the quaternion.
* <p>
* Also note that this expects the qw value to be last, not first.
* <p>
* Also, when researching this, remember that: yaw === heading === z-axis pitch ===
* elevation/attitude === y-axis roll === bank === x-axis
*/
public static void quaternionToDegreesXYZ(double[] q, double[] result) {
double qx = q[0], qy = q[1], qz = q[2], qw = q[3];
double qw2 = qw * qw;
double qx2 = qx * qx;
double qy2 = qy * qy;
double qz2 = qz * qz;
double test = qx * qy + qz * qw;
double unit = qw2 + qx2 + qy2 + qz2;
double conv = 180 / Math.PI;

if (test > 0.49999 * unit) {
result[0] = 0;
result[1] = 2 * Math.atan2(qx, qw) * conv;
result[2] = 90;
return;
}
if (test < -0.49999 * unit) {
result[0] = 0;
result[1] = -2 * Math.atan2(qx, qw) * conv;
result[2] = -90;
return;
}

result[0] = roundTo3Places(Math.atan2(2 * qx * qw - 2 * qy * qz, 1 - 2 * qx2 - 2 * qz2) * conv);
result[1] = roundTo3Places(Math.atan2(2 * qy * qw - 2 * qx * qz, 1 - 2 * qy2 - 2 * qz2) * conv);
result[2] = roundTo3Places(Math.asin(2 * qx * qy + 2 * qz * qw) * conv);
}

public static double roundTo3Places(double n) {
return Math.round(n * 1000d) * 0.001;
}
Expand All @@ -425,6 +367,10 @@ 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;
Expand All @@ -437,13 +383,11 @@ public static void applyTranslate3D(double[] m, double x, double y, double z) {
}

public static void applySkewX(double[] m, double radians) {
m[4] = Math.sin(radians);
m[5] = Math.cos(radians);
m[4] = Math.tan(radians);
}

public static void applySkewY(double[] m, double radians) {
m[0] = Math.cos(radians);
m[1] = Math.sin(radians);
m[1] = Math.tan(radians);
}

public static void applyRotateX(double[] m, double radians) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static void processTransform(ArrayList<Object> transforms, double[] resul
MatrixUtil.applyScaleY(helperMatrix, scale);
} else if ("scaleX".equals(transformType) && value instanceof Number) {
MatrixUtil.applyScaleX(helperMatrix, ((Number) value).doubleValue());
} else if ("scaleY".equals(transformType)) {
} else if ("scaleY".equals(transformType) && value instanceof Number) {
MatrixUtil.applyScaleY(helperMatrix, ((Number) value).doubleValue());
} else if ("translate".equals(transformType) && value instanceof ArrayList) {
double x = 0d, y = 0d, z = 0d;
Expand Down

0 comments on commit 403221c

Please sign in to comment.