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.
Related
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!
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
I have a vector of values that I want to plot as brightness on a circle through the radius of it (I.e. If it was 0 3 1 5 I'd want a circle that was dark at the centre, then a bright ring around it, then a slightly darker ring, then a brighter ring).
To do this I've attempted to rotate my radial vector (E) around the y axis, as such
[X,Y,Z] = cylinder(E);
h = surf(X,Y,Z),
However I'm clearly not doing it right, as this appears to be rotating my curve around the x axis. I've tried just swapping X and Y, but it still rotates it around the x axis. Any help would be greatly appreciated.
One way would be to rotate your vector and create a surface. The Z data of the surface (your rotated vector) will be color coded according to the colormap you choose, if you display the surface from the top you get your circles at the different brightness.
If you are really only interested from the "top view" of this surface, then no need to create a full surface, a simple pcolor will do the job.
example:
%% // input data (and assumptions)
E=[0 3 1 5 2 7];
nBrightness = 10 ; %// number of brightness levels
r = (0:numel(E)) ; %// radius step=1 by default for consecutive circles
%// otherwise define different thickness for each circle
So if I use stairs([E 0]) you get your different brightness levels:
I had to add a last 0 to the vector to "close" the last level, we'll have to do that again in the solution below.
Now to rotate/replicate that around Y, color code the height, and look at it from the top:
%% // replicate profile around axis
ntt = 50 ; %// define how many angular division for the plot
theta = linspace(0,2*pi,ntt) ; %// create all the angular divisions
[rr,tt]=meshgrid(r,theta) ; %// generate a grid
z = repmat( [E 0] , ntt , 1 ) ; %// replicate our "E" vector to match the grid
[xx,yy,zz] = pol2cart(tt,rr,z) ; %// convert everything to cartesian coordinates
pcolor(xx,yy,zz) %// plot everything
colormap(gray(nBrightness)) %// make sure we use only "nBrightness" colors (Shades of gray)
caxis([0 nBrightness])
shading flat ; axis equal %// refine the view (axis ratio and "spokes" not visible) etc...
colorbar
axis off
will yield the following :
Note that your problem was not fully defined, I had to take assumptions on:
What radius each brightness circle should have ? (I made them all the same but you can modify that)
How many brightness levels you want ? (You can also modify that easily though).
Have you tried the rotate function?
direction = [0 1 0];
rotate(h,direction,90);
In this example a 90 degree rotation is performed around the y axis.
Using this library http://www.mathworks.com/matlabcentral/fileexchange/45952-circle-plotter
%http://www.mathworks.com/matlabcentral/fileexchange/45952-circle-plotter
x0 = 0;
y0 = 0;
colors = [0 3 1 5];
maxC = max(colors);
sz = numel(colors);
for i=fliplr(1:sz)
c = colors(i);
circles(x0,y0,i,'facecolor',[c/maxC c/maxC 0]) % http://au.mathworks.com/help/matlab/ref/colorspec.html
end
I am attempting to make a pattern consisting of annular rings with radii proportional to the square root of the natural numbers. Also I want the inner most circle to be white followed by a black circle followed by a white and so on.
c = [0 0; 0 0];
r = [5.2494 9.0922];
viscircles(c, r)
r1 = [7.4328 10.4988];
viscircles(c, r1)
I have generated the above code to form the annular ring structure but I want to fill in the color as well. What should I do?
You could go the mathematical route and plot the function ceil(sin(pi*(X.^2 + Y.^2))):
zoomlevel = 50;
for n = 1:zoomlevel
[X,Y] = ndgrid(linspace(-n,n,500));
I = ceil(sin(pi*(X.^2 + Y.^2)));
imshow(mat2gray(I));
drawnow;
pause(0.03);
end
Of course this will only be a raster graphic instead of a vector one, so don't zoom in too much. ;-) (Although the aliasing artefacts will look quite cool if you zoom out. Plot at your own risk.)
My Matlab version doesn't have viscircles, so here's an approach which plots each individual circle with alternating colors. It uses the rectangle function, which lets you define the curvature of the corners so that the rectangle/square becomes an ellipse/circle. Bigger circles should be drawn first, so that they don't completely cover smaller circles.
colors = [.9 .9 .9; 0 0 0]; %// light gray and black
N = 16; %// maximum number
hold on
for n = N:-1:1; %// bigger circles first
s = sqrt(n);
rectangle('curvature', [1 1], 'position', [-s/2 -s/2 s s], ...
'edgecolor', 'none', 'facecolor', colors(mod(n-1,2)+1,:));
end
axis square
You can also create a "surface" with value 1 for all your r radiuses and 0 for the r1. Then either plot as a surface seen form top, or directly use pcolor.
r = [0 5.2494 7.4328 9.0922 10.4988] ; %// define all your radiuses
bw = mod( 1:numel(r) , 2 ) ; %// create an alternance of 0 and 1 (same size as "r")
ntt = 50 ; %// define how many angular division for the plot
theta = linspace(0,2*pi,ntt) ; %// create all the angular divisions
[rr,tt]=meshgrid(r,theta) ; %// generate a grid
z = repmat( bw , ntt , 1 ) ; %// replicate our [0 1 0 ...] vector to match the grid
[xx,yy,zz] = pol2cart(tt,rr,z) ; %// convert everything to cartesian coordinates
pcolor(xx,yy,zz) %// plot everything
colormap(gray(2)) %// make sure we use only 2 colors (black and white)
shading flat ; axis equal %// refine the view (axis ratio and "spokes" not visible)
You can send as many radiuses as you like in the original r.
This will render:
This method look a bit longer at first than other solution, but you could remove many intermediate steps by consolidating some lines, and if you are to reuse the graphics later on, it may present 2 benefits:
if you get the handle of the graphic object (hp=pcolor(xx,yy,zz)), you only have one graphic object to handle.
if you need to change the color, you do not need to cycle through each circle, just change the colormap to the 2 colors you want (for example if you want "red" and "green", just call the colormap colormap([1 0 0;0 1 0]) and you're done.
viscircles returns an hggroup object. One of the properties of such an object is its Children, which is an array of handles to the graphics objects it creates. For instance you could write
h1 = viscircles(c, r)
c1 = h1.Children
The children here should just be the handles to the circular patches defined by viscircles. Now, to set the color of the ith circular patch, you can set the FaceColor property of the handle c1(i).
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: