Get currently selected data point from axes in a figure - matlab

I have a MATLAB figure with an axes containing a scatter plot.
Every point on this scatter plot has a signal data array associated with it.
I want to take user input as point selection from scatter plot and plot the corresponding signal data in another axes on the same figure.

Combine a global definition of your second axes with an UpdateFcn on the datacursor. See example below, which generates a sine wave based on a random variable selected.
function getSelectedDataPoint()
% create figure
fig = figure;
% make second axes a global to adress in myupdatefcn
global ax2
% define axes
ax1 = axes('parent',fig,'position',[0.05 0.05 0.9 0.4]);
ax2 = axes('parent',fig,'position',[0.05 0.55 0.9 0.4]);
% Random scatter
scatter(ax1,rand(25,1),rand(25,1),25,'filled')
% Set datacursormode to on
dcm_obj = datacursormode(fig);
datacursormode on
% Specify objective function for clock
set(dcm_obj,'UpdateFcn',#myupdatefcn)
% Define objective function
function text = myupdatefcn(~,obj)
text = sprintf('X: %f \n Y: %f',[obj.Position(1),obj.Position(2)]);
% Find corresponding signal
id = find(and(obj.Position(1) == obj.Target.XData,obj.Position(2) ==
obj.Target.YData));
% Do your thing with the signals
x = 0:0.1:100;
y = sin(obj.Target.XData(id)*x);
% plot on second axes
plot(ax2,x,y)
end
end

I am not sure I entirely understand what you want to do, but you might want to use the "Tag" option available for many objects in MATLAB.
Replace line under "Random scatter" by:
% Random scatter
hold(ax1,'on')
scatTag = cell(1,10);
for i = 1:10
scatTag{i} = scatter(ax1,rand(1,1),rand(1,1),25,'filled');
scatTag{i}.Tag = num2str(i);
end
In the data cursor update function, replace the line "id= find(..."
tagname = obj.Target.Tag;
And modify the signal function to point to your target tag, whatever function that is. In my example, you could do this to define the y-values:
y = sin(str2double(tagname)*x);
It will generate another sine wave, based on the tag.
Hope this helps :)

Related

Separate initialization and display of plot

What is a proper way to separate the initialization and the display of plots in Matlab? (I mean plots in a wide sense here; could be plot, plot3, scatter etc.) To give a concrete example, I have a pretty complex 3D visualization that uses sphere and mesh to draw a static sphere mesh and then scatter3 to plot a moving trajectory on the sphere. To be able to do this in real time I have implemented some simple optimizations, such as only updating the scatter3 object each frame. But the code is a bit messy, making it hard to add additional features that I want, so I would like improve code separation.
I also feel like it might sometimes be useful to return some kind of plot object from a function without displaying it, for example to combine it with other plots in a nice modular way.
An example of what I have in mind would be something like this:
function frames = spherePlot(solution, options)
% Initialize sphere mesh and scatter objects, configure properties.
...
% Configure axes, maybe figure as well.
...
% Draw sphere.
...
if options.display
% Display figure.
end
for step = 1:solution.length
% Update scatter object, redraw, save frame.
% The frames are saved for use with 'movie' or 'VideoWriter'.
end
end
Each step might also be separated out as a function.
So, what is a neat and proper way to do stuff like this? All documentation seems to assume that one wants to display everything right away.
For example
% some sample data
N = 100;
phi = linspace(-pi, pi, N);
theta = linspace(-pi, pi, N);
f = #(phi, theta) [sin(phi).*cos(theta); sin(phi).*sin(theta); cos(phi)];
data = f(phi, theta);
% init plot
figure(1); clf
plot3(data(1,:), data(2,:), data(3,:)); % plot path, not updated
hold on
p = plot3([0 data(1,1)], [0 data(2,1)], [0 data(3,1)]); % save handle to graphics objects to update
s = scatter3(data(1,1), data(2,1), data(3,1), 'filled');
axis equal
xlabel('x'); ylabel('y'); zlabel('z');
t = title('first frame'); % also store handle for title or labels to update during animation
% now animate the figure
for k = 1:N
p.XData = [0 data(1,k)]; % update line data
p.YData = [0 data(2,k)];
p.ZData = [0 data(3,k)];
s.XData = data(1,k); % update scatter data
s.YData = data(2,k);
s.ZData = data(3,k);
t.String = sprintf('frame %i', k); % update title
drawnow % update figure
end
Basically you can update all values for a graphics handle, in this case 'p' and 's'. If you open the matlab doc for plot or plot3 you will find a link to all properties of that primitive: e.g. Line Properties. Similar documentation pages exist for scatter/imagesc etc.
So the general idea is to first create a figure with the first frame, save the handles to the objects you would like to update (p = plot(...), and then enter a loop in which you update the required property of that graphics object (e.g. p.Color = 'r', or p.XData = ...).

Reset ColorOrder index for plotting in Matlab / Octave

I have matrices x1, x2, ... containing variable number of row vectors.
I do successive plots
figure
hold all % or hold on
plot(x1')
plot(x2')
plot(x3')
Matlab or octave normally iterates through ColorOrder and plot each line in different color. But I want each plot command to start again with the first color in colororder, so in default case the first vector from matrix should be blue, second in green, third in red etc.
Unfortunately I cannot find any property related to the color index niether another method to reset it.
Starting from R2014b there's a simple way to restart your color order.
Insert this line every time you need to reset the color order.
set(gca,'ColorOrderIndex',1)
or
ax = gca;
ax.ColorOrderIndex = 1;
see:
http://au.mathworks.com/help/matlab/graphics_transition/why-are-plot-lines-different-colors.html
You can shift the original ColorOrder in current axes so that the new plot starts from the same color:
h=plot(x1');
set(gca, 'ColorOrder', circshift(get(gca, 'ColorOrder'), numel(h)))
plot(x2');
You can wrap it in a function:
function h=plotc(X, varargin)
h=plot(X, varargin{:});
set(gca, 'ColorOrder', circshift(get(gca, 'ColorOrder'), numel(h)));
if nargout==0,
clear h
end
end
and call
hold all
plotc(x1')
plotc(x2')
plotc(x3')
Define a function that intercepts the call to plot and sets 'ColorOrderIndex' to 1 before doing the actual plot.
function plot(varargin)
if strcmp(class(varargin{1}), 'matlab.graphics.axis.Axes')
h = varargin{1}; %// axes are specified
else
h = gca; %// axes are not specified: use current axes
end
set(h, 'ColorOrderIndex', 1) %// reset color index
builtin('plot', (varargin{:})) %// call builtin plot function
I have tested this in Matlab R2014b.
I found a link where a guy eventually solves this. He uses this code:
t = linspace(0,1,lineCount)';
s = 1/2 + zeros(lineCount,1);
v = 0.8*ones(lineCount,1);
lineColors = colormap(squeeze(hsv2rgb(t,s,v)))
ax=gca
ax.ColorOrder = lineColors;
Which should work for you assuming each of your matrices has the same number of lines. If they don't, then I have a feeling you're going to have to loop and plot each line separately using lineColors above to specify an RBG triple for the 'Color' linespec property of plot. So you could maybe use a function like this:
function h = plot_colors(X, lineCount, varargin)
%// For more control - move these four lines outside of the function and make replace lineCount as a parameter with lineColors
t = linspace(0,1,lineCount)'; %//'
s = 1/2 + zeros(lineCount,1);
v = 0.8*ones(lineCount,1);
lineColors = colormap(squeeze(hsv2rgb(t,s,v)));
for row = 1:size(X,1)
h = plot(X(row, :), 'Color', lineColors(row,:), varargin{:}); %// Assuming I've remembered how to use it correctly, varargin should mean you can still pass in all the normal plot parameters like line width and '-' etc
hold on;
end
end
where lineCount is the largest number of lines amongst your x matrices
If you want a slightly hacky, minimal lines-of-code approach perhaps you could plot an appropriate number of (0,0) dots at the end of each matrix plot to nudge your colourorder back to the beginning - it's like Mohsen Nosratinia's solution but less elegant...
Assuming there are seven colours to cycle through like in matlab you could do something like this
% number of colours in ColorOrder
nco = 7;
% plot matrix 1
plot(x1');
% work out how many empty plots are needed and plot them
nep = nco - mod(size(x1,1), nco); plot(zeros(nep,nep));
% plot matrix 2
plot(x2');
...
% cover up the coloured dots with a black one at the end
plot(0,0,'k');

Change the LineWidth in some parts of a plot

I have an ECG signal and some special points of it calculated.
I want to have thicker LineWidth between those points (each pair). I did a sample with brush.
Here are my variables,
signal % the ECG signal
t % time
Q % location of red points
T % location of yellow points
Four of these pairs are visible in picture, but there are more.
Is it possible without loop _ hold on?
You can just use hold on and plot the data again on the region of interest:
% Some dummy data
x = 0:0.01:10;
y = sin(x);
plot(x,y)
% Data that we want emphasized
% You can also select a subset of your existing data
x_start = 2;
x_end = 4;
x_thick_line = x_start:0.01:x_end;
y_thick_line = sin(x_thick_line);
% Plot over the existing plot with thicker line
hold on
plot([x_start x_end],[y_thick_line(1) y_thick_line(end)],'ro',...
x_thick_line,y_thick_line,'Color','r','LineWidth',6')
This gives the following result in Octave, should be the same in MATLAB:
You should plot that function three times (assuming a..b shall be styled differently):
0..a - standard settings
a..b - use alternate color / line_width / ...
b..end

Matlab plot of several digital signals

I'm trying to find a way to nicely plot my measurement data of digital signals.
So I have my data available as csv and mat file, exported from an Agilent Oscilloscope. The reason I'm not just taking a screen shot of the Oscilloscope screen is that I need to be more flexible (make several plots with one set of data, only showing some of the lines). Also I need to be able to change the plot in a month or two so my only option is creating a plot from the data with a computer.
What I'm trying to achieve is something similar to this picture:
The only thing missing on that pic is a yaxis with 0 and 1 lines.
My first try was to make a similar plot with Matlab. Here's what I got:
What's definitely missing is that the signal names are right next to the actual line and also 0 and 1 ticks on the y-axis.
I'm not even sure if Matlab is the right tool for this and I hope you guys can give me some hints/a solution on how to make my plots :-)
Here's my Matlab code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*1.5, 'color', cm(i-1,:));
end
hold off;
legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
set(gca, 'ytick', []); % hide y axis
Thank you guys for your help!
Dan
EDIT:
Here's a pic what I basically want. I added the signal names via text now the only thing that's missing are the 0, 1 ticks. They are correct for the init done signal. Now I just need them repeated instead of the other numbers on the y axis (sorry, kinda hard to explain :-)
So as written in my comment to the question. For appending Names to each signal I would recommend searching the documentation of how to append text to graph. There you get many different ways how to do it. You can change the position (above, below) and the exact point of data. As an example you could use:
text(x_data, y_data, Var_Name,'VerticalAlignment','top');
Here (x_data, y_data) is the data point where you want to append the text and Var_Name is the name you want to append.
For the second question of how to get a y-data which contains 0 and 1 values for each signal. I would do it by creating your signal the way, that your first signal has values of 0 and 1. The next signal is drawn about 2 higher. Thus it changes from 2 to 3 and so on. That way when you turn on y-axis (grid on) you get values at each integer (obviously you can change that to other values if you prefer less distance between 2 signals). Then you can relabel the y-axis using the documentation of axes (check the last part, because the documentation is quite long) and the set() function:
set(gca, 'YTick',0:1:last_entry, 'YTickLabel',new_y_label(0:1:last_entry))
Here last_entry is 2*No_Signals-1 and new_y_label is an array which is constructed of 0,1,0,1,0,....
For viewing y axis, you can turn the grid('on') option. However, you cannot chage the way the legends appear unless you resize it in the matlab figure. If you really want you can insert separate textboxes below each of the signal plots by using the insert ->Textbox option and then change the property (linestyle) of the textbox to none to get the exact same plot as above.
This is the end result and all my code, in case anybody else wants to use the good old ctrl-v ;-)
Code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*2, 'color', cm(i-1,:));
text(MD.D(2,1), (i-2)*2+.5, MD.N(i-1));
end
hold off;
%legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
% make y axis and grid the way I want it
set(gca, 'ytick', 0:size(MD.D,2)*2-3);
grid off;
set(gca,'ygrid','on');
set(gca, 'YTickLabel', {'0'; '1'});
ylim([-1,(size(MD.D,2)-1)*2]);

How do I reach first and second plots from bode()

I know how to create the Bode plots with bode() function. If I want to overlap two or more systems frequency responses, I use
bode(sys1,sys2,...)
or
hold on
When I want to reach the plot in order to put a legend with text(), for instance, is easy to reach the second plot. Something like the figure pointer always returns to the second plot (phase graph).
i.e., if try these lines:
G = tf([1],[1 6]); figure(1); bode(G); text(10,-20,'text');
G = tf([1],[1 6]); figure(2); bode(G); text(10,-20,'text');
when I return to the first figure, with figure(1), and try
figure(1); text(10,-20,'text')
legend is displayed in the second plot (Phase plot)
I try these other lines:
P = bodeoptions; % Set phase visiblity to off
P.PhaseVisible = 'off';
G = tf([1],[1 6]);
figure(1); bode(G,P); text(10,-20,'text');
figure(1); text(10,-20,'text');
As you can see, even I turn off the phase plot visiblity, the legend is not displayed.
Essentialy, my question is, how do I reach first and second plots, one by one? I tried with subplot(), but it is pretty clear this is not the way Matlab traces these plots.
Thanks in advance.
It all comes to getting into upper plot, since after bodeplot command the lower one is active. Intuitively one would want to call subplot(2,1,1), but this just creates new blank plot on top of if. Therefore we should do something like this:
% First, define arbitrary transfer function G(s), domain ww
% and function we want to plot on magnitude plot.
s = tf('s');
G = 50 / ( s*(1.6*s+1)*(0.23*s+1) );
ww = logspace(0,3,5000);
y = 10.^(-2*log10(ww)+log10(150));
hand = figure; % create a handle to new figure
h = bodeplot(G,ww);
hold on;
children = get(hand, 'Children') % use this handle to obtain list of figure's children
% We see that children has 3 objects:
% 1) Context Menu 2) Axis object to Phase Plot 3) Axis object to Magnitude Plot
magChild = children(3); % Pick a handle to axes of magnitude in bode diagram.
% magChild = childern(2) % This way you can add data to Phase Plot.
axes(magChild) % Make those axes current
loglog(ww,y,'r');
legend('transfer function','added curve')
you can get magnitude and phase data separately for each system using:
[mag,phase] = bode(sys,w)
now you can use subplot or plot to plot the diagram you want.
The only solution I was able to perform is taking into account axis position. It is not very clean but it works.
Here is the code to select mag plot:
ejes=findobj(get(gcf,'children'),'Type','axes','visible','on');
posicion=get(ejes,'pos');
tam=length(posicion);
for ii=1:tam
a=posicion{ii}(2);
vectorPos(ii)=a;
end
[valorMax,ind]=max(vectorPos); % min for choosing the phase plot
axes(ejes(ind))