How to properly translate a point by a matrix?

I wrote a simple Matrix class and it has methods like rotate, translate, etc. They all seem to be working, but whenever I try to translate a rectangle using the matrix, the translation axis seem to be flipped. It doesn't appear to be the fault of the matrix, however, because if I do matrix.translate(50, 0), It gives me a matrix that looks like this:

$$\begin{bmatrix} 1 & 0 & 50 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}$$ As expected. It appears that I am applying the matrix incorrectly instead. I am using this code to get the resulting X/Y axis for each point in the rectangle:

pointX = x * mat.row1.x + y * mat.row1.y + mat.translateX
pointY = x * mat.row2.x + y * mat.row2.y + mat.translateY


But when I call translate(50, 0), I get this:

As you would expect. What am I doing wrong? Am I incorrectly calculating the points?

• have you checked if the output coordinates are correct or might the issue be in your render code? – Luis W Aug 15 '13 at 21:29

I would expect your matrix multiplication code to look like

$$\begin{bmatrix} x & y & 1 \\ \end{bmatrix} * \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 50 & 0 & 1 \end{bmatrix} = \begin{bmatrix} x+50 & y & 1 \\ \end{bmatrix}$$ This is because matrix multiplication is defined so that you go by row in the first operand and by column in the second operand.

Here's the relevant formula from wikipedia, although you should consider reviewing the whole page: $$(AB)_{ij}=\sum_{k=1}^m i^2 =A_{ik}B_{kj}$$ As LuisW points out in the comments, this is merely a row- vs column- major issue. As an example, your matrix would be just fine this way:

$$\begin{bmatrix} 1 & 0 & 50 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix} x+50 \\ y \\ 1 \end{bmatrix}$$

But that is apparently not how you are multiplying it.

