0
votes

I'm writing a rotation matrix function that I want to work with vector inputs. The goal is to return a cell array of rotation matrices for each of the angles in the vector "angle". However, the cosine and sine functions return row arrays when given a vector input, so the final matrix going into the mat2cell call needs to have its columns rearranged depending on the size of "angle", ie to have the correct 3 columns per input angle. Here is an example of the input, function call, function, and result.

angle = [0 90];
RotZ(angle);

Call to RotZ

function [ rot ] = RotZ(angle)
%RotX Rotation matrix about Z axis
%   Generates a rotation matrix about Z axis for the  given angle in
%   degrees
angle = deg2rad(angle);

rot =   [cos(angle)           -sin(angle)         zeros(size(angle));...
         sin(angle)           cos(angle)          zeros(size(angle));...
         zeros(size(angle))   zeros(size(angle))  ones(size(angle))];
break
  if size(angle,2)>1
    rot = mat2cell(rot,3,3*ones(size(rot,2)/3,1));
  end
end

Returns matrix rot: (before mat2cell call)

rot =

    1.0000    0.0000         0   -1.0000         0         0
         0    1.0000    1.0000    0.0000         0         0
         0         0         0         0    1.0000    1.0000

I need a generalized way to group every 1+nth column where n is the number of elements in "angle". The correct matrix for a two element vector "angle" should have columns ordered [1 3 5] and [2 4 6]. For a two angle input this is trivial, but I don't know how to generalize for larger size vectors of "angle" ie angle = [0 45 90 etc...]

1

1 Answers

0
votes

Instead of trying to group or determine which indices to select from each column to build a rotation matrix per angle, try using arrayfun and iterate through each value in angle. With this, you can specify the uni=0 flag to output a cell array. I would rather do it this way because mat2cell essentially uses a for loop under the hood. If you're going to do that, you might as well use arrayfun and the loop is more sensible to use.

Try doing something like this:

function [ rot ] = RotZ(angle)
%RotX Rotation matrix about Z axis
%   Generates a rotation matrix about Z axis for the  given angle in
%   degrees
angle = deg2rad(angle);

f = @(x)[cos(x) -sin(x) 0;...
         sin(x) cos(x) 0;...
         0 0 1];

rot = arrayfun(f, angle, 'uni', 0);

end

rot will contain a cell array of 3 x 3 rotation matrices for each angle stored in angle. BTW, angle is an actual function in MATLAB so calling a variable in that way would unintentionally shadow over the command itself, so if you wanted to use it later, you wouldn't be able to.


Here's an example run given a bunch of angles:

ang = [0 30 60 90 120];
rot = RotZ(ang);

We get:

>> celldisp(rot)

rot{1} =

     1     0     0
     0     1     0
     0     0     1



rot{2} =

    0.8660   -0.5000         0
    0.5000    0.8660         0
         0         0    1.0000



rot{3} =

    0.5000   -0.8660         0
    0.8660    0.5000         0
         0         0    1.0000



rot{4} =

    0.0000   -1.0000         0
    1.0000    0.0000         0
         0         0    1.0000



rot{5} =

   -0.5000   -0.8660         0
    0.8660   -0.5000         0
         0         0    1.0000

However, if you prefer to do it your way, we can generate a matrix of indices where each column corresponds to the order of which columns you would need to sample from the matrix. You can easily achieve that by specifying a vector from 1 up to 3M where M is the total number of elements in angle. After, use reshape. For example, given that we have two matrices, you would do this:

>> M = 2;
>> ind = reshape(1:3*M, M, []).'

ind =

     1    2   
     3    4 
     5    6

Similarly when M = 3:

>> M = 3;
>> ind = reshape(1:3*M, M, []).'

ind =

     1     2     3
     4     5     6
     7     8     9

As you can see, for each column there are three values as we need three columns to build a rotation matrix and each value is M away from each other. These values are used to construct the rotation matrices to be sampled from the larger matrix rot. Also, the exact ordering you need per matrix is maintained. Next, we can perform a reordering of the matrix rot so that each consecutive chunk of three columns corresponds to a particular rotation matrix. You can then invoke mat2cell on this reordered matrix:

rot = rot(:, ind(:));
rot = mat2cell(rot, 3, 3*ones(M,1));

The first line is a bit tricky, but nothing we can't handle. We are accessing all of the rows of rot but we want to select out which columns we want out of rot and we want to place them in a particular order. By doing ind(:), we are unrolling the matrix ind into a vector where the columns of ind are stacked on top of the each other from the left column to the right column until we get a single vector out of this. Once we do that, each triplet of distinct values in ind corresponds to the right columns of rot we would need to sample from to create our reordered matrix. The result would be your large rot matrix, but consecutive triples of distinct columns yields a particular rotation matrix that you desire for each value of angle. The last call is pretty straight forward. Simply use mat2cell and split up the matrix into chunks of three and place each chunk into a cell array.

By running this, you'll also get what we did above. To complete the picture, here's what your function would be:

function [ rot ] = RotZ(angle)
%RotX Rotation matrix about Z axis
%   Generates a rotation matrix about Z axis for the  given angle in
%   degrees
angle = deg2rad(angle);

rot =   [cos(angle)           -sin(angle)         zeros(size(angle));...
         sin(angle)           cos(angle)          zeros(size(angle));...
         zeros(size(angle))   zeros(size(angle))  ones(size(angle))];

M = numel(angle);
ind = reshape(1:3*M, M, []).';
rot = rot(:, ind(:));
rot = mat2cell(rot, 3, 3*ones(M,1));

end