Affine Decomposition

All that's left to do is to implement the actual AffineDecompose function. This page is mostly just a code-dump of how to put everything together. Let's start with the structures and function signatures of what we have already written. The only decomposition method that isn't needed here is QRDecompositon, because it is only used as a helper function for SpectralDecompositon.

struct FactorTranslationResult {
    Matrix T; // Translation
    Matrix X; // Linear transformation

struct PolarDecompResult {
    Matrix Q; // Q is a rotation, or the negative of a rotation
    Matrix S; // Scale and skew matrix

struct FactorRotationResult {
    Matrix F; // Positive or negative identity (flip if negative)
    Matrix R; // Rotation matrix

struct SpectralDecompositionResult {
    Matrix U; // Each basis vector is an eigenvector
    Matrix K; // Contains eigenvalues on main diagonal
    Matrix Ut; // Transpose of U

struct SpectralAdjustmentResult {
    Matrix U; // Each basis vector is an eigenvector
    Matrix K; // Contains eigenvalues on main diagonal
    Matrix Ut; // Transpose of U

FactorTranslationResult FactorTranslation(Matrix M);
PolarDecompResult PolarDecomposition(Matrix X);
FactorRotationResult FactorRotation(Matrix Q);
SpectralDecompositionResult SpectralDecomposition(Matrix S);
SpectralAdjustmentResult SpectralDecompositonAdjustment(SpectralDecompositionResult input)

Having already written all of the above functions, implementing AffineDecomposition becomes a matter of calling the helper functions in the appropriate order, like so:

struct AffineDecompositionResult {
    Matrix T; // Holds translation of the matrix
    Matrix F; // Flip data (positive or negative identity)
    Matrix R; // Holds rotation of the matrix
    Matrix U; // Holds eigenvectors of the matrix
    Matrix K; // Holds eigenvalues (scale) of the matrix
    Matrix Ut;// Transpose of U

AffineDecompositionResult AffineDecomposition(Matrix M) {
    FactorTranslationResult factorTranslation = FactorTranslation(M);
    PolarDecompResult polarDecomposition = PolarDecomposition(factorTranslation.X);
    FactorRotationResult factorRotation = FactorRotation(polarDecomposition.Q);
    SpectralDecompositionResult spectralDecomp = SpectralDecomposition(factorRotation.R);
    SpectralAdjustmentResult spectralAdjustment = SpectralDecompositonAdjustment(spectralDecomp);

    AffineDecompositionResult result;
    result.T = factorTranslation.T;
    result.F = factorRotation.F;
    result.R = factorRotation.R;
    result.U = spectralAdjustment.U;
    result.K = spectralAdjustment.K;
    result.Ut = spectralAdjustment.Ut;

    return result;

We might want to re-format the output to match shoemake's reference code a bit more. For the most part this is just a matter of converting some matrices to quaternions, like so:

struct ShoemakeResult {
    Vector3 T; // Translation
    float F; // Sign of determinant
    Quaternion R; // Rotation (q in shoemake's code)
    Quaternion U; //Stretch matrix
    Vector3 K; // Scale info

ShoemakeResult ConvertResultToShoemakeFormat(AffineDecompositionResult affine) {
    ShoemakeResult result;

    result.T = Vector3(affine.T[12], affine.T[13], affine.T[14]);
    result.F = affine.F[0];
    result.R = ToQuaternion(affine.R);
    result.U = ToQuaternion(affine.U);
    result.K = Vector3(affine.K[0], affine.K[5], affine.K[10]);

    return result;