24

I know there is a function named annotation can plot arrows or double arrows. But annotation can only plot in normalized unit. For example:

annotation('arrows',[x1 x2],[y1 y2])

Here, [x1, x2] should be a ratio number less than one.

So, my question is how can I plot arrows with a true value rather than a normalized value?

I wonder if there is any other function can approach this or is there any function I can get the axis value of the figure so that I can adjust the true value into a normalized value.

Jonas Stein
  • 6,826
  • 7
  • 40
  • 72
datcn
  • 751
  • 5
  • 13
  • 20

8 Answers8

13

For the positioning of annotations, Matlab offers the function dsxy2figxy to convert data space points to normalized space coordinates. However, for whatever reasons, the function is not included in the Matlab distribution and has to be "created" first.

Copy the following line into the command window and execute it to open the function in your editor.

edit(fullfile(docroot,'techdoc','creating_plots','examples','dsxy2figxy.m'))

To use the function dsxy2figxy save it somewhere in your matlab search path.

Please find the full instructions for the function dsxy2figxy at matlab-central: http://www.mathworks.de/help/techdoc/creating_plots/bquk5ia-1.html

H.Muster
  • 9,297
  • 1
  • 35
  • 46
  • Thanks for your answer. It is useful for me. – datcn Jul 17 '12 at 05:06
  • 1
    Alternatively, `addpath(fullfile(docroot,'techdoc','creating_plots','examples'))` will also put the example function on your MATLAB path (assuming the file exists). – Emil Lundberg Jan 21 '14 at 10:47
12

Even though annotation uses normalized as default units, you can associate these objects to the current axes (gca) and use data units for setting X and Y properties.

Here is an example of plotting a single arrow.

plot(1:10);
ha = annotation('arrow');  % store the arrow information in ha
ha.Parent = gca;           % associate the arrow the the current axes
ha.X = [5.5 5.5];          % the location in data units
ha.Y = [2 8];   

ha.LineWidth  = 3;          % make the arrow bolder for the picture
ha.HeadWidth  = 30;
ha.HeadLength = 30;

enter image description here

marsei
  • 7,691
  • 3
  • 32
  • 41
  • This seems to work quite well if the arrows are horizontal or vertical, but the arrowhead seems to be misaligned if it points in other directions. – Phil Goddard Aug 19 '17 at 00:21
8

For anyone who comes across this topic looking to draw arrows in "data space" rather than in units relative to the figure and/or axes, I highly recommend arrow.m from the file exchange.

tmpearce
  • 12,523
  • 4
  • 42
  • 60
5

I've just discovered this method, since I don't want to have to bother with normalised units. Use the latex interpreter:

figure
plot([1:5],[1:5]*3,'.-')
%// Say I want to put an arrow pointing to the location, [3 9]    
text(2.94,8.3,'\uparrow','fontsize',20)
text(2.8,7.8,'point [3,9]')

To make the arrow longer, use a larger fontsize.

Pros

  • Easier, faster and quicker than using normalised units
  • Don't need to install any functions (good for us lazy people..)
  • making use of the LaTeX interpreter, there is a whole range of arrows (up, down, left, right and other angles (see Symbol list)

Cons

  • Definitely needs trial and error/tweaking to get the correct location of the arrow head relative to the POI.
  • There is limited control over the length of the arrow
  • Some latex commands aren't understood by the interpreter (boo).
David_G
  • 1,127
  • 1
  • 16
  • 36
2

If I remember correctly you need to calculate the position of the axes in relation to the figure.

it should go like:

%% example plot
clf
plot(rand(5,2)*5)
%% get info specific to the axes you plan to plot into
set(gcf,'Units','normalized')
set(gca,'Units','normalized')
ax = axis;
ap = get(gca,'Position')
%% annotation from 1,2 to 3,4
xo = [1,3];
yo = [2,4];
xp = (xo-ax(1))/(ax(2)-ax(1))*ap(3)+ap(1);
yp = (yo-ax(3))/(ax(4)-ax(3))*ap(4)+ap(2);
ah=annotation('arrow',xp,yp,'Color','r');

Note Fixed offset in original calculation - ap(3),ap(4) are width and height of gca, not corner positions

Community
  • 1
  • 1
bdecaf
  • 4,652
  • 23
  • 44
1

After creating the annotation object you should set the property Units to an absolute one. Example:

arrowObj = annotation('arrow', [0.1 0.1], [0.5 0.5]);
set(arrowObj, 'Units', 'centimeters');
set(arrowObj, 'Position', [1 1 3 5]);
Deve
  • 4,528
  • 2
  • 24
  • 27
  • The advantage of this solution is that the arrow stays a fixed size and position with respect to the lower-left corner when you resize the figure. Also, only the first argument to `annotation(..)` is necessary, and the other typically useful choice of units is `'pixels'`. – Evgeni Sergeev Apr 22 '13 at 00:47
  • Another important note: the `'Position'` must be set **after** setting `'Units'`, but it can be done in one line: `set(arrowObj, 'units', 'pixels', 'position', [100 100 200 400])`, in which case also, the `'units'` must appear before `'position'`. – Evgeni Sergeev Apr 22 '13 at 00:50
0

One approach would be to define an arrowhead in the axis units:

Ax=[0 -0.003 0.003 0];       % (Ax,Ay) form an upward pointing arrowhead.
Ay=[0.01 0.0060 0.0060 0.01];
Ax=Ax-mean(Ax);  % center it on zero
Ay=Ay-mean(Ay);

Then at desired arrowhead index in on a curve vv, compute

x1=vv(in,1); y1=vv(in,2);
x2=vv(in+1,1); y2=vv(in+1,2);
u=x2-x1;
v=y2-y1;
th=-pi/2+atan2(v,u);
R=[cos(th) -sin(th); sin(th) cos(th)];   % Rotation matrix for local slope of vv.
A=R*[Ax;Ay];    % Rotate the arrowhead.
patch(x1+A(1,:),y1+A(2,:),'r','LineWidth',0.01)   % plot rotated arrowhead at (x1,y1).
plot(x1+A(1,:),y1+A(2,:),'r','LineWidth',0.01)    % Kludge to make boundary red too (I'm sure there is a more elegant way).

Worked for me, for my particular circumstances.

LasVegas
  • 1
  • 2
0

You can use the 'arrow' component in the (well-documented) DaVinci Draw toolbox (full disclosure: I wrote/sell the toolbox, though arrows are free).

Example syntax and example output are below.

davinci( 'arrow', 'X', [0 10], 'Y', [0 2], <plus-lots-of-options> )

enter image description here

Leonard Wayne
  • 185
  • 1
  • 2
  • 4