Rotating and working on MATLAB 3D objects - matlab

How do you rotate 3D objects around each of the three axes when coordinates are of MATLAB style (X, Y and Z kept in different arrays).
This code is a start. I think I have found the rotation matrix (for pi/2 rotation around x), here it is called rotX90_2. But how should rotX90_2 operate on X, Y, Z?
[X,Y,Z] = cylinder;
% rotX90_1 = makehgtform('xrotate',pi/2) gives
rotX90_1 = ...
[1 0 0 0;
0 0 -1 0;
0 1 0 0;
0 0 0 1];
rotX90_2 = rotX90_1(1:3, 1:3);
% Here rotX90_2 should operate on [X,Y,Z] in order to ...
% rotate it 90 degrees around x, but how is this done?
% == What code should be put here to rotate the cylinder? ==
surf(X,Y,Z);
I have just started using MATLAB. As I understand, the basic ways to manipulate 3D graphics are to either operate on the X, Y, Z, like here or, you can first run graphics routines like h = surf(X, Y, Z); and then operate on graphics objects, using f.ex. hgtransform.
It is convenient to translate and scale using X, Y, Z. - You just add and multiply by scalars. But I ask this question to understand how to rotate.
If you operate on the graphic objects, on the other hand, you can use the function hgtransform. But you must then first create other objects, since hgtransform does not operate directly on the graphic objects, as I understand. (Except functions like rotatex(h, angle). F.ex, I have not found a corresponding "translatex(h, distance)". That surprised me. Maybe I didn't look well enough.)
OK I am new to this. Any simple, practical pointers how to easily rotate, scale and translate MATLAB 3D coordinates/objects (around the coordinate system axes) would be appreciated.
Edit:
According to Prakhar's answer below, which works, the code needed to fill the gap above is the following. Thank you, Prakhar.
[row, col] = size(X);
coordinates = [reshape(X, [row*col, 1]), reshape(Y, [row*col, 1]), reshape(Z, [row*col, 1])];
rC = coordinates * rotX90_2;
X = reshape(rC(:, 1), [row, col]);
Y = reshape(rC(:, 2), [row, col]);
Z = reshape(rC(:, 3), [row, col]);

Let's say R is the appropriate 3x3 rotation matrix.
coordinates = [X Y Z];
rotatedCoordinates = coordinates * R;
(Assuming X, Y, and Z are column vectors of same size)
Now, you can get the new X, Y, and Z coordinates from rotatedCoordinates as rotatedCoordinates(:, 1), rotatedCoordinates(:, 2), and rotatedCoordinates(:, 3), respectively.
EDIT: Another alternative when X, Y, Z are 2D matrices:
[X, Y, Z] = cylinder;
[row, col] = size(X);
coordinates = [reshape(X, [row*col, 1]), reshape(Y, [row*col, 1]), reshape(Z, [row*col, 1])];
rC = coordinates*R;
Xn = reshape(rC(:, 1), [row, col]);
Yn = reshape(rC(:, 2), [row, col]);
Zn = reshape(rC(:, 3), [row, col]);

Related

Volumetric 3D data plotting from 2D map in MATLAB?

I have a heat map
and want to convert this 2D matrix to a 3D volume/shape/surface data points for further processing. Not simply display it in 3D using surf.
What would be a good way to do this?
With a lot of help from this community I could come closer:
I shrunk the size to 45x45 px for simplicity.
I = (imread("TESTGREYPLASTIC.bmp"))./2+125;
Iinv = 255-(imread("TESTGREYPLASTIC.bmp"))./2-80;%
for i = 1:45
for j = 1:45
A(i, j, I(i,j) ) = 1;
A(i, j, Iinv(i,j) ) = 1;
end
end
volshow(A)
Its not ideal but the matrix is what I wanted now. Maybe the loop can be improved to run faster when dealing with 1200x1200 points.
How do I create a real closed surface now?
Following your conversation with #BoilermakerRV, I guess you are looking for one of the following two results:
A list of 3d points, where x and y are index of pixels in the image, and z is value of corresponding pixels. The result will be an m*n by 3 matrix.
An m by n by 256 volume of zeros and ones, that for (i,j)-th pixel in the image, all voxels of the (i, j)-the pile of the volume are 0, except the one at I(i, j).
Take a look at the following example that generates both results:
close all; clc; clear variables;
I = rgb2gray(imread('data2.png'));
imshow(I), title('Data as image')
% generating mesh grid
[m, n] = size(I);
[X, Y] = meshgrid(1:n, 1:m);
% converting image to list of 3-d points
P = [Y(:), X(:), I(:)];
figure
scatter3(P(:, 1), P(:, 2), P(:, 3), 3, P(:, 3), '.')
colormap jet
title('Same data as a list of points in R^3')
% converting image to 256 layers of voxels
ind = sub2ind([m n 256], Y(:), X(:), I(:));
V = zeros(m, n, 256);
V(ind) = 1.0;
figure
h = slice(V, [250], [250], [71]) ;
[h.EdgeColor] = deal('none');
colormap winter
camlight
title('And finally, as a matrix of 0/1 voxels')
The contour plot that is shown can't be generated with "2D" data. It requires three inputs as follows:
[XGrid,YGrid] = meshgrid(-4:.1:4,-4:.1:4);
C = peaks(XGrid,YGrid);
contourf(XGrid,YGrid,C,'LevelStep',0.1,'LineStyle','none')
colormap('gray')
axis equal
Where XGrid, YGrid and C are all NxN matrices defining the X values, Y values and Z values for every point, respectively.
If you want this to be "3D", simply use surf:
surf(XGrid,YGrid,C)

Polygon Rotation using matrix-vector multiplication

Consider the polygon with vertices (0, 0), (1, 0), (7/10, 1), (1/2, 1/2), and (3/10, 1). Make a plot of this polygon in Matlab using the fill function. Rotate this polygon by an angle of 100 degrees using matrix-vector multiplication in Matlab with an appropriately chosen rotation matrix R. Make another plot of the rotated polygon using fill.
% Makes original polygon
X = [0 1 7/10 1/2 3/10];
Y = [0 0 1 1/2 1];
norm_poly = fill(X,Y,'k');
thetad = 100;
R = [cosd(thetad) -sind(thetad); sind(thetad) cosd(thetad)];
C = repmat([0 0], 5, 1)';
axis([-10 10 -10 10])
V = get(norm_poly,'Vertices')'; % get the current set of vertices
V = R*(V - C) + C; % do the rotation relative to the centre of the
square
set(norm_poly,'Vertices',V'); % update the vertices
How would I make to different plots to show them? Does the code to rotate make sense and answer all the requirements?
The rotation itself makes sense. To plot multiple things to the same graph use hold on after making the first plot. Alternatively you can make a new figure and plot a new fill there.
P = [0, 1, 7/10, 1/2, 3/10;
0, 0, 1, 1/2, 1];
fill(P(1,:), P(2,:), 'k');
theta = 100;
R = #(t) [cosd(t) -sind(t); sind(t) cosd(t)];
axis([-3 3 -3 3])
grid on
V = R(theta)*P;
hold on;
fill(V(1,:), V(2,:), 'k');

How to make a matrix for a particular surface plot?

I have a time column vector
t =
[0;
1;
3;
7;
10]
a frequency column vector
f =
[978;
573;
269;
102;
14]
and an impedance column vector
Z =
[0;
10;
64;
97;
103]
The kind of surface plot I'm looking for would be so that if you rotate the figure to display Z vs. t, you would see something equivalent to
plot(t, Z)
and if you rotate the figure to display Z vs. f, you would see something equivalent to
semilogx(f, Z)
x axis time, y axis frequency, and z axis impedance.
Here's what I have so far:
Zimp1 = flipud(toeplitz(flip(Z)));
figure(1);
surf(t, f, Zimp1);
set(gca, 'Yscale', 'log');
The problem lies within the Zimp1 matrix--the matrix that shows what's going on in the surface plot with the values of frequency and time. How do I construct the Zimp1 matrix to give me exactly that: plot(t, Z) on the view([-90, 0]) side and semilogx(f, Z) on the view([0, 0]) side?

Generate coordinate points with circle pattern points in Octave

I want to generate 300 samples of both types, red and blue, of coordinate points with these patterns.
Using rand() for x and then calculate y using Pythagorean theorem doesn't help because for the same x, we can have different y.
As suggested by Luis Mendo, you can use the typical rand function of matlab to generate random points in polar coordinates as follows:
figure
hold on
red = sampleCircle([1.4 1.6], 300);
plot(red(:, 1), red(:, 2), 'r*');
blue = sampleCircle([0 0.5], 300);
plot(blue(:, 1), blue(:, 2), 'b*');
function X = sampleCircle(rangeR, n)
r = rand(n, 1) * diff(rangeR) + rangeR(1);
theta = rand(n, 1) * 2*pi;
X = r .* [cos(theta) sin(theta)];
end

Isosurface from X,Y,Z,V ordered data in Matlab/Octave

I have a set of 3d data points for each of which the value V of a certain quantity is associated. The data are organized in ordered columns X Y Z V, where the spatial coordinates are distributed on a grid. I have 21 points in every direction, so that the lengths of the several columns is 21x21x21=9261. How can I transform the data into a meshgrid that can be used by isosurface in Octave/Matlab? My code is the following
a=load("data.txt");
X=reshape(a(:,1), 21,21,21);
Y=reshape(a(:,2), 21,21,21);
Z=reshape(a(:,3), 21,21,21);
V=reshape(a(:,2), 21,21,21);
fv=isosurface (X,Y,Z,V,0.9);
patch(fv)
But the result is not meaningful (i get two flat surfaces located at x=0.9 and 1.). The data can be downloaded here.
Here's a way to create a proper meshgrid of your data:
a = load('data.txt');
[X, Y, Z] = meshgrid(unique(a(:, 1)), unique(a(:, 2)), unique(a(:, 3)));
V = zeros(21, 21, 21);
for i = 1:numel(V)
idx = find(a(:, 1) == X(i) & a(:, 2) == Y(i) & a(:, 3) == Z(i));
V(i) = a(idx, 4);
end
fv = isosurface (X,Y,Z,V,0.9);
p = patch(fv);
set(p,'FaceColor','red','EdgeColor','none');
camlight;
lighting gouraud;
xlabel('x');
ylabel('y');
zlabel('z');
If you have much larger data and this is too slow, I can probably come up with a way of reshaping the original data to avoid the for-loop I've used above.