# How to rotate 3D cube so that one face is normal to a given axis

7 views (last 30 days)
Robert Malkin on 20 Oct 2020
Commented: Robert Malkin on 21 Oct 2020
Hello everyone,
I am havign a bit of a headache and am a little stuck. I would really appreciate any help.
I have some 3D points (in a cube shape shown as blue in the figure). I would like to rotate these points such that one face of the cube is normal to a line passing through the origin of the system (red dot in the figure) and the centre of the cube. I have tried a few things but it doesn't quite work and some edge cases throw the system way off.
In the example below it looks about right but when you rotate the right hand figure you can see that the cube is not quite normal to the line.
Hopefully the script below will help explain what I mean.
%% Variables for Matlab question
% Centre of system
coords_source_xyz= [-0.5,-0.5,0; -0.5,0,0;-0.5,0.5,0;0,-0.5,0;0,0,0;0,0.5,0;0.5,-0.5,0;0.5,0,0;0.5,0.5,0];
array_centre =[0,0,0];
% Grid of unrotated cloud points.
sample_grid_coords_x = linspace(2,3,10);
sample_grid_coords_y = linspace(-2,-3,10);
sample_grid_coords_z = linspace(2,3,10);
% Centre of un-rotated point cloud
centre_grid_x = mean(sample_grid_coords_x);
centre_grid_y = mean(sample_grid_coords_y);
centre_grid_z = mean(sample_grid_coords_z);
% NDGRID OF points
[sample_X, sample_Y, sample_Z] = ndgrid(sample_grid_coords_x, sample_grid_coords_y, sample_grid_coords_z);
% Distance from centre of array to centre of unrotated point cloud.
array_to_grid_x = centre_grid_x-array_centre(1);
array_to_grid_y = centre_grid_y-array_centre(2);
array_to_grid_z = centre_grid_z-array_centre(3);
array_to_grid_xy = sqrt((array_to_grid_x^2) + (array_to_grid_y^2));
% Move the point cloud back to the origin temporarily.
sample_X = sample_X - array_to_grid_x;
sample_Y = sample_Y - array_to_grid_y;
sample_Z = sample_Z - array_to_grid_z;
% Step 1 - Calculate rotation angle about Y.
rot_y_axis_deg = atand(array_to_grid_z / array_to_grid_x);
rot_y_axis_deg(isnan(rot_y_axis_deg))=0;
% Rotate points about Y axis first.
X_rot_1 = sample_X*cosd(rot_y_axis_deg) + sample_Z*sind(rot_y_axis_deg);
Y_rot_1 = sample_Y;
Z_rot_1 = sample_Z*cosd(rot_y_axis_deg) - sample_X*sind(rot_y_axis_deg);
% Step 2 - Calculate rotation angle about Z.
rot_z_axis_deg = atand(array_to_grid_x / array_to_grid_y);
rot_z_axis_deg(isnan(rot_z_axis_deg))=0;
% Rotate points about Z axis second.
X_rot_2 = X_rot_1*cosd(rot_z_axis_deg) - Y_rot_1*sind(rot_z_axis_deg);
Y_rot_2 = X_rot_1*sind(rot_z_axis_deg) + Y_rot_1*cosd(rot_z_axis_deg);
Z_rot_2 = Z_rot_1;
% Move them back to their original centre
X_rot_2 = X_rot_2+centre_grid_x;
Y_rot_2 = Y_rot_2+centre_grid_y;
Z_rot_2 = Z_rot_2+centre_grid_z;
figure(1)
subplot(1,2,1);
% Location of array points
scatter3(coords_source_xyz(:,1), coords_source_xyz(:,2), coords_source_xyz(:,3),'k', 'filled'); daspect([1 1 1]); title('Un-Rotated');
hold on
% Location of centre of array
scatter3(array_centre(1), array_centre(2), array_centre(3),'r', 'filled'); daspect([1 1 1]);
% Location of sample grid points - unrotated
scatter3(sample_X(:)+array_to_grid_x, sample_Y(:)+array_to_grid_y, sample_Z(:)+array_to_grid_z, 'b');
% Line connecting centre of array and centre of grid
x = [array_centre(1), centre_grid_x];y = [array_centre(2), centre_grid_y]; z=[array_centre(3), centre_grid_z]
plot3(x*1.5, y*1.5, z*1.5); hold off
subplot(1,2,2);
% Location of array points
scatter3(coords_source_xyz(:,1), coords_source_xyz(:,2), coords_source_xyz(:,3),'k', 'filled'); daspect([1 1 1]); title('Rotated');
hold on
% Location of centre of array
scatter3(array_centre(1), array_centre(2), array_centre(3),'r', 'filled'); daspect([1 1 1]);
% Location of sample grid points - rotated
scatter3(X_rot_2(:), Y_rot_2(:), Z_rot_2(:), 'b');
% Line connecting centre of array and centre of grid
x = [array_centre(1), centre_grid_x];y = [array_centre(2), centre_grid_y]; z=[array_centre(3), centre_grid_z]
plot3(x*1.5, y*1.5, z*1.5); hold off

