2
\$\begingroup\$

I'm currently building some kind of fake-3D dungeon, and I'm facing some difficulties understanding the behavior of the setTransform() function.

Here's my simplified code:

var can = document.getElementById('c'),
    ctx = can.getContext('2d'),
    angle = Math.PI / 4;

ctx.fillStyle = "grey";

ctx.setTransform(1, Math.tan(angle), 0, 1, 0, 0);
ctx.fillRect(100, 0, 50, 50);

Here's also s JsBin with the same code for you to see the result: http://jsbin.com/oxaSIpe/7/edit

Changing the x coordinate in the fillRect seems to also affect the y coordinate. It makes no sense. Skewing an element changes its position? No documentation mentions anything like that.

If anyone could please try to explain this function and its weird behavior, I would be very grateful.

\$\endgroup\$
  • \$\begingroup\$ Just an idea: The setTransform transforms the whole context. Maybe try drawing a small rectangle on a big canvas with different transforms. \$\endgroup\$ – MartinTeeVarga Jan 26 '14 at 4:48
  • \$\begingroup\$ I simplified my demo to make it easier to see what the real problem is. \$\endgroup\$ – Skwal Jan 26 '14 at 21:59
4
\$\begingroup\$

setTrasnform(a, b, c, d, e, f) has six parameters:

  • a Scales the drawings horizontally
  • b Skew the the drawings horizontally
  • c Skew the the drawings vertically
  • d Scales the drawings vertically
  • e Moves the the drawings horizontally
  • f Moves the the drawings vertically

It scales and skews the whole canvas. Imagine you are drawing on the canvas that is not skewed. It is skewed (transformed) only just before it is printed to the screen. See the following diagram:

Diagram

The whole canvas is skewed. I added four squares to the red rectangle. The skew causes the Y shift.

Here's the code:

var game = function() {
    var can = document.getElementById('c'),
        ctx = can.getContext('2d');

    ctx.fillStyle = "red";
    ctx.setTransform(1, 0.2, 0, 1, 0, 0);
    ctx.fillRect(20, 20, 250, 150);

    ctx.fillStyle = "blue";
    ctx.fillRect(30, 30, 20, 20);
    ctx.fillRect(60, 30, 20, 20);
    ctx.fillRect(90, 30, 20, 20);
    ctx.fillRect(120, 30, 20, 20);
};

game();

Philipp's answer is correct and it does answer your question. But maybe it's hard for you to digest. Use my example and add more small squares with different transforms to get a better idea of the setTransform.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Well no, I asked for an explanation, not a "yes it's normal because the specs say so". Thanks to you, I get it now :) \$\endgroup\$ – Skwal Jan 27 '14 at 14:41
1
\$\begingroup\$

The context.setTransform function (as well as all the convenience projection-functions like scale or rotate) works by changing the coordinate-system of the canvas. So yes, changing the transformation to a skewed projection changes the position of everything you draw. It doesn't just affect how you draw, it also affects where you draw.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ But why? This doesn't answer the question. Please take a look at the jsbin link, you'll see the problem. \$\endgroup\$ – Skwal Jan 26 '14 at 21:57
  • \$\begingroup\$ @Skwal Why? Because the specification says so? \$\endgroup\$ – Philipp Jan 27 '14 at 7:39
  • \$\begingroup\$ Yeah your answer makes more sense now that I understood, but it didn't help sorry. \$\endgroup\$ – Skwal Jan 27 '14 at 14:45
  • \$\begingroup\$ @Skwal Maybe you should ask a new question which doesn't ask "please explain this behavior" but rather "how can I solve problem X?" \$\endgroup\$ – Philipp Jan 27 '14 at 14:54
  • 1
    \$\begingroup\$ Hey relax :) Well my question was not about a problem X. I'm sorry if I hurt your feelings by not understanding your answer. I just feel that it's so obvious to you, that you don't see how someone can not get it. And just sending someone to the specs is irritating. If I ask for an explanation here, it's because my little research and experiment were not helpful. And your answer was not wrong, on the contrary, but it just didn't help me, it's no big deal. Thank you for trying :) \$\endgroup\$ – Skwal Jan 27 '14 at 16:05
0
\$\begingroup\$

Set transform sets the whole transform at once, which means if you want to specify where the rectangle ends up in screen coordinates, then you need to modify the translation component to compensate for the scale and skewing. In some sense, these are applied "first," which is just the convention used by most drawing APIs. Or you could just do:

ctx.translate(23,40);
ctx.transform(1, 1/2, 0, 1, 0, 0);

ctx.fillRect(0, 0, 50, 50);
ctx.rect(-10,-10,20,20);
ctx.stroke();

http://jsbin.com/oxaSIpe/11/edit

Now the translate part controls where the top left corner of the square goes, with an extra box to show where the origin is of this new space. You can modify the translate line to put it anywhere in screen space.

Note that most isometric games use 2-1 slope for the lines, or about 26.5 degrees, which I have done here. "True" isometric is 30 degrees, not 45.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.