Affine transformations to Object

Hello.
I am trying to make an application to calculate the 2D affine transformation of an object, but not only the standard transformations (translation, rotation, scaling, etc), I want the user to be able to input any number in 9 text fields (a 3x3 matrix) and for the object to transform based on those numbers. The object is square shaped.
This is the formula I wil be using:

Where the matrix with the "a"s is my affine transformation matrix (where the user will be inputting) and the x and y have to be calculated for each of the four points that limit my square.
Now, my question is, to what values should I assign the position, rotation, and scaling properties of my Unity object, considering that those four points could be anywhere on my plane?
I am aware that this is a mathematical question more than a Game Development question; however I am using Unity for my application would like to know if someone can help me with this.
Thanks!

Okay so here is a Matrix3x3 class, you could quite easily add a lot more function for other things, but I think what I have done will suffice as a good start, so the main class:

``````using UnityEngine;
using System.Collections;

public class Matrix3x3{
public static Matrix3x3 identity = new Matrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);
public static Matrix3x3 zero = new Matrix3x3(0, 0, 0, 0, 0, 0, 0, 0, 0);

public float[,] matrix;

public Matrix3x3(){
matrix = new float[3, 3];
matrix[0, 0] = 0;
matrix[1, 0] = 0;
matrix[2, 0] = 0;
matrix[0, 1] = 0;
matrix[1, 1] = 0;
matrix[2, 1] = 0;
matrix[0, 2] = 0;
matrix[1, 2] = 0;
matrix[2, 2] = 0;
}
public Matrix3x3(float a11, float a12, float a13, float a21, float a22, float a23){
matrix = new float[3, 3];
matrix[0, 0] = a11;
matrix[1, 0] = a12;
matrix[2, 0] = a13;
matrix[0, 1] = a21;
matrix[1, 1] = a22;
matrix[2, 1] = a23;
matrix[0, 2] = 0;
matrix[1, 2] = 0;
matrix[2, 2] = 1;
}
public Matrix3x3(Vector3 a1, Vector3 a2){
matrix = new float[3, 3];
matrix[0, 0] = a1.x;
matrix[1, 0] = a1.y;
matrix[2, 0] = a1.z;
matrix[0, 1] = a2.x;
matrix[1, 1] = a2.y;
matrix[2, 1] = a2.z;
matrix[0, 2] = 0;
matrix[1, 2] = 0;
matrix[2, 2] = 1;
}
public Matrix3x3(float a11, float a12, float a13, float a21, float a22, float a23, float a31, float a32, float a33){
matrix = new float[3, 3];
matrix[0, 0] = a11;
matrix[1, 0] = a12;
matrix[2, 0] = a13;
matrix[0, 1] = a21;
matrix[1, 1] = a22;
matrix[2, 1] = a23;
matrix[0, 2] = a31;
matrix[1, 2] = a32;
matrix[2, 2] = a33;
}

//MATRIX MULTIPLICATION
public static Matrix3x3 operator *(Matrix3x3 m1, Matrix3x3 m2){
float a11 =
m1.matrix[0, 0] * m2.matrix[0, 0] +
m1.matrix[1, 0] * m2.matrix[0, 1] +
m1.matrix[2, 0] * m2.matrix[0, 2];
float a12 =
m1.matrix[0, 0] * m2.matrix[1, 0] +
m1.matrix[1, 0] * m2.matrix[1, 1] +
m1.matrix[2, 0] * m2.matrix[1, 2];
float a13 =
m1.matrix[0, 0] * m2.matrix[2, 0] +
m1.matrix[1, 0] * m2.matrix[2, 1] +
m1.matrix[2, 0] * m2.matrix[2, 2];
float a21 =
m1.matrix[0, 1] * m2.matrix[0, 0] +
m1.matrix[1, 1] * m2.matrix[0, 1] +
m1.matrix[2, 1] * m2.matrix[0, 2];
float a22 =
m1.matrix[0, 1] * m2.matrix[1, 0] +
m1.matrix[1, 1] * m2.matrix[1, 1] +
m1.matrix[2, 1] * m2.matrix[1, 2];
float a23 =
m1.matrix[0, 1] * m2.matrix[2, 0] +
m1.matrix[1, 1] * m2.matrix[2, 1] +
m1.matrix[2, 1] * m2.matrix[2, 2];
float a31 =
m1.matrix[0, 2] * m2.matrix[0, 0] +
m1.matrix[1, 2] * m2.matrix[0, 1] +
m1.matrix[2, 2] * m2.matrix[0, 2];
float a32 =
m1.matrix[0, 2] * m2.matrix[1, 0] +
m1.matrix[1, 2] * m2.matrix[1, 1] +
m1.matrix[2, 2] * m2.matrix[1, 2];
float a33 =
m1.matrix[0, 2] * m2.matrix[2, 0] +
m1.matrix[1, 2] * m2.matrix[2, 1] +
m1.matrix[2, 2] * m2.matrix[2, 2];
return new Matrix3x3(a11, a12, a13, a21, a22, a23, a31, a32, a33);
}

//FLOAT MULTIPLICATION
public static Matrix3x3 operator *(Matrix3x3 m, float f){
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
m.matrix[i, j] *= f;
}
}
return m;
}
public static Matrix3x3 operator *(float f, Matrix3x3 m){
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
m.matrix[i, j] *= f;
}
}
return m;
}

//ToSTRING OVERRIDE
public override string ToString(){
string str = "[";
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
str += matrix[j, i].ToString();
str += i*j == 4 ? "]" : ", ";
}
}
return str;
}
}

