Related
I am writing a code for a real time experiment using psychtoolbox to present the stimulus. In my experiment, I need to show the subject a graph that indicates his performance. I have plotted the graph using this simple code:
% Draw the graph
figure('visible','off','color',[0 0 0]);
pcolor([0 Num_timepoint+2],[-10 0],ones(2,2));
hold on;
pcolor([0 Num_timepoint+2],[0 10],2*ones(2,2));
colormap([79 167 255;255 187 221]/256);
plot(1:subloop,value0,'*-',...
'color',[0,0,0],...
'LineWidth',1,...
'MarkerSize',5,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[0.5,0.5,0.5]);
axis([0,Num_timepoint+2,-10,10]);
saveas(gcf,'line_chart.png'); %save it
close(figure);
line_chart=imread('line_chart.png'); %read it
resized_plot=imresize(line_chart,0.5);
imageSize = size(resized_plot);
[imageHeight,imageWidth,colorChannels]=size(resized_plot);
bottomRect = [xCenter-imageWidth/1.5, yCenter+gapdown, xCenter+imageWidth/1.5, yCenter+gapdown+imageHeight];
imageDisplay=Screen('MakeTexture', win0, resized_plot);
Screen('DrawTexture', win0, imageDisplay, [], bottomRect);
Unfortunately, this simple code is very slow. In addition, I couldn't make the graph moving along x axis, as soon as the new value comes.
Any help would be Awesome. Thanks in advance for your efforts.
Why are you saving the figure and redisplaying as an image? Maybe I'm missing something but you should be able to accomplish what you need by updating the existing plot with the fresh data using the handles properties of the plot:
.... First time through we need the initial plot ....
% Set up figure with colormaps and such but leave as 'visible','on'
hPlot = plot(plot(1:subloop,value0,'*-',...
'color',[0,0,0],...
'LineWidth',1,...
'MarkerSize',5,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[0.5,0.5,0.5]);
hAxes = gca;
.... Loop over your real time updates ....
% Assuming value0 has subject's results from t=0 to t=now
hPlot.XData = value0; hPlot.YData = [1:subloop];
hAxes.XLim = [0 numTimePoints+2];
hAxes.YLim = [-10 10]
.... Continue test and update value0 ....
I think that should keep your plots current without having to save the figure as image to file then reopen the image to display to subject.
If you want to move your data one sample, you can use the circshift function. For example, if you want your new values to appear on the left hand side, you can shift all values 1 sample rightward, then add your new value in the first position.
For converting a MATLAB figure to a Psychtoolbox texture, you don't need to save, then load the temporary images. You can instead use the getframe function to capture the MATLAB figure data, which can then be given to MakeTexture to turn it into a Psychtoolbox texture.
I'm not sure what values you're actually using for subloop, value0, etc. but there is an example that I think might be close to what you want. In this example, 30 frames of figures are plotted, with each figure being on screen for 1 second. New data points are generated randomly and appear from the left hand side of the figure.
Depending on the details of your experiment, you may find that this approach is still too slow. You could also create the figure directly via Psychtoolbox drawing methods like DrawLines, etc. though that would require more effort.
try
win0 = Screen('OpenWindow', 0, 0);
Num_timepoint = 100;
subloop = 100;
value0 = zeros(1,100);
num_demo_frames = 30;
% Draw the graph
fig_h = figure('visible','off','color',[0 0 0]);
pcolor([0 Num_timepoint+2],[-10 0],ones(2,2));
hold on;
pcolor([0 Num_timepoint+2],[0 10],2*ones(2,2));
colormap([79 167 255;255 187 221]/256);
plot_h = plot(1:subloop,value0,'*-',...
'color',[0,0,0],...
'LineWidth',1,...
'MarkerSize',5,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[0.5,0.5,0.5]);
axis([0,Num_timepoint+2,-10,10]);
for f = 1:num_demo_frames
new_value = randn(1,1);
data_values = plot_h.YData;
data_values = circshift(data_values, 1);
data_values(1) = new_value;
plot_h.YData = data_values;
plot_values = getframe(fig_h);
imageDisplay=Screen('MakeTexture', win0, plot_values.cdata);
Screen('DrawTexture', win0, imageDisplay);
Screen('Flip', win0);
WaitSecs(1);
end
sca;
catch e
sca;
rethrow(e);
end
I've got a lab where I need to first derrive a transfer function for a fourth order mass spring damper system, then plot this for a 200N step response.
The dashpot damping coefficient is a variable and we are to plot a range of values graphically and choose the optimium value i.e (the one closest to a critically damped response).
I have all of the above tasks working fully as expected, and maybe I'm just being dumb here but I can't get the graph legend to behave as desired.
I'd ideally like it to say "B="Current Value of B for that itteration of the loop.
%Initialisations
close all;format short g;format compact;clc;clear;
k1=500000;
k2=20000;
m1=20;
m2=400;
opt=stepDataOptions('StepAmplitude',200);
for (b = [1000:1000:15000])
Hs = tf([b k2],[(m1*m2) (b*(m1+m2)) (k2*(m1+m2)+k1*m2) (b*k1) (k1*k2)]);
hold on;
stepplot(Hs,opt)
title("Shock Absorber Performance to Step Input");
ylabel("Force (N)");
legend;
hold off;
end
Thanks in advance :)
Although not such a clean solution, this will work:
close all;format short g;format compact;clc;clear;
k1=500000;
k2=20000;
m1=20;
m2=400;
opt=stepDataOptions('StepAmplitude',200);
% initialize a cell array that will hold the labels
labels = {};
for (b = [1000:1000:15000])
Hs = tf([b k2],[(m1*m2) (b*(m1+m2)) (k2*(m1+m2)+k1*m2) (b*k1) (k1*k2)]);
hold on;
stepplot(Hs,opt)
title("Shock Absorber Performance to Step Input");
ylabel("Force (N)");
hold off;
% set the label name, with the value for b
labels{end+1} = ['B = ' num2str(b)];
end
% create the legend with the labels
legend(labels)
I Need help regarding MovStr Function Update in MATLAB Version 2015. I was using MATLAB Function 2013 before. The function given below is not working for me. How should I make it workable ? Thanks
function MoveStr(ws)
% ws: with of a blank character
p1 = get(gca,'CurrentPoint');
set(gcf,'Pointer','fleur')
set(gcf,'WindowButtonUpFcn', sprintf('MoveStrUp(%1.20g,%1.8g,%1.8g,%1.8g)',gcbo,p1(1,1),p1(1,2),ws))
set(gcf,'WindowButtonMotionFcn',sprintf('MoveStrMo(%1.20g,%1.8g,%1.8g)',gcbo,p1(1,1),p1(1,2)))
% alle Textobjekte mitbewegen
ch = [gcbo get(gcbo,'UserData')];
for i = 1:length(ch)
if strcmpi(get(ch(i),'Type'),'text')
set(ch(i),'Selected','on')
end
end
function MoveStrMo(obj,x,y)
%set(gcf,'WindowButtonMotionFcn','')
d_pos = get(gca,'CurrentPoint');
set(gcf,'WindowButtonMotionFcn',sprintf('MoveStrMo(%20.15f,%f,%f)',obj,d_pos(1,1),d_pos(1,2)))
%set(gcf, 'WindowButtonMotionFcn', #(s,e)MoveStrMo(obj,d_pos(1,1),d_pos(1,2)));
d_pos = [d_pos(1,1)-x,d_pos(1,2)-y 0];
ch = [obj get(obj,'UserData')];
for i = 1:length(ch)
if isgraphics(ch( i ),'text')
pos = get(ch(i),'Position');
set(ch(i),'Position',[pos(1) pos(2) 2] + d_pos)
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function MovePointer
ans = get(gcbo,'UserData');
if strcmpi(get(ans(1),'Type'),'string')
% if isgraphics (ans1(1), 'string')
ans = ans(2);
end
set(gcf,'Pointer','fleur')
% set(gcf,'WindowButtonUpFcn',sprintf('MovePointerUp(%20.15f,%20.15f)',gcbo,ans(1)))
% set(gcf,'WindowButtonMotionFcn',sprintf('MovePointerMo(%20.15f,%20.15f)',gcbo,ans(1)))
set(gcf,'WindowButtonUpFcn',sprintf('MovePointerUp(%20.15f,%20.15f)',double(gcbo),ans(1)))
set(gcf,'WindowButtonMotionFcn',sprintf('MovePointerMo(%20.15f,%20.15f)',double(gcbo),ans(1)))
% set(gcf, 'WindowButtonUpFcn', #(s,e)MoveStrUp(gcbo, ans(1)));
% set(gcf, 'WindowButtonMotionFcn', #(s,e)MoveStrMo(gcbo, ans(1)));
set(gcbo,'Selected','on')
In older versions of MATLAB, graphics handles were simply floating point numbers or integers which could be used to refer to the graphics object. Beginning with R2014b, all graphics objects are objects by default which breaks your code since sprintf('%g') isn't going to be able to convert the object to a number itself.
You could still get the numeric handle of a graphics object by using the double method of the graphics object:
set(gcf,'WindowButtonMotionFcn',sprintf('MoveStrMo(%1.20g,%1.8g,%1.8g)',double(gcbo),p1(1,1),p1(1,2)))
BUT Please do not to this. You should be using function handles to create your callback instead of constructing an elaborate string that will be evaluated by MATLAB. This way you can pass the graphics object directly and you don't have to worry about any loss of precision when converting floating point numbers to strings. Also anyone trying to read your code later will really appreciate it as it is much cleaner.
set(gcf, 'WindowButtonUpFcn', #(s,e)moveStrUp(gcbo, p1(1,1), p1(1,2), ws));
set(gcf, 'WindowButtonMOtionFcn', #(s,e)MoveStrMo(gcbo, p1(1,1), p1(1,2)));
Update
Since it appears that you assign callbacks from within your callbacks, you will need to modify those as well to use the anonymous function format:
function MoveStrMo(obj,x,y)
d_pos = get(gca,'CurrentPoint');
set(gcf, 'WindowButtonMotionFcn', #(s,e)MoveStrMo(obj, d_pos(1,1), d_pos(1,2));
d_pos = [d_pos(1,1)-x,d_pos(1,2)-y 0];
ch = [obj get(obj,'UserData')];
for i = 1:length(ch)
if isgraphics(ch( i ),'text')
pos = get(ch(i),'Position');
set(ch(i),'Position',[pos(1) pos(2) 2] + d_pos)
end
end
end
And MovePointer
function MovePointer
ans = get(gcbo,'UserData');
if strcmpi(get(ans(1),'Type'), 'text')
ans = ans(2);
end
set(gcf,'Pointer','fleur')
set(gcf, 'WindowButtonUpFcn', #(s,e)MoveStrUp(gcbo, ans(1)));
set(gcf, 'WindowButtonMotionFcn', #(s,e)MoveStrMo(gcbo, ans(1)));
set(gcbo,'Selected','on')
end
I'm working on this function which gets axis handler and data, and is supposed to plot it correctly in the axis. The function is called in for loop. It's supposed to draw the multiple data in one figure. My resulted figure is shown below.
There are only two correctly plotted graphs (those with four colors). Others miss areas plotted before the final area (red area is the last plotted area in each graph). But the script is same for every axis. So where can be the mistake? The whole function is written below.
function [] = powerSpectrumSmooth(axis,signal,fs)
N= length(signal);
samplesPer1Hz = N/fs;
delta = int16(3.5*samplesPer1Hz); %last sample of delta frequncies
theta = int16(7.5*samplesPer1Hz); %last sample of theta frequncies
alpha = int16(13*samplesPer1Hz); %last sample of alpha frequncies
beta = int16(30*samplesPer1Hz); %last sample of beta frequncies
x=fft(double(signal));
powerSpectrum = 20*log10(abs(real(x)));
smoothPS=smooth(powerSpectrum,51);
PSmin=min(powerSpectrum(1:beta));
y1=[(smoothPS(1:delta)); zeros(beta-delta,1)+PSmin];
y2=[zeros(delta-1,1)+PSmin; (smoothPS(delta:theta)); zeros(beta-theta,1)+PSmin];
y3=[zeros(theta-1,1)+PSmin; (smoothPS(theta:alpha)); zeros(beta-alpha,1)+PSmin];
y4=[zeros(alpha-1,1)+PSmin; (smoothPS(alpha:beta))];
a1=area(axis,1:beta,y1);
set(a1,'FaceColor','yellow')
hold on
a2=area(axis,1:beta,y2);
set(a2,'FaceColor','blue')
a3=area(axis,1:beta,y3);
set(a3,'FaceColor','green')
a4=area(axis,1:beta,y4);
set(a4,'FaceColor','red')
ADDED
And here is the function which calls the function above.
function [] = drawPowerSpectrum(axesContainer,dataContainer,fs)
size = length(axesContainer);
for l=1:size
powerSpectrumSmooth(axesContainer{l},dataContainer{l},fs)
set(axesContainer{l},'XTickLabel','')
set(axesContainer{l},'YTickLabel','')
uistack(axesContainer{l}, 'top');
end
ADDED 29th July
Here is a script which reproduces the error, so you can run it in your computer. Before running it again you might need to clear variables.
len = 9;
axesContainer = cell(len,1);
x = [0.1,0.4,0.7,0.1,0.4,0.7,0.1,0.4,0.7];
y = [0.1,0.1,0.1,0.4,0.4,0.4,0.7,0.7,0.7];
figure(1)
for i=1:len
axesContainer{i} = axes('Position',[x(i),y(i),0.2,0.2]);
end
dataContainer = cell(len,1);
N = 1500;
for i=1:len
dataContainer{i} = rand(1,N)*100;
end
for l=1:len
y1=[(dataContainer{l}(1:N/4)) zeros(1,3*N/4)];
y2=[zeros(1,N/4) (dataContainer{l}(N/4+1:(2*N/4))) zeros(1,2*N/4)];
y3=[zeros(1,2*N/4) (dataContainer{l}(2*N/4+1:3*N/4)) zeros(1,N/4)];
y4=[zeros(1,3*N/4) (dataContainer{l}(3*N/4+1:N))];
axes=axesContainer{l};
a1=area(axes,1:N,y1);
set(a1,'FaceColor','yellow')
hold on
a2=area(axes,1:N,y2);
set(a2,'FaceColor','blue')
hold on
a3=area(axes,1:N,y3);
set(a3,'FaceColor','green')
hold on
a4=area(axes,1:N,y4);
set(a4,'FaceColor','red')
set(axes,'XTickLabel','')
set(axes,'YTickLabel','')
end
My result of this script is plotted below:
Again only one picture contains all areas.
It looks like that every call to plot(axes,data) deletes whatever was written in axes.
Important note: Do not use a variable name the same as a function. Do not call something sin ,plot or axes!! I changed it to axs.
To solve the problem I just used the classic subplot instead of creating the axes as you did:
len = 9;
axesContainer = cell(len,1);
x = [0.1,0.4,0.7,0.1,0.4,0.7,0.1,0.4,0.7];
y = [0.1,0.1,0.1,0.4,0.4,0.4,0.7,0.7,0.7];
figure(1)
dataContainer = cell(len,1);
N = 1500;
for i=1:len
dataContainer{i} = rand(1,N)*100;
end
for l=1:len
y1=[(dataContainer{l}(1:N/4)) zeros(1,3*N/4)];
y2=[zeros(1,N/4) (dataContainer{l}(N/4+1:(2*N/4))) zeros(1,2*N/4)];
y3=[zeros(1,2*N/4) (dataContainer{l}(2*N/4+1:3*N/4)) zeros(1,N/4)];
y4=[zeros(1,3*N/4) (dataContainer{l}(3*N/4+1:N))];
axs=subplot(3,3,l);
a1=area(axs,1:N,y1);
set(a1,'FaceColor','yellow')
hold on
a2=area(axs,1:N,y2);
set(a2,'FaceColor','blue')
hold on
a3=area(axs,1:N,y3);
set(a3,'FaceColor','green')
hold on
a4=area(axs,1:N,y4);
set(a4,'FaceColor','red')
set(axs,'XTickLabel','')
set(axs,'YTickLabel','')
axis tight % this is to beautify it.
end
As far as I know, you can still save the axs variable in an axescontainer and then modify the properties you want (like location).
I found out how to do what I needed.
len = 8;
axesContainer = cell(len,1);
x = [0.1,0.4,0.7,0.1,0.4,0.7,0.1,0.4];
y = [0.1,0.1,0.1,0.4,0.4,0.4,0.7,0.7];
figure(1)
for i=1:len
axesContainer{i} = axes('Position',[x(i),y(i),0.2,0.2]);
end
dataContainer = cell(len,1);
N = 1500;
for i=1:len
dataContainer{i} = rand(1,N)*100;
end
for l=1:len
y1=[(dataContainer{l}(1:N/4)) zeros(1,3*N/4)];
y2=[zeros(1,N/4) (dataContainer{l}(N/4+1:(2*N/4))) zeros(1,2*N/4)];
y3=[zeros(1,2*N/4) (dataContainer{l}(2*N/4+1:3*N/4)) zeros(1,N/4)];
y4=[zeros(1,3*N/4) (dataContainer{l}(3*N/4+1:N))];
axes=axesContainer{l};
Y=[y1',y2',y3',y4'];
a=area(axes,Y);
set(axes,'XTickLabel','')
set(axes,'YTickLabel','')
end
The area is supposed to work with matrices like this. The tricky part is, that the signal in every next column is not plotted absolutely, but relatively to the data in previous column. That means, if at time 1 the data in first column has value 1 and data in second column has value 4, the second column data is ploted at value 5. Source: http://www.mathworks.com/help/matlab/ref/area.html
I want to write a script for a candlestick-plot that I can run in Octave as well as in Matlab.
So far I use the following code:
D=[ '15-Jul-2013'
'16-Jul-2013'
'17-Jul-2013'
'18-Jul-2013'
'19-Jul-2013'
'22-Jul-2013'
'23-Jul-2013'
'24-Jul-2013'];
O=[25.93 26.39 26.37 26.75 25.82 25.99 26.10 26.32];
H=[26.43 26.75 26.78 26.77 26.11 26.13 26.30 26.53];
L=[25.60 26.01 26.30 26.12 25.60 25.72 25.97 26.05];
C=[26.28 26.32 26.65 26.18 25.88 26.05 26.13 26.51];
datapoints=length(C);
hold on;
for i=1:datapoints
plot([i i],[L(i) H(i)],'linewidth',2,'Color','k');
if C(i)>O(i)
plot([i i],[O(i) C(i)],'linewidth',5,'color','r');
else
plot([i i],[O(i) C(i)],'linewidth',5,'color','g');
end
end
hold off;
grid on;
xlim([0 datapoints+1]);
y=get(gca,'ylim');
ymin=int16(y(1)-0.5);
ymax=int16(y(2)+0.5);
ylim([ymin ymax]);
XTick=zeros(1,length(datapoints));
j=1;
for i=1:1:datapoints
XTick(j)=i;
j=j+1;
end
set(gca,'XTick',XTick,'XTickLabel','')
pos = get(gca,'Position');
set(gca,'Position',[pos(1), .15, pos(3) .75])
for i=1:length(XTick)
hText = text(XTick(i), double(ymin), D(XTick(i),:));
set(hText,'Rotation',45,'HorizontalAlignment','right','VerticalAlignment','top');
end
The resulting plot looks quite nice in Matlab but looks very terrible in Octave. How can I make the plot looks nice in both programms?
The solution here was to use the fill function instead of plot.
The following code leads to similar results in octave and matlab independent of the plotting interface:
D=[ '15-Jul-2013'
'16-Jul-2013'
'17-Jul-2013'
'18-Jul-2013'
'19-Jul-2013'
'22-Jul-2013'
'23-Jul-2013'
'24-Jul-2013'];
O=[25.93 26.39 26.37 26.75 25.82 25.99 26.10 26.32];
H=[26.43 26.75 26.78 26.77 26.11 26.13 26.30 26.53];
L=[25.60 26.01 26.30 26.12 25.60 25.72 25.97 26.05];
C=[26.28 26.32 26.65 26.18 25.88 26.05 26.13 26.51];
colorDown = 'g';
colorUp = 'r';
colorLine = 'k';
date = (1:length(O))';
% w = Width of body, change multiplier to draw body thicker or thinner
% the 'min' ensures no errors on weekends ('time gap Fri. Mon.' > wanted
% spacing)
w=.1*min([(date(2)-date(1)) (date(3)-date(2))]);
%%%%%%%%%%%Find up and down days%%%%%%%%%%%%%%%%%%%
d=C-O;
l=length(d);
hold on
%%%%%%%%draw line from Low to High%%%%%%%%%%%%%%%%%
for i=1:l
line([date(i) date(i)],[L(i) H(i)],'Color',colorLine, 'linewidth',2)
end
%%%%%%%%%%draw white (or user defined) body (down day)%%%%%%%%%%%%%%%%%
n=find(d<0);
for i=1:length(n)
x=[date(n(i))-w date(n(i))-w date(n(i))+w date(n(i))+w date(n(i))-w];
y=[O(n(i)) C(n(i)) C(n(i)) O(n(i)) O(n(i))];
fill(x,y,colorDown)
end
%%%%%%%%%%draw black (or user defined) body(up day)%%%%%%%%%%%%%%%%%%%
n=find(d>=0);
for i=1:length(n)
x=[date(n(i))-w date(n(i))-w date(n(i))+w date(n(i))+w date(n(i))-w];
y=[O(n(i)) C(n(i)) C(n(i)) O(n(i)) O(n(i))];
fill(x,y,colorUp)
end
datapoints=length(C);
xlim([0 datapoints+1]);
y=get(gca,'ylim');
ymin=int16(y(1)-0.5);
ymax=int16(y(2)+0.5);
ylim([ymin ymax]);
XTick=zeros(1,length(datapoints));
j=1;
for i=1:2:datapoints
XTick(j)=i;
j=j+1;
end
set(gca,'XTick',XTick,'XTickLabel','')
pos = get(gca,'Position');
set(gca,'Position',[pos(1), .15, pos(3) .75])
for i=1:length(XTick)
hText = text(XTick(i), double(ymin), D(XTick(i),:));
set(hText,'Rotation',45,'HorizontalAlignment','right','VerticalAlignment','top');
end
hold off
Here's what I get with Octave 3.6.2 (I have used screen captures to illustrate what it actually looks like on the screen):
Graphics toolkit qt
Graphics toolkit gnuplot
Graphics toolkit fltk
The bodies of the candles are vertical in all 3 cases.