# v_roteucode

## PURPOSE

V_ROTEUCODE decodes a string specifying a rotation axis sequence

## SYNOPSIS

function mv=v_roteucode(m)

## DESCRIPTION

    V_ROTEUCODE decodes a string specifying a rotation axis sequence
         M(n)     a string of n characters from the set determining the order of rotation axes
                  as listed below. Note that the control characters 'rdoOaA' may occur anywhere in the string:
                    'x','y','z'    rotate around the given axis by the corresponding angle
                                   given in e()
                    '1','2','3'    90 degree rotation around x,y or z axis; doesn't use a value from e()
                    '4','5','6'    180 degree rotation around x,y or z axis; doesn't use a value from e()
                    '7','8','9'    270 degree rotation around x,y or z axis; doesn't use a value from e()
                    'r','d'        all angles are given in radians or degrees  [radians]
                 'o','O','a','A'   selects whether to rotate the object or the coordinate axes and
                                   whether the rotation axes remain fixed in space for consecutive
                                   rotations (extrinsic) or else move with each rotation (intrinsic).
                                      'o' = object-extrinsic [default]
                                      'O' = object-intrinsic
                                      'a' = axes-extrinsic
                                      'A' = axes-intrinsic
     Outputs:

         mv(7,k)    where k-1 is the number of non-control characters in the input string m
                        mv(1,j) = Code for the j'th rotation: 1,2,3 for x,y,z rotation and 4 to 12 for the fixed rotations listed above.
                                  All entries are in the range [1,12] except for mv(1,k)=0.
                        mv(2,j) = index into euler angle array for x,y,z rotations. mv(2,k) gives total number of euler angles needed
                        mv(3,j) = rotation class before rotation j. mv(3,k) is the final rotation class and equals 52 for arbitrary rotations.
                        mv(4,j) = index into a vectorized matrix of the entry that becomes non-zero after rotation j
                        mv(5,j) = index into a vectorized matrix of the other changing element in the same column
                        mv(6,j) = +-1 = sign of the sine term affecting entry mv(4,j). For mv(1,j) in [1,3], mv(6,j)=0 if the rotation is unnecessary
                        mv(7,j) = +-1 = sign of entry mv(5,j) before rotation j if known
                    Special entries:
                        mv(7,k) = -1 to invert the rotation (i.e. transpose the matrix) or +1 otherwise
                        mv(4,k) = scale factor for euler angles: +-1 or +-pi/180

     The string M specifies the seqeunce of axes about which the rotations are performed. There are 12
     possible 3-character sequences that avoid consecutive repetitions. These are 'Euler angles' if
     there is a repeated axis or 'Tait-Bryan angles' if not. Common choices are:
     (1) 'zxz' the most common Euler angle set
     (2) 'xyz' corresponds to 'roll, pitch, yaw' for an aeroplane heading in the x direction with y to
         the right and z down. The intrinsic equivalent is 'Ozyx' corresponding to 'yaw, pitch, roll'.
     (3) 'z1z1z' involves 5 rotations, in which all the non-fixed rotations are around the z axis.

## CROSS-REFERENCE INFORMATION

This function calls:

This function is called by:

- v_roteu2qr ROTEU2QR converts a sequence of Euler angles to a real unit quaternion
- v_rotro2eu V_ROTRO2EU converts a 3x3 rotation matrix into the corresponding euler angles

