Drawing circles on a torus in matlab - matlab

Using the answer found here, I was able to draw a nice torus. I would like to draw two "circles" on this torus, the first of which is standard. The second should be somewhat random, perhaps like a ladder, ideally on the opposite side from the first. It might be kind of jagged, but should remain on the edges of the lattice and should connect back to itself. Here is what I have so far
am = 1.;
rm = 3.;
t = linspace(-pi,pi,16);
p = linspace(0.,2.*pi,16);
[t,p] = meshgrid(p,t);
x = (rm + am.*cos(p)).*cos(t);
y = (rm + am.*cos(p)).*sin(t);
z = am.*sin(p);
hsurf = surf(x,y,z);
axis equal;
set(hsurf, 'FaceColor','interp','FaceAlpha',0.5,'EdgeAlpha',0.25);
hold on
plot3((rm+am.*cos(p)).*cos(t(8)),(rm+am.*cos(p)).*sin(t(8)),am.*sin(p), 'b', 'LineWidth',2);
I have the standard loop, but can't get the jagged one. I have been trying to specify the other loop by specifying the vertices, but it doesn't seem to be working. Can I specify a curve on this mesh by specifying its vertices? If not, are there any suggestions? Thanks for any help you can provide.
EDIT: Sorry for being vague earlier. The picture below shows what I would like. Note that the right circle is given by the above code, whereas the left hand 'circle' is drawn by hand, and is what I would like to automate.

