matlab Rendering a torus from vertices - matlab

I have an assignment to render a torus. This is my first time with matlab and i've managed to struggle along and get 2/3 parts done with some horrible kludged code.
The first step of the assignment is to render a circle as a set of 20 points. For which i produced:
circle (IMG)
Then the next step is to rotate and translate that circle and draw it 20 times to represent a torus shape, so i got this:
torus points (IMG)
The next step is to render a 3d representation of this torus from the list of vertices.
What I have is massive a list of a vertices in a 400x3 matrix like so:
7.66478245119846 -1.84059939326890 0.292371704722737
7.53434247103331 -1.79821687453702 0.573576436351046
7.32764268084884 -1.73105604149887 0.798635510047293
7.06491629627043 -1.64569106442929 0.945518575599317
6.77188080634298 -1.55047806205660 0.999847695156391
6.47722056651889 -1.45473714644104 0.956304755963036
... ... ...
where each subsequent 20 rows is another circle.
The assignment recommends that I use the surf function in order to render this but I can't figure out how. All the examples I've seen use surf to represent 2 dimensional planes that get distorted by a height value. Which doesn't seem appropriate for rendering this kind of 3 dimensional shape at all.
The approach I'm trying is to build a list of faces and then using the patch function to render the circle. Where the first 2 points of each circle makes a square with the corresponding 2 points of the next circle, then rendering.
Using something like this:
for i=1:400
face = [(i) (i+1) (i+21) (i+20)];
patch('Faces',face,'Vertices',torus_vertices,'FaceColor','r'); %Should do this at the end
end
For which i get something like this:
3d Torus (IMG)
It twists and and some of the side and inner side faces are messed up. I think it might have something to do with the ordering of the vertices flipping around at some point.
What would be the best way to approach this problem? If at all possible i want to do it with the surf function.
Ex1.m
%Initial positions
position = [2 0 0];
normal = [0 1 0];
%Rotation matrix
rotate18 = [cos(todeg(18)) -sin(todeg(18)) 0;
sin(todeg(18)) cos(todeg(18)) 0;
0 0 1];
% translate along the x axis by 5
translate = [5 0 0];
%% iterate 20 times to get a list of all the vertices
taurus_vertices = zeros(0, 3);
for i=0:20
%rotate translation by 18 degrees
translate = translate * rotate18;
%translate
position = position + translate;
%rotate the normal so it faces the right direction
normal = normal * rotate18;
%Get vertices for the circle and append to vertices list
circle_vertices = circle_3D(1, position, normal);
taurus_vertices = cat(1, taurus_vertices, circle_vertices);
%translate back to original position
position = position - translate;
end
%scatter3(taurus_vertices(1:end, 1), taurus_vertices(1:end, 2), taurus_vertices(1:end, 3));
%% Render each face
for i=1:400
face = [(i) (i+1) (i+21) (i+20)];
patch('Faces',face,'Vertices',taurus_vertices,'FaceColor','r');
end
circle.m
function h_circle=circle_3D(r, M, n)
%% Prepare input parameters
if size(n,2)>size(n,1)
n=n';
end
if size(M,2)>size(M,1)
M=M';
end
%% Define unit vectors u and v
% u and v define a new coordinate system in a plane perpendicular to n
a=[1;0;0];
b=[0;1;0];
if isempty(find(cross(a,n), 1))==1
a=[0;0;1];
elseif isempty(find(cross(b,n), 1))==1
b=[0;0;1];
end
alpha=dot(n,a)/dot(n,n);
u=a-alpha*n;
v=cross(u,n);%b-beta*n-gamma*u;
u=u/sqrt(sum(u.*u));
v=v/sqrt(sum(v.*v));
%% Plot the circle
hold on
axis equal
degs = 0;
points = 0;
verts = zeros(20, 3);
for phi=0: pi()/180 : 2*pi()
degs=degs+1;
if (mod(degs,18) == 0 )
points = points + 1;
verts(points,1)=M(1,1)+r*cos(phi)*u(1,1)+r*sin(phi)*v(1,1);
verts(points,2)=M(2,1)+r*cos(phi)*u(2,1)+r*sin(phi)*v(2,1);
verts(points,3)=M(3,1)+r*cos(phi)*u(3,1)+r*sin(phi)*v(3,1);
end
end
h_circle= verts;

