Merge line stiles in MATLAB legend - matlab

I have two datasets which I want to plot in the same figure, e.g. two cosine and two sine plots which just differ in the amplitude:
x = -pi:pi/20:pi;
hold all;
amplitude = 1;
plot(x,amplitude*cos(x),'-');
plot(x,amplitude*sin(x),'-');
ax = gca;
ax.ColorOrderIndex = 1;
amplitude=3;
plot(x,amplitude*cos(x),'.');
plot(x,amplitude*sin(x),'.');
legend('1*cos(x)','1*sin(x)', '2*cos(x)','2*sin(x)');
hold off;
I want to "compress" the legend so that the two line stiles (normal line and dotted line) are "merged" and appear next to the same textual entry in the legend, such as:
How can I achieve this in MATLAB? I am currently using R2015b.

This is the closest I have got having a quick look with r2015b:
%%
f = figure;
ax = axes;
x = -pi:pi/20:pi;
hold all;
amplitude = 1;
c1 = plot(x,amplitude*cos(x),'-', 'DisplayName', 'cos(x)');
s1 = plot(x,amplitude*sin(x),'-', 'DisplayName', 'sin(x)');
ax.ColorOrderIndex = 1;
amplitude=3;
c2 = plot(x,amplitude*cos(x),'.', 'DisplayName', ' ');
s2 = plot(x,amplitude*sin(x),'.', 'DisplayName', ' ');
lg = legend([c1 c2 s1 s2]);
hold off;
Manipulating legends was easier pre HG2 - so using an older version of Matlab (r2013a) I get:
%%
f = figure;
ax = handle(axes);
x = -pi:pi/20:pi;
hold all;
amplitude = 1;
c1 = plot(x,amplitude*cos(x),'r-', 'DisplayName', 'cos(x)');
s1 = plot(x,amplitude*sin(x),'b-', 'DisplayName', 'sin(x)');
amplitude=3;
c2 = plot(x,amplitude*cos(x),'r.', 'DisplayName', ' ');
s2 = plot(x,amplitude*sin(x),'b.', 'DisplayName', ' ');
lg = handle(legend([c1 c2 s1 s2]));
hold off;
% You need to find which of the children on the legend is
% each of the plots:
c1 = handle(lg.Children(1));
c1.YData = 0.3;
s1 = handle(lg.Children(7));
s1.YData = 0.75;

Related

How to filter high freq signals?

So I have a task to do for university to filter High frequency signals in MATLAB. I wrote code for it but it doesn't filter correctly. This is my code:
A1 = 7;
A2 = 10;
A3 = 2;
f1 = 934;
f2 = 232;
f3 = 844;
th1 = 5*pi/4;
th2 = pi/4;
th3 = 5*pi/8;
M = 21;
T = 0.7823;
T1 = 0.0331;
T2 = 0.08;
fd = 6395;
K = 3;
Td = 1/fd; % diskretizavimo periodas
N = fix(T/Td); %modeliuojamo signalo reiksmiu skaicius
t = (0:N-1)*Td;
y = A1*sin(2*pi*f1*t + th1)+A2*sin(2*pi*f2*t + th2);
df = 1/T;
Nf=fd/df;
frib=(f1+f2)/2;
Wn = frib/fd;
b = fir1(M, Wn, 'high');
N2=Nf/2;
fasis = (0:Nf-1)*df;
filt_y = filter(b,1, y);
FILT_Y = abs(fft(filt_y, Nf));
figure(17)
subplot(2,1,1)
plot(t(1:T1*2/Td),filt_y(1:T1*2/Td));box('off'); axis tight;
set(gca, 'fontsize', 12); title('Filtruotas sinusu sumos signalas')
subplot(2,1,2)
stem(fasis(1:N2),FILT_Y(1:N2),'.');box('off'); axis tight;
set(gca, 'fontsize', 12); title('Filtruoto sinusu sumos signalo spektras')
frib is the cut-off frequency
And these are my diagrams:
Can you please tell me what can I do to make only one visible in the second diagram (near 1000 freq)? I don't know what I could do more
If you see the Fourier components of your signal before and after, you can clearly see how the first main frequency gets hugely reduced:
noFILT_Y = abs(fft(y, Nf));
subplot(2,1,2)
stem(fasis(1:N2),FILT_Y(1:N2),'.');box('off'); axis tight;
subplot(2,1,1)
stem(fasis(1:N2),noFILT_Y(1:N2),'.');box('off'); axis tight;
But if you follow #irreducible's suggestion:
Wn = frib/(fd/2);

