6

I am rotating an object in html5 canvas around a variable point of origin.

If a user clicks on a given point in the newly rotated rectangle, I need the returned mouse coordinates to be rotated back around the same point of origin.

I have made a very quick drawing to hopefully illustrate better:

enter image description here

I essentially need a function that will take the actual clicked mouse coordinates as x and y and transform them to the objects position BEFORE it was rotated.

var origin = {
        x: 100,
        y: 100
};

var angle = 45;

function transformCoordinates(x,y){

         //Perform calculation to transform coordinates

         return {
                 x : newx,
                 y : newy
         };
}

The variables available will be the transformation origin of the rotation and the angle. As well as the mouse click coordinate on the canvas (relative to the canvas itself, with 0,0 being the top left point etc)

Unfortunately math is not my strong point. Hopefully someone can help.

gordyr
  • 6,078
  • 14
  • 65
  • 123
  • Your image appears to be incorrect. You placed the "Mouse click" text on the "New" rotated position of the element, instead of the old one. – Cerbrus Jan 25 '13 at 12:36
  • Nope, that's actually the behavior I want. The user should click on the NEW object, and it should return the coordinates as though the position was clicked on the OLD un-rotated object. Confusing I know. – gordyr Jan 25 '13 at 12:51
  • And you also drew that you rotated it with 45 degrees, but you actually rotated it with 90 degrees. – Calmarius Feb 06 '14 at 13:11

3 Answers3

5

There is an even more elegant way to solve this problem than the way you did, but you need to know how to do matrix multiplication to understand it.

You can use context.currentTransform to get the transformation matrix of the canvas. This matrix represents all the coordinate modifications resulting from all scale- translate- and rotate modifiers currently applied to the context state. Here is the documentation of the matrix.

To convert internal coordinates to screen coordinates, use these formulas:

screen_X = internal_X * a + internal_Y * c + e;    
screen_Y = internal_X * b + internal_Y * d + f;
Philipp
  • 67,764
  • 9
  • 118
  • 153
  • 1
    This will be awesome when the currentTransform property is widely implemented. Until then, this is just one more reason why HTML5 is not a "Flash killer." – Jesse Crossen Aug 14 '13 at 15:12
  • I have tried this but it doesn't work. How should I define internal_X and internal_Y? I get my internal_X using ````var rect = canvas.getBoundingClientRect();``` ```var internal_X = (event.clientX - rect.left) *canvas.widht /rect.widht)``` Here is my full function that is called onMouseDown to undo canvas transform https://stackoverflow.com/questions/59245199/get-canvas-mouse-coordinates-after-transformation-using-ctx-gettransformation – NicoWheat Dec 09 '19 at 16:12
4

Embarrassingly I solved this problem straight after asking the question.

In case anyone else needs the same sort of function, here is how I did it:

getTransformedCoords : function(coords){
    var self = this;
    var obj = self.activeObj;
    var angle = (obj.angle*-1) * Math.PI / 180;   
    var x2 = coords.x - obj.left;
    var y2 = coords.y - obj.top;
    var cos = Math.cos(angle);
    var sin = Math.sin(angle);

    var newx = x2*cos - y2*sin + obj.left; 
    var newy = x2*sin + y2*cos + obj.top;

    return {
        x : newx,
        y : newy
    };
},
Cerbrus
  • 70,800
  • 18
  • 132
  • 147
gordyr
  • 6,078
  • 14
  • 65
  • 123
  • 3
    I don't get it, what is self.activeObj how are you simply asking for obj.angle etc... please explain? P.S I cant use `currentTransform` because it is an experimental feature. – Imran Bughio Jan 24 '17 at 13:29
2

I was also searching for this kind of query for a quite a bit of time. In my case, I was experimenting in canvas to display a dot point, when user mouse-click over the canvas, even after rotation. The issue was, after context rotation the points where wrongly displayed. I took the method provided by gordyr .

I have modified the code slightly for my code base. I have put my complete code base here.

let model = {
    ctx: {},
    canvas: {},
    width: 500,
    height: 500,
    angleInRad: 0
};
function getTransformedCoords(coords) {
    var angle = (model.angleInRad * -1);
    var x2 = coords.x;
    var y2 = coords.y;
    var cos = Math.cos(angle);
    var sin = Math.sin(angle);

    var newx = Math.floor(x2 * cos - y2 * sin);
    var newy = Math.floor(x2 * sin + y2 * cos);

    return new Position(newx, newy);
}

Once again thank you gordyr for your code.

MishkuMoss
  • 118
  • 2
  • 6
  • I have tried these - but how did you find the coords value? I am using ```var rect = canvas.getBoundingClientRect(); ``` ```var coords.x = (event.clientX - rect.left) * canvas.widht /rect.widht)```, but this gives the wrong answer. Could you please elaborate on how the original coords should be calculated? To be clear, I am letting the user draw on canvas with mouse after canvas rotation, so the event.clientX is mouse event. – NicoWheat Dec 09 '19 at 16:20
  • I have shared my working code base earlier. you may can check that. – MishkuMoss Dec 11 '19 at 06:26
  • What that code does is, when a user click on the canvas, program will calculate the mouse points based on canvas' position in the browser and then if there is any rotation, program will recalculate the mouse points in order to show it on the exact location that user has clicked, on the canvas. – MishkuMoss Dec 11 '19 at 06:35