If you want the vertices of the (jagged or not) circle to match the points of the surface, you do not need to recalculate any points. You can simply re-use some of the points of your surface, choosing the path which define the shape you want.
I'll use your code to define the initial tore, with just a slight modification: I'll use the upper-case letter for variable which define a matrix (and lower case for variables which are simple 1D vector). So your initial tore becomes:
%% // define initial tore
am = 1.; rm = 3.;
t = linspace(-pi,pi,16);
p = linspace(0,2.*pi,16);
[T,P] = meshgrid(p,t);
X = (rm + am.*cos(P)).*cos(T);
Y = (rm + am.*cos(P)).*sin(T);
Z = am.*sin(P);
hsurf = surf(X,Y,Z,'FaceColor','interp','FaceAlpha',0.5,'EdgeAlpha',0.25);
axis equal ; hold on
Not a big deal but this allow to have only 1D vector coordinates in the case where you define your circle by equation as you were doing. It keeps your variables and workspace cleaner and reduce the risk of mistakes and dimension errors.
For the simple circle, you could redefine the coordinate by equation as you did (developed here for clarity)
%% // your simple circle - calculated
colID = 8 ;
xs = (rm+am.*cos(p)).*cos(t(colID)) ;
ys = (rm+am.*cos(p)).*sin(t(colID)) ;
zs = am.*sin(p) ;
hp = plot3(xs,ys,zs, 'b', 'LineWidth',2);
%// This was generating 2D arrays (matrices) for xs, ys and zs when you
%// were using the meshed version of T and P. Using only the vector version of
%// 't' and 'p' generate only vector coordinates. => cleaner and safer.
But I'd like to bring your attention to another way, just use the points of the surface already defined. This (i) eliminates the need for new calculations, and (ii) insure that the vertices of the circle will be matching exactly some vertices of the tore.
%% // another simple circle - extracted from the tore vertices
colID = 8 ;
xs = X(:,colID) ;
ys = Y(:,colID) ;
zs = Z(:,colID) ;
hpc = plot3(xs,ys,zs, 'b', 'LineWidth',2);
Again, there are extra lines for clarity but you can compact the code once you understand what is going on. These 2 different ways of producing a circle result in the following figures:
For illustration purpose, I highlighted the vertices of the tore (small black dots), so you can see if the circles vertices match or not.
Now for the jagged circle, the last method presented will have even more interest. As you observed, to get a circle coordinate we simply extracted one column of each of the tore matrix coordinates. We used a fixed value for the colID to retrieve. To "jagg" your circle, we just need to introduce a small perturbation in the column number, so we will occasionally retrieve the adjacent vertex coordinates.
The following code generate a column ID varying around the staring column ID. You can define the maximum total spread as well as the maximum single increment:
%% // generate index of columns to be retrieved
idxcol = zeros(size(X,1),1) ;
maxDeviation = 5 ; %// total maximum deviation to the initial center line
maxPerturbation = 2 ; %// max deviation for 1 increment
deviation = cumsum(randi([-maxPerturbation maxPerturbation],size(X,1),1)) ;
%// bound deviations to maximum values
deviation(deviation>maxDeviation) = maxDeviation ;
deviation(deviation<-maxDeviation) = -maxDeviation ;
startColID = 8 ;
colID = startColID + deviation ;
%// close the profile by repeating first point in the end if it's not closed already
if colID(end) ~= colID(1) ; colID(end) = colID(1) ; end
If we now use coordinate from theses columns, we get:
npt = size(colID,1) ;
xcj = zeros(npt) ; ycj = xcj ; zcj = xcj ;
for k=1:npt
xcj(k) = X( k , colID(k) ) ;
ycj(k) = Y( k , colID(k) ) ;
zcj(k) = Z( k , colID(k) ) ;
end
hpc = plot3(xcj,ycj,zcj, 'b', 'LineWidth',2);
It is a closed profile turning around the tore but the lines do not match the lines of the surface as in your drawn example. This is because the transition from one column ID to another happen at each change of x index.
To rectify that, we can use the stairs function:
%% // create a stepped profile
[xd,yd] = stairs(colID) ;
%% // display rectified jagged circle
npt = size(yd,1) ;
xcj = zeros(npt) ; ycj = xcj ; zcj = xcj ;
for k=1:npt
xcj(k) = X( xd(k) , yd(k) ) ;
ycj(k) = Y( xd(k) , yd(k) ) ;
zcj(k) = Z( xd(k) , yd(k) ) ;
end
hpc = plot3(xcj,ycj,zcj, 'b', 'LineWidth',2);
This will now generate the following profile:
Much better ... However, when the column ID moves by more than one index for the same x, we still miss anchor points to keep the jagged circle profile matching the tore profile. If your basic column deviation increment is more than one, you will even have many more of these artefact.
To completely rectify that, we need to add the missing anchor points. I couldn't find a built-in function to do that so I went the good old loop way. Feel free to optimize if you find some way.
%% // recreate index vectors with all the necessary anchor points
colX(1,1) = xd(1) ;
colY(1,1) = yd(1) ;
k = 2 ;
for t=2:size(yd,1)
inc = sign(yd(t)-yd(t-1)) ; %// define increment of 1 with correct sign
ids = yd(t-1):inc:yd(t)-inc ; %// range of index to be covered
if isempty(ids) ; ids = yd(t) ; end %// catch corner cases
%// now add these points to the list
n = length(ids) ;
colX(k:k+n-1,1) = xd(t-1) ;
colY(k:k+n-1,1) = ids ;
k=k+n;
end
%// close profile (add last point in the cases where it is necessary)
if inc ~= 0
colX(end+1,1) = xd(end) ;
colY(end+1,1) = yd(end) ;
end
%% // display fully rectified jagged circle
npt = size(colX,1) ;
xcj = zeros(npt) ; ycj = xcj ; zcj = xcj ;
for k=1:npt
xcj(k) = X( colX(k) , colY(k) ) ;
ycj(k) = Y( colX(k) , colY(k) ) ;
zcj(k) = Z( colX(k) , colY(k) ) ;
end
And now we don't have gaps between two column ID any more, all the points of the jagged circle match a point of the surface profile:

Related

Loop function in Matlab Programming

