How to draw good looking arrows in Matlab? - matlab

I have been trying to draw arrows in Matlab in my figure but they all look terrible.
Also, I want my arrowhead to be a solid triangle, not a V.
I tried using the packages available on the Matlab File Exchange: arrow, arrows, arrows3, and probably at least one other one.
I even tried manually creating an arrow in the Matlab figure editor, but when I adjust the line width, the arrow looks like this:
I used the annotation command to create the arrow above:
annotation(gcf,'arrow',[0.621875 0.457916666666667],...
[0.205421152030217 0.40755429650614],...
'HeadLength',4,'LineWidth',5);
Here's the result trying to use the arrow package available here: Arrow.m (notice how the bottom arrow head is not perpendicular to the line:
I even tried the following and here is the result below (notice the terrible looking arrowhead):
figure
plot(1:10, 1:10)
annotation(gcf,'arrow',[0.621875 0.457916666666667],...
[0.205421152030217 0.40755429650614],...
'HeadLength',4,'LineWidth',5);

Vector graphics is hard. Though Matlab's typography is just as bad, but here's a simplistic text-based solution (I refuse to do this sort of annotation in Matlab any more):
figure
plot(1:10, 1:10)
text(5,4,'\rightarrow','FontSize',54,'Rotation',135,...
'HorizontalAlignment','center');
which yields a figure like this
Note that I have used '\leftarrow' because it points in the direction of zero degrees, which makes doing math in my head easier. This is no canned solution, you'll still need to fiddle with position to overcome the fact that Matlab is aligning this as text (see the 'Extent' and 'Margin' properties). Not surprisingly, you may see small glitches. The LaTeX interpreter can be used to obtain a different style arrow head:
text(5,4,'$\rightarrow$','FontSize',54,'Rotation',135,...
'HorizontalAlignment','center','Interpreter','latex');
I don't get the small glitches with this option, but the arrows look different (there are likely other LaTeX arrow styles that could be substituted). Changing the font may also have an effect and there are certainly other text-based arrows that could be used. More details on adding arrows can be found in this article from The MathWorks.

Another solution is to use the open-source Waterloo graphics - a library that addresses this by providing a pure Java library of 2D graphics functions that can easily be integrated in Matlab. See some examples here...
For example, try this code (after properly installing waterloo)
f = GXFigure();
x = -5:0.1:5;
gr1 = gxgca();
a1 = line(gr1, x, cos(x), 'LineSpec','-ob');
b1 = line(gr1, x, sin(x), 'LineSpec','-sg');
annotation(gr1,'arrow',[0.1 0.1],[0.4 0.4],'HeadLength',0.2,'HeadWidth', 0.5, 'LineWidth',2);
gr1.getObject().getView().autoScale();

Having worked with Matlab for more than 10 years and seeing almost zero progress in the quality of the plots (anti-aliased output to bitmap, decent looking eps-files, ...), I decided that my long time solution will be here. Some examples of decent looking arrows here, more beautiful graphs here. Unfortunately, some of the toolboxes prevent me from going completely to numpy/scipy/matplotlib. I know this is more of a rant than an answer, but that is my solution ...

Using quiver or quiver3 for 3-D plots will certainly look better than the that last arrow you made using annotation. I'm not sure it will look much better than the ones you made using the Arrow.m package though. It's possible to change the stlyes on quiver as well if you want.

I resorted to installing InkScape and drawing arrows in there. Nothing beat the simplicity and quality.
InkScape Website

When line-based arrows fail, you can always try patch-based ones:
xArrow = [0 1 1 1.2 1 1 0]; % X-coords of arrow edge points
yArrow = [-0.1 -0.1 -0.2 0 0.2 0.1 0.1]; % Y-coords of arrow edge points
hArrow = fill(xArrow,yArrow, [1 0 0]); % Plot a red arrow
axis equal % Set axis tick marks to be equal
Although more work, you can potentially parameterize the above into a function that takes a number of arguments for adjusting the scaling, aspect ratio, rotation, and position of the arrow. You can even adjust the color, edge line color, and alpha transparency.
This sort of idea appears to have been implemented by François Beauducel in his File Exchange submission ARROWS: generalized 2-D arrows plot.

