I would like to connect two or multiple data points during multiple Matlab plot calls. Below is some dummy code that shows what I am trying to accomplish,
function main()
close all
t = 0;
while t<10
x = rand(1,1);
y = rand(1,1);
t = t+1;
calculateErrorAndPlot(1,x,y,t)
end
return
function calculateErrorAndPlot(figureNumber,x,y,time)
figure(figureNumber);
if ~ishold
hold on
end
error = x-y;
plot(time,error,'.b');
return
Right now, I have to use '.b' to at least see the data points being plotted. Note that plot is being called with scalars.
You could update the XData and YData properties of a line already plotted to add a new point. For example:
function main
fig = figure;
ax = axes( 'Parent', fig );
line = plot( NaN, NaN, 'Parent', ax );
for t = 0:9
x = rand( 1, 1 );
y = rand( 1, 1 );
calculateErrorAndPlot( line, x, y, t )
end
end
function calculateErrorAndPlot( line, x, y, t )
xData = get( line, 'XData' );
yData = get( line, 'YData' );
xData(end+1) = t;
yData(end+1) = x - y;
set( line, 'XData', xData, 'YData', yData );
end
I would rewrite this, and remove the function:
time = [1:10];
for ii = 1:length(time)
x = rand(1,1);
y = rand(1,1);
error(ii) = x-y;
end
plot(time,error,'.-b')
Here is some code that works. However, I am open to suggestions or other code that may be simpler and/or faster :)
function calculateErrorAndPlot(figureNumber,x,y,time)
figure(figureNumber);
if ~ishold
hold on
end
error = x-y;
h = findobj(figure(figureNumber),'Type','line');
if isempty(h)
xx = time;
yy = error;
else
xx = [h(1).XData(end) time];
yy = [h(1).YData(end) error];
end
plot(xx,yy,'.-black');
return
Related
By plotting the curves in a loop that each curve is related to its own stored data (i.e. method) in the previous question, how is it possible to change and define a new specific color and line type (e.g. "--") for each method's curve after the loop?
figure
hold on
for m = 1:length(methods)
prFileName = strcat(readpath,dataset, '_', methods{m}, '_PRCurve.txt');
R = load(prFileName);
precision = R(:, 1);
recall = R(:, 2);
plot(recall, precision,'color',methods_colors(m,:),'linewidth',2);
end
axis([0 1 0 1]);
hold off
grid on;
box on;
legend('methode one','method two')
xlabel('Recall','fontsize',12);
ylabel('Precision','fontsize',12);
set(gcf,'color','w'); %Background color
ax = gca; % current axes
ax.GridLineStyle='-';
ax.GridAlpha=0.7;
ax.XAxis.LineWidth=4;
ax.YAxis.LineWidth=4;
Grid.LineWidth = 3;
set(gca,'FontName','Arial','FontWeight','bold');
For convenience in implementation, each method's data can be assumes as rand(256x2).
You can use lines_obj = findobj(h, 'Type', 'Line'), and set(lines_obj, ....
Example:
h = figure;
%Plot something
x = -6:0.1:6;
plot(x, sin(x), x, cos(x), x, sin(x.^2), x, cos(x.^2));
%Find all lines in figure
lines_obj = findobj(h, 'Type', 'Line');
%Set style of all lines to '--'
set(lines_obj, 'LineStyle', '--');
result:
In case you need to set different style for each line, and you need to match as specific type to specific plot...
Keep an array of line objects while plotting, and use a for loop:
close all
g = gobjects(4); %Initialize and array with 4 graphics objects.
x = -6:0.1:6;
g(1) = plot(x, sin(x), 'r');hold on %Keep returned object in g(1)
g(2) = plot(x, cos(x), 'g'); %Keep returned object in g(2)
g(3) = plot(x, sin(x.^2), 'b');
g(4) = plot(x, cos(x.^2), 'y');
%Cell array of styles.
styles = {'-', '--', ':', '-.'};
%Array of width values
widths = [0.5, 1, 1.5, 2];
%Modify the style of each line:
for i = 1:length(g)
g(i).LineStyle = styles{i};
g(i).LineWidth = widths(i);
end
Result:
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)).
This is a continuation from the question already posted here. I used the method that #Andrey suggested. But there seems to be a limitation. the set(handle, 'XData', x) command seems to work as long as x is a vector. what if x is a matrix?
Let me explain with an example.
Say we want to draw 3 rectangles whose vertices are given by the matrices x_vals (5,3 matrix) and y_vals (5,3 matrix). The command that will be used to plot is simply plot(x,y).
Now, we want to update the above plot. This time we want to draw 4 rectangles. whose vertices are present in the matrices x_new(5,4 matrix) and y_new (5,4 matrix) that we obtain after some calculations. Now using the command set(handle, 'XData', x, 'YData', y) after updating x and y with new values results in an error that states
Error using set
Value must be a column or row vector
Any way to solve this problem?
function [] = visualizeXYZ_struct_v3(super_struct, start_frame, end_frame)
% create first instance
no_objs = length(super_struct(1).result);
x = zeros(1,3000);
y = zeros(1,3000);
box_x = zeros(5, no_objs);
box_y = zeros(5, no_objs);
fp = 1;
% cascade values across structures in a frame so it can be plot at once;
for i = 1:1:no_objs
XYZ = super_struct(1).result(i).point_xyz;
[r,~] = size(XYZ);
x(fp:fp+r-1) = XYZ(:,1);
y(fp:fp+r-1) = XYZ(:,2);
% z(fp:fp+r-1) = xyz):,3);
fp = fp + r;
c = super_struct(1).result(i).box;
box_x(:,i) = c(:,1);
box_y(:,i) = c(:,2);
end
x(fp:end) = [];
y(fp:end) = [];
fig = figure('position', [50 50 1280 720]);
hScatter = scatter(x,y,1);
hold all
hPlot = plot(box_x,box_y,'r');
axis([-10000, 10000, -10000, 10000])
xlabel('X axis');
ylabel('Y axis');
hold off
grid off
title('Filtered Frame');
tic
for num = start_frame:1:end_frame
no_objs = length(super_struct(num).result);
x = zeros(1,3000);
y = zeros(1,3000);
box_x = zeros(5, no_objs);
box_y = zeros(5, no_objs);
fp = 1;
% cascade values accross structures in a frame so it can be plot at once;
for i = 1:1:no_objs
XYZ = super_struct(num).result(i).point_xyz;
[r,~] = size(XYZ);
x(fp:fp+r-1) = XYZ(:,1);
y(fp:fp+r-1) = XYZ(:,2);
fp = fp + r;
c = super_struct(num).result(i).box;
box_x(:,i) = c(:,1);
box_y(:,i) = c(:,2);
end
x(fp:end) = [];
y(fp:end) = [];
set(hScatter, 'XData', x, 'YData', y);
set(hPlot, 'XData', box_x, 'YData', box_y); % This is where the error occurs
end
toc
end
Each line on the plot has its own XData and YData properties, and each can be set to a vector individually. See the reference. I am not at a Matlab console right now, but as I recall...
kidnum = 1
h_axis = gca % current axis - lines are children of the axis
kids = get(h_axis,'Children')
for kid = kids
kid_type = get(kid,'type')
if kid_type == 'line'
set(kid,'XData',x_new(:,kidnum))
set(kid,'YData',y_new(:,kidnum))
kidnum = kidnum+1
end
end
Hope that helps! See also the overall reference to graphics objects and properties.
To add a series, say
hold on % so each "plot" won't touch the lines that are already there
plot(x_new(:,end), y_new(:,end)) % or whatever parameters you want to plot
After that, the new series will be a child of h_axis and can be modified.
I need to plot lines on a 3d surface in matlab. I wrote two scripts one to plot the surface and one to plot the lines on the surface.
script1
function figHandle = plotFunction(funchandle, a , b)
x_vec = -a:.1:a;
y_vec = -b:.1:b;
figHandle = figure();
[X,Y] = meshgrid(x_vec, y_vec);
Z = [];
z_temp = [];
for i = x_vec
for j = y_vec
z_temp =[z_temp; funchandle([i; j])];
end;
Z = [Z, z_temp];
z_temp = [];
end;
h = surf(X,Y,Z);
set(h, 'edgecolor', 'none');
%colormap gray;
colorbar;
view(0,90);
end
script2
function plotStep(figHandle,funcHandle, P1, P2)
figure(figHandle);
hold on;
plot3(P1(1), P1(2), funcHandle([P1(1);P1(2)]), 'r*');
if ~isequal(P1,P2)
Z = [];
X = [];
Y = [];
d = [P2(1)-P1(1), P2(2) - P1(2)];
if (P1(1) < P2(1))
X = P1(1):0.0001:P2(1);
if (P1(2) ~= P2(2))
Y = X.*(d(2)/d(1))+(P1(2)-(d(2)/d(1))*P1(1));
else
for i = X
Y = [Y, P1(2)];
end
end
elseif P1(1) > P2(1)
X = P1(1):-0.0001:P2(1);
if (P1(2) ~= P2(2))
Y = X.*(d(2)/d(1))+(P1(2)-(d(2)/d(1))*P1(1));
else
for i = X
Y = [Y, P1(2)];
end
end
else
if P1(2) < P2(2)
Y = P1(2):0.0001:P2(2);
for i = Y
X = [X, P1(1)];
end
else
Y = P1(2):-0.0001:P2(2);
for i = Y
X = [X, P1(1)];
end
end
end
for i = 1:length(X)
Z = [Z;funcHandle([X(i);Y(i)])];
end
plot3(X, Y, Z, 'r', 'LineWidth', 2);
end
My problem is no matter how close too each other I make the points which form the line after zooming in on the line it does not seem to be continuous.
The simplest way to fix this is to raise the line a little bit off the surface,
plot3(X, Y, Z+1, 'r', 'LineWidth', 2);
but you may have to experiment with the offset. It can sometimes help to make the line a little wider, and to try the different renderers as well.
I want to draw a set of disconnected lines on top of an image by dragging the mouse while pressing the left button. However, if I click on a previously drawn line to specify the starting point of the next line, the callback function is not being called and I don't get the point. Here is my code:
function main_test
S.fH = figure('menubar','none');
im = imread( 'image.jpg' );
S.aH = axes;
S.iH = imshow( im ); hold on
axis image;
X = [];
Y = [];
set(S.aH,'ButtonDownFcn',#startDragFcn)
set(S.iH,'ButtonDownFcn',#startDragFcn)
set(S.fH, 'WindowButtonUpFcn', #stopDragFcn);
function startDragFcn(varargin)
set( S.fH, 'WindowButtonMotionFcn', #draggingFcn );
pt = get(S.aH, 'CurrentPoint');
x = pt(1,1);
y = pt(1,2);
X = x;
Y = y;
end
function draggingFcn(varargin)
pt = get(S.aH, 'CurrentPoint');
x = pt(1,1);
y = pt(1,2);
X = [X x];
Y = [Y y];
plot(X, Y, 'r', 'LineWidth', 6);
hold on
drawnow
end
function stopDragFcn(varargin)
set(S.fH, 'WindowButtonMotionFcn', ''); %eliminate fcn on release
end
end
Would you please help me to identify the problem in this.
Thank you in a advance..
cheers,
Sawsan
You need to set the ButtonDownFcn property on the plotted line as well, i.e.
plot(X,Y,'r','LineWidth',6,'ButtonDownFcn',#startDragFcn)