Ordered Isoline Calculation from 3D Triangular Surface in MATLAB

I need to extract the isoline coordinates of a 4D variable from a 3D surface defined using a triangulated mesh in MATLAB. I need the isoline coordinates to be a ordered in such a manner that if they were followed in order they would trace the path i.e. the order of the points a 3D printer would follow.
I have found a function that can calculate the coordinates of these isolines (see Isoline function here) but the problem is this function does not consider the isolines to be joined in the correct order and is instead a series of 2 points separated by a Nan value. This makes this function only suitable for visualisation purposes and not the path to follow.
Here is a MWE of the problem of a simplified problem, the surface I'm applying it too is much more complex and I cannot share it. Where x, y and z are nodes, with TRI providing the element connectivity list and v is the variable of which I want the isolines extracted from and is not equal to z.
If anyone has any idea on either.....
A function to extract isoline values in the correct order for a 3D tri mesh.
How to sort the data given by the function Isoline so that they are in the correct order.
.... it would be very much appreciated.
Here is the MWE,
% Create coordinates
[x y] = meshgrid( -10:0.5:10, -10:0.5:10 );
z = (x.^2 + y.^2)/20; % Z height
v = x+y; % 4th dimension value
% Reshape coordinates into list to be converted to tri mesh
x = reshape(x,[],1); y = reshape(y,[],1); z = reshape(z,[],1); v = reshape(v,[],1);
TRI = delaunay(x,y); % Convertion to a tri mesh
% This function calculates the isoline coordinates
[xTows, yTows, zTows] = IsoLine( {TRI,[x, y, z]}, v, -18:2:18);
% Plotting
figure(1); clf(1)
subplot(1,2,1)
trisurf(TRI,x,y,z,v)
hold on
for i = 1:size(xTows,1)
plot3( xTows{i,1}, yTows{i,1}, zTows{i,1}, '-k')
end
hold off
shading interp
xlabel('x'); ylabel('y'); zlabel('z'); title('Isolines'), axis equal
%% This section is solely to show that the isolines are not in order
for i = 1:size(xTows,1)
% Arranging data into colums and getting rid of Nans that appear
xb = xTows{i,1}; yb = yTows{i,1}; zb = zTows{i,1};
xb = reshape(xb, 3, [])'; xb(:,3) = [];
yb = reshape(yb, 3, [])'; yb(:,3) = [];
zb = reshape(zb, 3, [])'; zb(:,3) = [];
subplot(1,2,2)
trisurf(TRI,x,y,z,v)
shading interp
view(2)
xlabel('x'); ylabel('y'); zlabel('z'); title('Plotting Isolines in Order')
axis equal; axis tight; hold on
for i = 1:size(xb,1)
plot3( [xb(i,1) xb(i,2)], [yb(i,1) yb(i,2)], [zb(i,1) zb(i,2)], '-k')
drawnow
end
end
and here is the function Isoline, which I have slightly adpated.
function [xTows, yTows, zTows] = IsoLine(Surf,F,V,Col)
if length(Surf)==3 % convert mesh to triangulation
P = [Surf{1}(:) Surf{2}(:) Surf{3}(:)];
Surf{1}(end,:) = 1i;
Surf{1}(:,end) = 1i;
i = find(~imag(Surf{1}(:)));
n = size(Surf{1},1);
T = [i i+1 i+n; i+1 i+n+1 i+n];
else
T = Surf{1};
P = Surf{2};
end
f = F(T(:));
if nargin==2
V = linspace(min(f),max(f),22);
V = V(2:end-1);
elseif numel(V)==1
V = linspace(min(f),max(f),V+2);
V = V(2:end-1);
end
if nargin<4
Col = 'k';
end
H = NaN + V(:);
q = [1:3 1:3];
% -------------------------------------------------------------------------
% Loop over iso-values ----------------------------------------------------
xTows = [];
yTows = [];
zTows = [];
for k = 1:numel(V)
R = {[],[]};
G = F(T) - V(k);
C = 1./(1-G./G(:,[2 3 1]));
f = unique(T(~isfinite(C))); % remove degeneracies by random perturbation
F(f) = F(f).*(1+1e-12*rand(size(F(f)))) + 1e-12*rand(size(F(f)));
G = F(T) - V(k);
C = 1./(1-G./G(:,[2 3 1]));
C(C<0|C>1) = -1;
% process active triangles
for i = 1:3
f = any(C>=0,2) & C(:,i)<0;
for j = i+1:i+2
w = C(f,q([j j j]));
R{j-i} = [R{j-i}; w.*P(T(f,q(j)),:)+(1-w).*P(T(f,q(j+1)),:)];
end
end
% define isoline
for i = 1:3
X{i} = [R{1}(:,i) R{2}(:,i) nan+R{1}(:,i)]';
% X{i} = [R{1}(:,i) R{2}(:,i)]'; % Changed by Matt
X{i} = X{i}(:)';
end
% plot isoline
if ~isempty(R{1})
% hold on
% H(k) = plot3(X{1},X{2},X{3},Col);
% Added by M.Thomas
xTows{k,1} = X{1};
yTows{k,1} = X{2};
zTows{k,1} = X{3};
end
end
What you will notice is that the isolines (xTows, yTows and zTows) are not in order there "jump around" when plotted sequentially. I need to sort the tows so that they give a smooth plot in order.

Putting one legend for many axes

I'm using three axes-Objects to scale my data on the x-axis.
My problem is that i do not know how to get a nice legend for the three plots.
I have to do this cause my real data is sampled with different sample rates.
I edited my m-file for the diagram slightly cause normally I'm reading the data out of some txt files.
In this example i used example_data 1 to 3 for my data.
In this example I'm scaling the example_data1 so it looks like the same frequency as example_data2.
I do the 'scaling' ax1.XLim = [0 length(x2)].
That's why this solution doesn't work for me: Plot with multiple axes but only one legend.
It uses set(l3,'Parent',ax2); which somehow ruins my approache to scale my data. The scaling is the only solution to my problem cause i don't know the exact relation between the two sampling rates.
my code:
example_data1 = repmat(1:100,1,10);
example_data2 = 2 * repmat(1:0.5:100.5,1,5);
example_data3 = [1:500 500:-1:1];
whole_length_data1 = length(example_data1);
% 1. step
start_of_data = 1;
end_of_data = 1000;
% data2
y2 = example_data2(start_of_data:end_of_data);
x2 = 0:length(y2)-1;
% data3
y3 = example_data3(start_of_data:end_of_data);
x3 = 0:length(y3)-1;
% data1
y1 = example_data1(1:length(example_data1));
x1 = 0:length(y1)-1;
% 2. step
start_one = 1;
y1 = example_data1(start_one:length(example_data1));
x1 = 0:length(y1)-1;
% 3.step
end_one = whole_length_data1 - 500;
y1 = example_data1(start_one:end_one);
x1 = 0:length(y1)-1;
Farbe1 = [0,1,0]*0.6; % Dunkelgrün
Farbe2 = [1,0,0]*0.8; % Dunkelrot
Farbe3 = get(groot,'DefaultAxesColorOrder') % default values
Farbe3 = Farbe3(1,:); % 1. Zeile der defaultvalues
figure(1)
% 3 axes
clf
%------------------------------------------------------------------
%-------------------------- plot1: ---------------------------
%------------------------------------------------------------------
plot(x2,y2,'green','LineWidth',2,'Color',Farbe1,...
'DisplayName','name of the first plot')
ax1 = gca;
ax1.XLim = [0 length(x2)]
ax1.YLim = [min(y2) max(y2)]
ax1.YTick = [0:25:300]
ax1.FontSize = 12;
legend('show')
%----------------------------------------------------------------
%-------------------------- plot2: --------------------------
%----------------------------------------------------------------
ax2 = axes('Position',ax1.Position);
plot(x3,y3,'blue','LineWidth',2,'Color',Farbe3,...
'DisplayName','plot2')
ax2.Color = 'none';
ax2.XTick = [];
ax2.XLim = [0 length(x3)];
ax2.YAxisLocation = 'right';
ax2.FontSize = 12;
legend('show')
%----------------------------------------------------------------
%-------------------------- plot3: -------------------------
%----------------------------------------------------------------
ax3 = axes('Position',ax1.Position);
plot(x1,y1,'red','LineWidth',2,'Color',Farbe2,...
'DisplayName','3')
ax3.XTick = [];
ax3.YTick = [];
ax3.Color = 'none';
ax3.XAxisLocation = 'top';
ax3.YAxisLocation = 'right';
ax3.XLim = [0 length(x1)];
ax3.YLim = [min(y1) max(y1)*2];
legend('show')
This results in a very bad looking legend:
I really hope somebody can help me.
Thank very much.
You can get better results by storing handles for each of your plot lines, then passing those to a single legend call:
...
h1 = plot(x2,y2,'green','LineWidth',2,'Color',Farbe1,...
'DisplayName','name of the first plot');
...
h2 = plot(x3,y3,'blue','LineWidth',2,'Color',Farbe3,...
'DisplayName','plot2');
...
h3 = plot(x1,y1,'red','LineWidth',2,'Color',Farbe2,...
'DisplayName','3');
...
hl = legend(ax3, [h1 h2 h3]); % Place legend in top-most axes
And here's the result:
Just use the real timestamps as x values:
fig = figure;
plot(x1/length(y1)*end_of_data, y1, 'LineWidth',2, 'Color',Farbe1, 'DisplayName','First plot')
hold on
plot(x2/length(y2)*end_of_data, y2, 'LineWidth',2, 'Color',Farbe2, 'DisplayName','Second plot')
plot(x3/length(y3)*end_of_data, y3, 'LineWidth',2, 'Color',Farbe3, 'DisplayName','Third plot')
legend

Cut/delete part of plot

I have the following plot:
As you can see, the plot has some data on the left (blue) and on the right (red). However, I want them to be closer together. For example by cutting the box area, because there is no data, such that the 'red area' is more shifted to the left (and thus closer to the 'blue area').
How to proceed further?
MWE:
x = 0:0.05:1.3
y = x;
plot(x,y,'color','k','Linewidth',1);
hold on;
x1 = [0.1:0.05:x(end)];
y1 = [0:0.05:y(end)-0.1];
plot(x1,y1,'--k','Linewidth',1);
x2 = [0:0.05:x(end)];
y2 = [0.10:0.05:y(end)+0.1];
plot(x2,y2,'--k','Linewidth',1);
xlim([0 x(end)]);
ylim([0 y(end)]);
hold on
for i = 1:9;
a = plot(P(i).mHlfA1,P(i).sHlfA1, 'bx');
hold on
b = plot(P(i).mHlfA1,P(i).sLFA1, 'bo');
hold on
c = plot(P(i).mHlfA2,P(i).sHlfA2, 'rd');
hold on
d = plot(P(i).mHlfA2,P(i).sLFA2, 'r*');
hold on;
end
Here is an alternative tweak (to that suggested in the comments), using the XtickLable:
x = [1:10 200:2:230];
y = 3.*x-9;
% define the parts of he x-axis:
xgap = 11;
% pasting together the two parts:
new_x = [x(1:xgap-1) x(xgap-1)+(x(xgap:end)-x(xgap))+(x(xgap+2)-x(xgap+1))];
ax = axes;
plot(ax,new_x,y,'o');
% get the ticks to change:
left_x_tick = ax.XTick(ax.XTick<=x(xgap-1));
right_x_tick = ax.XTick(ax.XTick>x(xgap-1));
slash = left_x_tick(end)+floor((right_x_tick(1)-left_x_tick(end))/2);
ax.XTick = [left_x_tick slash right_x_tick];
% get the spacing between ticks
inc = right_x_tick - min(right_x_tick);
% create new tick labels:
new_tick = cellstr(num2str(inc.' + min(x(xgap:end))+(right_x_tick(1)-new_x(xgap))));
% replace the tick labels in the plot
ax.XTickLabel(ax.XTick>new_x(xgap)) = new_tick;
% put a // where there is a gap:
slash_loc = find(ax.XTick==slash,1,'first');
ax.XTickLabel(slash_loc) = {'//'};
ax.FontSize = 14;
This is before:
and after:
This is just an idea for workaround, it could be further adjusted to the specific needs.

How to manage the legend for many plots

How do I handle this issue
clear all; close all; clc;
r = 0:5;
zeta = 0:0.1:1;
for i = 1:size(zeta(:))
for j = 1:size(r(:))
X(i,j) = sqrt((1+(2*zeta(i)*r(j))^2)/((1-r(j)^2)^2+ (2*zeta(i)*r(j))^2));
end
plot(r,X);
xlabel('r = \omega/\omega_n');
ylabel('M = \frac{X}{Y}');
hold all
grid
[~,~,~,current_entries] = legend;
legend([current_entries {sprintf('\zeta = %i',zeta(i))}]);
end
figure
plot(r,X)
grid
The hold all command doesn't seem to be working properly. What can I do to fix this?
You will want to set the DisplayName property of the plot. Then when you create the legend, the labels will automatically be populated.
plot(r, X, 'DisplayName', 'name')
Also, the string that you were passing to sprintf for your legends needs to be escaped becuase sprintf thinks that \z is a control character.
plot(r, X, 'DisplayName', sprintf('\\zeta = %0.1f',zeta(k)))
Also, hold on is recommended over hold all. Also, it is best practice to specify the axes handle when calling hold to ensure that it is applied to the current axes.
hold(hax, 'on')
So if we incorporate these changes into your plotting code (along with #R.Falque's idea to use semilogy)
r = 0:0.001:5;
zeta = 0:0.1:1;
hax = axes();
colors = hsv(numel(zeta));
for k = 1:numel(zeta)
X = sqrt((1 + (2 * zeta(k) * r).^2) ./ ((1-r.^2).^2+ (2*zeta(k)*r).^2));
semilogy(r, X, ...
'DisplayName', sprintf('\\zeta = %0.1f',zeta(k)), ...
'Color', colors(k,:));
hold(hax, 'on')
end
grid(hax, 'on')
xlabel(hax, 'r = \omega/\omega_n', 'Interpreter', 'tex');
ylabel(hax, 'M = $\displaystyle\frac{X}{Y}$', 'Interpreter', 'latex');
L = legend('show');
You can also use cells array as follow:
clear all; close all; clc;
r = 0:0.001:5;
zeta = 0:0.1:1;
figure;
for i = 1:length(zeta)
for j = 1:length(r)
X(j) = sqrt((1+(2*zeta(i)*r(j))^2)/((1-r(j)^2)^2+ (2*zeta(i)*r(j))^2));
end
semilogy(r,X);
hold on
legend_string{i} = ['\zeta = ', num2str(zeta(i))];
end
hold off
grid
xlabel('r = \omega/\omega_n');
ylabel('M = $\frac{X}{Y}$','Interpreter', 'Latex');
legend(legend_string);
Note that you have an error in the X definition (corrected from X(i,j) to X(j)).