There is now the DaVinci Draw toolbox (full disclosure: I wrote/sell the toolbox), which uses low-level Matlab commands like plot() and patch() to draw mid-level shapes like arrows. Example syntax:
davinci( 'arrow', 'X', [0 10], ...
'Y', [0 2], ...
'Shaft.Width', 1, ...
'Head.Length', 2.5, ...
'Color', 'w', ...
'EdgeColor', 'k', ...
'LineWidth', 2.5 )
From the documentation:

Related

add semi-transparency to 2d histogram

I am trying to make a plot like this one:
I want a 2D histogram with bar color proportional to the height and semi-transparent bars.
I tried to put together the examples provided here
x = randn(100, 2);
figure
hist3(x, [20 20]);
colormap(hot) % heat map
grid on
view(3);
%bar color
set(get(gca,'child'),'FaceColor','interp','CDataMode','auto');
%semitransparency
set(gcf,'renderer','opengl');
An example of the result is this:
The semi-transparency is absent.
I do not know if it is a problem of my Matlab version (R2014a) or if ---more probably--- I am messing up something. Maybe the axis handles?
Next
Outside this question: I would also like to add a transparent surface interpolating the histogram values (I got also some problems with this). I think I saw something like this on SO recently, but I cannot find it anymore. Does anyone have some hits?
You didn't actually change the surface transparency in your example. All you did was change renderers. Here's the extra line you would need:
set(get(gca,'child'),'FaceAlpha',0.8);
A value of 1 would be opaque, 0 invisible.
You may also want to change the edge line transparency too:
set(get(gca,'child'),'EdgeAlpha',0.2);

Matlab - highlight a specific interval using patch

