How to make a MATLAB plot interactive? - matlab

I'm trying to create a simple interface for plotting quadratic Lagrange polynomials. For this, you need just 3 points (with each their own x,y,z coordinates), which are then interpolated using the quadratic Lagrange polynomials.
It's easy to make a static version, or even one that lets the user input the 3 points before plotting the curve. But it should also be possible for the user to drag an existing point in the plot window to another position, and then re-plot the curve automatically using the new position of this point!
So in short, the user should be able to drag these black dots to another location. After that (or while dragging), the curve should be updated.
function Interact()
% Interactive stuff here
figure();
hold on;
axis([0 7 0 5])
DrawLagrange([1,1; 3,4; 6,2])
function DrawLagrange(P)
plot(P(:,1), P(:,2), 'ko--', 'MarkerSize', 10, 'MarkerFaceColor', 'k')
t = 0:.1:2;
Lagrange = [.5*t.^2 - 1.5*t + 1; -t.^2 + 2*t; .5*t.^2 - .5*t];
CurveX = P(1,1)*Lagrange(1,:) + P(2,1)*Lagrange(2,:) + P(3,1)*Lagrange(3,:);
CurveY = P(1,2)*Lagrange(1,:) + P(2,2)*Lagrange(2,:) + P(3,2)*Lagrange(3,:);
plot(CurveX, CurveY);
I think I either have to use functions like WindowButtonDownFcn, WindowButtonUpFcn and WindowButtonMotionFcn, or the ImPoint from the Image Processing Toolbox. But how?
[Edit]
It should also work in 3D, since I'd like to generalize this concept to tensor product surfaces.

