decomposeMatrix static method

void decomposeMatrix(
  1. List<double> transformMatrix,
  2. MatrixDecompositionContext ctx
)

Implementation

static void decomposeMatrix(
    List<double> transformMatrix, MatrixDecompositionContext ctx) {
  // output values
  final perspective = ctx.perspective;
  final quaternion = ctx.quaternion;
  final scale = ctx.scale;
  final skew = ctx.skew;
  final translation = ctx.translation;
  final rotationDegrees = ctx.rotationDegrees;

  // create normalized, 2d array matrix
  // and normalized 1d array perspectiveMatrix with redefined 4th column
  if (_isZero(transformMatrix[15])) {
    return;
  }
  var matrix = <List<double>>[
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
  ];
  var perspectiveMatrix = List<double>.filled(16, 0);
  for (var i = 0; i < 4; i++) {
    for (var j = 0; j < 4; j++) {
      var value = transformMatrix[(i * 4) + j] / transformMatrix[15];
      matrix[i][j] = value;
      perspectiveMatrix[(i * 4) + j] = j == 3 ? 0 : value;
    }
  }
  perspectiveMatrix[15] = 1;

  // test for singularity of upper 3x3 part of the perspective matrix
  if (_isZero(determinant(perspectiveMatrix))) {
    return;
  }

  // isolate perspective
  if (!_isZero(matrix[0][3]) ||
      !_isZero(matrix[1][3]) ||
      !_isZero(matrix[2][3])) {
    // rightHandSide is the right hand side of the equation.
    // rightHandSide is a vector, or point in 3d space relative to the origin.
    var rightHandSide = <double>[
      matrix[0][3],
      matrix[1][3],
      matrix[2][3],
      matrix[3][3]
    ];

    // Solve the equation by inverting perspectiveMatrix and multiplying
    // rightHandSide by the inverse.
    var inversePerspectiveMatrix = inverse(perspectiveMatrix);
    var transposedInversePerspectiveMatrix =
        transpose(inversePerspectiveMatrix);
    multiplyVectorByMatrix(
        rightHandSide, transposedInversePerspectiveMatrix, perspective);
  } else {
    // no perspective
    perspective[0] = perspective[1] = perspective[2] = 0;
    perspective[3] = 1;
  }

  // translation is simple
  for (var i = 0; i < 3; i++) {
    translation[i] = matrix[3][i];
  }

  // Now get scale and shear.
  // 'row' is a 3 element array of 3 component vectors
  var row = <List<double>>[
    [0, 0, 0],
    [0, 0, 0],
    [0, 0, 0]
  ];
  for (var i = 0; i < 3; i++) {
    row[i][0] = matrix[i][0];
    row[i][1] = matrix[i][1];
    row[i][2] = matrix[i][2];
  }

  // Compute X scale factor and normalize first row.
  scale[0] = v3Length(row[0]);
  row[0] = v3Normalize(row[0], scale[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]);

  // 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]);
  skew[0] /= scale[1];

  // Compute XZ and YZ shears, orthogonalize 3rd row
  skew[1] = v3Dot(row[0], row[2]);
  row[2] = v3Combine(row[2], row[0], 1.0, -skew[1]);
  skew[2] = v3Dot(row[1], row[2]);
  row[2] = v3Combine(row[2], row[1], 1.0, -skew[2]);

  // Next, get Z scale and normalize 3rd row.
  scale[2] = v3Length(row[2]);
  row[2] = v3Normalize(row[2], scale[2]);
  skew[1] /= scale[2];
  skew[2] /= scale[2];

  // At this point, the matrix (in rows) is orthonormal.
  // Check for a coordinate system flip.  If the determinant
  // is -1, then negate the matrix and the scaling factors.
  var pdum3 = v3Cross(row[1], row[2]);
  if (v3Dot(row[0], pdum3) < 0) {
    for (var i = 0; i < 3; i++) {
      scale[i] *= -1;
      row[i][0] *= -1;
      row[i][1] *= -1;
      row[i][2] *= -1;
    }
  }

  // Now, get the rotations out
  quaternion[0] = 0.5 * sqrt(max(1 + row[0][0] - row[1][1] - row[2][2], 0));
  quaternion[1] = 0.5 * sqrt(max(1 - row[0][0] + row[1][1] - row[2][2], 0));
  quaternion[2] = 0.5 * sqrt(max(1 - row[0][0] - row[1][1] + row[2][2], 0));
  quaternion[3] = 0.5 * sqrt(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] = 0;
    rotationDegrees[2] =
        roundTo3Places(atan2(row[0][1], row[0][0]) * 180 / pi);
  } else {
    quaternionToDegreesXYZ(quaternion, rotationDegrees);
  }
}