public static class ExtensionMethods{
public static Vector3 MultiplyPoint(this Matrix3x3 m, Vector3 point){
Vector3 newPoint;
newPoint.x = m.matrix[0, 0]*point.x + m.matrix[1, 0]*point.y + m.matrix[2, 0];
newPoint.y = m.matrix[0, 1]*point.x + m.matrix[1, 1]*point.y + m.matrix[2, 1];
newPoint.z = point.z;
return newPoint;
}

public static Vector3 MultiplyVector(this Matrix3x3 m, Vector3 vec){
Vector3 newVec;
newVec.x = m.matrix[0, 0]*vec.x + m.matrix[1, 0]*vec.y;
newVec.y = m.matrix[0, 1]*vec.x + m.matrix[1, 1]*vec.y;
newVec.z = vec.z;
return newVec;
}

public static Matrix3x3 Transpose(this Matrix3x3 m){
Matrix3x3 newM = new Matrix3x3();
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
newM.matrix[i, j] = m.matrix[j, i];
}
}
return newM;
}

public static Matrix3x3 Inverse(this Matrix3x3 m){
Matrix3x3 newM;
float a11 = m.matrix[0, 0];
float a12 = m.matrix[1, 0];
float a13 = m.matrix[2, 0];
float a21 = m.matrix[0, 1];
float a22 = m.matrix[1, 1];
float a23 = m.matrix[2, 1];
float a31 = m.matrix[0, 2];
float a32 = m.matrix[1, 2];
float a33 = m.matrix[2, 2];

float m11 = (a22*a33)-(a23*a32);
float m12 = (a21*a33)-(a23*a31);
float m13 = (a21*a32)-(a22*a31);
float m21 = (a12*a33)-(a13*a32);
float m22 = (a11*a33)-(a13*a31);
float m23 = (a11*a32)-(a12*a31);
float m31 = (a12*a23)-(a13*a22);
float m32 = (a11*a23)-(a13*a21);
float m33 = (a11*a22)-(a12*a21);

newM = new Matrix3x3(m11, -m12, m13, -m21, m22, -m23, m31, -m32, m33);
newM = newM.Transpose();
float detM = (m*newM).matrix[0, 0];
if(detM == 0){
Debug.Log("determinant is 0, no inverse could be found (original matrix has been returned)");
return m;
}else{
detM = 1f/detM;
newM = detM*newM;
return newM;
}
}
}
``````

So far I think I have covered creating a 3x3 matrix, invereses, identity, zero matrix, transposing a matrix, multiplying a point and multiplying a vector, as well as some quality of life stuff like operator overloads for easily multiplying matricies by matricies or floats. To get an idea of how to use, check out the following:

``````Matrix3x3 m;
Matrix3x3 iM;
Matrix3x3 tM;
Matrix3x3 newM;

public Vector3 point;
public Vector3 vector;

void Start(){
m = new Matrix3x3(1, 2, 3, 1, 1, 1, 3, 7, 9);
iM = m.Inverse();
tM = m.Transpose();

Debug.Log("matrix: " + m);
Debug.Log("inverse of matrix: " + iM);
Debug.Log("transpose of matrix: " + tM);
Debug.Log("inverse times matrix: " + m*iM);
Debug.Log("identity matrix: " + Matrix3x3.identity);
Debug.Log("zero matrix: " + Matrix3x3.zero);

m = new Matrix3x3(1, 0, 3, 0, 1, 12);
point = new Vector3(1, 2, 3);
Debug.Log("matrix: " + m);
Debug.Log("point: " + point);
point = m.MultiplyPoint(point);
Debug.Log("transformed point: " + point);

m = new Matrix3x3(new Vector3(2, 3, 4), new Vector3(12, 2, 1));
vector = new Vector3(2, 1, 3);
Debug.Log("matrix: " + m);
Debug.Log("vector: " + vector);
vector = m.MultiplyVector(vector);
Debug.Log("transformed vector: " + vector);

m = new Matrix3x3(1, 2, 3, 1, 1, 1, 3, 7, 9);
Debug.Log("matrix: " + m);
m *= 3;
Debug.Log(" 3 x matrix: " + m);
}
``````

I hope that helps,

Scribe

After much investigation and mathematical calculations I came upon the answer. I share it in case it is useful for someone else.
I excecuted 3 methods after assigning each of the four points’ coordineates using the aforementioned formula:

``````void calculateRotation()
{
transform.rotation = (new Quaternion (0, 0, Mathf.Atan ((point1.y * point1Previous.x - point1.x * point1Previous.y) / (point1.x * point1Previous.x + point1.y * point1Previous.y)),0));
}

void  calculateScale()
{
transform.localScale = (new Vector3 (Vector3.Distance (point1, point2) / distScale, Vector3.Distance (point1, point3) / distScale, 1));

}

void calculateTrans()
{

transform.position = (new Vector3 (original.x + matrixFields [2], original.y + matrixFields [5],0));
}
``````

My points are distributed the following way: point1 is in the upper left corner, point 2 on the upper right corner, 3 lower left and 4 lower right. distScale is a variable where I store the original distance between points 1 and 2 (assinged on Start, same for x and y because mine is a square object), original stores the original position of the parent object.

EDIT:
After a lot of testing this doesn’t work correctly. IT can’t handle skewing and it sometimes reflects and rotates the object wrong. Still open to solution.
Someone in the Forum suggested that I use Unity’s 4x4 Matrix. However, I don’t know how to do it.
Thanks!

Look at the source code for OpenCV getPerspectiveTransform.