Your problem is perfect for trisurf- given a set of points, you need to build a triplet to connect a mesh to.
For your problem, you can use:
%inner circle points and radius
N1=20;
r1=1;
%outer circle points and radius
N2=30;
r2=5;
%inner cicle angles
thC=linspace(0,2*pi*(1-1/N1),N1)';
%inner cicle points
xyzC=[r1*sin(thC), zeros(N1,1),r1*cos(thC)]';
%torus points
xyzT = zeros(3,N1*N2);
for i=1:N2
%circle transformation
thT = 2*pi*i/N2;
T = [1 0 0 r2*cos(thT); 0 1 0 r2*sin(thT);0 0 1 0]*[cos(thT) -sin(thT) 0 0;sin(thT) cos(thT) 0 0 ; 0 0 1 0; 0 0 0 1];
%add points
xyzT(:,(i-1)*N1+1:i*N1)=T*[xyzC ;ones(1,N1)];
end
%build patch triples
tri=[];
for i=1:N2
for j=1:N1
%get three points:
% jth from ith circle
% j+1th from ith circle
% jth from i+1th circle
tri(end+1,:)=[(i-1)*N1+j (i-1)*N1+j+1 i*N1+j];
%get three points:
% j+1th from ith circle
% j+1th from i+1th circle
% jth from i+1th circle
tri(end+1,:)=[ i*N1+j (i-1)*N1+j+1 i*N1+j+1];
end
end
tri=mod(tri-1,N1*N2)+1;
trisurf(tri,xyzT(1,:),xyzT(2,:),xyzT(3,:));axis equal
%fancy
shading interp
camlight left
and get:

Thank you for the example. It would of been nicer to render the torus using triangle instead of rectangles but deadline is like an hour away and I managed to get my current one working!
I figured out what was causing my issues. Rotating the translation matrix was having an adverse effect on the orientation of each circles points (they don't line up), causing it to twist.
After looking through the notes more thoroughly (that is going several years worth of online notes back), I managed to find some psuedo code for a sweep function that I used to completely rewrite. I now generate 20 circles in the same spot, and rotate each one around the origin by increasing amounts instead. Which led to this:
%% Settings
points = 20; %Number of points in each circle
circles = 20; %Number of circles making up the torus
radius = 1; %Radius of the circle
scale = 0.75; %Scale to apply to the whole torus
center = [2 0 0]; %Center point of the first circle to sweep into a torus
%% Create (circles+1) circles after the other in an array at point [2 0 0]
%The extra circle overlaps the first, this is to make face generation much
%simpler.
V = zeros(circles*points, 3);
for i=0:points:points*circles
for k=1:points
V(i+k,1) = center(1) + radius * cosd((k-1)*(360/points));
V(i+k,2) = center(2) + 0;
V(i+k,3) = center(3) + radius * sind((k-1)*(360/points));
end
end
%% Sweep the circles, rotate each circle 18 degrees more than the previous
for n=0:points:circles*points
%Calculate degrees for current circle
D = (n/points) * 360/circles;
%Create a Z-rotation matrix
Rz = [
cosd(D) sind(D) 0;
-sind(D) cosd(D) 0;
0 0 1;
];
%Rotate each point of the circle
for i=1:points
V(n+i, :) = Rz * V(n+i, :)';
end
end
%% Scale the torus
%Create a scalar matrix
S = [
scale 0 0;
0 scale 0;
0 0 scale
];
%Scale each point
for n=0:points:circles*points
for i=1:points
V(n+i, :) = S * V(n+i, :)';
end
end
%% Generate faces
F = zeros(circles*points, 4);
for n=1:points:circles*points
for k=1:points
%If it's an endface then have destination face vertices wrap around to the first face of the current circle
if(mod(k, points) == 0)
F((n-1)+k,2)= (n-1)+k+1 - points;
F((n-1)+k,3)= n+points+k - points;
else
%otherwise use the next faces starting vertices
F((n-1)+k,2)= (n-1)+k+1;
F((n-1)+k,3)= n+points+k;
end
%Set the points coming from the previous face
F((n-1)+k,1)= (n-1)+k;
F((n-1)+k,4)= n+points+k-1;
end
end
%% Render
%Configure renderer
axis equal;
hold on;
%Render points
scatter3(V(1:end, 1), V(1:end, 2), V(1:end, 3), 'MarkerEdgeColor', 'b');
%Render faces
patch('Faces', F, 'Vertices', V, 'FaceColor', 'g');
Which makes:

Related

How to convert the estimated radius of a circle to its actual range?

I generate a random number between 1 and 2 as a radius for my circle. Then I plot my circle and saves it as a png. Also, several data points are generated both inside and outside the circle to make it noisy.
Then I will use Hough Transform to estimate the radius for the circle but, the number which it returns is more than 100. Although they are the same circles(I plotted to make sure).
I have tried to use polyfit function to map these two numbers but, the estimated radius seems to be smaller than the real one in some examples. After mapping It returns the same number. For example, a random radius is 1.2 and Hough estimates it 110 however it seems that it should be near 1.(Because I plot it and it is clear that is it smaller). Also, for Hough Transform I am using this code https://www.mathworks.com/matlabcentral/fileexchange/35223-circle-detection-using-hough-transforms
I tried the predefined Matlab function (imfindcircles) but it returns null for all my circles.
r1 = 1 + abs((1)*randn(1,1)); %radius of inner circle
r1 = abs(r1);
r2 = (1.2)*r1; %radius of outercircle
k = 500; %number of random numbers
% circle coordinate points
t = 0:2*pi/59:2*pi;
xv = cos(t)';
yv = sin(t)';
%%%%%%%%random points
xq = 2*randn(k,1)-1;
yq = 2*randn(k,1)-1; %distribution 1
xq1 = 2*(rand(k,1))-1; %distribution 2
yq1 = 2*(rand(k,1))-1;
in = inpolygon(xq1,yq1,r1*xv,r1*yv); %number of points inside the geometry
in1= inpolygon(xq,yq,r2*xv,r2*yv); %points inside outer circle
in2 = xor(in,in1); %points between circle
in3= inpolygon(xq1,yq1,r2*xv,r2*yv); %points inside outer circle
fig = figure(22);hold on;
% Random points
plot(xq(in2),yq(in2),'bo','MarkerFaceColor','r','MarkerSize',5,'Marker','o','MarkerEdgeColor','none');
axis equal;
plot(xq1(~in2),yq1(~in2),'bo','MarkerFaceColor','r','MarkerSize',5,'Marker','o','MarkerEdgeColor','none');
axis equal;
img= getframe(fig);
figure;
imshow(img.cdata)
hold on;
[r,c,rad] = circlefinder(img.cdata);
[m I] = max(rad);
%ploting the bigest estimated circle
angle = 2*pi*randn(k,1);
haffpointX = rad(I).*cos(angle)+ c(I);
haffpointY = rad(I).*sin(angle)+ r(I);
scatter(haffpointX,haffpointY,'g');
axis equal
I expect that for different random circles with noisy data Hough Transform estimates its circle with the number between the range of 1 and 2 so, I can use its results.
Thank you in advance

Find contour/edge in pcolor in Matlab

I'm trying to make a contour that follows the edges of the 'pixels' in a pcolor plot in Matlab. This is probably best explained in pictures. Here is a plot of my data. There is a distinct boundary between the yellow data (data==1) and the blue data (data==0):
Note that this is a pcolor plot so each 'square' is essentially a pixel. I want to return a contour that follows the faces of the yellow data pixels, not just the edge of the yellow data.
So the output contour (green line) passes through the mid-points of the face (red dots) of the pixels.
Note that I don't want the contour to follow the centre points of the data (black dots), which would do something like this green line. This could be achieved easily with contour.
Also, if it's any help, I have a few grids which may be useful. I have the points in the middle of the pixels (obviously, as that's what I've plotted here), I also have the points on the corners, AND I have the points on the west/east faces and the north/south faces. IF you're familiar with Arakawa grids, this is an Arakawa-C grid, so I have the rho-, u-, v- and psi- points.
I've tried interpolation, interweaving grids, and a few other things but I'm not having any luck. Any help would be HUGELY appreciated and would stop me going crazy.
Cheers, Dave
EDIT:
Sorry, I simplified the images to make what I was trying to explain more obvious, but here is a larger (zoomed out) image of the region I'm trying to separate:
As you can see, it's a complex outline which heads in a "southwest" direction before wrapping around and moving back "northeast". And here is the red line that I'd like to draw, through the black points:
You can solve this with a couple of modifications to a solution I posted to a related question. I used a section of the sample image mask in the question for data. First, you will need to fill the holes in the mask, which you can do using imfill from the the Image Processing Toolbox:
x = 1:15; % X coordinates for pixels
y = 1:17; % Y coordinates for pixels
mask = imfill(data, 'holes');
Next, apply the method from my other answer to compute an ordered set of outline coordinates (positioned on the pixel corners):
% Create raw triangulation data:
[cx, cy] = meshgrid(x, y);
xTri = bsxfun(#plus, [0; 1; 1; 0], cx(mask).');
yTri = bsxfun(#plus, [0; 0; 1; 1], cy(mask).');
V = [xTri(:) yTri(:)];
F = reshape(bsxfun(#plus, [1; 2; 3; 1; 3; 4], 0:4:(4*nnz(mask)-4)), 3, []).';
% Trim triangulation data:
[V, ~, Vindex] = unique(V, 'rows');
V = V-0.5;
F = Vindex(F);
% Create triangulation and find free edge coordinates:
TR = triangulation(F, V);
freeEdges = freeBoundary(TR).';
xOutline = V(freeEdges(1, [1:end 1]), 1); % Ordered edge x coordinates
yOutline = V(freeEdges(1, [1:end 1]), 2); % Ordered edge y coordinates
Finally, you can get the desired coordinates at the centers of the pixel edges like so:
ex = xOutline(1:(end-1))+diff(xOutline)./2;
ey = yOutline(1:(end-1))+diff(yOutline)./2;
And here's a plot showing the results:
imagesc(x, y, data);
axis equal
set(gca, 'XLim', [0.5 0.5+size(mask, 2)], 'YLim', [0.5 0.5+size(mask, 1)]);
hold on;
plot(ex([1:end 1]), ey([1:end 1]), 'r', 'LineWidth', 2);
plot(ex, ey, 'k.', 'LineWidth', 2);
Take a look at the following code:
% plotting some data:
data = [0 0 0 0 0 0 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1];
p = pcolor(data);
axis ij
% compute the contour
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
y = (1:size(data,1));
% compute the edges shift
Y = get(gca,'YTick');
y_shift = (Y(2)-Y(1))/2;
% plot it:
hold on
plot(x,y+y_shift,'g','LineWidth',3,'Marker','o',...
'MarkerFaceColor','r','MarkerEdgeColor','none')
It produces this:
Is this what you look for?
The most important lines above is:
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
which finds the place of shifting between 0 to 1 for every row (assuming there is only one in a row).
Then, within the plot I shift y by half of the distance between two adjacent y-axis tick, so they will be placed at the center of the edge.
EDIT:
After some trials with this kind of data, I have got this result:
imagesc(data);
axis ij
b = bwboundaries(data.','noholes');
x = b{1}(:,1);
y = b{1}(:,2);
X = reshape(bsxfun(#plus,x,[0 -0.5 0.5]),[],1);
Y = reshape(bsxfun(#plus,y,[0 0.5 -0.5]),[],1);
k = boundary(X,Y,1);
hold on
plot(X(k),Y(k),'g','LineWidth',3,'Marker','o',...
'MarkerFaceColor','r','MarkerEdgeColor','none')
It's not perfect, but may get you closer to what you want in a more simple approach:
OK, I think I've solved it... well close enough to be happy.
First I take the original data (which I call mask_rho and use this to make masks mask_u, mask_v, which is similar to mask_rho but is shifted slightly in the horizontal and vertical directions, respectively.
%make mask_u and mask_v
for i = 2:size(mask_rho,2)
for j = 1:size(mask_rho,1)
mask_u(j, i-1) = mask_rho(j, i) * mask_rho(j, i-1);
end
end
for i = 1:size(mask_rho,2)
for j = 2:size(mask_rho,1)
mask_v(j-1, i) = mask_rho(j, i) * mask_rho(j-1, i);
end
end
I then make modified masks mask_u1 and mask_v1 which are the same as mask_rho but averaged with the neighbouring points in the horizontal and vertical directions, respectively.
%make mask which is shifted E/W (u) and N/S (v)
mask_u1 = (mask_rho(1:end-1,:)+mask_rho(2:end,:))/2;
mask_v1 = (mask_rho(:,1:end-1)+mask_rho(:,2:end))/2;
Then I use the difference between the masks to locate places where the masks change from 0 to 1 and 1 to 0 in the horizontal direction (in the u mask) and in the vertical direction (in the v mask).
% mask_u-mask_u1 gives the NEXT row with a change from 0-1.
diff_mask_u=logical(mask_u-mask_u1);
lon_u_bnds=lon_u.*double(diff_mask_u);
lon_u_bnds(lon_u_bnds==0)=NaN;
lat_u_bnds=lat_u.*double(diff_mask_u);
lat_u_bnds(lat_u_bnds==0)=NaN;
lon_u_bnds(isnan(lon_u_bnds))=[];
lat_u_bnds(isnan(lat_u_bnds))=[];
%now same for changes in mask_v
diff_mask_v=logical(mask_v-mask_v1);
lon_v_bnds=lon_v.*double(diff_mask_v);
lon_v_bnds(lon_v_bnds==0)=NaN;
lat_v_bnds=lat_v.*double(diff_mask_v);
lat_v_bnds(lat_v_bnds==0)=NaN;
lon_v_bnds(isnan(lon_v_bnds))=[];
lat_v_bnds(isnan(lat_v_bnds))=[];
bnd_coords_cat = [lon_u_bnds,lon_v_bnds;lat_u_bnds,lat_v_bnds]'; %make into 2 cols, many rows
And the result grabs all the coordinates at the edges of the boundary:
Now my answer goes a bit awry. If I plot the above vector as points plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'kx' I get the above image, which is fine. However, if I join the line, as in: plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'-' then the line jumps around, as the points aren't sorted. When I do the sort (using sort and pdist2) to sort by closest points, Matlab sometimes chooses odd points... nevertheless I figured I'd include this code as an appendix, and optional extra. Someone may know a better way to sort the output vectorbnds_coords_cat:
% now attempt to sort
[~,I]=sort([lon_u_bnds,lon_v_bnds]);
bnd_coords_inc1 = bnd_coords_cat(I,1);
bnd_coords_inc2 = bnd_coords_cat(I,2);
bnd_coords = [bnd_coords_inc1,bnd_coords_inc2];
bnd_coords_dist = pdist2(bnd_coords,bnd_coords);
bnd_coords_sort = nan(1,size(bnd_coords,1));
bnd_coords_sort(1)=1;
for ii=2:size(bnd_coords,1)
bnd_coords_dist(:,bnd_coords_sort(ii-1)) = Inf; %don't go backwards?
[~,closest_idx] = min(bnd_coords_dist(bnd_coords_sort(ii-1),:));
bnd_coords_sort(ii)=closest_idx;
end
bnd_coords_final(:,1)=bnd_coords(bnd_coords_sort,1);
bnd_coords_final(:,2)=bnd_coords(bnd_coords_sort,2);
Note that the pdist2 method was suggested by a colleague and also from this SO answer, Sort coordinates points in matlab. This is the final result:
To be honest, plotting without the line is fine. So as far as I'm concerned this is close enough to be answered!

Translating 2D image with RGB colours along axes

I am trying to create my own voronoi diagram. I have an arbitrary shape defined by their x and y coordinates stored in separate vectors. Inside this shape are some points of interest (with known coordinates) that belong to two different groups and act as seeds to the voronoi diagram. As an example, the whole diagram ranges from x=-10 to x=90 and y=-20 to y=60. The boundary shape is not rectangular but falls within the axes range above.
What I have done so far is to create a 3D matrix (100 X 80 X 3), C, with all ones so that the default colour will be white. I then looped through from i=1:100, j=1:80, testing individual pixels to see if they fall within the shape using inpolygon. If they do, I then find out which point is the pixel closest to and assign it a colour based on whether the closest point belongs to group 1 or 2.
All is good so far. I then used imagesc to display the image with a custom axis range. The problem is that the voronoi diagram has the general shape but it is off in terms of position as the pixel coordinates are different from the actual world coordinates.
I tried to map it using imref2d but I do not know how it really works or how to display the image after using imref2d. Please help me on this.
I am open to other methods too!
Thank you!
Edit:
As requested, let me give a more detailed example and explanation of my problem.
Let us assume a simple diamond shape boundary with the following vectors and 4 points with the following coordinate vectors:
%Boundary vectors
Boundary_X = [-5 40 85 40 -5];
Boundary_Y = [20 50 20 -10 20];
%Point vectors
Group_One_X = [20 30];
Group_One_Y = [10 40];
Group_Two_X = [50 70];
Group_Two_Y = [5 20];
Next I plot all of them, with different groups having different colours.
%Plot boundary and points
hold on
plot(Boundary_X,Boundary_Y)
scatter(Group_One_X,Group_One_Y,10,'MarkerFaceColor','Black',...
'MarkerEdgeColor','Black')
scatter(Group_Two_X,Group_Two_Y,10,'MarkerFaceColor','Red',...
'MarkerEdgeColor','Red')
hold off
axis([-10, 90, -20, 60])
This is the result:
Boundary with points
Next I test the whole graph area pixel by pixel, and colour them either cyan or yellow depending on whether they are closer to group 1 or 2 points.
%Create pixel vector with default white colour
C=ones(100,80,3);
Colour_One = [0 1 1];
Colour_Two = [1 1 0];
%Loop through whole diagram
for i=1:100
for j=1:80
x=i;
y=j
if inpolygon(x,y,Boundary_X,Boundary_Y)
%Code for testing which point is pixel closest to
%If closest to group 1, assign group 1 colour, else group 2
%colour
end
end
end
%Display image
hold on
imagesc(C)
hold off
This is the result
Failed Voronoi Diagram
The shape is somewhat correct for the right side but not for the others. I understand that this because my world coordinates start from negative values but the pixel coordinates start from 1.
Hence I am at a lost as to how can I solve this problem.
Thank you!
One thing to notice is that you have to convert between image and plot coordinates. The plot coordinates are (x,y) where x goes to the right, and y goes up. The matrix coordinates are (i,j) where i goes down, and j to the right. An easy way to do this is with the use of vec_X,vec_Y as shown below.
Another solution for the newer Matlab versions would be - as you said - using imref2d but unfortunately I have no experience with that command.
%Boundary vectors
Boundary_X = [-5 40 85 40 -5];
Boundary_Y = [20 50 20 -10 20];
%Point vectors
Group_One_X = [20 30];
Group_One_Y = [10 40];
Group_Two_X = [50 70];
Group_Two_Y = [5 20];
%Coordinate system
min_X = -10;
max_X = 90;
min_Y = -20;
max_Y = 60;
axis([min_X, max_X, min_Y, max_Y])
%Create pixel vector with default white colour
rows_N = 100;
columns_N = 80;
C=ones(rows_N,columns_N,3);
%These vectors say where each of the pixels is in the plot coordinate
%system
vec_X = linspace(min_X,max_X,columns_N);
vec_Y = linspace(min_Y,max_Y,rows_N);
Colour_One = [0 1 1];
Colour_Two = [1 1 0];
%Loop through whole diagram
for i=1:100
for j=1:80
if inpolygon(vec_X(j),vec_Y(i),Boundary_X,Boundary_Y)
%calculate distance to each point
Distances_One = zeros(size(Group_One_X));
Distances_Two = zeros(size(Group_Two_X));
for k=1:numel(Group_One_X);
Distances_One(k) = norm([Group_One_X(k),Group_One_Y(k)]-[vec_X(j),vec_Y(i)]);%assuming euclidean norm, but can be adjusted to whatever norm you need
end
for k=1:numel(Group_Two_X);
Distances_Two(k) = norm([Group_Two_X(k),Group_Two_Y(k)]-[vec_X(j),vec_Y(i)]);%assuming euclidean norm, but can be adjusted to whatever norm you need
end
if min(Distances_One) < min(Distances_Two);
C(i,j,:) = Colour_One;
else
C(i,j,:) = Colour_Two;
end
end
end
end
%Display image
imagesc(vec_X,vec_Y,C) %lets you draw the image according to vec_X and vec_Y
%Plot boundary and points
hold on
plot(Boundary_X,Boundary_Y)
scatter(Group_One_X,Group_One_Y,10,'MarkerFaceColor','Black',...
'MarkerEdgeColor','Black')
scatter(Group_Two_X,Group_Two_Y,10,'MarkerFaceColor','Red',...
'MarkerEdgeColor','Red')
hold off

Building a hollow cube and filing it with small cubes in MATLAB

I want to build a hollow cube with some dimensions x, y, and z, and fill its volume with many small cubes. The following image is similar to what I want to do,
But, instead of small spheres, I want to use small cubes.
After building the cube and filling it with other small cubes, I want to build a matrix that represents these small cubes inside the big one, this is because I want to be able to access every small cube, where I need to change its color.
Is it possible to have this matrix representing the structure of the small cubes? Let's say every small cube is represented by -1, I mean that all -1s in a row of the matrix are small cubes at the same row, and all -1s in a column are actually small cubes in the same column (neighbors inside the big cube must be neighbors inside the matrix). Since the big cube is a 3D shape, I expect such a matrix to be a 3D matrix with row, column, and depth measures. Depth may represent the layers of different small cubes we have, i.e at depth 1 we have a set of rows and columns representing the small cubes at the first depth. After that I want to iterate over this matrix and change the -1 to some other number that represents some color. How to change the color of some small cube using the corresponding number in the matrix? For example, let the number at index (1,1,1) be 0 and let it represent yellow, how can I change the corresponding cube color to yellow? I'm think about patch function, but how to apply it to the corresponding cube?
How can I fill the cube with small cubes? And, how can I build the matrix described above?
Here is a code for placing a small cube inside the big one, but this places it in the center, I tried to fill the big cube with small cubes in an organized way as in the provided picture, but I couldn't figure out how to do that.
clf;
figure(1);
format compact
h(1) = axes('Position',[0.2 0.2 0.6 0.6]);
%These are the different 8 vertices of the cube, each is defined by its 3 x
%y z coordinates:
vert = [1 1 -1;
-1 1 -1;
-1 1 1;
1 1 1;
-1 -1 1;
1 -1 1;
1 -1 -1;
-1 -1 -1];
%These are the 6 faces of the cube, each is defined by connecting 4 of the
%available vertices:
fac = [1 2 3 4;
4 3 5 6;
6 7 8 5;
1 2 8 7;
6 7 1 4;
2 3 5 8];
% I defined a new cube whose length is 1 and centers at the origin.
vert2 = vert * .05;
fac2 = fac;
patch('Faces',fac,'Vertices',vert,'Facecolor', 'w'); % patch function for the first big cube.
axis([-1, 1, -1, 1, -1, 1]);
axis equal;
hold on;
patch('Faces', fac2, 'Vertices', vert2, 'FaceColor', 'r');
material metal;
alpha('color');
alphamap('rampdown');
view(3);
Can anyone please tell me how to fill the cube and construct the matrix?
Thank You.
Some slight modifications to your code can easily be done to populate the cube with smaller cubes. You already have the code to place one cube in the centre. All you really have to do is randomize the centre of the base small cube, adjust the centre of the cube with this centre and place it in the larger cube. You can also randomize the colour of the cube as well. We can loop for as many times as you want cubes and you can generate random centre positions for each cube as well as random colours and place them onto the final cube.
Do this at the end of your code:
hold on;
rng(123); %// Set seed for reproducibility
num_squares = 1000; %// Set total number of squares
%// For each square...
for idx = 1 : num_squares
%// Take the base cube and add an offset to each coordinate
%// Each coordinate will range from [-1,1]
vert_new = bsxfun(#plus, vert2, 2*rand(1,3)-1);
%// Generate a random colour for each cube
color = rand(1,3);
%// Draw the cube
patch('Faces', fac, 'Vertices', vert_new, 'FaceColor', color);
end
%// Post processing
material metal;
alpha('color');
alphamap('rampdown');
view(3);
We get this image:
Now if you want to build a 3D matrix of these said coordinates, that's quite simple. Simply have a matrix and concatenate these randomly generated coordinates at each iteration:
hold on;
rng(123); %// Set seed for reproducibility
num_squares = 1000; %// Set total number of squares
%// New - to store the coordinates
coords = [];
%// For remembering the colours
colors = [];
%// For each square...
for idx = 1 : num_squares
%// Take the base cube and add an offset to each coordinate
%// Each coordinate will range from [-1,1]
vert_new = bsxfun(#plus, vert2, 2*rand(1,3)-1);
%// New - For the coordinates matrix
coords = cat(3, coords, vert_new);
%// Generate a random colour for each cube
color = rand(1,3);
%// New - Save the colour
colors = cat(1, colors, color);
%// Draw the cube
patch('Faces', fac, 'Vertices', vert_new, 'FaceColor', color);
end
%// Post processing
material metal;
alpha('color');
alphamap('rampdown');
view(3);
coords will now be a 3D matrix where each slice is a set of 3D coordinates that represents one cube. Similarly, colors will represent a 2D matrix where each row is the colour associated with the cube you drew.
If you want to reconstruct this by just using coords and colors, you can do this:
close all;
clf;
figure(1);
format compact
h(1) = axes('Position',[0.2 0.2 0.6 0.6]);
patch('Faces',fac,'Vertices',vert,'Facecolor', 'w'); % patch function for the first big cube.
axis([-1, 1, -1, 1, -1, 1]);
axis equal;
vert = [1 1 -1;
-1 1 -1;
-1 1 1;
1 1 1;
-1 -1 1;
1 -1 1;
1 -1 -1;
-1 -1 -1];
fac = [1 2 3 4;
4 3 5 6;
6 7 8 5;
1 2 8 7;
6 7 1 4;
2 3 5 8];
vert2 = vert * .05;
%// For each square...
for idx = 1 : num_squares
%// Take the base cube and add an offset to each coordinate
%// Each coordinate will range from [-1,1]
vert_new = coords(:,:,idx);
%// Generate a random colour for each cube
color = colors(idx,:);
%// Draw the cube
patch('Faces', fac, 'Vertices', vert_new, 'FaceColor', color);
end
%// Post processing
material metal;
alpha('color');
alphamap('rampdown');
view(3);
This should reproduce the same figure by using the matrices you wanted to save.

Take Plot Data and Turn Into Animation

I have written code that simulates the rolling of a triangle and gives the output as a trace plot of the three vertices and the centroid for a specified number of rotations.
I would like to turn this plot into an animation and add a circle at each of the triangle vertices to represent wheels.
Can anyone suggest an efficient method to do this?
I am looking to make an animation something like this, except in my case I am not using a ruleaux traingle and just need an animation, not something interactive:
http://demonstrations.wolfram.com/ARollingReuleauxTriangle/
close all
clear
%functions to create rigid body transform matrix---------------------------
hat = #(w) [0,-w(3),w(2); w(3),0,-w(1); -w(2),w(1),0];
rotMatrix = #(th,w) eye(3) + hat(w)*sind(th) + hat(w)*hat(w)*(1-cosd(th));
rbTrans = #(th,w,q) [rotMatrix(th,w), (eye(3) - rotMatrix(th,w))*q(1:3); 0 0 0 1];
%arguments: th=rotation angle, w=rotation axis, q=center of rotation (column vector)
nTurns=5;
degPerTurn=120; %degrees
nStepsPerTurn=1000; %resolution of each "turn". number of points
d = 5.2; %distance from centroid to a vertex
r = 4;
w = [0;0;1;0]; %rotation vector=z-axis
X0 = [0;0;0]; %initial position of centroid
%initialize vertices
v=[0;1;0];
p1 = X0 + [0;d;0];
p2 = X0 + d*rotMatrix(-120,w)*v;
p3 = X0 + d*rotMatrix(120,w)*v;
P(1,1,:) = p1;
P(2,1,:) = p2;
P(3,1,:) = p3;
P(4,1,:) = X0;
rotVertex=2;
thStep=-degPerTurn/nStepsPerTurn;
for i=1:nTurns
for j=1:nStepsPerTurn
%center of rotation
q = squeeze(P(rotVertex,end,:));
q = q + [0; -r; 0];
%transform is a rotation of 120/nSteps about z-axis centered at a
%particular vertex
g = rbTrans(thStep, w, q);
I=size(P,2)+1; %add new element by inserting to (end+1)
for k=1:size(P,1) %for each point we're following-- vertices and centroid
pt = g*[squeeze(P(k,I-1,:)); 1]; %apply transform to last existing value of this point
P(k,I,:) = pt(1:3); %and add it to the end
end
end
%vertices are numbered clockwise around the triangle
rotVertex=rotVertex-1;
if rotVertex<1
rotVertex=rotVertex+3;
end
end
%extract data from 3D array
P1=squeeze(P(1,:,:));
P2=squeeze(P(2,:,:));
P3=squeeze(P(3,:,:));
C=squeeze(P(4,:,:));
figure
plot(P1(:,1),P1(:,2),'b'),hold on
plot(P2(:,1),P2(:,2),'r')
plot(P3(:,1),P3(:,2),'g')
plot(C(:,1),C(:,2),'k')
xlabel('Horizontal Distance (inches)')
ylabel('Vertical Distance (inches)')
axis equal
legend('Vertex 1','Vertex 2','Vertex 3','Centroid', 'Location','Best')
figure
subplot(4,1,1),plot(P1(:,1),P1(:,2)),axis equal,ylabel('P1')
subplot(4,1,2),plot(P2(:,1),P2(:,2)),axis equal,ylabel('P2')
subplot(4,1,3),plot(P3(:,1),P3(:,2)),axis equal,ylabel('P3')
subplot(4,1,4),plot(C(:,1),C(:,2)),axis equal,ylabel('C')
xlabel('Horizontal Distance (inches)')
figure
plot(P3(:,1),P3(:,2),'b'),hold on
plot(C(:,1),C(:,2),'k')
axis equal
xlabel('Horizontal Distance (inches)')
ylabel('Vertical Distance (inches)')
legend('Vertex 3','Centroid', 'Location','Best')
I would encapsulate the drawing of your points in a for loop, drawing each triple of points individually then placing a pause command for a few milliseconds accompanied with a drawnow. If you want this to be an animation, I'm assuming you only want to show one triangle per "frame", and so what you can do is when you want to plot the next set of points, take the previous points and plot them as white to artificially clear them without clearing the entire frame.
Do something like this. Bear in mind I'm only targeting the code where I think is where you want the animation to occur:
figure;
for i = 1 : size(P1, 1)
if (i > 1)
plot(P1(i-1,1), P1(i-1,2), 'w.', P2(i-1,1), P2(i-1,2), 'w.', ...
P3(i-1,1), P3(i-1,2), 'w.', C(i-1,1), C(i-1,2), 'w.');
end
plot(P1(i,1), P1(i,2), 'b.', P2(i,1), P2(i,2), 'r.', ...
P3(i,1), P3(i,2), 'g.', C(i,1), C(i,2), 'k.');
axis equal;
xlabel('Horizontal Distance (inches)');
ylabel('Vertical Distance (inches)');
legend('Vertex 1','Vertex 2','Vertex 3','Centroid', 'Location','Best');
drawnow;
pause(0.1); %// Pause for 0.1 seconds
end