Recently, I've been using matlab to run a particle aggregation simulation, and I use its plotting itnerface to represent each particle. Thus, I've put together some code which adjusts the size of a marker such that it is equivalent to the actual particle's diameter - in relation to the window's aspect ratio and such. Well, this works very well if I'm dealing with a square domain; see below.
As you can see, the markers are sized such that particles will settle out on topof each other perfectly. Here's the plotting code for that image:
%%Initial Plot at time t = 0 Along with Scaling in the Y-Direction
figure; h=scatter(Pos(1,:),Pos(2,:),6,jet(length(Pos(1,:))),'filled','MarkerEdgeColor','k','linewidth',1);
hold on
axis equal
axis ([0 dp 0 dp]*L*Scale)
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = dp/diff(ylim)*axpos(4); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)
Well, I added in periodic boundary conditions to my simulation, and this means that I can have 3X the window size shown above without much additional computation cost. So, I wanted to plot up a 3x1 domain (x is basically 3X longer than the y dimension of the domain). However, I'm having troubling sizing the particles appropriately such that they will be scaled properly in the y-direction yet also touch along the x-direction. I think I could do this using some kind of aspect ratio code, but I can't get it to work. Any ideas? Here's the code/result I came up with:
%Plot all of the data
figure;
h=scatter(Pos(1,:),Pos(2,:),6,jet(length(Pos(1,:))),'filled','MarkerEdgeColor','k','linewidth',1);
hold on
axis equal
axis ([0 3*dp 0 3*dp]*L*Scale)
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = dp/diff(ylim)*axpos(4); % Calculate Marker width in points
set(h, 'SizeData', (markerWidth)^2)
axis ([0 3*dp 0 dp]*L*Scale)
It produces the following result,which is obviously incorrect :(...
You could use something like
%cirlce def
circx = sin(linspace(0,2*pi,100));
circy = cos(linspace(0,2*pi,100));
%example data
[mgx mgy] = meshgrid(5:5:100, 5:5:100);
x = reshape(mgx,[],1); %circles centers x
y = reshape(mgy,[],1); %circles centers x
r = 2.5*rand(400,1); %radii
c = 250*rand(400,1); %colors
% draw
figure
hold on
for i=1:numel(x)
fill(r(i)*circx+x(i), r(i)*circy+y(i), c(i))
end
so that all all geometric data is fixed into coordinate axes space.
Example output is:
Related
I'm using Ezpolar function in order to plot some graphics. Three of them have as maximum value 4, but the last one only has a bit more than 2.
When I plot them, the last one is plotted with it's function over it (seems like if ezpolar paints it some pixels after it maxium value used as radius).
Code and Generated Plot
% This subplot is used since I've 4 graphics to draw.
subplot(2,2,4)
ezpolar('0.25*(5 - 4*cosd(-180 * sin(t) ))');
title('D')
If I don't use this subplot, using a complete figure to draw the graphics seems fine. However, since I need to have all four of them together, it results in (will draw only the problematic one, subplot 2,2,4):
As you can see, r = 0.25 (5 - 4...) is plotted just over the polar axes.
Why is it happening? How can I fix it?
The issue appears to be with the position of that annotation and the fact that when you use a subplot, the limits on the radius of the polar plot actually change but the position of the annotation does not.
To combat this, you could actually compute the limits of the axes and change the position of the text to be explicitly outside of the plot.
hax = subplot(2,2,4);
p = ezpolar('0.25*(5 - 4*cosd(-180 * sin(t) ))');
% Get the handle to the label text object
label = findobj(hax, 'type', 'text');
% Figure out the current axes limits
ylims = get(hax, 'ylim');
% Add some padding (as a percent) to the position
padding = 0.2;
set(label, 'Position', [0 ylims(1)*(1 + padding), 0]);
A better way to do this would be to change the Units of the label to use Normalized units (relative to the axes) rather than Data units. This way, it will not change if the axes limits change.
hax = subplot(2,2,4);
p = ezpolar('0.25*(5 - 4*cosd(-180 * sin(t) ))');
% Get the handle to the label text object
label = findobj(hax, 'type', 'text');
set(label, 'Units', 'Normalized', 'Position', [0.5, -0.2]);
I have two datasets. One detailing a list of angles (which I am plotting onto a rose plot):
angles
-0.8481065519
0.0367932161
2.6273740453
...
n
The other, detailing directional statistics from this group of angles:
angle,error
-0.848106563,0.8452778824
Where angle essentially defines the directional mean, and error the circular variance, essentially an error bar either side of the angle
I have thus far plotted a rose histogram using the set of angles, as such:
h = rose(angles,36)
I would like to create a plot of the directional statistic angle (it does not need a length/magnitude - just to the edge of the circle plot) with the error around it.
As an example:
I added the lines by hand in Matlab. If possible it would be good to perhaps have shading within the arc too. Alternatively, (and possibly preferred) would be to have just a sliver above the rose plot bins (so it doesn't cover the data) with a centre line (showing the angle and shading surrounding for the error.
Thanks in advance.
How about this?
%// Data
angles = 2*pi*.8*randn(1,1e4);
angle = -0.848106563;
error = 0.8452778824;
%// Plot rose
rose(angles, 36);
axis image %// make axis square
hold on
%// Plot mean
a = axis;
a = a(2); %// size of axis
plot([0 cos(angle)*a], [0 sin(angle)*a], 'r')
%// Plot error as many shaded triangles that compose a circular wedge
t = linspace(-error/2+angle,error/2+angle,100); %// increase "100" if needed
for k = 1:numel(t)-1
h = patch([0 cos(t(k))*a cos(t(k+1))*a 0], ...
[0 sin(t(k))*a sin(t(k+1))*a 0], [.5 0 0], 'edgecolor', 'none');
%// change color [.5 0 0] to something else if desired. Note also alpha
set(h,'Facealpha',.3) %// make transparent
end
%// Place rose on top by rearranging order of axis children
ch = get(gca,'children');
set(gca,'children',[ch(2:end); ch(1)]);
For this to work, you need to use a figure renderer capable of transparency. So you may need to adjust the figure's renderer property.
I have an X Y Z dataset.
X and Y are the 2D coordinates and Z is the intensity.
I plot the data using the scatter function:
markerSize=100;
scatter(x,y,markerSize,z,'s','fill');
I use the options 's' and 'fill' to get filled squares.
My problem is the markerSize value corresponds to the area of the marker, and its unit is points (1/72 of one inch).
The marker size is constant, even if I resize the figure plot. So that the gap between the data points increases when I increase the figure size.
What I would like is a constant marker size which is a constant of the axis unit. For instance, the marker size should be 5x5 (5 in X axis and 5 in Y axis).
Thanks for your help.
You want to make the size of markers proportional to the figure size.
The size of markers is controlled by SizeData parameter of the scattergroup object. The size of figure is stored in Position parameter of the figure object. The difficult part is to interactively resize the marker when the figure size is changed. So you need to use ResizeFcn callback and call setmarkersize function that you define.
function [ ] = setmarkersize( src, evnt )
% # get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% # get the scattergroup object
h = get(get(src,'children'),'children');
% # resize the marker
relativesize = 0.5;
set(h,'SizeData', pos(3)*relativesize);
end
================================================
% # attach the callback to figure
f = figure('ResizeFcn', #setmarkersize);
h = scatter(x,y,markerSize,z,'s','fill');
You will have to set the marker size manually according to the actual figure size on screen. Using axes property Position you can convert data units to relative figure units. In the next step this size can be converted to the absolute size in points on screen. With that information you can set the marker size accordingly. In the following code snippet I've set the x/y axis limits and width/height of the axis to identical values, because the square marker area can only be calculated reasonably if the marker width is equal to the marker height.
Set marker size relative to data units
% test data
x = [25*rand(1,10) 2.5];
y = [25*rand(1,10) 2.5];
z = [rand(1,10) 0.5];
% relative marker size in squared normalized figure units
marker_rel = 5;
%% Set relative marker size (approximately)
scatter(x,y,100,z,'s','fill');
% Set identical x/y limits and set axes height=widht, so that markers
% really represent squares in data units
xlim([0 25]);
ylim([0 25]);
set(gca, 'Units', 'Points');
axpos_pt = get(gca, 'Position');
axpos_pt = [axpos_pt(1) axpos_pt(2) min(axpos(3:4)) min(axpos(3:4))];
set(gca, 'Position', axpos_pt);
grid on;
grid minor;
% Set marker size relative to data units
markerSize = (marker_rel * axpos_pt(3) / diff(xlim))^2;
set(get(gca, 'children'), 'sizedata', markerSize);
As it turns out, the displayed marker size is slightly smaller than expected. Obviously, there's some bounding box of unknown (at least to me) size, see here.
An alternative approach is to plot rectangles "manually" as shown in the following code snippet (same test data is used). When the figure is resized, the rectangles are resized as well without any special callback function being needed.
Draw rectangles manually
%% Use rectangles (exact)
figure;
axes;
cmp = colormap;
z_range = max(z) - min(z);
line_style = 'none'; % set to '-' to make the edges visible
for k = 1:length(x)
x_pos = x(k) - marker_rel/2;
y_pos = y(k) - marker_rel/2;
w = marker_rel;
h = marker_rel;
color = cmp( round(((z(k) - min(z))/z_range)*(length(cmp) - 1)) + 1, : );
rectangle('Position', [x_pos y_pos w h], 'FaceColor', color,...
'LineStyle', line_style);
end
grid on;
grid minor;
The above code produces the desired marker size:
In general, these are no squares. They can (and will) only be squares if xlim = ylim and absolute height of axis = absolute width of axis. I have shown in my first code snippet how to achieve this.
I found an answer on the Matlab central forum which does not use the useful dsxy2figxy function. Here is the link to it (link)
The code is the following:
x = rand(1,50);
y = rand(1,50);
s = 5; %Marker width in units of X
h = scatter(x,y); % Create a scatter plot and return a handle to the 'hggroup' object
%Obtain the axes size (in axpos) in Points
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = s/diff(xlim)*axpos(3); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)
The answer by ysakamoto doesn't work in Matlab 2014, where Mathworks changed figure handles from double to object. The following modification makes it work again:
function [ ] = setmarkersize( src, ~ )
% get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% marker size
relativesize = 0.01;
% axes is not necessarily the only child of figure (e.g. colorbar may be first)
for i = 1:numel(src.Children)
if strcmpi(src.Children(i).Type, 'axes')
% make marker size depend on figure width
src.Children(i).Children.SizeData = pos(3) * relativesize;
break
end
end
end
Also, when creating the figure it will not set the marker size to the correct value until resized. So call setmarkersize explicitly:
f = figure('ResizeFcn', #setmarkersize);
...
setmarkersize(f)
Consider abandoning 'markers' and drawing directly on the axis using rectangles or circles. The 'rectangle' command with 'curvature' set to [1 1] is essentially an open circle. Units are in plot units, so each rectangle can be scaled:
rectangle('position', [rectCentX rectCentY widthInPlotUnits, heightInPlotUnits],'curvature',[1 1])
what I need is a histogram with X axis displayed on a log scale. However, I still want every bar in a histogram to be of the same width.
Whatever, I came up with (or upon) display bars with higher values as narrower ones (they are of the same width on ordinary scale and not on log scale).
Here is what I am doing now:
edges = 10.^(c_min:0.1:c_max);
h = histc(data, edges);
bar(edges, h); set(gca, 'Xscale', 'log');
Since your bars should be evenly spaced, you can plot them with x-values 1,2,.. and then change the axis labels.
edges = 10.^(c_min:0.1:c_max);
h = histc(data, edges);
centers = sqrt(edges(1:end-1).*edges(2:end));
bar(h)
%# fix the x-labels, x-axis extents
xlim([0.5,length(centers)+0.5])
set(gca,'xticklabel',num2str(centers(:),'%5.2f'))
Use patch instead of bar:
figure, hold on
edges = 10.^(c_min:0.1:c_max);
h = histc(data, edges);
for ii = 1:numel(edges)-1
patch(...
[edges(ii) edges(ii) edges(ii+1) edges(ii+1)], ...
[0 h(ii) h(ii) 0],...
'b');
end
Although this particular code does not do what you want, that is a problem with the definition of edges rather than the methodology, so you'd have to fiddle a bit with the edges (perhaps re-define them for the patch)
I need to plot several plots along a sloped line at different positions.
For example, if I:
plot(0:200,'k');
plotpts = 5:5:200;
I would like to be able to plot a smaller plot at each of my plotpts on top of the original 0:200 line.
I know you can use hold on and plot over top that way, but I need to change my origin each time. Does anyone have any suggestions? I would really like to stay in matlab. Thanks!
Here is a flexible way I usually do it:
plot(1:10, 'k')
plotpts = 2:2:8;
mainbox = get(gca, 'Position');
xlims = get(gca, 'XLim');
ylims = get(gca, 'Ylim');
for i=1:length(plotpts)
originx = mainbox(1) + (plotpts(i) - xlims(1)) * (mainbox(3)) / (xlims(2) - xlims(1));
originy = mainbox(2) + (plotpts(i) - ylims(1)) * (mainbox(4)) / (ylims(2) - ylims(1));
axes('position', [originx originy 0.1 0.1], 'Color', 'none')
% Do some plotting here...
end
It's quite a bit of work, but you probably want to use the axes command. A figure window can host any number of axes, where each axes has it's own position, data, annotations, color etc.
The most difficult thing for the application you describe is that each axis position needs to be defined in the coordinate frame of the underlying figure, which means that some math may be required to create the illusion that the axis is correctly positioned within a parent axes/
For example, if you first create a simple plot
figure(1234); clf;
plot(1:10, rand(1,10),'.k-','linewidth',5);
xlim([1 10]);
ylim([0 1]);
set(gca,'color','y'); %This just helps demonstrate the next steps
You can place another axis directly on top of the first, and then
ha = axes('position',[.2 .3 .1 .1])
plot(linspace(0,2*pi,100), sin(linspace(0,2*pi,100)), 'b-')
xlim([0 2*pi])
You can adjust the the properties of the inset axis to suit your particular needs, for example
set(ha,'color','none'); %A transparent axis
set(ha,'xtick',[],'ytick',[]); %Remove tick labels
title(ha,'This is an inset plot')
Is the command subplot not what you're looking for?