I'm trying to dynamically add information to a 3D plot:
A = [ -8/3 0 0; 0 -10 10; 0 28 -1 ];
y = [35 -10 -7]';
h = 0.01;
p = plot3(y(1),y(2),y(3),'.','EraseMode','none','MarkerSize',2);
axis([0 50 -25 25 -25 25])
hold on
while 1
A(1,3) = y(2);
A(3,1) = -y(2);
ydot = A*y;
y = y + h*ydot;
set(p,'XData',y(1),'YData',y(2),'ZData',y(3))
drawnow
end
I get the following errors:
Warning: The EraseMode property is no longer supported and will error
in a future release.
In strange_attractor (line 4) Warning: The EraseMode property is no longer supported and will error in a future release.
In strange_attractor (line 4) Error using matlab.graphics.chart.primitive.Line/set Invalid or deleted object.
Error in strange_attractor (line 12)
set(p,'XData',y(1),'YData',y(2),'ZData',y(3))
I understand that I'm using the outdated syntax for dynamically updating the plot that is no longer supported. How do I make it work?
Note that I don't want to call plot3 inside the loop, because it makes the graphing slow and consumes a lot of memory. I hope to call plot3 once and just add new points to the existing plot inside the loop. Can it be done in the new version?
Matlab version: R2018a
In your comment above, when you say "This does not work", what do you mean? A direct modification of your code according to the given link to the documentation works as expected:
A = [ -8/3 0 0; 0 -10 10; 0 28 -1 ];
y = [35 -10 -7]';
h = 0.01;
p = animatedline(y(1),y(2),y(3),'Marker','.','MarkerSize',2);
axis([0 50 -25 25 -25 25])
hold on
while 1
A(1,3) = y(2);
A(3,1) = -y(2);
ydot = A*y;
y = y + h*ydot;
addpoints(p,y(1),y(2),y(3))
drawnow
end
Related
I need to plot horizontal bars using the grouped style such that all the bars belonging to each group have the same color but different from other groups (i.e. all bars of the top group are red, the group below it - green, and so on...).
Also, how can I put the values on the top of each bar horizontally? How can I control the position of such values?
This is my code:
y = [91.9 8.1 94.4 5.6; 84.9 15.1 90.12 9.88; 89.4 10.6 91.2 8.8; 72 28 50.9 49.1];
h = barh(y,'grouped');
job = {'group1','group2 ','group 3','group4'};
legend(job,'location','northeast');
This is my current figure:
Here's a hack:
Plot bar graph for 1 row at a time and fill the space with NaNs while specifying a single color for all rows. Plotting NaNs will plot nothing.
[ry, cy] = size(y); %Number of rows and columns of y
hold on;
for k = 1:ry
tmp = [NaN(k-1,cy); y(k,:); NaN(4-k,cy)]; %Filling with NaNs
h{k} = barh(tmp, 'FaceColor', rand(1,3)); %Plotting bar graph in random color
h{k} = h{k}(1); %Store handle of any of the rows (Required for legend)
end
job = {'group1', 'group2', 'group3', 'group4'}; %Required legend entries
legend([h{:}], job); %Default location is already 'northeast' (so can be skipped)
Output:
This is another type of hack; tested on R2017b.
The reason why what you're asking is difficult to do is because each group in a bar plot is actually a Quadrilateral object (i.e. polygon). For example, let's take the 1st Bar object:
>> h(1).Face.VertexData
ans =
3×16 single matrix
Columns 1 through 9
0 91.9000 91.9000 0 0 84.9000 84.9000 0 0
0.6545 0.6545 0.8000 0.8000 1.6545 1.6545 1.8000 1.8000 2.6545
0 0 0 0 0 0 0 0 0
Columns 10 through 16
89.4000 89.4000 0 0 72.0000 72.0000 0
2.6545 2.8000 2.8000 3.6545 3.6545 3.8000 3.8000
0 0 0 0 0 0 0
And if we add this line to the code:
drawnow; hold on; scatter(h(1).Face.VertexData(1,:), h(1).Face.VertexData(2,:));
we get (notice the green circles):
What's interesting is that the points in VertexData are not read-only, which means we can just intelligently "rearrange" the VertexData matrices of the N bar groups to get the desired result. Here's one way to do it (notice I renamed h to hBar):
% Obtain the old VertexData:
vd_old = cell2mat(reshape(get([hBar.Face],'VertexData'),1,1,[]));
% Rearrange VertexData:
nB = numel(hBar);
vd_new = cell2mat(permute(mat2cell(vd_old,3,repelem(4,nB),repelem(1,nB)),[1,3,2]));
% Assign the new VertexData back into the Bar objects' Edge and Face fields:
for indB = 1:nB
hBar(indB).Edge.VertexData = vd_new(:,:,indB);
hBar(indB).Face.VertexData = vd_new(:,:,indB);
end
Finally, we manually add labels using the text command:
xOffset = 1;
for indB = 1:nB
text(double(vd_new(1,2:4:end,indB)) + xOffset, double(tmpY(2:4:end)), ...
string(vd_new(1,2:4:end,indB)),'VerticalAlignment','middle');
end
The end result:
The final code:
function q47978293
close all force;
y = [91.9 8.1 94.4 5.6; 84.9 15.1 90.12 9.88; 89.4 10.6 91.2 8.8; 72 28 50.9 49.1];
figure(47978293); set(gcf,'Position',[786,556,887,420]); hBar = barh(y,'grouped');
legend("group" + (1:4),'location','northeast');
drawnow;
% hold on; scatter(h(1).Face.VertexData(1,:), h(1).Face.VertexData(2,:));
% Obtain the old VertexData:
vd_old = cell2mat(reshape(get([hBar.Face],'VertexData'),1,1,[]));
% Rearrange VertexData:
nB = numel(hBar);
vd_new = cell2mat(permute(mat2cell(vd_old,3,repelem(4,nB),repelem(1,nB)),[1,3,2]));
% Assign the new VertexData back into the Bar objects' Edge and Face fields:
xOffset = 1; % < Adjust as you see fit
for indB = 1:nB
hBar(indB).Edge.VertexData = vd_new(:,:,indB);
hBar(indB).Face.VertexData = vd_new(:,:,indB);
try
text( y(indB,:) + xOffset, mean(reshape(vd_new(2,1:2:end,indB),2,[]),1,'double'), ...
string(vd_new(1,2:4:end,indB)),'VerticalAlignment','middle');
catch % Compatibility fix for R2016b & R2017a. Credit #SardarUsama
text( y(indB,:) + xOffset, mean(reshape(vd_new(2,1:2:end,indB),2,[]),1,'double'), ...
split(num2str(vd_new(1,2:4:end,indB))),'VerticalAlignment','middle');
end
end
end
Note: due to the hacky nature of this solution, if the figure is redrawn (due to e.g. resizing), the bar colors will return to their original state - so make sure that the part that changes colors is the very last thing you do.
P.S.
The R2017b release notes state that this customization should be officially supported now (through the CData property of Bar objects), but I couldn't get the legend to display the correct colors. If you want to try it, just plot using the following command (R2017b+):
barh(y,'grouped','FaceColor','flat','CData',lines(size(y,1)));
The two requests overlap making everything kinda complex, since using uniform colors for each group implies using a workaround that needs another workaround to male bar text work. Here is the code:
y = [
91.90 8.10 94.40 5.60;
84.90 15.10 90.12 9.88;
89.40 10.60 91.20 8.80;
72.00 28.00 50.90 49.10
];
[rows,cols] = size(y);
n = NaN(rows,cols);
c = {'r' 'g' 'b' 'y'};
bh = cell(rows,1);
lh = cell(rows,1);
hold on;
for k = 1:rows
curr = n;
curr(k,:) = y(k,:);
bar = barh(curr,'FaceColor',c{k});
bh{k} = bar;
lh{k} = bar(1);
end
hold off;
legend([lh{:}],{'G1','G2','G3','G4'},'Location','southoutside');
yl = get(gca,'XLim');
yl_up = yl(2);
xoff = yl_up * 0.01;
set(gca,'XLim',[yl(1) (yl_up + (yl_up * 0.1))]);
set(gcf,'Units','normalized','Position',[0.1 0.1 0.8 0.8]);
for i = 1:numel(bh)
bh_i = bh{i};
for j = 1:numel(bh_i)
bh_ij = bh_i(j);
hx = bh_ij.YData + xoff;
hy = bh_ij.XData + bh_ij.XOffset;
text(hx,hy,num2str(hx.','%.2f'), ...
'FontSize',10, ...
'HorizontalAlignment','left', ...
'VerticalAlignment','middle');
end
end
And here is the output:
In my research area (meteorology) graphs within graphs are commonly produced.
more information about it can be found here.
Each of those lines joints up data points that have:
An x-value, between 0 and 1 (values greater than 1 should not be represented in the graph).
A y-value, between 0 and 1.
A PSS value, between 1 and -1.
A Frequency Bias value, ranging from 0 to +∞, but values higher than 4 are not displayed.
A False Alarm Ratio (FAR) value, ranging from 0.0 to 0.9. The False Alarm Ratio value is held constant at a particular value for each data point on any given line.
EDIT: To make things really concrete, I've drawn a pink dot on the graph. That dot represents a data point for which x=0.81, y=0.61, PSS=-0.2, B=3.05, FAR=0.8.
I am trying to reproduce something similar in MATLAB. Googling turned up a lot of answers like this, which feature inset figures rather than what I'm looking for.
I have the data organized in a 3D array, where each page refers to a different level of False Alarm Ratio. The page with a FAR of 0.8 (data here) starts out like this
Then there are other pages on the 3D array devoted to FARs of 0.7, 0.6, and so on.
Questions
1. Is it even possible to create such an graph in MATLAB?
2. If so, what function should I use, and what approach should I take? EDIT: I have working code (below) that creates a somewhat similar figure using the linear plot function, but the documentation for this function does not indicate any way to insert a graph inside another graph. I am not sure how helpful this code is, but have inserted it in response to the downvoter.
H = [0:0.01:1];
figure; hold on
fill([0 1 1],[0 0 1],[0 0.2 0.4]) % Deep blue
fill([0 1 0],[0 1 1],[0.4 0 0]) % Purple
low_colours = {[0 0.501 1],[0 0.8 0.4], [0.4 0.8 0], [0.8 0.8 0]};
high_colours = {[0.6 0 0],[0.8 0 0], [1 0.5019 0], [0.988 0.827 0.196]};
colour_counter = 0;
for ii = -0.8:0.2:0
colour_counter = colour_counter + 1;
if colour_counter < 5
colour_now = low_colours{colour_counter};
end
ORSS = ones(1,size(H,2))*ii;
F = (H .* (1-ORSS)) ./ ((1-2.*H) .* ORSS + 1);
plot(F,H)
fill(F,H,colour_now);
end
colour_counter = 0;
for ii = 0.8:-0.2:0
colour_counter = colour_counter + 1;
if colour_counter < 5
colour_now = high_colours{colour_counter};
end
ORSS = ones(1,size(H,2))*ii;
F = (H .* (1-ORSS)) ./ ((1-2.*H) .* ORSS + 1);
plot(F,H)
fill(F,H,colour_now);
end
I think I got what you want, but before you go to the code below, notice the following:
I didn't need any of your functions in the link (and I have no idea what they do).
I also don't really use the x and y columns in the data, they are redundant coordinates to the PSS and B.
I concat all the 'pages' in your data to one long table (FAR below) with 5 columns (FAR,x,y,PSS,FB).
If you take closer look at the data you see that some areas that supposed to be colored in the graph has no representation in it (i.e. no values). So in order to interpolate the color to there we need to add the corners:
FAR{end+1,:} = [0.8 0 0 0 4];
FAR{end+1,:} = [0.9 0 0 -0.66 3.33];
FAR{end+1,:} = [1 0 0 0 0];
FAR{end+1,:} = [1 0 0 -1 3];
Next, the process has 2 parts. First we make a matrix for each variable, that ordered in columns by the corresponding FAR value, so for instance, in the PSS matrix the first column is all PSS values where FAR is 0, the second column is all PSS values where FAR is 0.1, and so on. We make such matrices for FAR(F), PSS and FreqBias(B), and we initialize them with NaNs so we can have columns with different number of values:
F = nan(max(histcounts(FAR.FAR,10)),10);
PSS = F;
B = F;
c = 1;
f = unique(FAR.FAR).';
for k = f
valid = FAR.FAR==k & FAR.x<=1;
B(1:sum(valid),c) = FAR.FB(valid);
B(sum(valid):end,c) = B(sum(valid),c);
PSS(1:sum(valid),c) = FAR.PSS(valid);
PSS(sum(valid):end,c) = PSS(sum(valid),c);
F(:,c) = k;
c = c+1;
end
Then we set the colors for the colormap (which I partially took from you), and set the labels position:
colors = [0 0.2 0.4
0 0.501 1;
0 0.8 0.4;
0.4 0.8 0;
0.8 0.8 0;
0.988 0.827 0.196;
1 0.5019 0;
0.8 0 0;
0.6 0 0.2;
0.4 0.1 0.5];
label_pos =[0.89 0.77
1.01 0.74
1.14 0.69
1.37 0.64
1.7 0.57
2.03 0.41
2.65 0.18
2.925 -0.195
2.75 -0.55];
And we use contourf to plot everything together, and set all kind of properties to make it look good:
[C,h] = contourf(B,PSS,F);
xlim([0 4])
ylim([-1 1])
colormap(colors)
caxis([0 1])
xlabel('Frequency Bias B')
ylabel('Pierce Skill Score PSS')
title('False Alarm Ratio')
ax = h.Parent;
ax.XTick = 0:4;
ax.YTick = -1:0.5:1;
ax.FontSize = 20;
for k = 1:numel(f)-2
text(label_pos(k,1),label_pos(k,2),num2str(f(k+1)),...
'FontSize',12+k)
end
And here is the result:
Getting the labels position:
If you wonder what is a fast way to obtain the variable label_pos, then here is how I made it...
You run the code above without the last for loop. Then you run the following code:
clabel(C,'manual')
f = gcf;
label_pos = zeros(numel(f.Children.Children)-1,2);
for k = 1:2:size(label_pos,1)
label_pos(k,:) = f.Children.Children(k).Position(1:2);
end
label_pos(2:2:size(label_pos,1),:) = [];
After the first line the script will pause and you will see this message in the command window:
Carefully select contours for labeling.
When done, press RETURN while the Graph window is the active window.
Click on the figure where you want to have a label, and press Enter.
That's it! Now the variable label_pos has the positions of the labels, just as I used it above.
i run below code, but get X, Y, Z, and C cannot be complex error, any idea what is wrong?
k=1;
u = linspace(0,2*pi,72);
v = [-3:.2:-1,1:.2:3];
[U,V] = meshgrid(u,v);
r=sqrt((4*V.^-k)./(cos(U).^2+k*sin(U).^2));
X = r.*cos(U);
Y = r.*sin(U);
Z = V;
This is the image I want to get:
http://adasu.info/plates.png
The full code is:
function simple_math_functions_animation1
clc, close all, clear all
hf1=figure(1);hold on,grid on,axis equal, view([1 -1 1])
set(hf1,'Color','w');set(hf1,'Position',[300, 600, 500, 400]);
xlabel('x');ylabel('y'),zlabel('z');
k=1;
u = linspace(0,2*pi,72);
v = [-3:.2:-1,1:.2:3];
[U,V] = meshgrid(u,v);
r=sqrt((4*V.^-k)./(cos(U).^2+k*sin(U).^2));
X = r.*cos(U);
Y = r.*sin(U);
Z = V;
surf(X,Y,Z,'EdgeColor',[0.5 1. 0.2],'FaceColor',[1 0.2 0.8],'FaceAlpha',0.6);
XYZ=[reshape(X,1,prod(size(X)));
reshape(Y,1,prod(size(Y)));
reshape(Z,1,prod(size(Z)));
ones(1,prod(size(Z)))];
phi=[0 : pi/20 : 50*pi];
h=[]; axis([-20 20 -20 20 -20 20]);
for beta=phi % animation loop *****************
T=[cos(beta) -sin(beta) 0 0; % rotation matrix
sin(beta) cos(beta) 0 0;
0 0 1 0;
0 0 0 1];
XYZ1=T*XYZ; % coordinates changing
X1=reshape(XYZ1(1,:),size(X));Y1=reshape(XYZ1(2,:),size(Y));Z1=reshape(XYZ1(3,:),size(Z));
pause(0.1);if ~isempty(h),delete(h);end
h=surf(X1,Y1,Z1,'EdgeColor',[0.5 1. 0.2],'FaceColor',[0.2 0.2 0.8],'FaceAlpha',0.6);
end % ******************************************
end
You are getting that complex error because r is complex-valued. r is used in both X and Y and so when it's time to use surf on these inputs, you finally get that error. That makes sense because your range of V has negative values, and when you set k=1 for this expression:
r=sqrt((4*V.^-k)./(cos(U).^2+k*sin(U).^2));
You are effectively trying to take the square root of values in V and some of them are negative, and hence r is complex valued. If you look at your actual image you uploaded, you are missing a 2 in the power of V. Therefore:
r=sqrt((4*V.^2-k)./(cos(U).^2+k*sin(U).^2));
When I do this, then try running your code, I get this:
I'm very new to matlab and I have to use a for loop with increments of 15 for a coordinate point.
I've tried
for theta = 0; and(theta <= 360, theta >= 0)
theta +15;
end
and
theta = 0;
for theta = [0:360];
theta = theta+15;
end
the second one is the closes ive gotten to making it work but it still goes over 360, the first seems to make more sense but it gives me an error saying theta was previously used as a variable.
y12 = sind(theta) + cosd(theta);
y1 = sind(theta);
y2=cosd(theta);
plot(theta,y12)
plot(theta,y1)
plot(theta,y2)
title 'Project 7D - 3 curves(0-2*pi)'
xlabel 'Angle in Radian'
ylabel 'Function Value'
This doesn't actually need a loop.
theta=0:15:360;
y12 = sind(theta) + cosd(theta);
y1 = sind(theta);
y2 = cosd(theta);
hold all;
plot(theta,y12)
plot(theta,y1)
plot(theta,y2)
title 'Project 7D - 3 curves(0-2*pi)'
xlabel 'Angle in Radian'
ylabel 'Function Value'
for theta=0:15:360
y12 = sind(theta) + cosd(theta);
y1 = sind(theta);
y2=cosd(theta);
hold all;
plot(theta,y12)
plot(theta,y1)
plot(theta,y2)
title 'Project 7D - 3 curves(0-2*pi)'
xlabel 'Angle in Radian'
ylabel 'Function Value'
end
0:15:360 starts at 0 and increments 15 each time and ends at 360
f1 = 1 ;
N = 1024 ;
fs = 200 ;
ts = 1/fs ;
t = -(N/(2*fs)):ts:(N/(2*fs)) ;
theta=rand(0:2*pi);
X = sin(2*pi*f1*t+theta) ;
plot(t,x)
grid
Error using +
Matrix dimensions must agree.
And how can i calculate the autocorrelation of x function Rxx(n) ?
Replace the theta line by
theta = 2*pi*rand; %// generates a random number between 0 and 2*pi
and the plot line by
plot(t, X); %// capital "X", as you have defined previously
For the autocorrelation, you can use conv (correlation for real signals is equivalent to convolution with a time-reversal):
c = conv(X,fliplr(X));
plot(-(N/fs):ts:(N/fs), c)
To add to what Luis Mendo has answered, the reason for the error message is that:
>> size(t)
ans =
1 1025
>> size(theta)
ans =
0 1 2 3 4 5 6
So you are trying to add two things which are not the same dimension in X = sin(2*pi*f1*t+theta), hence the error message.
Use Luis's suggestions to fix your code.