Larger subplots in MATLAB - matlab

I have 6 Y variables all with the same X Variable (time)
I want to have 6 plots in a column but when I use subplot(6,1,1) the plots become tiny vertically.
I have tried using:
x=0:360;
y1=sind(x);
y2=cosd(x);
h=subplot(6,1,1);
plot(x,y1);
d = get(h,'Position');
d(4)=d(4)*3;
set(h,'Position',d);
h=subplot(6,1,2);
plot(x,y2);
d = get(h,'Position');
d(4)=d(4)*3;
set(h,'Position',d);
....(For 6 subplots)
In the hope that each subplot would be 3 times larger vertically, which works but the spacing between the subplots does not update so the subplots start to overlap:
How can I make it so the subplots are larger vertically but equally spaced out like they were before I changed the height of each subplot?
Also if you could help me hide the xTick labels (numbers) but keep the ticks (lines) themselves on all subplots but the very bottom one that would also be a great help. Thanks!

Matlab sucks pretty hard for easily making this kind of graphs. You either have to do it by hand or search around on the file-exchange to see if someone already implemented something better.
This blog discussed one script that seems to do what you want.
To hide the tick labels:
set(gca, 'XTickLabel', '')
If you want to build your own subplot, you can use something like this (not tested very well):
function ax = mySubplot(nrow, ncol)
% returns a matrix of axis handles
% to plot in the second subplot, you would use plot(ax(1,2), x, y)
% standard x, y, dx, dy for subplot(111)
x0 = 0.1300;
y0 = 0.1100;
w0 = 0.7750;
h0 = 0.8150;
w = w0 / ncol;
h = h0 / nrow;
figure()
ax = nan(nrow, ncol);
for irow = 1:nrow
for icol = 1:ncol
ax(irow, icol) = axes('position', ...
[x0 + (icol - 1) * w, y0 + (nrow - irow) * h, 0.9*w, 0.9*h]);
end
end
Result of mySubplot(6,2):
You could play yourself with the spacings.

Related

matlab: moving circle along a graph and axis equal

Hello and pardon me if my english is a bit rusty. I'm trying to create a circle that moves along a parametric function (coordinates are stored in vectors). I have written a function for drawing the circle and I know that you can use the axis equal command in matlab in order to create a circle shape and avoid an ellipse. My problem is that when I do this the figure window becomes very wide relative to the plotted graph. Any input is appreciated.
MAIN CODE:
t = linspace(0,3);
x = 30*cos(pi/4)/2*(1-exp(-0.5*t));
y = (30*sin(pi/4)/2 + 9.81/0.5^2)*(1-exp(0.5*t)) - 9.81*t/0.5;
for i = 1:length(t)
plot(x,y)
axis equal
hold on
cirkel(x(i),y(i),1,1,'r') % argument #3 is the radius #4 is 1 for fill
hold off
pause(0.01)
end
CIRCLE CODE:
function cirkel(x,y,r,f,c)
angle = linspace(0, 2*pi, 360);
xp = x + r*cos(angle);
yp = y + r*sin(angle);
plot(x,y)
if f == 1 && nargin == 5
fill(xp,yp,c)
end
When you call axis equal it makes one unit of the x axis be the same size as one unit of the y axis. You are seeing what you are because your y values span a much larger range than the x values.
One way to deal with this would be to query the aspect ratio and x/y limits of the current axes as shown in the second part of this answer. However, an easier approach is rather than using fill to plot your circle, you could instead use scatter with a circular marker which will be circular regardless of the aspect ratio of your axes.
t = linspace(0,3);
x = 30*cos(pi/4)/2*(1-exp(-0.5*t));
y = (30*sin(pi/4)/2 + 9.81/0.5^2)*(1-exp(0.5*t)) - 9.81*t/0.5;
% Plot the entire curve
hplot = plot(x, y);
hold on;
% Create a scatter plot where the area of the marker is 50. Store the handle to the plot
% in the variable hscatter so we can update the position inside of the loop
hscatter = scatter(x(1), y(1), 50, 'r', 'MarkerFaceColor', 'r');
for k = 1:length(t)
% Update the location of the scatter plot
set(hscatter, 'XData', x(k), ... % Set the X Position of the circle to x(k)
'YData', y(k)) % Set the Y Position of the circle to y(k)
% Refresh the plot
drawnow
end
As a side note, it is best to update existing plot objects rather than creating new ones.
If you want the small dot to appear circular, and you want to have a reasonable domain (x-axis extent), try this:
function cirkel(x,y,r,f,c)
angle = linspace(0, 2*pi, 360);
xp = x + 0.04*r*cos(angle); %% adding scale factor of 0.04 to make it appear circular
yp = y + r*sin(angle);
plot(x,y)
if f == 1 && nargin == 5
fill(xp,yp,c)
end
Note the addition of the scale factor in the computation of xp. If you want to automate this, you can add another parameter to cirkel(), let's call it s, that contains the scale factor. You can calculate the scale factor in your script by computing the ratio of the range to the domain (y extent divided by x extent).