## SOURCE CODE

    0001 function mv=v_roteucode(m)
    0002 %V_ROTEUCODE decodes a string specifying a rotation axis sequence
    0003 %     M(n)     a string of n characters from the set determining the order of rotation axes
    0004 %              as listed below. Note that the control characters 'rdoOaA' may occur anywhere in the string:
    0005 %                'x','y','z'    rotate around the given axis by the corresponding angle
    0006 %                               given in e()
    0007 %                '1','2','3'    90 degree rotation around x,y or z axis; doesn't use a value from e()
    0008 %                '4','5','6'    180 degree rotation around x,y or z axis; doesn't use a value from e()
    0009 %                '7','8','9'    270 degree rotation around x,y or z axis; doesn't use a value from e()
    0010 %                'r','d'        all angles are given in radians or degrees  [radians]
    0011 %             'o','O','a','A'   selects whether to rotate the object or the coordinate axes and
    0012 %                               whether the rotation axes remain fixed in space for consecutive
    0013 %                               rotations (extrinsic) or else move with each rotation (intrinsic).
    0014 %                                  'o' = object-extrinsic [default]
    0015 %                                  'O' = object-intrinsic
    0016 %                                  'a' = axes-extrinsic
    0017 %                                  'A' = axes-intrinsic
    0018 % Outputs:
    0019 %
    0020 %     mv(7,k)    where k-1 is the number of non-control characters in the input string m
    0021 %                    mv(1,j) = Code for the j'th rotation: 1,2,3 for x,y,z rotation and 4 to 12 for the fixed rotations listed above.
    0022 %                              All entries are in the range [1,12] except for mv(1,k)=0.
    0023 %                    mv(2,j) = index into euler angle array for x,y,z rotations. mv(2,k) gives total number of euler angles needed
    0024 %                    mv(3,j) = rotation class before rotation j. mv(3,k) is the final rotation class and equals 52 for arbitrary rotations.
    0025 %                    mv(4,j) = index into a vectorized matrix of the entry that becomes non-zero after rotation j
    0026 %                    mv(5,j) = index into a vectorized matrix of the other changing element in the same column
    0027 %                    mv(6,j) = +-1 = sign of the sine term affecting entry mv(4,j). For mv(1,j) in [1,3], mv(6,j)=0 if the rotation is unnecessary
    0028 %                    mv(7,j) = +-1 = sign of entry mv(5,j) before rotation j if known
    0029 %                Special entries:
    0030 %                    mv(7,k) = -1 to invert the rotation (i.e. transpose the matrix) or +1 otherwise
    0031 %                    mv(4,k) = scale factor for euler angles: +-1 or +-pi/180
    0032 %
    0033 %
    0034 % The string M specifies the seqeunce of axes about which the rotations are performed. There are 12
    0035 % possible 3-character sequences that avoid consecutive repetitions. These are 'Euler angles' if
    0036 % there is a repeated axis or 'Tait-Bryan angles' if not. Common choices are:
    0037 % (1) 'zxz' the most common Euler angle set
    0038 % (2) 'xyz' corresponds to 'roll, pitch, yaw' for an aeroplane heading in the x direction with y to
    0039 %     the right and z down. The intrinsic equivalent is 'Ozyx' corresponding to 'yaw, pitch, roll'.
    0040 % (3) 'z1z1z' involves 5 rotations, in which all the non-fixed rotations are around the z axis.
    0041 %
    0042
    0043 %      Copyright (C) Mike Brookes 2007-2020
    0044 %      Version: $Id: v_roteucode.m 11260 2020-07-18 20:07:58Z dmb $
    0045 %
    0046 %   VOICEBOX is a MATLAB toolbox for speech processing.
    0047 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
    0048 %
    0049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0050 %   This program is free software; you can redistribute it and/or modify
    0051 %   it under the terms of the GNU Lesser General Public License as published by
    0052 %   the Free Software Foundation; either version 3 of the License, or
    0053 %   (at your option) any later version.
    0054 %
    0055 %   This program is distributed in the hope that it will be useful,
    0056 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
    0057 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0058 %   GNU Lesser General Public License for more details.
    0059 %
    0060 %   You can obtain a copy of the GNU Lesser General Public License from
    0061 %   https://www.gnu.org/licenses/ .
    0062 %
    0063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0064
    0065 persistent mes trmap zel mch mvch  jch nch
    0066 if isempty(mes)     % setup fixed arrays and initialize cache of mode strings
    0067     nch=5;          % size of cache
    0068     mch=cell(nch,1); % cache of input character strings
    0069     mvch=cell(nch,1);  % cache of output mv codes
    0070     flefch=zeros(nch,2);  % cache of output flef codes
    0071     jch=(1:nch); % cache usage order jch(1) is the most recent, jch(nch) the oldest
    0072     for i=1:nch
    0073         mch{i}='';
    0074         mvch{i}=[0;0;1;1;0;0;1];
    0075     end
    0076     mes=[1:3 10:12 7:9 4:6]; % sign reversal look-up table
    0077     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0078     % The trmap and zel arrays contain information about each of 52 different  %
    0079     % patterns of -1,0,+1 that may exist in a rotation matrix as follows:      %
    0080     %                                                                          %
    0081     %   1-3 : identity matrix rows in order: 123, 231, 312                     %
    0082     %   4-6 : negated identity matrix rows in order: 132, 213, 321             %
    0083     %   7-12: As 1-6 but with rows 2,3 negated                                 %
    0084     %  13-18: As 1-6 but with rows 1,3 negated                                 %
    0085     %  19-24: As 1-6 but with rows 1,2 negated                                 %
    0086     %  25-33: +1 in position (i-24) and 0's in remainder of this row and col   %
    0087     %  34-42: -1 in position (i-24) and 0's in remainder of this row and col   %
    0088     %  43-51: 0 in position (i-42)                                             %
    0089     %  52: no special symmetry                                                 %
    0090     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0091     % trmap(i,j) gives the pattern that i is transformed into by rotation j    %
    0092     % where j=1:3 corresponds to x,y,z and j=4:12 corresponds to the 9         %
    0093     % multiples of 90 degree rotations listed in the main comments.            %
    0094     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0095     trmap=[ 25 29 33 16 24 11  7 13 19 22 12 17;
    0096         28 32 27 17 22 12  8 14 20 23 10 18;
    0097         31 26 30 18 23 10  9 15 21 24 11 16;
    0098         34 41 39 13 20  9 10 16 22 19  8 15;
    0099         37 35 42 14 21  7 11 17 23 20  9 13;
    0100         40 38 36 15 19  8 12 18 24 21  7 14;
    0101         25 38 42 22  6 23  1 19 13 16 18  5;
    0102         28 41 36 23  4 24  2 20 14 17 16  6;
    0103         31 35 39 24  5 22  3 21 15 18 17  4;
    0104         34 32 30 19  2 21  4 22 16 13 14  3;
    0105         37 26 33 20  3 19  5 23 17 14 15  1;
    0106         40 29 27 21  1 20  6 24 18 15 13  2;
    0107         34 29 42 10 12  5 19  1  7  4 24 23;
    0108         37 32 36 11 10  6 20  2  8  5 22 24;
    0109         40 26 39 12 11  4 21  3  9  6 23 22;
    0110         25 41 30  7  8  3 22  4 10  1 20 21;
    0111         28 35 33  8  9  1 23  5 11  2 21 19;
    0112         31 38 27  9  7  2 24  6 12  3 19 20;
    0113         34 38 33  4 18 17 13  7  1 10  6 11;
    0114         37 41 27  5 16 18 14  8  2 11  4 12;
    0115         40 35 30  6 17 16 15  9  3 12  5 10;
    0116         25 32 39  1 14 15 16 10  4  7  2  9;
    0117         28 26 42  2 15 13 17 11  5  8  3  7;
    0118         31 29 36  3 13 14 18 12  6  9  1  8;
    0119         25 44 45 25 36 26 25 34 34 25 27 35;
    0120         43 26 45 27 26 34 35 26 35 36 26 25;
    0121         43 44 27 35 25 27 36 36 27 26 34 27;
    0122         28 47 48 28 39 29 28 37 37 28 30 38;
    0123         46 29 48 30 29 37 38 29 38 39 29 28;
    0124         46 47 30 38 28 30 39 39 30 29 37 30;
    0125         31 50 51 31 42 32 31 40 40 31 33 41;
    0126         49 32 51 33 32 40 41 32 41 42 32 31;
    0127         49 50 33 41 31 33 42 42 33 32 40 33;
    0128         34 44 45 34 27 35 34 25 25 34 36 26;
    0129         43 35 45 36 35 25 26 35 26 27 35 34;
    0130         43 44 36 26 34 36 27 27 36 35 25 36;
    0131         37 47 48 37 30 38 37 28 28 37 39 29;
    0132         46 38 48 39 38 28 29 38 29 30 38 37;
    0133         46 47 39 29 37 39 30 30 39 38 28 39;
    0134         40 50 51 40 33 41 40 31 31 40 42 32;
    0135         49 41 51 42 41 31 32 41 32 33 41 40;
    0136         49 50 42 32 40 42 33 33 42 41 31 42;
    0137         43 52 52 43 45 44 43 43 43 43 45 44;
    0138         52 44 52 45 44 43 44 44 44 45 44 43;
    0139         52 52 45 44 43 45 45 45 45 44 43 45;
    0140         46 52 52 46 48 47 46 46 46 46 48 47;
    0141         52 47 52 48 47 46 47 47 47 48 47 46;
    0142         52 52 48 47 46 48 48 48 48 47 46 48;
    0143         49 52 52 49 51 50 49 49 49 49 51 50;
    0144         52 50 52 51 50 49 50 50 50 51 50 49;
    0145         52 52 51 50 49 51 51 51 51 50 49 51;
    0146         52 52 52 52 52 52 52 52 52 52 52 52];
    0147     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0148     % Each Euler angle is chosen so that the inverse rotation forces a specific element  %
    0149     % of the rotation matrix to zero. zel(k,j,i) gives information about which element   %
    0150     % ceases to be zero when a rotation around axis j is applied to pattern i.           %
    0151     %    k=1 gives the index into a vectorized matrix of the entry that becomes non-zero %
    0152     %    k=2 gives the index of the other element in the same column that changes        %
    0153     %    k=3 gives the sign of the sine term affecting the first of these entries        %
    0154     %    k=4 gives the sign of the initial value of the second of these entries if known %
    0155     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0156     zel=reshape([  6  5  1  1  3  1 -1  1  2  1  1  1;
    0157         2  3 -1  1  1  3  1  1  5  4  1  1;
    0158         3  2  1  1  4  6  1  1  1  2 -1  1;
    0159         5  6 -1 -1  3  1 -1 -1  2  1  1 -1;
    0160         3  2  1 -1  6  4 -1 -1  1  2 -1 -1;
    0161         2  3 -1 -1  1  3  1 -1  4  5 -1 -1;
    0162         6  5  1 -1  3  1 -1  1  2  1  1  1;
    0163         2  3 -1 -1  1  3  1 -1  5  4  1  1;
    0164         3  2  1 -1  4  6  1 -1  1  2 -1 -1;
    0165         5  6 -1  1  3  1 -1 -1  2  1  1 -1;
    0166         3  2  1  1  6  4 -1 -1  1  2 -1  1;
    0167         2  3 -1  1  1  3  1  1  4  5 -1  1;
    0168         6  5  1  1  3  1 -1 -1  2  1  1 -1;
    0169         2  3 -1 -1  1  3  1 -1  5  4  1 -1;
    0170         3  2  1  1  4  6  1 -1  1  2 -1  1;
    0171         5  6 -1  1  3  1 -1  1  2  1  1  1;
    0172         3  2  1 -1  6  4 -1  1  1  2 -1 -1;
    0173         2  3 -1  1  1  3  1  1  4  5 -1 -1;
    0174         6  5  1 -1  3  1 -1 -1  2  1  1 -1;
    0175         2  3 -1  1  1  3  1  1  5  4  1 -1;
    0176         3  2  1 -1  4  6  1  1  1  2 -1 -1;
    0177         5  6 -1 -1  3  1 -1  1  2  1  1  1;
    0178         3  2  1  1  6  4 -1  1  1  2 -1  1;
    0179         2  3 -1 -1  1  3  1 -1  4  5 -1  1;
    0180         0  0  0  0  3  1 -1  1  2  1  1  1;
    0181         3  2  1  1  0  0  0  0  1  2 -1  1;
    0182         2  3 -1  1  1  3  1  1  0  0  0  0;
    0183         0  0  0  0  6  4 -1  1  5  4  1  1;
    0184         6  5  1  1  0  0  0  0  4  5 -1  1;
    0185         5  6 -1  1  4  6  1  1  0  0  0  0;
    0186         0  0  0  0  9  7 -1  1  8  7  1  1;
    0187         9  8  1  1  0  0  0  0  7  8 -1  1;
    0188         8  9 -1  1  7  9  1  1  0  0  0  0;
    0189         0  0  0  0  3  1 -1 -1  2  1  1 -1;
    0190         3  2  1 -1  0  0  0  0  1  2 -1 -1;
    0191         2  3 -1 -1  1  3  1 -1  0  0  0  0;
    0192         0  0  0  0  6  4 -1 -1  5  4  1 -1;
    0193         6  5  1 -1  0  0  0  0  4  5 -1 -1;
    0194         5  6 -1 -1  4  6  1 -1  0  0  0  0;
    0195         0  0  0  0  9  7 -1 -1  8  7  1 -1;
    0196         9  8  1 -1  0  0  0  0  7  8 -1 -1;
    0197         8  9 -1 -1  7  9  1 -1  0  0  0  0;
    0198         0  0  0  0  1  3  1  1  1  2 -1  1;
    0199         2  3 -1  1  0  0  0  0  2  1  1  1;
    0200         3  2  1  1  3  1 -1  1  0  0  0  0;
    0201         0  0  0  0  4  6  1  1  4  5 -1  1;
    0202         5  6 -1  1  0  0  0  0  5  4  1  1;
    0203         6  5  1  1  6  4 -1  1  0  0  0  0;
    0204         0  0  0  0  7  9  1  1  7  8 -1  1;
    0205         8  9 -1  1  0  0  0  0  8  7  1  1;
    0206         9  8  1  1  9  7 -1  1  0  0  0  0;
    0207         0  0  0  0  0  0  0  0  0  0  0  0]',4,3,52);
    0208 end
    0209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0210 % Convert the m string
    0211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0212 if ~ischar(m) % lecacy call with integer m argument
    0213     m=char(m+'w'); % convert to characters
    0214 end
    0215 ich=find(strcmp(m,mch),1);      % check if already in the cache
    0216 if isempty(ich)                 % not yet in the cache
    0217     mm=m-'w';                   % convert to integers with x -> 1
    0218     mi=mm>=-31 & mm<=-29;       % find characters XYZ
    0219     mm(mi)=mm(mi)+32;           % convert XYZ to xyz (for compatibility)
    0220     mi=mm>=-70 & mm<=-62;       % find digits 1:9
    0221     mm(mi)=mm(mi)+74;           % convert to 4:12
    0222     mi=mm<=0;                   % select control characters
    0223     mc=mm(mi);                  % controls
    0224     mm=mm(~mi);                 % rotations
    0225     ef=1;                       % angle scale factor
    0226     es=1;                       % angle sign
    0227     fl=1;                       % default to no rotation matrix tranposing
    0228     for i=1:length(mc)
    0229         switch mc(i)
    0230             case -5             % 'r' = radians
    0231             case -19            % 'd' = degrees
    0232                 ef=pi/180;      % scale factor to convert to radians
    0233             case -37            % 'R' = negated radians
    0234                 ef=-1;
    0235             case -51            % 'D' = negated degrees
    0236                 ef=-pi/180;      % scale factor to convert to radians
    0237             case -8             % 'o' = object-extrinsic
    0238             case -40            % 'O' = object-intrinsic
    0239                 fl=-1;
    0240                 es=-1;
    0241             case -22            % 'a' = axes-extrinsic
    0242                 fl=-1;
    0243             case -54            % 'A' = axes-intrinsic
    0244                 es=-1;
    0245             otherwise
    0246                 error('Invalid character: %s',mc(i)+'w')
    0247         end
    0248     end
    0249     ef=ef*es;               % change sign of scale factor if necessary
    0250     if es<0
    0251         mm=mes(mm);         % sign-reverse: interchage 4,5,6 with 10,11,12
    0252     end
    0253     nm=length(mm);
    0254     mv=zeros(7,nm+1);
    0255     mv(1,:)=[mm 0];
    0256     mv(2,:)=cumsum([mm<=3 0]);      % index into euler angle array
    0257     mv(3,1)=1; % initial pattern is the identity matrix
    0258     for i=1:nm                % loop for each rotation
    0259         mmi=mm(i); % rotation code
    0260         mv(3,i+1)=trmap(mv(3,i),mmi); % pattern ID after rotation
    0261         if mmi<4
    0262             mv(4:7,i)=zel(:,mmi,mv(3,i)); % information about which matrix elements change from zero
    0263         end
    0264     end
    0265     mv(end)=fl;
    0266     mv(end-3)=ef;
    0267     % now save in the cache
    0268     ich=jch(nch);       % find oldest cache entry
    0269     mch{ich}=m;                 % save input string
    0270     mvch{ich}=mv;               % save parameters
    0271     jch=[ich jch(1:nch-1)];     % age all the other cache entries
    0272 else                            % already in the cache
    0273     kch=find(jch==ich,1);       % find existing ich entry
    0274     jch(1:kch)=[ich jch(1:kch-1)];
    0275     mv=mvch{ich};               % retrieve from cache
    0276 end