Matt J on 21 Oct 2020
Once the cube is aligned as you described, 2 of the 6 sides will be perpendicular to the oblique line, which I will denote L, and the other 4 sides will be parallel to L. Beyond that, though, you have made no requirement on which direction the other 4 sides will face. We know they will face in a direction perpendicular to L, but nothing more. I can rotate the cube by any angle theta about L and still satisfy all the requirements you have listed.
Robert Malkin on 21 Oct 2020
That is a very good point. Well spotted.
Do you have any suggestions on how to constrain the system better than I have tried to?
I'm going to need to think a little deeper I think. Unless I'm just missing the obvious.
Robert Malkin on 21 Oct 2020
PS, In my code I believe that by rotating twice about 2 axis I add a constraint to the system.

Matt J on 21 Oct 2020
Edited: Matt J on 21 Oct 2020
If I can choose the missing rotation angle freely, then here is one solution.
% Centre of system
coords_source_xyz= [-0.5,-0.5,0; -0.5,0,0;-0.5,0.5,0;0,-0.5,0;0,0,0;0,0.5,0;0.5,-0.5,0;0.5,0,0;0.5,0.5,0];
array_centre =[0,0,0];
% Grid of unrotated cloud points.
sample_grid_coords_x = linspace(2,3,10);
sample_grid_coords_y = linspace(-2,-3,10);
sample_grid_coords_z = linspace(2,3,10);
[sample_X, sample_Y, sample_Z] = ndgrid(sample_grid_coords_x, sample_grid_coords_y, sample_grid_coords_z);
Cube=[sample_X(:), sample_Y(:), sample_Z(:)];
% Perform rotation
cube_centre=mean(Cube,1); %Cube center
ray=cube_centre-array_centre;
R=normalize([ray(:),null(ray)],1,'norm'); %rotation matrix
C = num2cell( (Cube-cube_centre)*R.'+ cube_centre, 1);
[X_rot, Y_rot, Z_rot] = deal(C{:});
%%%% Plot %%%%
figure(1)
% Location of array points
scatter3(coords_source_xyz(:,1), coords_source_xyz(:,2), coords_source_xyz(:,3),'k', 'filled'); daspect([1 1 1]); title('Rotated');
hold on
% Location of centre of array
scatter3(array_centre(1), array_centre(2), array_centre(3),'r', 'filled'); daspect([1 1 1]);
% Location of sample grid points - rotated
scatter3(X_rot, Y_rot, Z_rot, 'b');
% Line connecting centre of array and centre of grid
x = [array_centre(1), cube_centre(1)];
y = [array_centre(2), cube_centre(2)];
z= [array_centre(3), cube_centre(3)];
plot3(x*1.5, y*1.5, z*1.5);
hold off

Show 1 older comment
Matt J on 21 Oct 2020
You're quite welcome. You might also find this Fle Exchange submission useful,
if you decide you need to correct the rotation of the cube about the oblique axis, L.
Robert Malkin on 21 Oct 2020
Looks like it might be just up my street.
I have added a small tag-on question to the above that might make immediate sense to you.
Robert Malkin on 21 Oct 2020
If anyone sees this...
The answer is to use local coordinate system for your rotated object. Not the global coordinate system.