• this will result in [x, y], because you set the z-component of the vector to 0, that way the translation part will have no effect. Also this notation and the notation OP used are equivalent, it´s just a matter of column- and rowmajor matrices. – Luis W Aug 15 '13 at 21:26
• @LuisW, I just added a bit more. The reason I wanted to use a standard orientation is because his "multiplication" example is going a different direction. i.e. his multiplication formula is column-major" while his "translate" is returning row-major. – Jimmy Aug 15 '13 at 21:28
• also, good call about the z :( fixed. – Jimmy Aug 15 '13 at 21:30
• I know I am going about the translation wrong because my rotation works. But as soon as I add the translation, it doesn't give me the expected result. I printed the matrix to the console so I know the matrix is not flipped. What I'm trying to figure out, is what my code should look like. – Lysol Aug 15 '13 at 21:48
• Well, try changing matrix.translate to match the top example, if you are confident that the multiplication is what you want. My point is that either your matrix or your multiplication function has to change. – Jimmy Aug 15 '13 at 21:51

Here is an excerpt for the integer Matrix struct I use, for comparison:

/// <summary>Row-major order representation of an immutable integer matrix.</summary>
/// <remarks> Represents Points as row vectors and planes as column vectors.
/// This representation is standard for computer graphics, though opposite
/// to standard mathematical (and physics) representation, and treats
/// row vectors as contravariant and column vectors as covariant.</remarks>
public struct IntMatrix2D : IEquatable<IntMatrix2D> {

static IntMatrix2D TransposeMatrix = new IntMatrix2D(0,1, 1,0);
public static IntMatrix2D Transpose(IntMatrix2D matrix) {
return matrix * TransposeMatrix;
}

/// <summary>Get the x-scale component.</summary>
public int M11 { get; private set; }
/// <summary>Get the X-shear component</summary>
public int M12 { get; private set; }
/// <summary>Get the y-shear component</summary>
public int M21 { get; private set; }
/// <summary>Get the Y-scale component</summary>
public int M22 { get; private set; }
/// <summary>Get the x-translation component</summary>
public int M31 { get; private set; }
/// <summary>Get the y-translationcomponent</summary>
public int M32 { get; private set; }
/// <summary>Ge the normalization component</summary>
public int M33 { get; private set; }
/// <summary>Get the identity matrix.</summary>
public static readonly IntMatrix2D Identity = new IntMatrix2D(1,0,0,1,0,0);

#region Constructors
/// <summary> Initializes a new <code>IntMatrix2D</code> as the translation defined by the vector v.</summary>
/// <param name="vector">the translation vector</param>
public IntMatrix2D(IntVector2D vector)  : this(1,0, 0,1, vector.X,vector.Y, 1) {}
/// <summary> Initializes a new <code>IntMatrix2D</code> as the translation (dx,dy).</summary>
/// <param name="dx">X-translate component</param>
/// <param name="dy">Y-translate component</param>
public IntMatrix2D(int dx, int dy)  : this(1,0, 0,1, dx,dy,1) {}
/// <summary> Initialies a new <code>IntMatrix2D</code> with a rotation.</summary>
/// <param name="m11">X-scale component.</param>
/// <param name="m12">Y-shear component</param>
/// <param name="m21">X-shear component</param>
/// <param name="m22">Y-scale component</param>
public IntMatrix2D(int m11, int m12, int m21, int m22) : this(m11,m12, m21,m22, 0,0, 1) {}
/// <summary>Copy Constructor for a new <code>IntMatrix2D</code>.</summary>
/// <param name="m">Source IntegerMatrix</param>
public IntMatrix2D(IntMatrix2D m) : this(m.M11,m.M21, m.M12,m.M22, m.M31,m.M32, m.M33) { }
/// <summary>Initializes a new fully-specificed <code>IntMatrix2D</code> .</summary>
/// <param name="m11">X-scale component.</param>
/// <param name="m12">Y-shear component</param>
/// <param name="m21">X-shear component</param>
/// <param name="m22">Y-scale component</param>
/// <param name="dx">X-translate component</param>
/// <param name="dy">Y-translate component</param>
public IntMatrix2D(int m11, int m12, int m21, int m22, int dx, int dy)
: this(m11,m12,m21,m22,dx,dy,1) {}
/// <summary>Initializes a new fully-specificed non-normed <code>IntMatrix2D</code>.</summary>
/// <param name="m11">X-scale component.</param>
/// <param name="m12">Y-shear component</param>
/// <param name="m21">X-shear component</param>
/// <param name="m22">Y-scale component</param>
/// <param name="dx">X-translate component</param>
/// <param name="dy">Y-translate component</param>
/// <param name="norm">Normalization component</param>
public IntMatrix2D(int m11, int m12, int m21, int m22, int dx, int dy, int norm) : this() {
M11 = m11;  M12 = m12;
M21 = m21;  M22 = m22;
M31 = dx;   M32 = dy;   M33 = norm;
}
#endregion

#region operators
/// <summary> Vector application.</summary>
/// <param name="v">IntVector2D to be transformed.</param>
/// <param name="m">IntMatrix2D to be applied.</param>
/// <returns>New IntVector2D resulting from application of this matrix to vector v.</returns>
public static IntVector2D operator * (IntVector2D v, IntMatrix2D m) {
return new IntVector2D (
v.X * m.M11 + v.Y * m.M21 + m.M31,   v.X * m.M12 + v.Y * m.M22 + m.M32,  v.W * m.M33
).Normalize();
}
/// <summary>Matrix multiplication.</summary>
/// <param name="m1">Prepended transformation.</param>
/// <param name="m2">Appended transformation.</param>
/// <returns></returns>
public static IntMatrix2D operator * (IntMatrix2D m1, IntMatrix2D m2) {
return new IntMatrix2D (
m1.M11*m2.M11 + m1.M12*m2.M21,           m1.M11*m2.M12 + m1.M12*m2.M22,
m1.M21*m2.M11 + m1.M22*m2.M21,           m1.M21*m2.M12 + m1.M22*m2.M22,
m1.M31*m2.M11 + m1.M32*m2.M21 + m2.M31,  m1.M31*m2.M12 + m1.M32*m2.M22 + m2.M32,  m1.M33 * m2.M33
);
}
}


where the IntVector2D struct is similar.

Complete code project available at HexgridUtilities.codplex.com