1
$\begingroup$

There is a method rotate is mathutils.Matrix class with the signature rotate(other).

The documentation says the other can be of type Euler,Quaternion or Matrix.

But no matter what I pass as the argument to rotate, I always get a ValueError: Matrix.rotate(): must have 3x3 dimensions

I tried passing each of the following to the rotate method:

  1. Euler((0,0,math.pi/2), 'XYZ')
  2. Euler((0,0,math.pi/2), 'XYZ').to_quaternion()
  3. Euler((0,0,math.pi/2), 'XYZ').to_matrix() (This is actually a 3x3 matrix)

Also tried converting the Euler to a 4x4 matrix and then applying it to the given matrix - mw (e.g. Euler((0,0,math.pi/2), 'XYZ').to_matrix().to_4x4() @ mw) . But this is not giving the correct result.

So how to rotate a 4x4 matrix?

Edit1: The linked question (which is supposed to answer the question) talks about creating a rotation matrix and applying it. The class method Rotation accepts an axis which needs to be one of 'X', 'Y' and 'Z'. This does not allow arbitrary rotation about any Vector as it's possible with an Euler object. So creating a rotation matrix does not solve the problem. Also, it does not answer the question how to use the rotate method of matrix.

Edit2: Giving below comparison between rotating around local Z axis through pi/2 radians from viewport (pressing R-Z-90) and running the script given in the answer by @Markus von Broady.

Rotating manually:

enter image description here

Rotating via script:

enter image description here

$\endgroup$
2
  • $\begingroup$ you tried passing a 3x3 matrix, but you still try to apply the rotation on a 4x4 matrix. Try this: L, R, S = M.decompose(); R.rotate(Euler((0,0,math.pi/2))); M = Matrix.LocRotScale(L, R, S) ... If you want to rotate around the world origin: M = Euler((0,0,math.pi/2)).to_matrix().to_4x4() @ M $\endgroup$ Sep 21, 2022 at 12:28
  • $\begingroup$ @MarkusvonBroady Yes your solution worked for me. I think this is not a duplicate question. So it should be reopened and you can put this as the answer. Also do you know what matrix.rotate(other) does and how to pass the correct parameter to it? $\endgroup$ Sep 21, 2022 at 15:49

1 Answer 1

2
$\begingroup$

This to some extent is a duplicate, I've seen many answers to similar questions, though none really dealt with the confusing error message:

ValueError: Matrix.rotate(): must have 3x3 dimensions

Why is it that you pass a 3x3 matrix to it, and it still says the same thing? This is because the error complains about the owner of the method (passed implicitly as self to the method), not the argument other passed explicitly to the method. As a confirmation of that:

>>> M1 = Euler().to_matrix()
>>> M1
Matrix(((1.0, 0.0, 0.0),
        (0.0, 1.0, 0.0),
        (-0.0, 0.0, 1.0)))

>>> M2 = Matrix()
>>> M2
Matrix(((1.0, 0.0, 0.0, 0.0),
        (0.0, 1.0, 0.0, 0.0),
        (0.0, 0.0, 1.0, 0.0),
        (0.0, 0.0, 0.0, 1.0)))

>>> M1.rotate(M2)
>>>

No error - even though a 4x4 matrix was passed as the other. And now the usual part:

Rotate around world origin

>>> ob = C.object
>>> rotmat = Euler((0, 0, pi/2)).to_matrix().to_4x4()
>>> ob.matrix_world = rotmat @ ob.matrix_world

Rotate around local axes

>>> ob = C.object
>>> L, R, S = ob.matrix_world.decompose()
>>> R.rotate(Euler((0, 0, pi/2)))
>>> ob.matrix_world = Matrix.LocRotScale(L, R, S)
$\endgroup$
4
  • $\begingroup$ When you say "Rotate around local axes", I assume you mean the same kind of rotation that we get when we set the Transform Orientation to local and rotate the object around an axis (in the example case pi/2 radians around local z axis) in viewport. But this does not seem to be the case. Just create a cube (with axis visibility turned on) rotate it arbitrarily in 3d viewport to change matrix_world from unit Matrix and then further rotate the cube with Z->90, while setting Transform Orientation to Local. The result is different than what we get by running the script you have mentioned. $\endgroup$ Sep 22, 2022 at 9:56
  • $\begingroup$ Please see Edit2 in the question. $\endgroup$ Sep 22, 2022 at 10:16
  • $\begingroup$ @LomaHarshana I think I know what you mean, if you create an Euler with multiply rotations, you get the local rotations (XYZ order by default), but if you create two eulers, and apply the technique twice, first for an Euler with just one non-empty component X, then by Euler with just one non-empty component Z, then you're effectively rotating around global axes (because matrix operations are operations in global context) $\endgroup$ Sep 22, 2022 at 10:22
  • $\begingroup$ @LomaHarshana it seems what you need (though maybe I should clarify in my answer too...) is instead of doing R.rotate(...) you want to do R.x += R2.x; R.y += R2.y; R.z += R2.z $\endgroup$ Sep 22, 2022 at 10:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .