Limited number of datacursormode on a scatter plot - Matlab - matlab

I have an ellipse plot (scatter) and I wish to have only two datacursormode and than get their respective X and Y coordinates. The code attached plot the ellipse and mark a point but how can I limit it and get the coordinates?
Code:
clc;
clear;
a=3;
b=7;
x0=0;
y0=0;
t=-pi:0.01:pi;
x=x0+a*cos(t);
y=y0+b*sin(t);
sz = 5;
scatter(x,y,sz)
dcm_obj = datacursormode;
set(dcm_obj,'UpdateFcn',#myupdatefcn)
function txt = myupdatefcn(empt,event_obj)
pos1 = get(event_obj,'Position');
txt = {['X position ',num2str(pos1(1))],...
['Y position: ',num2str(pos1(2))]};
end

You can use a while loop for selecting two points, and use nested functions for sharing variables.
Using nested functions is a possible solution for passing the selected coordinates and a counter out to a parent function.
You can limit the number of selected (marked) points to two, using a while loop.
Placing a pause inside the while is important for responsiveness.
Here is a working code sample:
function ellip()
%Parent function (use a function instead of a script,
%allows sharing variables with internal function).
clc;
%clear;
a=3;
b=7;
x0=0;
y0=0;
t=-pi:0.01:pi;
x=x0+a*cos(t);
y=y0+b*sin(t);
sz = 5;
h = figure; %Open new figure, and keep the handle.
scatter(x,y,sz);
dcm_obj = datacursormode;
dcm_obj.Enable = 'on';
set(dcm_obj,'UpdateFcn',#myupdatefcn)
%pos_x, pos_y and pos_counter are known both in scope of myupdatefcn and in ellip
pos_x = zeros(1,2);
pos_y = zeros(1,2);
pos_counter = 0;
%Loop until two points are selected
while (pos_counter < 2) && isvalid(h)
pause(0.01);
end
if isvalid(h)
close(h); %Close the figure;
%Display position of the two selected coordinates.
fprintf('pos1 = (%f, %f), pos2 = (%f, %f)\n', pos_x(1), pos_y(1), pos_x(2), pos_y(2));
end
dcm_obj.delete() %Delete the object (cleanup)
%myupdatefcn is a nested function, inside ellip function
function txt = myupdatefcn(empt, event_obj)
pos1 = get(event_obj,'Position');
txt = {['X position ',num2str(pos1(1))],...
['Y position: ',num2str(pos1(2))]};
disp(txt);
pos_counter = pos_counter + 1; %Increase counter.
pos_x(pos_counter) = pos1(1); %Store position
pos_y(pos_counter) = pos1(2);
end
end
I hope that I got it right, it is difficult understanding what you are asking...
Update to comment:
Creating the scatter plot outside the function is simple.
Saving the 4 positions is simple - the function can return the points.
I couldn't find an elegant way for leaving the tool-tip information on the plot.
The problem is that calling createDatatip triggers the event and executes myupdatefcn few more times.
You may take a look at Set data tips programmatically?
Updated code:
clc
clear
close all
a=3;
b=7;
x0=0;
y0=0;
t=-pi:0.01:pi;
x=x0+a*cos(t);
y=y0+b*sin(t);
sz = 5;
scatter(x,y,sz);
[pos_x, pos_y] = ellip();
%Display position of the two selected coordinates.
fprintf('pos1 = (%f, %f), pos2 = (%f, %f)\n', pos_x(1), pos_y(1), pos_x(2), pos_y(2));
function [pos_x, pos_y] = ellip()
%Parent function (use a function instead of a script,
%allows sharing variables with internal function).
dcm_obj = datacursormode;
dcm_obj.Enable = 'on';
set(dcm_obj,'UpdateFcn',#myupdatefcn)
h = dcm_obj.Figure; %Get hendle to the figure.
hScatter = findobj(h, 'Type', 'scatter'); %Handle to scatter plot
%pos_x, pos_y and pos_counter are known both in scope of myupdatefcn and in ellip
pos_x = zeros(1,2);
pos_y = zeros(1,2);
pos_counter = 0;
%Loop until two points are selected
while (pos_counter < 2) && isvalid(h)
pause(0.01);
end
% if isvalid(h)
% close(h); %Close the figure;
% end
%dcm_obj.delete() %Delete the object (cleanup)
if isvalid(h)
dcm_obj.Enable = 'off';
set(dcm_obj,'UpdateFcn',[]) %Replace the callback with an empty function instead of deleting the object
end
%myupdatefcn is a nested function, inside ellip function
function txt = myupdatefcn(empt, event_obj)
pos1 = get(event_obj,'Position');
txt = {['X position ',num2str(pos1(1))],...
['Y position: ',num2str(pos1(2))]};
if (pos_counter > 0) && (pos_x(pos_counter) == pos1(1)) && (pos_y(pos_counter) == pos1(2))
return %Return if pod1 is already selected.
end
disp(txt);
pos_counter = pos_counter + 1; %Increase counter.
pos_x(pos_counter) = pos1(1); %Store position
pos_y(pos_counter) = pos1(2);
%Try keeping the tool-tip data
hdtip = dcm_obj.createDatatip(hScatter);
set(hdtip, 'MarkerSize',5, 'MarkerFaceColor','none', 'MarkerEdgeColor','r', 'Marker','o', 'HitTest','off');
end
end

Related

How to cut part of the data out of a plot in Matlab

I just wanted to cut part of my data out in MATLAB, for example:
If I click on two points on the axis, it will cut the elements after the I click on with respect to the x-axis. I will post my code and a pic for further details
Thank you in advance
load sample.mat
X = sample.current;
X1 = sample.voltage;
Ts = 0.01;
Fs = 1/Ts;
Fm = Fs/2;
Fc = 2;
N =10;
d = fdesign.lowpass('N,Fc',N,Fc,Fs);
designmethods(d);
Hd = design(d);
%fvtool(Hd)
%X is a variable form csv
%X1 is a variable from csv
output = filter(Hd,X);
output1 = filter(Hd,X1);
figure;
plot(X,X1,'-g');
hold on
plot(output, output1,'r');
hold off
legend('raw signal','filtered signal')
grid on
x = output, output1;
y = output1;
figure
subplot(2,1,1)
plot(x,y,'r');
title('Original plot');
uiwait(msgbox('Select an x-value from which to crop','modal'));
[x_user ~] = ginput(1); % Let the user select an x-value from which to crop.
x(x>x_user) = [];
subplot(2,1,2);
plot(x,y,'r');
title('New plot with cropped values');
xlim([min(x(:)) max(x(:))]);
enter image description here
*Posting this as an answer to format code.
If its only one graphic you can just select the points that you want to delete using the "Brush/Select Data" (icon of a brush with a red square located at the menubar of the figure) selecting the data you want to be gone and then pressing the delete key.
If you want to do it with code you can try to find the index of the point where the signal starts to decrease over the X using something like:
% Find the index where X starts to decrease
maxIndex = find(data.x == max(data.x));
% In case of multiple indexs, ensure we get the first one
maxIndex = maxIndex(1);
% Copy data to new vector
saveData.x = data.x(1:maxIndex);
saveData.y = data.y(1:maxIndex);
If you want to use the users' click position you can use find to locate the index of the first element after the click:
% Get the coords of the first click
userFirstClick = ginput(1);
% Get the X component of the coords
xCoordInit = userFirstClick(1);
% Locate the index of the first element that is greater than
% the xCoord
firstXIndex = find(data.x >= xCoordInit);
% In case of multiple indexs, ensure we get the first one
firstXIndex = firstXIndex(1);
% Do the same to get the final index
userSecondClick = ginput(1);
xCoordFinal = userSecondClick(1);
finalXIndex = find(data.x > xCoordFinal);
finalXIndex = finalXIndex(1)-1;
% -1 because data.x(finalXIndex) is already greater than xCoordFinal
% Copy data to the new vector
saveData.x = data.x(firstXIndex:finalXIndex);
saveData.y = data.y(firstXIndex:finalXIndex);
Then just plot saveData.
Edit
There was a typo on my previous code, here you have a fully functional example where you just need to click over the two points where you want to crop.
function cropSine()
% create a period of a Sine to initialize our data
data.x = -pi*3:0.01:pi*3;
data.y = sin(data.x);
% we make it loop back just as in your picture
data.x = [data.x,data.x(end:-1:1)];
data.y = [data.y, -data.y*0.5+5];
% create a figure to show the signal we have just created
figure
% create the axes where the data will be displayed
mainAx = axes();
% Draw our fancy sine!
plot(data.x, data.y, 'b-', 'Parent', mainAx);
% Request the initial position to crop
userFirstClick = ginput(1);
% Get the index of the nearest point
initIndex = getNearest(userFirstClick, data);
% Do the same to get the final index
userSecondClick = ginput(1);
% Get the index of the nearest point
finalIndex = getNearest(userSecondClick, data);
% check if its a valid point
if isempty(initIndex) || isempty(finalIndex)
disp('No points in data vector!');
return;
end
% Ensure that final index is greater than first index
if initIndex > finalIndex
tempVal = initIndex;
initIndex = finalIndex;
finalIndex = tempVal;
end
% Copy the data that we want to save into a new variable
saveData.x = data.x(initIndex:finalIndex);
saveData.y = data.y(initIndex:finalIndex);
% Plot the cropped data in red!
hold(mainAx, 'on');
plot(saveData.x, saveData.y, 'r-', 'Parent', mainAx);
hold(mainAx, 'off');
end
function nearestIndex = getNearest(clickPos, vector)
nearestIndex = [];
numPoints = length(vector.x);
if numPoints == 0
return;
end
nearestIndex = 1;
minDist = calcDist(vector.x(1), vector.y(1), clickPos(1), clickPos(2));
for pointID = 1:numPoints
dist = calcDist(vector.x(pointID), vector.y(pointID), clickPos(1), clickPos(2));
if dist < minDist
nearestIndex = pointID;
minDist = dist;
end
end
end
function dist = calcDist(p1x, p1y, p2x, p2y)
dist = sqrt(power(p1x-p2x,2)+power(p1y-p2y,2));
end

MATLAB - Plot appearing in new figure?

I'm trying to plot a 3D figure(surf plot) inside a figure that I have already created but for some reason my new plot is created in a separate figure.
The code consist of 2 scripts which I will be posting.
The first script initializes the act frame I want to draw in and its callback (from the slider) draws my cylinder which for some reason appears inside a new frame leading to this mess:
Main script that creates the main figure I want to draw in.
%%
clf;
clear;
close all;
clc;
load('myHeatMap.mat','myHeatMap');
filename = ('C:\Users\Ali\Desktop\Documents\DataVis\Projekt\data\day\filenames.txt');
%This line simply gives us a table of all filenames in this file.
T = readtable(filename);
tsize = size(T);
tsize2 = size(T, 1);
filename = strcat('\Users\Ali\Desktop\Documents\DataVis\Projekt\data\day\', string(T{1,1}));
map100 = getCylinderHeatMap(filename);
%Figure/parent container (uifigure) properties%
App = uifigure('Scrollable','on','Name','Heatmap Plots','NumberTitle','off');
App_Width = 1000; App_Height = 500;
App.Position = [0 0 App_Width App_Height];
%Slider label (uilabel) properties%
Slider_Label = uilabel('Parent',App);
Slider_Label.Text = "Cylinder Number";
Slider_Label.Position = [25 20 200 100];
%Slider (uislider) properties%
Slider = uislider('Parent',App);
Slider.Limits = [1 1000];
Slider.Value = 1;
Slider_Width = App_Width - 500;
Margin = (App_Width - Slider_Width)/2;
Slider.Position = [Margin 50 Slider_Width 3];
Slider.MajorTicks = (1:100:1000);
Slider.FontSize = 6;
Red = 87; Green = 207; Blue = 220;
Slider.FontColor = [Red/255 Green/255 Blue/255];
%Plot (uiaxes) properties%
Heatmap_Cylinder_Plot = uiaxes('Parent',App);
Heatmap_Cylinder_Plot_X_Position = 100;
Heatmap_Cylinder_Plot_Y_Position = 100;
Heatmap_Cylinder_Plot_Height = 350;
Heatmap_Cylinder_Plot_Width = 400;
Heatmap_Cylinder_Plot.Position = [Heatmap_Cylinder_Plot_X_Position Heatmap_Cylinder_Plot_Y_Position Heatmap_Cylinder_Plot_Width Heatmap_Cylinder_Plot_Height];
Heatmap_Cylinder_Plot.GridColor = [0.15 0.15 0.15];
Heatmap_Cylinder_Plot.XGrid = 'on';
Heatmap_Cylinder_Plot.YGrid = 'on';
Heatmap_Cylinder_Plot.ZGrid = 'on';
%Image (uiimage) properties%
Heatmap_Image = uiimage('Parent',App);
Heatmap_X_Position = (App_Width/2) + 50;
Heatmap_Y_Position = 80;
Heatmap_Height = 350;
Heatmap_Width = 400;
Heatmap_Image.Position = [Heatmap_X_Position Heatmap_Y_Position Heatmap_Height Heatmap_Width];
%Callback function as the slider is moved%
Slider.ValueChangedFcn = #(Slider,event) Snap_Slider(Slider,Slider_Label,Heatmap_Cylinder_Plot,Heatmap_Image,T,...
myHeatMap);
%%
%Callback function definition%
function [] = Snap_Slider(Slider,Slider_Label,Heatmap_Cylinder_Plot,Heatmap_Image,T,myHeatMap)
Slider.Value = round(Slider.Value);
filename = strcat('\Users\Ali\Desktop\Documents\DataVis\Projekt\data\day\', string(T{Slider.Value,1}));
map100 = getCylinderHeatMap(filename);
splitFileName = strsplit(string(T{Slider.Value,1}),'.');
Slider_Label.Text = "Time Stamp: " + splitFileName{1,1};
%Put plotting code here%
plot(Heatmap_Cylinder_Plot,createSurfCylinder(map100));
%Put image plotting code here%
Heatmap_Image.ImageSource = "";
colormap(myHeatMap);
end
%%
Script for drawing actual Cylinder.
function cylinder = createSurfCylinder(matrix)
%Load heat map.
load('myHeatMap.mat','myHeatMap');
%%
%Cylinder creation
Sample_Range = 255 - 0;
Temperature_Range = 450 - 50;
Multiplier = Temperature_Range/Sample_Range;
map100 = matrix.*Multiplier + 50;
%Setting up the figure%
Radius = 1.5;
Number_Of_Data_Points = 360;
theta = linspace(0,2*pi,Number_Of_Data_Points);
%The xy values according to radius and number of points%
Z_Circle = Radius*cos(theta);
Y_Circle = Radius*sin(theta);
map100 = rot90(map100);
Height = 512;
Z_Circle = repmat(Z_Circle,Height,1);
Y_Circle = repmat(Y_Circle,Height,1);
X_Length = (1:512)';
X_Length = repmat(X_Length,1,Number_Of_Data_Points);
figure('Position', [10 10 800 800])
clf;
close;
cyl = surf(X_Length,Y_Circle,Z_Circle,'Cdata',map100);
title("3D Cylinder Heatmap Plot");
zlabel("Z-Position");
ylabel("Y-Position");
xlabel("Length(Cm)");
set(gca,'Ydir','reverse')
colormap(myHeatMap);
colorbar;
shading interp
Maximum_Value = 450;
Minimum_Value = 50;
caxis([Minimum_Value Maximum_Value]);
%Show the image in the subplot and add custome color coding to it.
% subplot(1,3,3); imshow(rot90(map100));
% colormap(myHeatMap);
% caxis([Minimum_Value Maximum_Value]);
cylinder = cyl;
%%
end
Any one that knows how to make may cylinder appear in my original Uiframe?
Any help would be much appreciated.
There are a few things you may want to note. First the closing-commands.
clf close (current) figure
clear clear all variables in the current workspace (this can the the base workspace in scripts or the workspace of the function, in which the command is called)
close all closes all open figure windows
clc clear command window.
Although these are handy commands, the are meant to be used when working in the command window and one should think twice when using them in scripts and never use them in functions. Why so? You might forget that you have used them and consequently, you code behaves unexpected. In particular if you call multiple functions/scripts, which all manipulate the same windows or output.
The command figure opens a new figure window. So every time this command is issued, a new window pops up and is set to "active", i.e. it is automatically the current figure. It returns its specific figure handle if you assign an output to it: fh = figure('Name','Figure A'). If you later what to activate a specific figure, you can call its handle: figure(fh). The function gcf always returns the handle of the current figure.
Now, let's see for example, in function createSurfCylinder, you are calling
figure('Position', [10 10 800 800])
clf;
close;
This opens a figure, closes the current figure (that is the figure you have just opened) and closes the current figure (which is now the one that was open before you opened & closed the last figure...
The plot()-function draws a line into the current axis (if not existing, it opens an axis). It can take as a first input a specific axis handle. The default is gca, which is a function meaning something like get current axis.
So to have all control over your plots, keep track of the handles. I always go like this:
fh1 = struct(); % create an empty struct
fh1.fig = figure('Name','very important figure'); % open figure & keep its handle
fh1.ax = subplot(1,1,1); % this opens a single axis & I keep the handle
% plot
plot( fh.ax, [1,10],[2,5])
ylabel( fh.ax, 'Amplitude Y / unit')
xlabel( fh.ax, 'Measure of time x / unit')
With the command subplot one can arrange multiple axes in one figure (here you need to be aware of the difference between a figure and an axis, and why there are two different types of handles). For example
fh2 = struct(); % create a new empty struct
fh2.fig = figure('Name','Multiple axes in one figure'); % open figure & keep its handle
fh2.ax = cell(3,1);
fh2.ax{1} = subplot(2,2,1); % this opens a single axis & keeps the handle
fh2.ax{2} = subplot(2,2,2); % this opens a single axis & keeps the handle
fh2.ax{3} = subplot(2,2,3:4); % this opens a single axis & keeps the handle
% do plotting with your axis-handles
(yes, I usually first open all axes and than fill them with graphs/lines)

Extracting boundaries from an image manually and preventing overlap of points selected

I am trying to import the image boundaries into CAD modeling software. For doing that I am manually selecting the boundaries from this image. The code I am using is the following:
I = imread('image.jpg'); %image file name
imshow(I);
uiwait(msgbox('Left click to choose points. Right click to exit'));
n = 0;
while true
[x, y, button] = ginput(1);hold on;
if isempty(x) || button(1) ~= 1; break; end
n = n+1;
x_n(n) = x; % save all points you continue getting
y_n(n) = y;
plot(x,y,'-o',...
'LineWidth',2,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[.49 1 .63],...
'MarkerSize',10);
end
figure;
x_n=x_n';
y_n=y_n';
plot(x_n,y_n);
A=[x_n,y_n];
Selecting points on the boundaries as shown by this image give the following outcome in CAD software model. The overlap in the boundaries is because of the way lines are drawn in CAD software as shown below:
s1.Line(point1=(-0.131218,39.556604),point2=(1.762436,40.503431))
s1.Line(point1=(1.762436,40.503431),point2=(4.602916,38.136364))
s1.Line(point1=(4.602916,38.136364),point2=(9.337050,31.035163))
s1.Line(point1=(9.337050,31.035163),point2=(11.230703,23.460549))
s1.Line(point1=(11.230703,23.460549),point2=(14.544597,17.779588))
s1.Line(point1=(14.544597,17.779588),point2=(15.491424,12.572041))
s1.Line(point1=(15.491424,12.572041),point2=(16.438250,6.417667))
s1.Line(point1=(16.438250,6.417667),point2=(17.385077,2.156947))
s1.Line(point1=(17.385077,2.156947),point2=(21.645798,5.944254))
s1.Line(point1=(21.645798,5.944254),point2=(26.853345,8.784734))
s1.Line(point1=(26.853345,8.784734),point2=(31.114065,11.625214))
s1.Line(point1=(31.114065,11.625214),point2=(35.848199,13.045455))
s1.Line(point1=(35.848199,13.045455),point2=(40.582333,14.939108))
Is there a way where I can prevent the overlapping during selection by pausing the selection? Or any other suggestion on how to get the model made efficiently? I had tried using image processing to determine the boundaries. But it was beyond me to think something about converting the image coordinates into a CAD software model.
Possible solution is creating a polygon out of the selected points, and not adding selected point to x_n, y_n if there are lines crossings.
The code uses polyshape function for creating a polygon object:
clearvars
I = imread('image.jpg'); %image file name
imshow(I);
uiwait(msgbox('Left click to choose points. Right click to exit'));
warning('Off', 'MATLAB:polyshape:repairedBySimplify'); %Disable warning "Polyshape has duplicate vertices, intersections..."
n = 0;
h_pgon = [];
while true
[x, y, button] = ginput(1);hold on;
if isempty(x) || button(1) ~= 1; break; end
n = n+1;
%Save points to temporary arrays
tmp_x_n(n) = x; % save all points you continue getting
tmp_y_n(n) = y;
is_overlapping = false;
if n > 2
%Check if there is "overlapping during selection":
%Creates a polygon object
pgon = polyshape(tmp_x_n, tmp_y_n);
if (pgon.NumRegions > 1) || (pgon.NumHoles > 0)
%There are intersections, so don't add the new x, y to x_n, y_n.
disp('Overlapping!'); %Print "Overlapping!" for testing.
is_overlapping = true;
n = n - 1;
end
end
if ~is_overlapping
%There is no overlapping - copy tmp_x_n, tmp_y_n to x_n, y_n.
x_n = tmp_x_n;
y_n = tmp_y_n;
plot(x,y,'-o',...
'LineWidth',2,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[.49 1 .63],...
'MarkerSize',10);
%Plot polygon for testing
if n > 2
if isobject(h_pgon), delete(h_pgon);end %Delete previouse polygon.
h_pgon = plot(pgon);
end
end
end
figure;
x_n=x_n';
y_n=y_n';
plot(x_n,y_n);
A=[x_n,y_n];

MATLAB adding slider on a figure

I have a 576x576x150 matrix. Each 576x576 set represents an image. When I want to plot one frame I do it by using the plot command:
figure(1);
imshow(B(:,:,45),[]) % plots frame 45
title('45') % tells frame number
However I would like to add a slider to the plot, so I can move from 1-150 frame within the figure.I've seen examples of people using uicontrol but I don't know how to code it. In addition to that, I would like to have a title on top of the figure telling me the frame number.
Here is how I do it. I like to keep a single function that does the plotting so you don't recycle commands elsewhere. You could replace the first two lines by function test(B) to use you own B matrix. This code is pretty easy to extend. You will also want to play with the layout to suit your purpose.
function test
B=rand(576,576,150);
fig=figure(100);
set(fig,'Name','Image','Toolbar','figure',...
'NumberTitle','off')
% Create an axes to plot in
axes('Position',[.15 .05 .7 .9]);
% sliders for epsilon and lambda
slider1_handle=uicontrol(fig,'Style','slider','Max',150,'Min',1,...
'Value',2,'SliderStep',[1/(150-1) 10/(150-1)],...
'Units','normalized','Position',[.02 .02 .14 .05]);
uicontrol(fig,'Style','text','Units','normalized','Position',[.02 .07 .14 .04],...
'String','Choose frame');
% Set up callbacks
vars=struct('slider1_handle',slider1_handle,'B',B);
set(slider1_handle,'Callback',{#slider1_callback,vars});
plotterfcn(vars)
% End of main file
% Callback subfunctions to support UI actions
function slider1_callback(~,~,vars)
% Run slider1 which controls value of epsilon
plotterfcn(vars)
function plotterfcn(vars)
% Plots the image
imshow(vars.B(:,:,get(vars.slider1_handle,'Value')));
title(num2str(get(vars.slider1_handle,'Value')));
The idea is to use uicontrol() to enable sliding/scrolling.
The following code is for scrolling (created by Evan Brooks, you can modify it to sliding):
function scrollfigdemo
% create new figure window
f = figure;
set(f,'doublebuffer', 'on', 'resize', 'off')
% set columns of plots
cols = 2;
% create 5 data sets to plot
x=0:1e-2:2*pi;
y{1}=sin(x);
y{2}=cos(x);
y{3}=tan(x);
y{4}=x.^2;
y{5}=x.^3;
% determine required rows of plots
rows = ceil(length(y)/cols);
% increase figure width for additional axes
fpos = get(gcf, 'position');
scrnsz = get(0, 'screensize');
fwidth = min([fpos(3)*cols, scrnsz(3)-20]);
fheight = fwidth/cols*.75; % maintain aspect ratio
set(gcf, 'position', [10 fpos(2) fwidth fheight])
% setup all axes
buf = .15/cols; % buffer between axes & between left edge of figure and axes
awidth = (1-buf*cols-.08/cols)/cols; % width of all axes
aidx = 1;
rowidx = 0;
while aidx <= length(y)
for i = 0:cols-1
if aidx+i <= length(y)
start = buf + buf*i + awidth*i;
apos{aidx+i} = [start 1-rowidx-.92 awidth .85];
a{aidx+i} = axes('position', apos{aidx+i});
end
end
rowidx = rowidx + 1; % increment row
aidx = aidx + cols; % increment index of axes
end
% make plots
axes(a{1}), plot(x,y{1}), title('sine'), xlabel('x'), ylabel('sin(x)')
axes(a{2}), plot(x,y{2}), title('cosine'), xlabel('x'), ylabel('cos(x)')
axes(a{3}), plot(x,y{3}), title('tangent'), xlabel('x'), ylabel('tan(x)')
axes(a{4}), plot(x,y{4}), title('x^2'), xlabel('x'), ylabel('x^2')
axes(a{5}), plot(x,y{5}), title('x^3'), xlabel('x'), ylabel('x^3')
% determine the position of the scrollbar & its limits
swidth = max([.03/cols, 16/scrnsz(3)]);
ypos = [1-swidth 0 swidth 1];
ymax = 0;
ymin = -1*(rows-1);
% build the callback that will be executed on scrolling
clbk = '';
for i = 1:length(a)
line = ['set(',num2str(a{i},'%.13f'),',''position'',[', ...
num2str(apos{i}(1)),' ',num2str(apos{i}(2)),'-get(gcbo,''value'') ', num2str(apos{i}(3)), ...
' ', num2str(apos{i}(4)),'])'];
if i ~= length(a)
line = [line,','];
end
clbk = [clbk,line];
end
% create the slider
uicontrol('style','slider', ...
'units','normalized','position',ypos, ...
'callback',clbk,'min',ymin,'max',ymax,'value',0);

MATLAB: How to store clicked coordinates using ButtonDownFcn

Goal: To perform several clicks in one figure, containing an image displayed with imshow and save the coordinates of the "clicked" point(s), to be used in further operations.
Notes: I know about the functions getpts/ginput but I would like to perform this without using them. Is this possible using ButtonDownFcn? (see the following code)
function testClicks
img = ones(300); % image to display
h = imshow(img,'Parent',gca);
set(h,'ButtonDownFcn',{#ax_bdfcn});
function ax_bdfcn(varargin)
a = get(gca,'CurrentPoint');
x = a(1,1);
y = a(1,2);
At this stage the variables x and y only "live" inside ax_bdfcn.
How can I make them available in the testClicks function? Is this possible using ButtonDownFcn? Is this a good approach?
Thanks a lot.
EDIT1:
Thanks for the answer Shai. But I still cannot accomplish what I intended.
function [xArray, yArray] = testClicks()
img = ones(300); % image to display
h = imshow(img,'Parent',gca);
x = [];
y = [];
xArray = [];
yArray = [];
stop = 0;
while stop == 0;
set(h,'ButtonDownFcn',{#ax_bdfcn});
xArray = [xArray x];
yArray = [yArray y];
if length(xArray)>15
stop = 1;
end
end
function ax_bdfcn(varargin)
a = get(gca, 'CurrentPoint');
assignin('caller', 'x', a(1,1) );
assignin('caller', 'y', a(1,2) );
end
end % must have end for nested functions
This code (buggy!) is the closest I can get to what I want (after all the clicking, having an array with the x and y coordinates of the clicked points). I am clealy not understanding the mechanics for the implementation of this task. Any help?
There are several ways
Using nested functions
function testClicks
img = ones(300); % image to display
h = imshow(img,'Parent',gca);
set(h,'ButtonDownFcn',{#ax_bdfcn});
x = []; % define "scope" of x and y
y = [];
% call back as nested function
function ax_bdfcn(varargin)
a = get(gca,'CurrentPoint');
x = a(1,1); % set x and y at caller scope due to "nested"ness of function
y = a(1,2);
end % close nested function
end % must have end for nested functions
Using assignin
function ax_bdfcn(varargin)
a = get(gca, 'CurrentPoint');
assignin('caller', 'x', a(1) );
assignin('caller', 'y', a(2) );
Using 'UserData' property of figure handle
function ax_bdfcn(varargin)
a = get(gca, 'CurrentPoint');
set( gcf, 'UserData', a(1:2) );
'UserData' can be accessed (as long as the figure is alive) using cp = get( gcf, 'UserData');.
EDIT:
An example of a way to "communicate" the clicked locations to 'base' workspace
function ax_bdfcn(varargin)
a = get(gca,'CurrentPoint');
% the hard part - assign points to base
if evalin('base', 'exist(''xArray'',''var'')')
xArray = evalin('base','xArray');
else
xArray = [];
end
xArray = [xArray a(1)]; % add the point
assignin('base','xArray',xArray); % save to base
% do the same for yArray
After calling testClicks there are NO xArray or yArray variables in the workspace (at least there shouldn't). After the first click these two variables will "miraculously" be created. After every other click these two arrays will increase their size until you close the figure.