Ok, I searched for some more information on the ImPoint option from the Image Processing Toolbox, and wrote this script.
Since ImPoint only works for a 2D setting (and I'd like to generalize this to 3D to be able to work with surfaces instead of curves), it is not really an acceptable answer! But somebody might benefit from it, or get an idea how to do this in 3D.
% -------------------------------------------------
% This file needs the Image Processing Toolbox!
% -------------------------------------------------
function Interact(Pos)
% This part is executed when you run it for the first time.
% In that case, the number of input arguments (nargin) == 0.
if nargin == 0
close all;
clear all;
clc;
figure();
hold on;
axis([0 7 0 5])
% I do not know how to do this without global variables?
global P0 P1 P2
% GCA = Get handle for Current Axis
P0 = ImPoint(gca,1,1);
setString(P0,'P0');
P1 = ImPoint(gca,2,4);
setString(P1,'P1');
P2 = ImPoint(gca,6,2);
setString(P2,'P2');
% Call subfunction
DrawLagrange(P0,P1,P2)
% Add callback to each point
addNewPositionCallback(P0,#Interact);
addNewPositionCallback(P1,#Interact);
addNewPositionCallback(P2,#Interact);
else
% If there _is_ some input argument, it has to be the updated
% position of a moved point.
global H1 H2 P0 P1 P2
% Display X and Y coordinates of moved point
Pos
% Important: remove old plots! Otherwise the graph will get messy.
delete(H1)
delete(H2)
DrawLagrange(P0,P1,P2)
end
function DrawLagrange(P0,P1,P2)
P = zeros(3,2);
% Get X and Y coordinates for the 3 points.
P(1,:) = getPosition(P0);
P(2,:) = getPosition(P1);
P(3,:) = getPosition(P2);
global H1 H2
H1 = plot(P(:,1), P(:,2), 'ko--', 'MarkerSize', 12);
t = 0:.1:2;
Lagrange = [.5*t.^2 - 1.5*t + 1; -t.^2 + 2*t; .5*t.^2 - .5*t];
CurveX = P(1,1)*Lagrange(1,:) + P(2,1)*Lagrange(2,:) + P(3,1)*Lagrange(3,:);
CurveY = P(1,2)*Lagrange(1,:) + P(2,2)*Lagrange(2,:) + P(3,2)*Lagrange(3,:);
H2 = plot(CurveX, CurveY);
I added some comments for clarity.
[Edit] In the preview the syntax highlighting doesn't look very good! Should I define the language to be highlighted somewhere?

A better solution (one that does not need any additional toolboxes) is to use events. First step:
H = figure('NumberTitle', 'off');
set(H, 'Renderer', 'OpenGL');
set(H, 'WindowButtonDownFcn', #MouseClick);
set(H, 'WindowButtonMotionFcn', #MouseMove);
set(H, 'WindowScrollWheelFcn', #MouseScroll);
set(H, 'KeyPressFcn', #KeyPress )
The next step is to define the functions like MouseClick, this is the place where you implement how to react to events (e.g. mouse buttons being clicked, keys being pressed).
In the meantime I implemented an interactive B-spline environment in MATLAB, the source code (along with a concise manual) can be downloaded from https://github.com/pjbarendrecht/BsplineLab.

Great question! I've had this problem too and wondered how to solve it before, but never looked into it. My first thought was to use ginput and then minimize the distance to the line and find the closest point. I thought that was a bit of a hack so I looked around. Seems like that's the only reasonable answer out there and was confirmed here with this code as an example.
%minimum absolute differences kick in again
xx = 1:10; %xdata
yy = exp(xx);
plot(xx,yy);
[xm ym] = ginput(1); %xmouse, ymouse
%Engine
[~, xidx] = min(abs(xx-xm)); %closest index
[~, yidx] = min(abs(yy-ym));
x_closest = xx(xidx) %extract
y_closest = yy(yidx)
Not sure how it scales to 3D, but I thought this would be a good start.

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 = ...).

Plot to figures without bringing them into foreground

figure;
ax1 = axes;
figure;
ax2 = axes;
x = 0; y = 0;
while ishandle(ax1) && ishandle(ax2)
x = x + 1;
y = y + 1;
figure(1)
scatter(x,y, 'MarkerEdgeColor', 'red')
hold on
figure(2)
scatter(x,y, 'MarkerEdgeColor', 'blue')
hold on
end
In my script I have multiple figures, which are going to be updated in a loop. The figures have to be displayed, while the script is running. Unfortunately the currently updated figure is always popping in the foreground, which makes it impossible to monitor a certain figure. I understand that the calling of figure(1) and figure(2) causes this behaviour, but I how can I plot to these figures, without bringing the window into foreground?
As mikkola suggested in a comment, you can specify to which axes scatter or plot add data points. However, there is a better method: create a single line object, and update its xdata and ydata properties. This is both faster and more memory efficient. Your code would become:
x = 0; y = 0;
figure;
h1 = plot(x,y,'ro');
figure;
h2 = plot(x,y,'bo');
while ishandle(h1) && ishandle(h2)
x = x + 1;
y = y + 1;
h1.XData(end+1) = x;
h1.YData(end+1) = y;
h2.XData(end+1) = x;
h2.YData(end+1) = y;
drawnow
pause(0.1)
end
I keep a set of rules of thumb for when working with MATLAB handle graphics. These are relevant to this question:
Use figure only to create a new figure, or to bring an existing figure to the front (which you want to avoid in general, but sometimes is necessary).
Always specify with which figure or axes you want to work, by keeping and using their handles. I never rely on gcf or gca (not explicitly nor implicitly). Using the current figure is useful when typing on the command line, but in a script or a function there is the real danger than someone clicks randomly on windows while the function is executing. Creating a window then writing to gcf could end up writing to a different figure (really, I click on random things all the time).
Don't create more objects than necessary. Creating a new line object for every point you plot is wasteful.
Note also that plot(...'o') is equivalent to scatter(...) unless you specify a different color or size for each point. But using the dot size or color to specify additional information is not a good way to convey that information. Read Tufte's "The visual display of quantitative information" if you're interested in learning about effective communication through graphs.
The relevant part can be found in the part of the documentation of scatter that includes the input ax:
scatter(ax,___) plots into the axes specified by ax instead of into
the current axes.
This allows the user to specify an axis handle pointing to which axes should be used for drawing the scatter plot. Thus if you skip using figure in your code and use the ax input instead, you avoid the "bring to front" behavior associated with figure.
You can modify your code as follows:
figure;
ax1 = axes;
figure;
ax2 = axes;
x = 0; y = 0;
while ishandle(ax1) && ishandle(ax2)
x = x + 1;
y = y + 1;
scatter(ax1, x,y, 'MarkerEdgeColor', 'red')
hold on
scatter(ax2, x,y, 'MarkerEdgeColor', 'blue')
hold on
end

Generate trajectories for 3 agents/robots with same tangent in specific point - MATLAB

I want to simulate the movements of 3 robots/agents in space and I would like to generate 3 different trajectories which have one constraint: in a certain time T the all the trajectories must have the same tangent.
I want something like in the following picture:
I need to do it through MATLAB and/or SIMULINK.
Thanks a lot for your help.
I do not know if this is enough for what I need or not but I probably figured out something.
What I did is to fit a polynomial to some points and constraining the derivative of the polynomial in a certain point to be equal to 0.
I used the following function:
http://www.mathworks.com/matlabcentral/fileexchange/54207-polyfix-x-y-n-xfix-yfix-xder-dydx-
It is pretty easy, but it saved me some work.
And, if you try the following:
% point you want your functions to pass
p1 = [1 1];
p2 = [1 3];
% First function
x1 = linspace(0,4);
y1 = linspace(0,4);
p = polyfix(x1,y1,degreePoly,p1(1),p1(2),[1],[0]);
% p = [-0.0767 0.8290 -1.4277 1.6755];
figure
plot(x1,polyval(p,x1))
xlim([0 3])
ylim([0 3])
grid on
hold on
% Second function
x2 = linspace(0,4);
y2 = linspace(0,4);
p = polyfix(x2,y2,degreePoly,[1],[3],[1],[0])
% p = [0.4984 -2.7132 3.9312 1.2836];
plot(x2,polyval(p,x2))
xlim([0 3])
ylim([0 3])
grid on
If you don't have the polyfix function and you don't want to download it you can try the same code commenting the polyfix lines:
p = polyfix(x1,y1,degreePoly,p1(1),p1(2),[1],[0]);
p = polyfix(x2,y2,degreePoly,p2(1),p2(1),[1],[0]);
And uncommenting the lines:
% p = [-0.0767 0.8290 -1.4277 1.6755];
% p = [0.4984 -2.7132 3.9312 1.2836];
You will get this:
Now I will use this polynomial as a position (x,y) over time of my robots and I think I should be done. The x of the polynomial will be also the time, in this way I am sure that the robots will arrive in the 0-derivative point at the same time.
What do you think? Does it make sense?
Thanks again.
To have tangents same all the time, curves (or trajectories) needs to be always parallel. Generate trajectory for one object and keep other objects at fixed distance apart and following master trajectory.
In image provides if we take time next to three dots trajectories will not be same (or parallel) as objects moved in different directions. So i guess you've to keep then always parallel

Prevent specific plot entry from being displayed on a MATLAB plot legend

I need to prevent a specific plot entry from being displayed on a Matlab plot legend.
Sample:
% x and y are any plot data
for i=1:5
plot(x,y);
plot(x2,y2,'PleaseNoLegend!'); % I need to hide this from legend
end
legend('show');
Is there any flag I can set inside the plot command so this specific entry doesn't show up in legend?
You can achieve that by setting the 'HandleVisibility' property to 'off'. Note that this hides the handles of those plots to all functions, not just to legend.
For example,
hold on
for k = 1:3
x = 1:10;
y = rand(1,10);
x2 = x;
y2 = y + 2;
plot(x,y);
plot(x2,y2,'--','HandleVisibility','off'); % Hide from legend
end
legend('show')
produces the graph
You can use the semi-documented function called hasbehavior, that allows you to ignore individual plots in a legend after you issued the plot command.
figure;
hold on;
for i=1:5
plot(x,y);
h = plot(x2,y2);
hasbehavior(h,'legend',false);
end
legend('show');
The fact that it's semi-documented suggests that it could break sooner or later in a newer MATLAB version, so use with care. It might still be a convenient choice for certain applications.
As #stephematician noted, this MATLAB built-in is also unavailable in Octave, which might be another reason why the other answers are preferable.
As Luis Mendo mentions (and I somehow missed this) the handle is hidden to all other functions in his answer, which will be ok in most situations, but an alternative solution which looks identical to the above and doesn't have this effect is:
k_values = 1:3;
h = nan(size(k_values));
x = 1:10;
hold on
for k = k_values
y = rand(size(x));
y2 = y + 2;
h(k) = plot(x,y);
plot(x,y2,'--');
end
hold off
legend(h, strcat('data', num2str(k_values')))
The final command sets the legend entry for each handle returned by the plot(x,y) command. The first argument is a 1x3 array of line handles which will appear in the legend, and the second argument is a 3x5 char matrix where each row is a label.

Trying to make MATLAB's figure stop 'blinking'

So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.