-->

Using transformation matrix in KineticJS

2019-05-08 06:18发布

问题:

I have a body simulated with Box2Dweb and an image attached to the body. I want to take the body transformation matrix and apply the transformation to the image (draw the image with the transformation), so that its position and orientation (relative) on the screen matches that of the body. I can't find a function for using with a transformation matrix in KineticJS. There are separate functions for moving and rotating, and there's a Kinetic.Transform class which holds some 'matrix' inside, but I have no idea what to do with it then. There's also a _setTransform function somewhere, but again, no idea how to use it (and does the underscore suggest that I must not call it directly or what?).

I used this code to draw images over bodies in plain Javascript:

function drawImageOverBody(context, body, image, scale)
{
    context.save();
    var pos = body.GetPosition();
    var center = [scale*pos.x, scale*pos.y];
    var R = body.GetTransform().R;
    context.translate(center[0], center[1]);
    context.transform(R.col1.x, R.col1.y, R.col2.x, R.col2.y, 0, 0);
    context.translate(-center[0], -center[1]);
    context.drawImage(image, center[0] - image.width/2.0, center[1] - image.height/2.0);
    context.restore();
}

How do I do the same with KineticJS?

EDIT: apparently, the only transformations I can get from Box2d are translation and rotation, which I can get by calling GetPosition() and GetAngle() on the Box2d body, then apply to a KineticJS image with setX(), setY() and setRotation(). So I don't really need the transformation in matrix form here. Still, curious.

回答1:

The Kinetic.Transform class can be used to calculate rotation, scale and offset from a given transformation matrix. Set the m attribute of Kinetic.Transform to the transformation matrix, then use the transform object to calculate rotation, scale and translation. Rotation, scale and translation can then be applied to any Kinetic.Node

Translation is given directly by the last two items in the matrix.

If you acquire a transformation from elsewhere, you can apply it to a Kinetic.Transform like this:

var tf = new Kinetic.Transform();
tf.m = [Math.sqrt(2), Math.sqrt(2), -3/2*Math.sqrt(2), 3/2*Math.sqrt(2), 4, 5];

Translation is the easiest. it is given by the last two elements: 4 in X direction, 5 in Y direction. There is also a method to get it.

var translation = tf.getTranslation();

You can get the scale from the transformation matrix:

var m = tf.getMatrix(); // or tf.m
var scaleX = Math.sqrt(m[0]*m[0] + m[1]*m[1]);
var scaleY = Math.sqrt(m[2]*m[2] + m[3]*m[3]);

Don't forget that the image can be flipped over the X or Y axis, or both. Calculate the determinant to take that into account:

if(m[0]*m[3] - m[1]*m[2] < 0){
    scaleY = -scaleY;
}

Adjust the sign of the scales to get the rotation more easily:

if(m[0] < 0){
    scaleX = -scaleX;
    scaleY = -scaleY;
}

Now the last one, rotation. You can get that by using Math.asin or Math.acos, but you must take the quadrant of the rotation angle into account. This is one way to solve it:

var angle = Math.acos(m[3] / scaleY);
if(m[2]/scaleY > 0){
    angle = -angle;
}

Note that the order in which to apply the different transformations is important. If you use the method explained above, first apply translation, then rotation, then scale.