I am trying to make a loop as below description in Matlab programming.
It likes two trees exploration.
Problem1:
I create a function with two input variables and output with four vars.
From originCoord(x,y,z) it creates two new coordinates R(x,y,z), L(x,y,z); (R means Right, L: left)
next R(x,y,z) -> new R1(x1,y1,z1) & L1(x1,y1,z1)
and L(x,y,z) -> new L2(x2,y2,z2) & R2(x2,y2,z2)
then to be continued
.....
loop will stop when one of the newCcoordinates() == targetCoordinates(xtarget, ytarget).
%from initial 3D point (x y z) using treeExploreation fuction to create two
%new3D points with coordinates (x1,y1,z1) and (x2,y2,z2)
%then from these new point, using this function two create new four 3D_Points...
%x y z are scala
rightPoint = [x1,y1,z1];
leftPoint = [x2,y2,z2];
targetPoint = [xtarget, ytarget, ztarget];
%store twop 3D points into coordExplore
coordExplore = [rightPoint,leftPoint];
temp = [];
flag = 0;
while (~flag)
for i=1:length(coordExplore)
[newrightPoint,newleftPoint]=treeExploration(coordExplore(i)); %help me at here !!!!
%this function with input (x,y,z) output two new coordinates i.e.
% newrightPoint(x3,y3,z3) and newleftPoint(x4,y4,z4)
if (newrightPoint==targetPoint) || (newleftPoint==targetPoint)
%this condition is to compare the output coordinates whether the same as
%the targetPoint
flag=1;
break;
else
temp =[newrightPoint,newleftPoint];
end
end
coordExplore = temp;
temp = [];
end
I got stuck when the output always greater than input. I cannot use all the output for the next loop.
Please help me to comment on how to make a loop for this case. Thanks.
Updated problems:
*I wish coordExplore(1) = rightPoint (x1 y1 z1)
and coordExplore(2) = leftPoint (x2 y2 z2)
BUT with index i=1, coordExplore(1) = x1; i=2 -> coordExplore(2) = y1;
I use the loop with coordExplore(i) in order to work from point-to-point within each point has its own coordinates(x y z). How can I do that?*
Problem2:
when one of the newCcoordinates() == targetCoordinates(xtarget, ytarget), how can I know which varibales in which direction from origin loop to last loop generating the result? (like which brands of the tree achieving the target).
In C++, I can use stack to save but in Matlab I do not know how can I save the variable for each loop.
Problem 1:
If I have understood what you want to achieve correctly, something like this should do the job.
reached_target = False;
coord = [x, y];
while ~reached_target
number_of_nodes = size(coord, 1)
new_coord = zeros(2*number_of_nodes)
for ii = 1:number_of_nodes
new_coord(ii*2-1, :) = doRightRunning(coord(ii, 1);
new_coord(ii*2, :) = doLeftRunning(coord(ii, 2);
end
target_ind = new_coord(:, 1) == x_target && new_coord(:, 2) == y_target;
if any(target_ind)
reached_target = True;
disp(find(target_ind));
end
end
However, the code you've shown doesn't match your explanations, so it's not clear if the next layer of the tree is 2 times or 4 times the size of the previous layer.
The code here basically calculates all the left and right runs for the current coordinates until the target is reached.
I haven't tested it yet, so look for bugs.
Problem 2:
It is not necessary to save the trail for knowing the turns it has made to reach the target. When a matching coordinate is found, its place in the list of coordinates reveal its trail. All you need to do is convert its index to binary and interpret 0 as right turn and 1 as left turn (since in the code, the right turn is placed on top, and the left turn on the bottom).
Example: Let's say we are in the 3rd layer of the tree with 8 coordinates and let's say the 6th coordinate matches the target. So starting from index 0, we have found a match at index which is 101 in binary format. It reveals that the turns to reach this coordinate are Left-Right-Left.

How do I adjust this code so that I can enter how many runs I want and it will store each run in a matrix?

I have created this code to generate a 1 set of lottery numbers, but I am trying to make it so that the user can enter how many sets they want (input n), and it will print out as one long matrix of size nX6? I was messing around with a few options from online suggestions, but to no avail. I put the initial for i=1:1:n at the beginning, but I do not know how to store each run into a growing matrix. Right now it still generates just 1 set.
function lottery(n)
for i=1:1:n
xlow=1;
xhigh=69;
m=5;
i=1;
while (i<=m)
lottonum(i)=floor(xlow+rand*(xhigh-xlow+1));
flag=0;
for j=1:i-1
if (lottonum(i)==lottonum(j))
flag=1;
end
end
if flag==0
i=i+1;
end
end
ylow=1;
yhigh=26;
m=1;
lottonum1=floor(ylow+rand*(yhigh-ylow+1));
z = horzcat(lottonum, lottonum1);
end
disp('The lotto numbers picked are')
fprintf('%g ',z)
disp (' ')
The problem is that you are not storing or displaying the newly generated numbers, only the last set. To solve this, initialize z with NaNs or zeros, and later index z to store each set in a row of z, by using z(i,:) = lottonum.
However, you are using i as iterator in the while loop already, so you should use another variable, e.g. k.
You can also set z as an output of the function, so you can use this matrix in some other part of a program.
function z = lottery(n)
% init z
z = NaN(n,6);
for k = 1:n
xlow=1;
xhigh=69;
m=5;
i=1;
while (i<=m)
lottonum(i)=floor(xlow+rand*(xhigh-xlow+1));
flag=0;
for j=1:i-1
if (lottonum(i)==lottonum(j))
flag=1;
end
end
if flag==0
i=i+1;
end
end
ylow=1;
yhigh=26;
lottonum1 = floor(ylow+rand*(yhigh-ylow+1));
z(k,:) = horzcat(lottonum, lottonum1); % put the numbers in a row of z
end
disp('The lotto numbers picked are')
disp(z) % prettier display than fprintf in this case.
disp (' ')
end
The nice answer from rinkert corrected your basic mistakes (like trying to modify your loop iterator i from within the loop => does not work), and answered your question on how to store all your results.
This left you with a working code, however, I'd like to propose to you a different way to look at it.
The porposed architecture is to divide the tasks into separate functions:
One function draw_numbers which can draw N numbers randomly (and does only that)
One function draw_lottery which call the previous function as many times as it needs (your n), collect the results and display them.
draw_lottery
This architecture has the benefit to greatly simplify your main function. It can now be as simple as:
function Draws = draw_lottery(n)
% define your draw parameters
xmin = 1 ; % minimum number drawn
xmax = 69 ; % maximum number drawn
nballs = 5 ; % number of number to draw
% pre allocate results
Draws = zeros( n , nballs) ;
for iDraw=1:1:n
% draw "nballs" numbers
thisDraw = draw_numbers(xmin,xmax,nballs) ;
% add them to the result matrix
Draws(iDraw,:) = thisDraw ;
end
disp('The lotto numbers picked are:')
disp (Draws)
disp (' ')
end
draw_numbers
Instead of using a intricated set of if conditions and several iterators (i/m/k) to branch the program flow, I made the function recursive. It means the function may have to call itself a number of time until a condition is satisfied. In our case the condition is to have a set of nballs unique numbers.
The function:
(1) draws N integer numbers randomly, using randi.
(2) remove duplicate numbers (if any). Using unique.
(3) count how many unique numbers are left Nu
(4a) if Nu = N => exit function
(4b) if Nu < N => Call itself again, sending the existing Nu numbers and asking to draw an additional N-Nu numbers to add to the collection. Then back to step (2).
in code, it looks like that:
function draw = draw_numbers(xmin,xmax,nballs,drawn_set)
% check if we received a partial set
if nargin == 4
% if yes, adjust the number of balls to draw
n2draw = nballs - numel(drawn_set) ;
else
% if not, make a full draw
drawn_set = [] ;
n2draw = nballs ;
end
% draw "nballs" numbers between "xmin" and "xmax"
% and concatenate these new numbers with the partial set
d = [drawn_set , randi([xmin xmax],1,n2draw)] ;
% Remove duplicate
drawn_set = unique(d) ;
% check if we have some more balls to draw
if numel(drawn_set) < nballs
% draw some more balls
draw = draw_numbers(xmin,xmax,nballs,drawn_set) ;
else
% we're good to go, assign output and exit funtion
draw = drawn_set ;
end
end
You can have both functions into the same file if you want.
I encourage you to look at the documentation of a couple of Matlab built-in functions used:
randi
unique

For Loop and indexing

Following is my code :
function marks(my_numbers)
handle = zeros(5,1)
x = 10 ;
y = 10:20:100 ;
for i = 1
for j = 1:5 ;
handle(j,i) = rectangle('position',[x(i),y(j),20 10],'facecolor','r')
end
end
end
now lets say input argument my_numbers = 2
so i have written the code :
set(handle(j(my_numbers),1),'facecolor','g')
With this command, rectangle with lower left corner at (30,10) should have turned green. But MATLAB gives an error of index exceeds matrix dimensions
This is more an illustrated comment than an answer, but as #hagubear mentioned your i index is pointless so you could remove it altogether.
Using set(handle(my_numbers,1),'facecolor','g') will remove the error, because you were trying to access handles(j(2),1) and that was not possible because j is a scalar.
Anyhow using this line after your plot works fine:
set(handle(my_numbers,1),'facecolor','g')
According to your comment below, here is a way to call the function multiple times and add green rectangles as you go along. There are 2 files for the purpose of demonstration, the function per se and a script to call the function multiple times and generate an animated gif:
1) The function:
function marks(my_numbers)
%// Get green and red rectangles to access their properties.
GreenRect = findobj('Type','rectangle','FaceColor','g');
RedRect = findobj('Type','rectangle');
%// If 1st call to the function, create your plot
if isempty(RedRect)
handle = zeros(5,1);
x = 10 ;
y = 10:20:100 ;
for j = 1:5 ;
handle(j) = rectangle('position',[x,y(j),20 10],'facecolor','r');
end
set(handle(my_numbers,1),'facecolor','g')
%// If not 1st call, fetch existing green rectangles and color them green. Then color the appropriate rectangle given by my_numbers.
else
RedRect = flipud(RedRect); %// Flip them to maintain correct order
if numel(GreenRect) > 0
hold on
for k = numel(GreenRect)
set(GreenRect(k),'facecolor','g')
set(RedRect(my_numbers,1),'facecolor','g')
end
end
end
2) The script:
clear
clc
%// Shuffle order for appearance of green rectangles.
iter = randperm(5);
filename = 'MyGifFile.gif';
for k = iter
marks(k)
pause(1)
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if k == iter(1)
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end
Here is the animated gif of the output:

Manual Mean Filtering

Before I begin, I would just like to clarify that I understand how to use kernels and the conv2() function in order to mean filter an image, but I have been tasked with doing this manually by calling up each pixel.
My goal here is to call up each pixel, find its neighbors, average them out, and replace previous values with the acquired mean, without using kernels or conv2(). I have so far attempted to find the neighbors of each pixel using
for
I = 1:512;
for
J = 1:683;
A = myimage;
neighbor_offsets = [-1, A, 1, -A, A + 1, -A + 1, -A-1, A - 1];
idx = [I J];
neighbors = bsxfun(#plus,idx,neighbor_offsets);
but it does not seem to work, and I am a bit lost in trying to fix it. I think I could finish the job if I were able to get the neighbors by using something like
sum(neighbors) / 9
then replace the previous values with that answer, but please correct me if I'm. I've developed somewhat of a tendency to ask poor questions, so if anything is unclear, please let me know so I can clarify for you. Thanks
Example below treats pixels at the edge in the manner that it only considers pixels that are inside the image. For example when program is computing average with kernel dy = (-1:1) and dx = (-1:1) on top-left corner, it only considers top-left corner and its immediate 3 neighbors (right, right-bottom, right), and does average of those 4 pixels.
I would strongly advise you to test every line separately in Matlab's command window to see it's behavior!
% find image size
imsz = size( myimage );
% initialize output image
imavg = zeros( imsz );
% iterate over pixels
for yy = 1 : imsz(1)
for xx = 1 : imsz(2)
% define rectangle-kernel width
dy = (-1:1); % 1 up, to 1 down and ...
dx = (-1:1); % 1 left, to 1 right from current pixel
% get indexes of image
indy = yy + dy;
indx = xx + dx;
% [!!!] keep indexes that are inside image
indy = indy( indy>0 & indy<=imsz(1) );
indx = indx( indx>0 & indx<=imsz(2) );
% create all the pairings of chosen indexes
[ IY, IX ] = meshgrid( indy, indx );
% take all values of chosen pixels
pixs = myimage( sub2ind(imsz,IY(:),IX(:)) );
% save mean of chosen pixels to the given location
imavg(yy,xx) = mean( pixs );
end
end
You can create function from the above code with creating mean_filter.m file with this contents:
function imagv = mean_filter( myimage )
% code from above ...
You can call function from command window by positioning yourself in the directory where it's at and executing filtered = mean_filter( myimage );.
You can repeatedly filter the same image with:
filtered_3_times = myimage;
for ii = 1 : 3
filtered_3_times = mean_filter( filtered_3_times );
end

MATLAB - Labeling Curves During Iteration

I want to show the p value that was used to generate each curve next to each of the curves plotted. Note that since there is a plot of E and -E, the same p value should be next to both. I've been attempting this for a while and I have not come across anything super useful.
t = -3.1;%coupling
a = 1;%distance between r1 and r3
n = 5;%latice vector span in a1 direction
m = 1;%latice vector span in a2 direction
i = -7;%unique axial vector t_hat direction
j = 11;%unique axial vector c_hat direction
max_p = abs((n*(i+j/2)-j*(m+n/2)));%# of unique p values
La = sqrt(3)*sqrt(m^2+n*m+n^2)*a/gcd(2*n+m,2*m+n);%unit cell length
C = sqrt(n^2+n*m+m^2);%circumference of the nanotube
hold on;
for p=0:1:max_p
kt = -pi/La:.05:pi/La;
kc = 2*pi*p/C;
ka1 = kc*a*.5*(2*n+m)/C + kt*a*sqrt(3)*.5*m/C;
ka2 = kc*a*.5*(n+2*m)/C - kt*a*sqrt(3)*.5*n/C;
E = abs(t+t*exp(1i*ka2)+t*exp(1i*ka1));
title_ = sprintf('(%d,%d) Carbon Nanotube Dispersion Diagram',n,m);
title(title_);
xlabel('k_{t}a');
ylabel('Energy (eV)');
plot(kt,E);
plot(kt,-E);
end
There is a command named text that writes comments into the figures,
http://www.mathworks.se/help/techdoc/ref/text.html
with if you can't solve it with that and the to string operation i misunderstood the question
First, do you need to plot both E and -E? Since these are the same except for their sign you don't really add any information to the plot by having -E there as well. However, if you do need both lines, then just construct an array of strings for the legend, during the loop, which has each string included twice (once for E and once for -E).
... Initial calculations ...
hold on;
for p=0:1:max_p
kt = -pi/La:.05:pi/La;
kc = 2*pi*p/C;
ka1 = kc*a*.5*(2*n+m)/C + kt*a*sqrt(3)*.5*m/C;
ka2 = kc*a*.5*(n+2*m)/C - kt*a*sqrt(3)*.5*n/C;
E = abs(t+t*exp(1i*ka2)+t*exp(1i*ka1));
plot(kt,E);
plot(kt,-E);
% Construct array containing legend text
legend_text{2*(p+1)-1} = strcat('p=', num2str(p));
legend_text{2*(p+1)} = strcat('p=', num2str(p));
end
title_ = sprintf('(%d,%d) Carbon Nanotube Dispersion Diagram',n,m);
title(title_);
xlabel('k_{t}a');
ylabel('Energy (eV)');
legend(legend_text)
I am sure there is a more elegant way of constructing the legend text, but the above code works. Also, notice that I moved the calls to xlabel, ylabel and title to outside of the loop. This way they are only called once and not for each iteration of the loop.
Finally, you need to take care to ensure that each iteration of the loop plots with a different line colour or line style (see edit below). You could colour/style each pair of E and -E lines the same for a given iteration of the loop and just display the legend for E (or -E), which would obviously halve the number of legend entries. To do this you will need to hide one of line's handle visibility - this prevents it from getting an item in the legend. To do this use the following in your loop:
plot(kt, E);
plot(kt,-E, 'HandleVisibility', 'off');
% Construct array containing legend text
legend_text{p+1} = strcat('p=', num2str(p));
Finally, it is best to include clear all at the top of your Matlab scripts.
Edit: To have each plotted line use a different colour for each iteration of your loop use something like the following
... initial calculations ...
cmap = hsv(max_p); % Create a max_p-by-3 set of colors from the HSV colormap
hold on;
for p = 0:1:max_p
plot(kt, E, 'Color', cmap(p,:)); % Plot each pair of lines with a different color
plot(kt, -E, 'Color', cmap(p,:));
end