2
votes

From a couple references (i.e., http://en.wikipedia.org/wiki/Rotation_matrix "Rotation matrix from axis and angle", and exercise 5.15 in "Computer Graphics - Principles and Practice" by Foley et al, 2nd edition in C), I've seen this definition of a rotation matrix (implemented below in Octave) that rotates points by a specified angle about a specified vector. Although I have used it before, I'm now seeing rotation problems that appear to be related to orientation. The problem is recreated in the following Octave code that

  • takes two unit vectors: src (green in figures) and dst (red in figures),
  • calculates the angle between them: theta,
  • calculates the vector normal to both: pivot (blue in figures),
  • and finally attempts to rotate src into dst by rotating it about vector pivot by angle theta.

    % This test fails: rotated unit vector is not at expected location and is no longer normalized.
    s = [-0.49647; -0.82397; -0.27311]
    d = [ 0.43726; -0.85770; -0.27048]
    test_rotation(s, d, 1);
    
    % Determine rotation matrix that rotates the source and normal vectors to the x and z axes, respectively.
    normal = cross(s, d);
    normal /= norm(normal);
    R = zeros(3,3);
    R(1,:) = s;
    R(2,:) = cross(normal, s);
    R(3,:) = normal;
    R
    
    % After rotation of the source and destination vectors, this test passes.
    s2 = R * s
    d2 = R * d
    test_rotation(s2, d2, 2);
    
    function test_rotation(src, dst, iFig)
        norm_src = norm(src)
        norm_dst = norm(dst)
    
        % Determine rotation axis (i.e., normal to two vectors) and rotation angle.
        pivot = cross(src, dst);
        theta = asin(norm(pivot))
        theta_degrees = theta * 180 / pi
        pivot /= norm(pivot)
    
        % Initialize matrix to rotate by an angle theta about pivot vector.
        ct = cos(theta);
        st = sin(theta);
        omct = 1 - ct;
    
        M(1,1) = ct - pivot(1)*pivot(1)*omct;
        M(1,2) = pivot(1)*pivot(2)*omct - pivot(3)*st;
        M(1,3) = pivot(1)*pivot(3)*omct + pivot(2)*st;
        M(2,1) = pivot(1)*pivot(2)*omct + pivot(3)*st;
        M(2,2) = ct - pivot(2)*pivot(2)*omct; 
        M(2,3) = pivot(2)*pivot(3)*omct - pivot(1)*st;
        M(3,1) = pivot(1)*pivot(3)*omct - pivot(2)*st;
        M(3,2) = pivot(2)*pivot(3)*omct + pivot(1)*st;
        M(3,3) = ct - pivot(3)*pivot(3)*omct;
    
        % Rotate src about pivot by angle theta ... and check the result.
        dst2 = M * src
        dot_dst_dst2 = dot(dst, dst2)
        if (dot_dst_dst2 >= 0.99999)
            "success"
        else
            "FAIL"
        end
    
        % Draw the vectors: green is source, red is destination, blue is normal.
        figure(iFig);
        x(1) = y(1) = z(1) = 0;
        ubounds = [-1.25 1.25 -1.25 1.25 -1.25 1.25];
        x(2)=src(1); y(2)=src(2); z(2)=src(3);
        plot3(x,y,z,'g-o');
        hold on
        x(2)=dst(1); y(2)=dst(2); z(2)=dst(3);
        plot3(x,y,z,'r-o');
        x(2)=pivot(1); y(2)=pivot(2); z(2)=pivot(3);
        plot3(x,y,z,'b-o');
        x(2)=dst2(1); y(2)=dst2(2); z(2)=dst2(3);
        plot3(x,y,z,'k.o');
        axis(ubounds, 'square');
        view(45,45);
        xlabel("xd");
        ylabel("yd");
        zlabel("zd");
        hold off
    end
    

Here are the resulting figures. Figure 1 shows an orientation that doesn't work. Figure 2 shows an orientation that works: the same src and dst vectors but rotated into the first quadrant.

enter image description here

enter image description here

I was expecting the src vector to always rotate onto the dst vector, as shown in Figure 2 by the black circle covering the red circle, for all vector orientations. However Figure 1 shows an orientation where the src vector does not rotate onto the dst vector (i.e., the black circle is not on top of the red circle, and is not even on the unit sphere).

For what it's worth, the references that defined the rotation matrix did not mention orientation limitations, and I derived (in a few hours and a few pages) the rotation matrix equation and didn't spot any orientation limitations there. I'm hoping the problem is an implementation error on my part, but I haven't been able to find it yet in either of my implementations: C and Octave. Have you experienced orientation limitations when implementing this rotation matrix? If so, how did you work around them? I would prefer to avoid the extra translation into the first quadrant if it isn't necessary.

Thanks,
Greg

1

1 Answers

3
votes

Seems two minus signs have escaped:

M(1,1) = ct - P(1)*P(1)*omct;
M(1,2) = P(1)*P(2)*omct - P(3)*st;
M(1,3) = P(1)*P(3)*omct + P(2)*st;

M(2,1) = P(1)*P(2)*omct + P(3)*st;
M(2,2) = ct + P(2)*P(2)*omct;      %% ERR HERE; THIS IS THE CORRECT SIGN
M(2,3) = P(2)*P(3)*omct - P(1)*st;

M(3,1) = P(1)*P(3)*omct - P(2)*st;
M(3,2) = P(2)*P(3)*omct + P(1)*st;
M(3,3) = ct + P(3)*P(3)*omct;      %% ERR HERE; THIS IS THE CORRECT SIGN

Here is a version that is much more compact, faster, and also based on Rodrigues' rotation formula:

function test

% first test: pass
s  = [-0.49647; -0.82397; -0.27311];
d  = [ 0.43726; -0.85770; -0.27048]
d2 = axis_angle_rotation(s, d)

% Determine rotation matrix that rotates the source and normal vectors to the x and z axes, respectively.
normal = cross(s, d);
normal = normal/norm(normal);

R(1,:) = s;
R(2,:) = cross(normal, s);
R(3,:) = normal;

% Second test: pass
s2 = R * s;
d2 = R * d
d3 = axis_angle_rotation(s2, d2)

end

function vec = axis_angle_rotation(vec, dst)

    % These following commands are just here for the function to act 
    % the same as your original function. Eventually, the function is 
    % probably best defined as 
    %
    %     vec = axis_angle_rotation(vec, axs, angle)
    %
    % or even 
    %
    %     vec = axis_angle_rotation(vec, axs)
    %
    % where the length of axs defines the angle. 
    %     
    axs = cross(vec, dst);
    theta = asin(norm(axs));

    % some preparations
    aa = axs.'*axs;        
    ra = vec.'*axs;

    % location of circle centers
    c = ra.*axs./aa;

    % first coordinate axis on the circle's plane
    u = vec-c;

    % second coordinate axis on the circle's plane
    v = [axs(2)*vec(3)-axs(3)*vec(2)
         axs(3)*vec(1)-axs(1)*vec(3)
         axs(1)*vec(2)-axs(2)*vec(1)]./sqrt(aa);

    % the output vector   
    vec = c + u*cos(theta) + v*sin(theta);        

end