Bar3 plot with seperate x,y,height and width values

Solution posted below function to plot bar 3 with separate x, y values and separate width and height values
bar3(x,y,z,xWidth,yWidth)
We are currently working on a project that allow one to visualize the area under a 3d function, f(x,y). The purpose of this is to demonstrate how the bars cut a 3d surface. Indirectly to visualize the desired integral.
We wish to have the bars match up with the intervals of the surface grid.
Below is a rough demonstration of the idea.
bar3 only has input for the x-values bar3(x,z), where as surf has a input for both the x and y surf(x,y,z)
Unfortunately this is what we are getting. - this is because bar3 cant be in terms of x and y
CODE:
clc;
cla;
d=eval(get(handles.edtOuterUpperB,'string'));
c=eval(get(handles.edtOuterLowerB,'string'));
b=eval(get(handles.edtInnerUpperB,'string'));
a=eval(get(handles.edtInnerLowerB,'string'));
n=eval(get(handles.edtInnerInterval,'string'));
m=eval(get(handles.edtOuterInterval,'string'));
h=(b-a)/n;
k=(d-c)/m;
[x,y] = meshgrid(a:h:b, c:k:d);
f=eval(get(handles.edtFunc,'string'));
surf(x,y,f);
hold on
bar3(f,1);
If you look closely, you will see that the XData and YData are different from the mesh to the 3D bar plot. This is because your mesh uses "real" x and y values while the bar plot uses indexes for the x and y values.
To fix this, you will want to change one or the other. For your case, the easiest one to change is going to be the surface. You can actually just omit the x and y inputs and the indexed x and y values will be used instead by default when generating the surface.
surf(f);
From the documentation for surf:
surf(Z) creates a three-dimensional shaded surface from the z components in matrix Z, using x = 1:n and y = 1:m, where [m,n] = size(Z). The height, Z, is a single-valued function defined over a geometrically rectangular grid. Z specifies the color data, as well as surface height, so color is proportional to surface height.
Update
If you want to keep the non-indexed values on the x and y axes, you will want to convert the bar3 plot instead. Unfortunately, MATLAB provides a way to specify the x axis bot not the y axis. You can take one of two approaches.
Change the XData
You can get the XData property of the resulting bar objects and change them to the data you want.
x = a:h:b;
y = c:k:d;
%// Anonymous function to scale things for us
scaler = #(vals)x(1) + ((vals-1) * (x(end) - x(1)) / (numel(x) - 1));
%// Create the bar plot
bars = bar3(y, f);
%// Change the XData
xdata = get(bars, 'XData');
xdata = cellfun(scaler, xdata, 'uni', 0);
set(bars, {'XData'}, xdata);
set(gca, 'xtick', x)
%// Now plot the surface
surf(x,y,f);
And just to demonstrate what this does:
x = linspace(0.5, 1.5, 5);
y = linspace(2.5, 4.5, 4);
f = rand(4,5);
scaler = #(vals)x(1) + ((vals-1) * (x(end) - x(1)) / (numel(x) - 1));
bars = bar3(y, f);
set(bars, {'XData'}, cellfun(scaler, get(bars, 'XData'), 'uni', 0))
set(gca, 'xtick', x)
axis tight
Change the XTickLabels
Instead of changing the actual data, you could simply change the values that are displayed to be what you want them to be rather than the indexed values.
x = a:h:b;
y = c:k:d;
labels = arrayfun(#(x)sprintf('%.2f', x), x, 'uni', 0);
bar3(y, f);
set(gca, 'xtick', 1:numel(x), 'xticklabels', labels);
hold on
%// Make sure to use the INDEX values for the x variable
surf(1:numel(x), y, f);
We found a user contributed function scatterbar3, which does what we want, in a different way than what bar3 uses:
http://www.mathworks.com/matlabcentral/fileexchange/1420-scatterbar3
There was however a slight hiccup that we had to correct:
hold on
scatterbar3(x,y,f,h);
scatterbar3 does not have separate inputs for the width and height of the bars, thus large gaps occur when the intervals do not equal one another. Demonstrated below.
We thus edited the scatterbar3 function in order to take both the width and height of the bars as inputs:
Edited scatterbar3 function:
function scatterbar3(X,Y,Z,widthx,widthy)
[r,c]=size(Z);
for j=1:r,
for k=1:c,
if ~isnan(Z(j,k))
drawbar(X(j,k),Y(j,k),Z(j,k),widthx/2,widthy/2)
end
end
end
zlim=[min(Z(:)) max(Z(:))];
if zlim(1)>0,zlim(1)=0;end
if zlim(2)<0,zlim(2)=0;end
axis([min(X(:))-widthx max(X(:))+widthx min(Y(:))-widthy max(Y(:))+widthy zlim])
caxis([min(Z(:)) max(Z(:))])
function drawbar(x,y,z,widthx,widthy)
h(1)=patch([-widthx -widthx widthx widthx]+x,[-widthy widthy widthy -widthy]+y,[0 0 0 0],'b');
h(2)=patch(widthx.*[-1 -1 1 1]+x,widthy.*[-1 -1 -1 -1]+y,z.*[0 1 1 0],'b');
h(3)=patch(widthx.*[-1 -1 -1 -1]+x,widthy.*[-1 -1 1 1]+y,z.*[0 1 1 0],'b');
h(4)=patch([-widthx -widthx widthx widthx]+x,[-widthy widthy widthy -widthy]+y,[z z z z],'b');
h(5)=patch(widthx.*[-1 -1 1 1]+x,widthy.*[1 1 1 1]+y,z.*[0 1 1 0],'b');
h(6)=patch(widthx.*[1 1 1 1]+x,widthy.*[-1 -1 1 1]+y,z.*[0 1 1 0],'b');
set(h,'facecolor','flat','FaceVertexCData',z)
Finally the working solution in action:
hold on
scatterbar3(x,y,f,h,k);

ContourPlot and inequalitie plot together

Im new to matlab and I want to plot a ContourPlot together with two inequalities but I dont know how. The function that I want to plot its ContourPlot is something like this:
Z = (X-2).^2 + (Y-1).^2;
and here are the two inequalities:
ineq1 = X.^2 - Y <= 2;
ineq2 = X + Y <= 2;
this is what I have dodne so far:
[X,Y] = meshgrid(-4:.2:4,-4:.2:4);
Z = (X-2).^2 + (Y-1).^2;
[C,h] = contour(X,Y,Z);
clabel(C,h)
ineq1 = X.^2 - Y <= 2;
ineq2 = X + Y <= 2;
range = (-4:.2:4);
hold on
plot(range,ineq1, range, ineq2)
hold off
but this does not feel right.
What I want to do is to visualize an optimization problem. First I want to plot the ContourPlot of the function and then the possible areas in that same plot, It would be great if I could show the intersection areas too.
If you want to draw the boundaries of the inequalities onto the contour plot, you can do this with line.
[X,Y] = meshgrid(-4:.2:4,-4:.2:4);
Z = (X-2).^2 + (Y-1).^2;
[C,h] = contour(X,Y,Z);
clabel(C,h)
x_vect = (-4:.05:4);
y_ineq1 = x_vect.^2 - 2;
y_ineq2 = 2 - x_vect;
line(x_vect, y_ineq1);
line(x_vect, y_ineq2);
Coloring the intersection area is a bit more tricky, and I'm not sure if it's exactly what you want to do, but you could look into using patch, something like
% Indexes of x_vect, y_ineq1 and y_ineq2 for region where both satisfied:
region_indexes = find(y_ineq1<y_ineq2);
region_indexes_rev = region_indexes(end:-1:1);
% To plot this area need to enclose it
% (forward along y_ineq1 then back along y_ineq2)
patch([x_vect(region_indexes),x_vect(region_indexes_rev)],...
[y_ineq1(region_indexes),y_ineq2(region_indexes_rev)],...
[1 0.8 1]) % <- Color as [r g b]
% May or may not need following line depending on MATLAB version to set the yaxes
ylim([-4 4])
% Get the original plot over the top
hold on
[C,h] = contour(X,Y,Z);
clabel(C,h)
hold off

Missing axes in figure with multiple subplots

I am drawing a figure with multiple subplots with the following code
w = 7;
h = 5;
figure;
m = 0.005;
for n = 1:w*h
[I, J] = ind2sub([w, h], n);
pos = [(I-1)/w+m, (J-1)/h+m, 1/w-2*m, 1/h-2*m];
h_axes = subplot(h, w, n, 'position', pos);
imagesc(rand(10), 'Parent', h_axes);
axis(h_axes, 'image')
axis(h_axes, 'off')
end
However, only the top two rows are plotted, whereas 5 rows are expected. Going through this code step by step, it seems that lower rows are initially drawn but later upper rows erase them. The axes handles are invalidated, so the axes are not simply hidden but really deleted. Any idea what is going on?
There is a problem with the way you use subplot. You can use either syntax:
subplot(m,n,p)
or
subplot('Position',positionVector)
... but not both in the same call.

quiver not drawing arrows just lots of blue, matlab

Can somebody tell me what I am doing wrong with the quiver plotting function when I don't really get arrows, it just fills the empty space with lots of blue.Look at the image below and then look at my code.
This is just a part of my contour since this eats up proccessing power if I try to draw it larger. But my function, the contours and everything else works, it's just the quiver I'm having trouble with.
interval = -100:100;
[X Y] = meshgrid(interval, interval);
h = figure;
contour(X, Y, Z);
hold on;
[FX,FY] = gradient(-Z);
quiver(X, Y, FX, FY);
hold off;
If I make my matrix more sparse, e.g. with "interval = linspace(-800, 1600, 1200);" the result will look like this:
EDIT:
What I need are contour lines like that, but the arrows should flow with them. Right now these just look like dots, even if I zoom in further. If I zoom out the entire window will be blue.
Here is the script in its entirety if anyone wants to play with it to figure this out.
m1 = 1;
m2 = 0.4;
r1 = [1167 0 0];
r2 = [-467 0 0];
G = 9.82;
w = sqrt( G*(m1+m2) / norm(r1-r2)^3 );
interval = linspace(-800, 1600, 1200);
% Element-wise 2-norm
ewnorm = #(x,y) ( x.^2 + y.^2 ).^(1/2);
% Element-wise cross squared
ewcross2 = #(w,x,y) w^2.*( x.*x + y.*y );
[X Y] = meshgrid(interval, interval);
Z = - G*m1 ./ ewnorm( X-r1(1), Y-r1(2) ) - G*m2 ./ ewnorm( X-r2(1), Y-r2(2) ) - 1/2*ewcross2(w,X,Y);
h = figure;
contour(Z);
daspect([1 1 1]);
saveas(h, 'star1', 'eps');
hold on;
[FX,FY] = gradient(-Z);
quiver(X, Y, FX,FY);
hold off;
The problem is that the mesh is too dense. You only want to have as few elements as necessary to generate a useful mesh. As such, try reducing the density of the mesh:
interval = -100:2:100
If you're going to be changing the limits often, you probably want to avoid using the X:Y:Z formulation. Use the linspace function instead:
interval = linspace(-100,100,10);
This will ensure that no matter what your limits, your mesh will be 10x10. In the comment below, you mention that the arrows are appearing as dots when you use a very large mesh. This is to be expected. The arrows reflect "velocity" at a given point. When your plot is scaled out to a very large degree, then the velocity at any given point on the plot will be almost 0, hence the very small arrows. Check out the quiver plot documentation, as well as the quivergroup properties, to see more details.
If you absolutely must see arrows at a large scale, you can try setting the AutoScale property to off, or increasing the AutoScaleFactor:
quiver(X, Y, FX, FY, 'AutoScale', 'off');
quiver(X, Y, FX, FY, 'AutoScaleFactor', 10);
You may also want to play with the MarkerSize and MaxHeadSize properties. I really just suggest looking at all the QuiverGroup properties and trying things out.
You could use a threshold
interval = -100:100;
[X Y] = meshgrid(interval, interval);
h = figure;
contour(X, Y, Z);
hold on;
[FX,FY] = gradient(-Z);
GM = sqrt(FX.^2 + FY.^2);
threshold = 0.1;
mask = GM > threshold;
quiver(X(mask), Y(mask), FX(mask), FY(mask));
hold off;
This will show only vectors with a magnitude > 0.1;