I would like to highlight a specific interval in my plot. I found a way using the patch object.
The problem is that my layout gets messy whenever i use it.
Here's an example of a plot:
x = 1:0.05:25;
plot(x,sin(x)+sin(100*x)/3);
xlabel('x axis');
ylabel('y axis');
title('\Omega \Delta test');
legend('sin \Sigma')
And to highlight a period:
yl = ylim;
patch([5 5 10 10],[yl(1) yl(2) yl(2) yl(1)],'k',...
'facecolor',[.4 .6 .4],'edgecolor',[.4 .6 .4],...
'facealpha',0.3,'edgealpha',0.3)
My results with and without the patch command:
Normal:
Messy:
Look at the fonts and the legend block. Any ideas on how to fix that?
Is there a better way to highlight an interval? I need to choose the color and set transparency.
Just one more question: Why do I have to use the third input (color) if it's not applied?
Thanks in advance!
Edit: This answer is only valid for Matlab versions before 2014b, as the incredibly useful erasemode property has been removed from all HG2 graphic objects on later Matlab versions.
I ran into this problem countless times and I had to learn to live with it. Most times I can accept the glitches of the OpenGL renderer if it buys me nice transparency effects, but in some cases it is just not acceptable.
I use patch objects to highlight intervals in many applications, usually over several curves. There is a trick you can use when transparency is not an option, it is the EraseMode property of the patch object. If you set the EraseMode property to 'xor' the patch will not be transparent, yet anything under the patch will xor the patch pixel colours so you can still see the curves under the patch.
This not being a transparency rendering, you can use the default painter renderer and avoid all the occasional glitches of the OpenGL.
So for example with your data:
hp = patch([5 5 10 10],[yl(1) yl(2) yl(2) yl(1)],'k',...
'facecolor','g','edgecolor','g',...
'erasemode','xor') ;
And the nice advantage of this trick is it works with monochrome display/prints. If you cannot use multiple colours, you can use that with only one colour (if you plan black & white printing for publication for example)
hpx = patch([5 5 10 10],[yl(1) yl(2) yl(2) yl(1)],'b',...
'facecolor','b','edgecolor','b',...
'erasemode','xor') ;
(Note: This answer only makes sense if you're using the OpenGL renderer.)
If that's the case, then this is one symptom of
a bug in MATLAB 6.5 (R13) and later versions in the way that axes are rendered in hardware accelerated OpenGL, when transparency is used
as mentioned here.
The simplest workaround is to force OpenGL to run in software mode by including the line:
opengl software
Apparently, this only works under Windows and Linux. For other symptoms of the problem and different approaches to solutions have a look at this related question.
Regarding your other questions:
Not sure if it's a better way, but it's another option: You could use an area-plot like this:
opengl software
figure
x = 1:0.05:25;
ax = plot(x,sin(x)+sin(100*x)/3);
hold on
basevalue = -2;
areaAx = area([5,10],[2,2], basevalue);
set(areaAx, 'FaceColor', [.4 .6 .4])
alpha = get(areaAx, 'Children');
%make area plot transparent
set(alpha, 'FaceAlpha', 0.5);
set(areaAx, 'EdgeColor', 'none');
xlabel('x axis');
ylabel('y axis');
title('\Omega \Delta test');
legend('sin \Sigma')
xlim('auto')
ylim('auto')
hold off
However, OpenGL renderer will also cause problems here, so it's not a fix, just another option.
And finally, I'm not sure what you mean by the color not being used. The 'facecolor' is used, that's why your plot is green. When you go through the area-option above step by step you'll see that you get a solid color first. Later on, transparency is added with the 'facealpha'-option.

Triangular split patches with painters renderer in MATLAB 2014b and above

MATLABs new graphics engine, HG2, fails to properly print patches using the painters renderer:
hist(randn(1,1000));
colorbar('Location','SouthOutside');
print('test.pdf','-dpdf');
The resulting patches, whether generated by hist or colorbar, have triangular splits:
The issue has been discussed on MATLAB Central here and here, where it was suggested that disabling the "smooth line art" option in the pdf-viewer should solve it. This conceals the problem in some readers (e.g. in Adobe Reader but not in Apple Preview), but it is hardly a solution to ask collaborators and readers to use a specific pdf-viewer with non-default settings for graphics to appear correctly. Looking at the resulting file in Inkscape, it is clear that the split is present in the output vector graphics. Here, I moved one half of the colorbar, proving that it is in fact split in half, and not just misinterpreted by the pdf-viewer:
The problem is not present using the OpenGL renderer (print('test.pdf','-opengl'), but then the output is not vectorized). The problem persists in MATLAB 2015a.
Is there a way to export artifact-free vector graphics in MATLAB 2014b or later?
Here's a questionable work-around until the actual problem is solved:
The diagonal lines are simply the empty space between the triangles, so what we are seeing is the white space behind the patches peek through. Silly idea:
Let's fill that space with matching colors instead of white.
To do so, we'll copy all objects and offset the new ones by just a tiiiiny bit.
Code:
hist(randn(1,1000));
colorbar('Location','SouthOutside');
print('test.pdf','-dpdf'); %// print original for comparison
f1 = gcf;
g = get(f1,'children');
n = length(g);
copyobj(g,f1); %// copy all figure children
The copied objects are now the first n elements in the 2*n f1.Children array. They are exactly on top of the old objects.
g=get(f1,'children');
for i=1:n;
if strcmpi(g(i).Type,'axes');
set(g(i),'color','none','position',g(i).Position+[0.0001 0 0 0]);
set(g(i+n),'position',g(i+n).Position); %// important!
end;
end;
print('test2.pdf','-dpdf');
Explanation:
g = get(f1,'children'); gets all axes, colorbars, etc., within the current figure.
colorbar objects are linked to an axes, which is why we'll only have to move the axes type children.
Setting the color to none makes the background of the new axes transparent (since they are on top of the old ones).
g(i).Position+[0.0001 0 0 0] shifts the new axes by 0.0001 normalized units to the right.
set(g(i+n),'position',g(i+n).Position); This line seems unnecessary, but the last image below shows what happens when printing if you don't include it.
Depending on the types of graphics objects you have plotted, you may need to tweak this to fit your own needs, but this code should work if you have only colorbar and axes objects.
Original:
With hack:
Without %// important! line:
In R2015b, histogram seemed to not show the white lines, but fill did.
For simple plots just paste the data again:
x = 0:pi/100:pi;
y = sin(x);
f = fill(x,y,'r');
hold on;
f2 = fill(x,y,'r'); %// Worked like magic
If the magic fails try similar to Geoff's answer: f2 = fill(x+0.0001,y,'r');
Depending on what version of Matlab you are using, you might try to use epsclean. It doesn’t seem to work with very recent versions of Matlab like 2017a.
Otherwise, epsclean can be run on an existing eps file (not pdf) exported with the -painters option to generate a vectorized figure, and it will rewrite (or create another file) with those white lines removed.

How do I make a Matlab plot fill the whole page?

I want to make a plot in Matlab which is twice as tall as it is long. I tried following the advice of this question using Position and PaperPositionMode, like so:
figure
set(gcf,'PaperPositionMode','auto');
set(gcf, 'Position', [0 0 100 200]);
barh(1:20);
print('test', '-dpng');
Annoyingly, this resizes the paper size but not the plot, as below.
Is there any way to make a graph with specified width and height? I'm running this on a headless server so clicking and dragging, or any other GUI-specific solutions, aren't an option. Obviously I don't actually want a 100x200 plot, I just wanted to make the figure small enough to fit nicely into the question.
You might try setting the paper size and units. There's a similar question on Mathworks. Relevant content:
set(0,'defaultfigurepaperunits','inches');
set(0,'defaultfigurepaperorientation','landscape');
set(0,'defaultfigurepapersize',[8.5 11]);
set(0,'defaultfigurepaperposition',[.25 .25 [8.5 11]-0.5]);
where set(0, ...) sets the root graphics system values. You could also use your figure instead. Hope this helps.
Alternative approach reference the documentation for figure(). Uses the units & outerposition properties.
figure('units','normalized','outerposition',[0 0 1 1])
See also the position property for another approach.

MATLAB: Interactive tool to "draw" a Plot?

Do you know a simple way to draw arbitrary splines or lines in a plot-figure? I've got the following figure which I created in powerpoint with more or less arbitrary spline-curve (blue) but I'd like to do the same in MATLAB now because of a better look in the final plot-output.
I'm now wondering if I've got to manually "find" data-values to draw some sort of spline (which looks roughly like the blue one below) myself or if there's maybe a tool where I can simply insert some points into a plot interactively and there's a curve fitted though it to create something similar!?
The green and red lines I can figure out myself (probably also have to plot them manually, do I)?!?
Thanks in advance!
EDIT
Okay I found a way myself doing it in MATLAB to gnerate a nice spline: Use splinetool, then either use an example or import some data and then you can interactively add and delete points until your spline looks roughly like it should. Then file->print to figure and then tools->edit plot! You can then delete everything you don't need, add title, xlabel and ylabel etc. and then export it to latex with e.g. matlab2tikz :-) Very nice stuff!!
According the purpose, print nice plots for the thesis, I have some out-of-Matlab recommendations.
1) plot everything as usual, you get a figure handle and an axes handle
h = figure( ... )
a = gca
2) use the data cursor function of the figure window and interactively insert the base points for your later splines. You can additional points by right-click.
t = linspace(0,2*pi,1000);
[x y] = deal(sin(t),cos(t))
Later you delete the "visual" part of the data tip inside Illustrator/Inkscape, if you just want to keep the anchor point of the vector graphic to snap your splines.
There is also the possibility of custom data tips: Tutorial at Matlab Central
3) I once wrote a function to nicely plot Matlab figures as vector graphic based PDFs. You can specify height, width and how much white margin around you want. You just need to pass figure and axes handle and the name:
function saveFigure( fig_handle, axes_handle, name , height , width , margin)
set(axes_handle,'LooseInset',get(gca,'TightInset'));
set(fig_handle, 'Units','centimeters','PaperUnits','centimeters')
% the last two parameters of 'Position' define the figure size
set(fig_handle,'Position',[-margin -margin width height],...
'PaperPosition',[0 0 width+margin height+margin],...
'PaperSize',[width+margin height+margin],...
'PaperPositionMode','auto',...
'InvertHardcopy', 'on',...
'Renderer','painters'... %recommended if there are no alphamaps
);
saveas(fig_handle,name,'pdf')
end
4) You get a PDF you could directly use for Latex - for use in MS Office use 'emf' (
Enhanced metafile) rather than 'pdf'. Open this file with Adobe Illustrator (preferable as it offers layers) or Inkskape (Open Source).
5) The datatips are recognized as graphical objects, you can catch them and draw a spline on them. For Illustrator I'd recommend to put the spline in another layer than the actual figure. Later you can just swap the figure and give the spline new anchor points. In Inkscape you could use the grouping function to keep everything together.
6) I'd say you save a lot of time over a only-Matlab-solution. Good look!