Get coordinates in a scaled and translated canvas

2.8k Views Asked by At

I'm drawing an image to a scaled and translated HTML5 canvas. It's zoomed to a certain point on the image to be more specific, so only a smaller partition is visible. In this smaller part, I want to select a point, and calculate the point relative to the original image. Here is an illustration:

Here is an illustration.

There is a similar answer on SO, which is explaining the canvas transformation nicely, but only shows how to get the point relative to the scaled image. What is the function, which gives the correct point described above?

Update:

There are three type of coordinates in this problem.

  1. Image point: point on the image.
  2. Transformed point: point on the transformed image, where the transformation can be scaling and translating.
  3. Viewport point: a point from the visible area.

The transformation represented by the following matrix:

$$ \begin{bmatrix} x' \\ y' \\ 1 \\ \end{bmatrix} = \begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix} $$

The only known data about the transformation (for me, at my current knowledge) is the transformation matrix, the original image size and the viewport size.

So, if I'm right, what needed here is the way, how can a viewport point converted to an image point.

1

There are 1 best solutions below

9
On BEST ANSWER

The StackOverflow answer tells you how to change a viewport point to a transformed point. What you want to do is change a viewport point to an image point. To do this, you need to take the inverse of the matrix. Basically, the inverse of a matrix does the exact opposite of what the original matrix did. For example, if the original matrix zooms in by a factor of $2$, then the inverse zooms out by a factor of $2$. This allows us to go from a viewport point to an image point given the transformation matrix.

However, the matrix of HTML5 canvas transformations can not be inversed because it is $2 \times 3$ and not $3 \times 3$ or some other square matrix. However, to fix this, we add the row $0 \ 0 \ 1$ to the bottom of the matrix. This works because the way translate and scale actually works in terms of regular matrices and vectors is that it uses a $3 \times 3$ matrix, except the last row is always $0 \ 0 \ 1$, and it uses a 3D point, except the $z$ coordinate is always $1$. Thus, if we add $0 \ 0 \ 1$ to the end of the matrix, we'll be able to take the inverse of it and find the image point given the viewport point. To take the inverse of a matrix in JavaScript, use math.js:

function turnArrayIntoMatrix(fakeMatrix) {
    /* fakeMatrix is our array of 6 elements that we need
       to turn into a multi-dimensional 3x3 array: */
    //realMatrix is the real matrix we will return:
    var realMatrix = [[], [], [0, 0, 1]];
    for (var i = 0; i < 3; i++) {
        //Add the (2*i)th element to the first row:
        realMatrix[0].push(fakeMatrix[2*i]);
        //Add the (2*i+1)th element to the second row:
        realMatrix[1].push(fakeMatrix[2*i+1]);
    }
    //Finally, return realMatrix:
    return realMatrix;
}
function turnMatrixIntoArray(realMatrix) {
    /* realMatrix is the 3x3 multi-dimensional array
       that we need to turn back into our six-element array: */
    //array is the array we're going to return:
    var array = [];
    for (var i = 0; i < 3; i++) {
        //Push the first and second element from each column into array:
        array.push(array[0][i]);
        array.push(array[1][i]);
    }
    //Finally, return array:
    return array;
}
//This shows us the inverse of our matrix:
console.log(turnMatrixIntoArray(math.inv(turnArrayIntoMatrix(matrix))));