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
Related
I'm trying to write a MatLab code which can extend the number of ODE's to solve for simply. This is the code that I currently have, for simplicity I am starting with a spring mass damper system.
clear;clc;close all;
tspan=[0:0.01:1];
x0=[1;0;-1;0;7;0;-7;0;5;0;-5;0;10;0;-10;0];
[t,x] = ode45(#Spring_Mass_Damper,tspan,x0);
figure(1)
plot(t,x(:,1));
hold on
plot(t,x(:,3));
hold on
plot(t,x(:,5));
hold on
plot(t,x(:,7));
hold on
plot(t,x(:,9));
hold on
plot(t,x(:,11));
hold on
plot(t,x(:,13));
hold on
plot(t,x(:,15));
grid on
xlabel('Time')
ylabel('Displacement(x)')
title('Displacement Vs Time')
function xp = Spring_Mass_Damper(t,x)
c1=10;
G=9.81;
m1=1;
k1=2000;
xp=[[x(2)];(-(c1/m1).*[x(2)]-(k1/m1).*[x(1)])-(G.*m1);
[x(4)];(-(c1/m1).*[x(4)]-(k1/m1).*[x(3)])-(G.*m1);
[x(6)];(-(c1/m1).*[x(6)]-(k1/m1).*[x(5)])-(G.*m1);
[x(8)];(-(c1/m1).*[x(8)]-(k1/m1).*[x(7)])-(G.*m1);
[x(10)];(-(c1/m1).*[x(10)]-(k1/m1).*[x(9)])-(G.*m1);
[x(12)];(-(c1/m1).*[x(12)]-(k1/m1).*[x(11)])-(G.*m1);
[x(14)];(-(c1/m1).*[x(14)]-(k1/m1).*[x(13)])-(G.*m1);
[x(16)];(-(c1/m1).*[x(16)]-(k1/m1).*[x(15)])-(G.*m1)];
end
The Main question I have regards this area of code:
xp=[[x(2)];(-(c1/m1).*[x(2)]-(k1/m1).*[x(1)])-(G.*m1);
[x(4)];(-(c1/m1).*[x(4)]-(k1/m1).*[x(3)])-(G.*m1);
[x(6)];(-(c1/m1).*[x(6)]-(k1/m1).*[x(5)])-(G.*m1);
[x(8)];(-(c1/m1).*[x(8)]-(k1/m1).*[x(7)])-(G.*m1);
[x(10)];(-(c1/m1).*[x(10)]-(k1/m1).*[x(9)])-(G.*m1);
[x(12)];(-(c1/m1).*[x(12)]-(k1/m1).*[x(11)])-(G.*m1);
[x(14)];(-(c1/m1).*[x(14)]-(k1/m1).*[x(13)])-(G.*m1);
[x(16)];(-(c1/m1).*[x(16)]-(k1/m1).*[x(15)])-(G.*m1)];
is there a simple way in which i can achieve the same number of systems(ODE's) without having to copy and paste the first equation in the block and manually change the x indexing?
This should also work:
xp = zeros(size(x,1),1)
for idx=1:size(x,1)
if mod(idx,2) == 0
xp(idx) = (-(c1/m1).*[x(idx)]-(k1/m1).*[x(idx-1)])-(G.*m1);
else
xp(idx) = x(idx);
end
end
The plotting routine can be simplified in a similar manner, like this:
figure(1)
hold on
for idx=1:2:size(x,2)
plot(t,x(:,idx));
end
grid on
xlabel('Time')
ylabel('Displacement(x)')
title('Displacement Vs Time')
Ifx length is an even number, then the code below could be a substitute to xp
xp = zeros(length(x), 1);
xp(1:2:end-1) = x(2:2:end);
xp(2:2:end) = -(c1/m1).*(x(2:2:end))-(k1/m1).*(x(1:2:end-1))-G.*m1;
I have one .m file per plot and want to see in my draft printouts, which file was used to create it.
This should be done with a function which can be placed in my .m file and
commented out, for the final version.
% addWatermarkFilename() %
So far I found mfilename(), but it could not get the name of the calling function. I am looking also for a good way to put the text in the picture without changing the size.
Solution:
I combined the suggestions by Luis Mendo and NKN to:
function [ output_args ] = watermarkfilename( )
% WATERMARKFILENAME prints the filename of the calling script in the
% current plot
s = dbstack;
fnames = s(2).name;
TH = text(0,0,fnames,'Interpreter','none');
TH.Color = [0.7 0.7 0.7];
TH.FontSize = 14;
TH.Rotation = 45;
uistack(TH,'bottom');
end
I suggest instead of commenting out a function from the code, use a proper flag. For instance the code can be something like this:
clc,clear,close all
addWaterMark = true; % if true you will get the filename in the title
fname = mfilename; % get file name mfilename('fullpath') for the full-path
t=-2*pi:0.1:2*pi;
y = sin(t);
plot(t,y); grid on;
xlabel('t');ylabel('y');
if addWaterMark
title(['filename: ' fname '.m']);
else
title('plot no.1');
end
A little bit playing with the text function, you can make a proper watermark. something like this:
clc,clear,close all
addWaterMark = true;
fname = mfilename;
fnames = [fname '.m'];
t=-2*pi:0.1:2*pi;
y = sin(t);
plot(t,y); grid on;
xlabel('t');ylabel('y');
if addWaterMark
title(['filename: ' fnames]);
t = text(-3,-0.4,fnames);
t.Color = [0.7 0.7 0.7];
t.FontSize = 40;
t.Rotation = 45;
else
title('plot no.1');
end
Note: Obviously the code between the if and else can be stored as a function that receives the string (char) from the fnames variable and a handle for the figure.
If you want to get the name of the function or script that called the current function or script, use dbstack as follows:
s = dbstack; % get struct with function call stack information
callerName = s(2).name; % get name of second function in the stack
The first entry in s refers to the current function; the second refers to the one that called it.
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 would like to know if some of you know of a way to automatically title plots with the name of the object plotted
so that for intance when I plot supermatrix(5:10,:,2:3)
the title (or the legend ..) on the plot says "supermatrix(5:10,:,2:3)"
thanks
Is this for debugging purposes? If not then I suggest you tell us your overall motivation because someone might be able to suggest a more robust method, but this might get you started:
vname = #(x)inputname(1); %//from here: https://www.mathworks.com/matlabcentral/newsreader/view_thread/251347
plot(supermatrix(5:10,:,2:3))
title(vname(supermatrix))
Although to be honest I cannot imagine why this would ever be useful
I think this does what you want and remains pretty flexible:
function h = plotwithtitle( plotstring, varargin )
argstoplot = evalin('caller', ['{', plotstring, '}']);
h = plot( argstoplot{:}, varargin{:} );
title(plotstring);
end
The following examples all work for me:
supermatrix=rand(10,10);
x=1:10;
y=rand(1,10);
plotwithtitle('supermatrix');
plotwithtitle('supermatrix(5:10,:)');
plotwithtitle('x, y');
plotwithtitle('x, y', '--r');
plotwithtitle('1:10', 'r');
plotwithtitle('rand(1,10)');
I've modified the function dfig originally created by F.Moisy for creating docked figures in order to have the command used for plotting show up in the figure name.
The idea is to read the last command in the command history and use that to generate the figure title.
function hh = dfig(varargin)
%DFIG Create docked figure window
% DFIG, by itself, creates a new docked figure window, and returns its
% handle.
%
% DFIG(H) makes H the current figure and docks it. If Figure H does not
% exist, and H is an integer, a new figure is created with handle H.
%
% DFIG(H, name, value,...) reads additional name-value pairs. See
% doc(figure) for available otions.
%
% DFIG will parse the command line input and use the text following dfig
% as figure name. E.g. calling dfig,plot(x(1:3),y(2:2:end)) results in
% the name "plot(x(1:3),y(2:2:end))"
% F. Moisy, moisy_at_fast.u-psud.fr
% Revision: 1.00, Date: 2007/09/11
% Modified (a lot) by Jonas
if nargin==0
h=figure; % create a new figure
else
% call figure with varargin
figure(varargin{:})
h = gcf;
end
if ~any(strcmp('name',varargin(1:2:end)))
% if no name has been supplied: try to use function call
javaHistory=com.mathworks.mlservices.MLCommandHistoryServices.getSessionHistory;
if ~isempty(javaHistory)
lastCommand = javaHistory(end).toCharArray';
funCall = regexp(lastCommand,'dfig\s*[,;]\s*(.*)$','tokens','once');
else
funCall = [];
end
if ~isempty(funCall)
if isnumeric(h)
set(h,'Name',[num2str(h),': ',funCall{1}],'NumberTitle','off')
else % HG2
h.Name = sprintf('%i: %s',h.Number,funCall{1});
h.NumberTitle = 'off';
end
end
end
set(h,'WindowStyle','docked'); % dock the figure
if nargout~=0 % returns the handle if requested
hh=h;
end
I am writing a wrapper for plot that automates some tasks that I find myself doing frequently.
An example code snippet might look like
function myplot(x,y,varargin)
plot(x,y,varargin{:})
xlabel('x axis')
ylabel('y axis')
end
I'm using Matlab's varargin to pass additional arguments to plot. However, I find that I might want to pass my own optional arguments in varargin. For example, I might want to write something like
>> myplot(1:10, 1:10, 'r', 'LineWidth', 2, 'legend', {'Series 1'})
to have the function automatically include a legend in the plot - that is, I want to be able to mix my own keyword arguments with the ones that you can supply to plot. Short of writing a full parser for my varargs, is there a way to do this simply and reusably in Matlab?
I've tried to use the inputParser object, but that would require me to manually add every possible additional argument to plot (and a default for it) which doesn't seem ideal.
inputParser may still be the best choice. You can construct the object for your additional arguments, and lump all the parameterName/parameterValue pairs that you want to pass to plot into Unmatched.
function myplot(x,y,varargin)
parserObj = inputParser;
parserObj.KeepUnmatched = true;
parserObj.AddParamValue('legend',false);
%# and some more of your special arguments
parserObj.parse(varargin);
%# your inputs are in Results
myArguments = parserObj.Results;
%# plot's arguments are unmatched
tmp = [fieldnames(parserObj.Unmatched),struct2cell(parserObj.Unmatched)];
plotArgs = reshape(tmp',[],1)';
plot(x,y,plotArgs{:})
xlabel('x axis')
ylabel('y axis')
if myArguments.legend
%# add your legend
end
end
To support the Linespec argument without interfering with input parser parameters that have a name of 4 characters or less (e.g., 'grid' or 'bins'), you should have a slightly more sophisticated way to check the validity of the Linespec argument than '#(x) ischar(x) && numel(x) <=4'. This check would return true also for 'grid' and 'bins', although they are no valid Linespec arguments.
The following function will return true only if a valid Linespec argument is entered:
function isls = islinespec(x)
isls = false;
if ~ischar(x)
return;
end
lineStyleSpecifiers = {'--','-.','-',':'};
markerSpecifiers = {'square','diamond','pentagram','hexagram','+','o','*','.','x','s','d','^','v','>','<','p','h'};
colorSpecifiers = {'r','g','b','c','m','y','k','w'};
for oo=1:length(lineStyleSpecifiers)
k = strfind(x,lineStyleSpecifiers{oo});
if ~isempty(k)
x(k:(k+length(lineStyleSpecifiers{oo})-1)) = [];
break;
end
end
for oo=1:length(markerSpecifiers)
k = strfind(x,markerSpecifiers{oo});
if ~isempty(k)
x(k:(k+length(markerSpecifiers{oo})-1)) = [];
break;
end
end
for oo=1:length(colorSpecifiers)
k = strfind(x,colorSpecifiers{oo});
if ~isempty(k)
x(k:(k+length(colorSpecifiers{oo})-1)) = [];
break;
end
end
if isempty(x)
isls = true;
end
(Admittedly, the function could have been written more elegantly with regular expressions, but it does the trick.)
Example usage:
parserObj = inputParser;
parserObj.KeepUnmatched = true;
parserObj.AddParamValue('legend',false);
parserObj.AddParamValue('grid',true);
parserObj.addOptional('LineSpec','-', #(x) islinespec(x));
To add support for LineSpec using the parser in a way that is unambiguous w/ param/val pairs, do:
function myplot(x,y,varargin)
parserObj = inputParser;
parserObj.KeepUnmatched = true;
parserObj.AddParamValue('legend',false);
parserObj.addOptional('LineSpec','-',#(x) ischar(x) && (numel(x) <= 4));
%# and some more of your special arguments
parserObj.parse(varargin{:});
%# your inputs are in Results
myArguments = parserObj.Results;
%# plot's arguments are unmatched
plotArgs = struct2pv(parserObj.Unmatched);
plot(x,y,myArguments.LineSpec,plotArgs{:})
xlabel('x axis')
ylabel('y axis')
if myArguments.legend
%# add your legend
end
end
function [pv_list, pv_array] = struct2pv (s)
p = fieldnames(s);
v = struct2cell(s);
pv_array = [p, v];
pv_list = reshape(pv_array', [],1)';
end