Separating axes from plot area in MATLAB - matlab

I find that data points that lie on or near the axes are difficult to see. The obvious fix, of course, is to simply change the plot area using axis([xmin xmax ymin ymax]), but this is not preferable in all cases; for example, if the x axis is time, then moving the minimum x value to -1 to show activity at 0 does not make sense.
Instead, I was hoping to simply move the x and y axes away from the plot area, like I have done here:
left: MATLAB generated, right: desired (image editing software)
Is there a way to automatically do this in MATLAB? I thought there might be a way to do it by using the outerposition axes property (i.e., set it to [0 0 0.9 0.9] and drawing new axes where they originally were?), but I didn't get anywhere with that strategy.

The answers here already show you most of the way - here is the last step to separate the x and y axle as per the example you put together.
f = figure ( 'color', 'white' );
% create the axes and set some properties
ax = axes ( 'parent', f, 'box', 'off', 'nextplot', 'add', 'XMinorTick', 'on', 'YMinorTick', 'on' );
% plot some data
plot ( ax, 0:10, [0:10].^2, 'rx-' )
% modify the x and y limits to below the data (by a small amount)
ax.XLim(1) = ax.XLim(1)-(ax.XTick(2)-ax.XTick(1))/4;
ax.YLim(1) = ax.YLim(1)-(ax.YTick(2)-ax.YTick(1))/4;
% Set the tick direction
ax.TickDir = 'out';
% draw the plot to generate the undocumented vertex data var
drawnow()
%% R2015a
% X, Y and Z row of the start and end of the individual axle.
ax.XRuler.Axle.VertexData(1,1) = 0;
ax.YRuler.Axle.VertexData(2,1) = 0;
%% R2015b
% extract the x axis vertext data
% X, Y and Z row of the start and end of the individual axle.
vd = get(ax.XAxis.Axle,'VertexData');
% reset the zero value
vd(1,1) = 0;
% Update the vertex data
set(ax.XAxis.Axle,'VertexData',vd);
% repeat for Y (set 2nd row)
vd = get(ax.YAxis.Axle,'VertexData');
vd(2,1) = 0;
set(ax.YAxis.Axle,'VertexData',vd);
Edit: The vertex is something that Matlab recreates whenever the axes/figure changes size or if you zoom or pan for example.
You can try to counteract this (remember you are using undocumented features here) by adding a listener to attempt to capture this. We can use the MarkedClean event which is called quite a lot of times.
addlistener ( ax, 'MarkedClean', #(obj,event)resetVertex(ax) );
Where you resetVertex function is something like: (R2015b shown only)
Edit 2 added the code to turn off the minor ticks below 0.
function resetVertex ( ax )
% extract the x axis vertext data
% X, Y and Z row of the start and end of the individual axle.
ax.XAxis.Axle.VertexData(1,1) = 0;
% repeat for Y (set 2nd row)
ax.YAxis.Axle.VertexData(2,1) = 0;
% You can modify the minor Tick values by modifying the vertex data
% for them, e.g. remove any minor ticks below 0
ax.XAxis.MinorTickChild.VertexData(:,ax.XAxis.MinorTickChild.VertexData(1,:)<0) = [];
ax.YAxis.MinorTickChild.VertexData(:,ax.YAxis.MinorTickChild.VertexData(2,:)<0) = [];
end
Note: this uses undocumented features -> so may only work in certain versions of Matlab (I have added the code for r2015a & r2015b) and Matlab may recreate the vertex data depending on what you do with the plots..

Here is a simple way for achieving that:
% some data:
x = 1:100;
f=#(x) 5.*x;
y=f(x)+rand(1,length(x))*50;
close all
% plotting:
f1 = figure('Color','white');
ax = axes;
plot(ax,x,y,'o');
% 'clean' the data area a little bit:
box off
ax.TickDir = 'out';
% pushing the axis a bit forward:
lims = axis;
pos = ax.Position;
axis([lims(1)-ax.XTick(2)/5 lims(2)+0.1 lims(3)-ax.YTick(2)/5 lims(4)+0.1])
% Create lines
firstXtick = 0.013; %this value need to be adjusted only once per figure
firstYtick = 0.023; %this value need to be adjusted only once per figure
lx = annotation(f1,'line',[pos(1) pos(1)+firstXtick],...
[pos(2) pos(2)],'Color',[1 1 1],'LineWidth',1);
ly = annotation(f1,'line',[pos(1) pos(1)],...
[pos(2) pos(2)+firstYtick],'Color',[1 1 1],'LineWidth',1);
Which yields this figure:
The only thing to adjust here, once per type of figure, is firstXtick and firstYtick values, that have to be fine tuned to the specific axis. After setting them to the correct value the figure can be resized with no problem. Zoom and pan require a little fixes.

You can start your axes from less than zero and then remove the less than zero ticks from your plot. e.g.
plot(0:3:30,0:3:30); %Some random data for plotting
h = gca;
axis([-1 30 -1 30]); %Setting the axis from less than zero
box off; %Removing box
h.TickDir = 'out'; %Setting Direction of ticks to outwards
h.XTickLabel(1)= {' '}; %Removing the first tick of X-axis
h.YTickLabel(1)= {' '}; %Removing the first tick of Y-axis
With this code, you'll get this result:
This may have a drawback, sometimes, that zero ticks may also get removed (as you can see in above figure). This is because the plot had set the first ticks of axes equal to zero. This can be avoided using if condition. So, the code can be modified as below:
plot(0:3:30,0:3:30);
h = gca;
axis([-1 30 -1 30]);
box off;
h.TickDir = 'out';
if str2num(cell2mat(h.XTickLabel(1))) <0
h.XTickLabel(1)= {' '};
end
if str2num(cell2mat(h.YTickLabel(1))) <0
h.YTickLabel(1)= {' '};
end
The above code will yield the following result:-
Also note that, for your case, since your axes ticks are very less, -1 may not be much suitable for the starting value of axes and you may need to use -0.1 instead i.e. axis([-0.1 30 -0.1 30]);

With a slight modification of #matlabgui's answer you can track the (major) tick limits:
ax = gca();
% Set the tick direction
ax.TickDir = 'out';
% Make sure this stays when saving, zooming, etc
addlistener ( ax, 'MarkedClean', #(obj,event) change_ticks(ax) );
% Draw the plot to generate the undocumented vertex data variable
% and call callback for the first time
drawnow();
The callback
function change_ticks( ax )
% Modify ruler
ax.XRuler.Axle.VertexData(1,1) = ax.XTick(1);
ax.XRuler.Axle.VertexData(1,2) = ax.XTick(end);
ax.YRuler.Axle.VertexData(2,1) = ax.YTick(1);
ax.YRuler.Axle.VertexData(2,2) = ax.YTick(end);
end
I haven't test extensively but seems to work for custom ticks too. This nicely cuts the rulers not only on zero but beyond the fist and last tick. This was tested in Matlab 2019a on Windows and ax.XRuler.Axle.VertexData works just fine. Note this is only for major ticks!

Related

plotting a surface plot in two colours depending on condition on z axis

z is 200*200 array and I have a surf plot using Matlab surf(x,y,z). I am trying to plot the surf plot so that when z<10 it will be blue and when z>10 it will be red. At the moment the surf plot is like this. Can someone please suggest a way to do this in Matlab?
One way to achieve that (there are several ways) is to use a custom colormap:
Build a colormap with only the color you want to appear in your graph, then just adjust the levels so the midpoint is for Z=10.
The first part of the code shows how to create your custom colormap and apply it:
Zt = 10 ; % threshold level for Z
z = peaks+Zt ; % dummy test data
% build a colormap with your 2 custom color
% [%Red %green %blue]
cmap = [0.79 0.22 0.81 ; % some kind of purple
0 0 1 ] ; % pure blue
surf(z) ; % plot the surface
colormap(cmap) ; % apply the custom colormap
hcb = colorbar ;
This produces a surface with your two chosen colors:
But wait! The separation is not exactly at the Z=10 level. No problem, if we adjust the boundaries of the colormap so your threshold level is bang in the middle, Matlab will take care of adjusting the coloring for us:
%% Now center the colormap boundaries around your threshold level
% get the minimum and maximum
zmax = ceil( max(max(z)) ) ;
zmin = floor( min(min(z)) ) ;
span = max( [zmax-Zt, Zt-zmin] ) ; % calculate the span each side of [Zt]
caxis([Zt-span , Zt+span]) ; % center the colormap around [Zt]
The last bit of code above allow to define an equal span around your chosen threshold level and take the content of the Z data into account. If you know in advance the limits of your data you don't need to do the calculations. On the example above I could also have simply use the last line with some hard coded values:
caxis([0 , 20]) ;
As long as the interval you specify for caxis is centered around your threshold level it will work.
Edit:
To control the labels of the colorbar, I usually set the Ticks and TickLabels after the colorbar (or axes) is created. For this you need the handle of the colorbar object.
Note that in the code above I modified the last line of the first code block. I changed colorbar, to hcb=colorbar;. This way we have the handle of the colorbar, which allow us to set any arbitrary tick and associated label.
The most straightforward way to get your result for this specific example is:
hcb.Ticks = [ 5 , 10 , 15 ] ;
hcb.TickLabels = {'<10','10','>10'} ;
However if you want a more generic solution which can work with any threshold Zt then you can use:
%% adjust colorbar labels
zl = caxis ; % get the limits of the color scale
Zstr = num2str(Zt) ; % get a string representing the threshold
hcb.Ticks = [ (Zt+zl(1))/2 , Zt , (zl(2)+Zt)/2 ] ;
hcb.TickLabels = { ['<' Zstr] , Zstr , ['>' Zstr] } ;
For your example, both options produce:
Adapted from MATLAB Answers
z = peaks+10; % sample data generated between 3.4 and 18.1
% Make colors be red above 10, and blue below 10.
redChannel = z > 10;
greenChannel = 0*z;
blueChannel = z < 10;
% Make the RGB image.
colors = double(cat(3, redChannel, greenChannel, blueChannel));
% Plot the surface with those colors.
surf(z, colors);
try this
surf(x,y,z)
map = [0.0 0.0 1.0
1.0 0.0 0.0];
colormap(map);
caxis([0 20]);

How to make previous inputs progressively fade out in a Matlab plot when I add new inputs

Let's say I have this very simple loop
for i=1:10
[xO, yO, xA, yA, xB, yB, xC, yC] = DoSomething(i);
line([xO,xA,xB,xC],[yO,yA,yB,yC]);
pause(0.1);
end
The coordinates that I am plotting correspond to the joints of a multibody system, and I am simulating their positions over time (please see a sample of the plot here):
Since some of the links move in a periodic way, it gets confusing to keep track visually of the movement. For this reason, now comes the question: how can I plot the lines in a way that, when a new line is plotted, the previous lines are faded progressively? In other words, so that I have a gradient from the most recently plotted data (most opaque) to the oldest data (increasingly transparent until it completely fades out).
This way when a new line is drawn in the same position as very old data, I will notice that it is a new one.
You can do this by modifying the 4th Color attribute of past lines.
Here's a demo resulting gif, where I faded out 10% of the transparency each frame, so only the most recent 10 lines are visible.
Here is the code, see my comments for details:
% Set up some demo values for plotting around a circle
a = 0:0.1:2*pi; n = numel(a);
[x,y] = pol2cart( a, ones(1,n) );
% Initialise the figure, set up axes etc
f = figure(1); clf; xlim([-1,1]); ylim([-1,1]);
% Array of graphics objects to store the lines. Could use a cell array.
lines = gobjects( 1, n );
% "Buffer" size, number of historic lines to keep, and governs the
% corresponding fade increments.
nFade = 10;
% Main plotting loop
for ii = 1:n
% Plot the line
lines(ii) = line( [0,x(ii)], [0,y(ii)] );
% Loop over past lines.
% Note that we only need to go back as far as ii-nFade, earlier lines
% will already by transparent with this method!
for ip = max(1,ii-nFade):ii
% Set the 4th Color attribute value (the alpha) as a percentage
% from the current index. Could do this various ways.
lines(ip).Color(4) = max( 0, 1 - (ii-ip)/nFade );
end
% Delay for animation
pause(0.1);
end
You may want to do some plot/memory management if you've got many lines. You can delete transparent lines by adding something like
if lines(ii).Color(4) < 0.01
delete(lines(ii));
end
Within the loop. This way your figure won't have loads of transparent remnants.
Notes:
I generated the actual gif using imwrite in case that's of interest too.
Apparently the 4th Color value 'feature' has been depreciated in R2018b (not sure it was ever officially documented).
Got enough upvotes to motivate making a slightly more fun demo...
Solution for Matlab 2018a or later (or earlier, later than 2012a at least)
Since the fourth color parameter as alpha value is no longer supported in Matlab 2018a (and apparently was never supposed to as Cris Luengo pointed out), here a solution that works in Matlab 2018a using the patchline function from the file exchange (credits to Brett Shoelson).
% init the figure
figure(); axes();
hold on; xlim([-1 0.5]); ylim([0 1]);
% set fraction of alpha value to take
alpha_fraction = 0.7;
n_iterations = 200;
% looping variable to prevent deleting and calling already deleted lines
% i.e. to keep track of which lines are already deleted
delete_from = 1;
for i=1:n_iterations
% your x, y data
[x, y] = doSomething(i);
% create line with transparency using patchline
p(i) = patchline(x,y, 'linewidth', 1, 'edgecolor', 'k');
% set alpha of line to fraction of previous alpha value
% only do when first line is already plotted
if i > 1
% loop over all the previous created lines up till this iteration
% when it still exists (delete from that index)
for j = delete_from:i-1
% Update the alpha to be a fraction of the previous alpha value
p(j).EdgeAlpha = p(j).EdgeAlpha*alpha_fraction;
% delete barely visible lines
if p(j).EdgeAlpha < 0.01 && delete_from > j
delete(p(j));
% exclude deleted line from loop, so edgealpha is not
% called again
delete_from = j;
end
end
end
% pause and behold your mechanism
pause(0.1);
end
I included the deletion of barely visible lines, as suggested by #Wolfie (my own, perhaps less elegant implementation)
And here a demonstration of a quick release mechanism:
I'm adding a 2nd answer to clearly separate two completely different approaches. My 1st answer uses the undocumented (and as of 2018b, depreciated) transparency option for lines.
This answer offers a different approach for line drawing which has no compatibility issues (these two 'features' could be implemented independently):
Create a fixed n lines and update their position, rather than creating a growing number of lines.
Recolour the lines, fading to white, rather than changing transparency.
Here is the code, see comments for details:
% "Buffer" size, number of historic lines to keep, and governs the
% corresponding fade increments.
nFade = 100;
% Set up some demo values for plotting around a circle
dt = 0.05; a = 0:dt:2*pi+(dt*nFade); n = numel(a); b = a.*4;
[x1,y1] = pol2cart( a, ones(1,n) ); [x2,y2] = pol2cart( b, 0.4*ones(1,n) );
x = [zeros(1,n); x1; x1+x2]; y = [zeros(1,n); y1; y1+y2];
% Initialise the figure, set up axes etc
f = figure(1); clf; xlim([-1.5,1.5]); ylim([-1.5,1.5]);
% Draw all of the lines, initially not showing because NaN vs NaN
lines = arrayfun( #(x)line(NaN,NaN), 1:nFade, 'uni', 0 );
% Set up shorthand for recolouring all the lines
recolour = #(lines) arrayfun( #(x) set( lines{x},'Color',ones(1,3)*(x/nFade) ), 1:nFade );
for ii = 1:n
% Shift the lines around so newest is at the start
lines = [ lines(end), lines(1:end-1) ];
% Overwrite x/y data for oldest line to be newest line
set( lines{1}, 'XData', x(:,ii), 'YData', y(:,ii) );
% Update all colours
recolour( lines );
% Pause for animation
pause(0.01);
end
Result:

matlab - set tick label at origin

In generating plots with Matlab, when both the x and y minimum are zero, I prefer to have only one zero marking the origin rather than denoting it on both axes.
Matlab defaults to the latter, like so
Whereas I want something more like this
This can be done manually, but I'm trying to automate the process. Removing the x and y tick labels for 0 are obviously easy enough. However, I can't seem to access any handle for the position of the axis tick labels to properly position a text box for the 'new origin'. It seems the tick label offsets from the x and y axes may be somewhat constant in a given figure (in physical units, not normalized units), but I don't know if this is a standard across all figures or axes.
Any ideas?
I'm running Matlab 2014b.
Here's a simple way to do that:
% somthing to plot:
x = 0:0.1:2*pi;
y = abs(sin(x)+cos(x));
plot(x,y)
% get the handle for the current axes:
ax = gca;
% get the position of the zero tick on y-axis:
zeroYtick = ax.YAxis.TickValues==0;
% remove it (tick and lable)
ax.YAxis.TickValues(zeroYtick) = [];
The result:
If you want the 0 to be offset a little to the left, so it will be at the corner of the plot, you can use annotation:
% get the position of the zero tick on x-axis:
zeroXtick = ax.XAxis.TickValues==0;
% remove it (tick and lable)
ax.XAxis.TickValues(zeroXtick) = [];
% place a new zero at the origin:
dim = [0.1 0.01 0.1 0.1];
annotation('textbox',dim,'String','0',...
'FitBoxToText','on','LineStyle','none')
And you will get:
The advantage in annotation is that the origin always placed relative to the corner of the axes, and you don't need to know the values of the axes ticks to properly offset it.
EDIT:
For pre 2016a version you can use the following (I wrote it a little more compact):
ax = gca;
% remove the zero tick on y-axis (tick and lable):
ax.YTick(ax.YTick==0) = [];
% remove the zero tick on x-axis (tick and lable):
ax.XTick(ax.XTick==0) = [];
% place a new zero at the origin:
dim = [0.1 0.01 0.1 0.1];
annotation('textbox',dim,'String','0',...
'FitBoxToText','on','LineStyle','none')
EDIT 2:
Another option to keep the 0 in place is to stick it to the axes. this is done by replacing the Parent with the axes handle. First, we need a handle to the annotation (continuing from the last edit):
t = annotation('textbox',dim,'String','0',...
'FitBoxToText','on','LineStyle','none');
then we switch the Parent and set the new position:
t.Parent = ax;
t.Position(1:2) = -[0.2 0.1];
and finally, we super-glue it by converting the Units to pixels:
t.Units = 'Pixels';
The following will exclude the first tick on yaxis without shifting the graph at all. You can ensure that the correct item is being removed by setting the axis limits, ylim([0,upper_limit]) prior to removing the first one, or manually setting your yTicks, set(h,'yTick',my_ticks). Note that yTick changes the position of the ticks, while yTickLabel will change the text appearing at each position of yTick
h = plot([0,1],[0,1]);
yTicks = get(h,'yTick');
set(h,'yTick',yTick(2:end));

Creating a point moving along a circle in MATLAB with speed and radius defined by the user

I am looking to create a simple circle graph within MATLAB in which the model shows the point moving along the circle with radius and angular velocity defined by the user.
Angular velocity in RADIANS/SEC
I am relatively new at MATLAB coding so any help would be very useful!
I tried this code:
r=1;
t = 0:.01:2*pi;
x = r*cos(t);
y = r*sin(t);
comet(x,y);
But when I change the 0.01 value the point doesn't move faster, it just skips more of the curve, also i'm unsure if the increments are in radians.
Thanks for your time
Edited version: See edit history for previous version.
Radius = 10;
AngularVelocity = 5; % in deg / s
AngleStep = 0.1
Angles = AngleStep : AngleStep : 2*pi;
CircleX = [Radius]; % empty array
CircleY = [0]; % empty array
%Initial zero-angle plot whose data we'll keep updating in the for loop:
a = plot([CircleX,CircleX], [CircleY,CircleY], 'r:');
hold on;
b = plot(CircleX, CircleY, 'o', 'markeredgecolor', 'k', 'markerfacecolor','g');
axis([-Radius, +Radius, -Radius, +Radius]); % make sure the axis is fixed
axis equal; % make x and y pixels of equal size so it "looks" a circle!
hold off;
for t = Angles
CircleX(end+1) = Radius * cos (t); % append point at end of CircleX array
CircleY(end+1) = Radius * sin (t); % append point at end of Circley array
set(a,'xdata',CircleX,'ydata',CircleY); % update plot 'a' data
set(b,'xdata', CircleX(end), 'ydata', CircleY(end)); % update plot 'b' data
drawnow; % ensure intermediate frames are shown!
pause(AngleStep/AngularVelocity) % pause the right amount of time!
end
This edit has made two changes compared to the previous version:
Instead of redrawing, now we're updating the data of an existing plot. This is generally faster as matlab doesn't have to redraw axes objects (i.e. the containers that hold the plot)
I increased AngleStep from 0.01 to 0.1. This means there's 10 times less angles to draw, so you can afford to draw then 10 times slower, therefore it becomes less likely that matlab will be unable to draw because of overhead. Having said that, this is at the cost of a less perfect circle. Try with AngleStep=1 to see what I mean.

Absolute scale for color map?

I am trying to make a plot with an intensity that varies over time:
[X,Y] = meshgrid(-30:.1:30);
figure;
colormap(bone);
for t = 0:0.1:2*pi
R = sqrt(X.^2 + Y.^2);
Z = cos(t)*abs(besselj(2,R));
surf(Z,'EdgeColor','None');
view(90,90);
axis([0 600 0 600 -0.5 0.5])
pause(0.1);
end
I want to look at this from the top, such that as the Z value changes, the color changes. The problem is that rather than having an absolute scale (black = -0.5, white = 0.5), the color scale is relative to the maximum and minimum values, such that the colors only change when the sign flips change. How can I set an absolute scale for the color map?
Thank you.
You have to use scaled colour mapping mode and set the limits of the scaling by using the caxis command.
Now the problem with your current code is that you call surf at each iteration of the loop, essentially destroying the current plot and generating a new plot each time. This will reset a lot of properties, including the caxis limits to auto. To overcome that, simply create your plot only once before the loop, then in the loop you only change the properties which are modified (the Z values in this case). This way everything else stays the same in the figure.
So you code becomes:
%% // Prepare and initialize the surface plot
[X,Y] = meshgrid(-30:.1:30);
R = sqrt(X.^2 + Y.^2) ; %// this doesn't need to be in the loop
Z = cos(0)*abs(besselj(2,R)) ; %// calculate initial value to create the surface
surfHandle = surf( Z , 'EdgeColor','None' ) ; %// create a first surface, and save the handle to the surface object
colormap(bone);
colorbar %// this is optional, just to make sure the colorbar does not vary
caxis([-5 5 ] ) ; %// this is what sets the color scaling to what you want
view(90,90);
axis([0 600 0 600 -0.5 0.5]) ; %// this doesn't need to be in the loop anymore
%% // Modify and update the surface plot
for t = 0:pi/100:2*pi
Z = cos(t)*abs(besselj(2,R));
set( surfHandle , 'ZData' , Z )
drawnow
pause(0.01);
end
Read coloring-mesh-and-surface-plots for more info on how surfaces can be colored.
If you just want white for values less than 0 and black for values greater than 0, you ca simply do:
surf(Z,sign(Z),'EdgeColor','None');
which uses the optional C argument to surf, telling Matlab to colour the plot depending on the values of C, not Z. sign(Z) is a matrix that has 1's where Z>0, 0's where Z=0, and -1's where Z<0.