# How to draw an arrow using non normalized coordinates?

314 views (last 30 days)

Show older comments

### Answers (8)

KarlHoff
on 26 Jun 2020

Edited: KarlHoff
on 7 Aug 2020

For me, using MATLAB R2018b,

the following works to produce an arrow at a location specified in data coordinates:

anArrow = annotation('arrow') ;

anArrow.Parent = gca; % or any other existing axes or figure

%EDIT thanks to @Moshe:

%anArrow.Position = [x_start, y_start, x_end, y_end] ;

anArrow.Position = [x_start, y_start, delta_x, delta_y] ;

The big advantage is that the arrow remains where it is with respect to other plot elements, even if the limits of the plot change afterwards.

This is not the case with all the approaches that convert data coordinates into other Units (Pixels, Inches, Normalized)

Walter Roberson
on 27 Jun 2017

marcus yoder
on 23 Aug 2018

Edited: marcus yoder
on 23 Aug 2018

I tested the code by Walter Robinson and had to make a few changes to get it to work.

function obj = dataArrow(Xdata,Ydata,ax)

%This function will draw an arrow on the plot for the specified data.

%The inputs are

oldunits = get(ax, 'Units');

set(ax, 'Units', 'Normalized');

axpos = ax.CurrentAxes.Position;

set(ax, 'Units', oldunits);

%get axes drawing area in data units

ax_xlim = ax.CurrentAxes.XLim;

ax_ylim = ax.CurrentAxes.YLim;

ax_per_xdata = axpos(3) ./ diff(ax_xlim);

ax_per_ydata = axpos(4) ./ diff(ax_ylim);

%these are figure-relative

Xpixels = (Xdata - ax_xlim(1)) .* ax_per_xdata + axpos(1);

Ypixels = (Ydata - ax_ylim(1)) .* ax_per_ydata + axpos(2);

obj = annotation('arrow', Xpixels, Ypixels, 'Units', 'pixels');

end

Robert
on 7 Sep 2019

I wanted to do something similar, here's an example that adds a double arrow between the x-values 1 and 5 with y-values 5 in a simple plot:

pos=[.1,.1,.85,.85];

figure;ax=axes('position',pos);plot(1:10)

x=[1,5];y=[5,5];

rx=xlim(ax);ry=ylim(ax);

cx=pos(3)/diff(rx);cy=pos(4)/diff(ry)

annotation('doublearrow',pos(1)+cx*(x-rx(1)),pos(2)+cy*(y-ry(1)))

##### 0 Comments

MichailM
on 4 Apr 2020

Maybe a function like the below could help. The x and y inputs are actual coordinates on the plot. Here I just need to draw an arrow but you can modify it

function myarrow(x,y)

ax = gca;

axpos = get(ax, 'Position');

X = get(gca,'XLim');

Y = get(gca,'YLim');

difX = X(2) - X(1);

difY = Y(2) - Y(1);

newx = x./difX;

newy = y./difY;

annotation('arrow',[newx(1)*axpos(3)+axpos(1) newx(2)*axpos(3)+axpos(1)],[newy(1)*axpos(4)+axpos(2) newy(2)*axpos(4)+axpos(2)])

end

##### 0 Comments

Marc Compere
on 14 Aug 2021

##### 1 Comment

Adam Danz
on 15 Aug 2021

Thanks for pointing out that function, Marc, I'm sure it will be helpful in many cases.

Since it's a static, once-and-done, conversion, the annotation object may no longer be in the correct position if there are any changes to the figure size, axis size or position, axis limits, or aspect ratios. Calling the function after all plotting is complete would help to solve some of those issues. A more robust solution would be to assign listeners that update annotation objects when a resize or reposition event occurs but really what we need is for MathWorks to update the annotation function to support data units or offer users an alternative.

MICHAEL MUTWIRI
on 21 Aug 2021

Create the 'annotation' object for each subplot and edit their properties using dot notation.

For the X and Y properties you use similar values as your graph cordinates. The x-end and y_end are the tip of the arrow.

Below is a tested sample code

%Create sample data

x = linspace(0,2*pi,1e3);

y = sin(x); % Plotted of first subplot

z = cos(x); % Plotted of second subplot

fg1=figure(1);

% Specify different textarrows for different subplots

%%%**************** SUBPLOT 1 ********************

subplot(2,1,1);

plot(x,y,'k')

xlabel('x')

ylabel('Amplitude')

title('Sin(x)')

ylim([-1.1 1.1])

% Define X-Beginning and ending x-coordinates

x_start =pi-1;x_end = pi;

%Y- Beginning and ending y-coordinates

y_start =sin(x_end);y_end = sin(x_end);

anArrow = annotation('textarrow');

anArrow.Parent = gca;

anArrow.X = [x_start,x_end]; % set the x-property

anArrow.Y = [y_start ,y_end];

anArrow.String = 'sin(\pi)';

anArrow.Color = 'red';

%%%**************** SUBPLOT 2 ********************

subplot(2,1,2);

plot(x,z,'k')

xlabel('x')

ylabel('Amplitude ')

ylim([-1.1 1.1])

% Define X-Beginning and ending x-coordinates

x_start =1.5*pi-1;x_end = 1.5*pi;

%Y- Beginning and ending y-coordinates

y_start =cos(x_end);y_end = cos(x_end);

anArrow = annotation('textarrow');

anArrow.Parent = gca;

anArrow.X = [x_start,x_end]; % set the x-property

anArrow.Y = [y_start ,y_end];

anArrow.String = 'cos(3\pi/2)';

anArrow.Color = 'green';

##### 0 Comments

Vitaly Fedoseev
on 26 May 2021

The following code (Matlab R2019a) draws an arrow in the plot coordinates from point P1 to point P2. Zoom in/out shifts position of the arrow:

P1=[10,-1]; %from point

P2=[70,2]; % to point

figure;

Xlim=[-1 110];

Ylim=[-2 3];

Pos = [0.10 0.55 0.85 0.4];

subplot('Position', Pos)

hold on

X_conv(1)=Pos(1)+(Pos(3))/(Xlim(2)-Xlim(1))*(P1(1)-Xlim(1));

X_conv(2)=Pos(1)+(Pos(3))/(Xlim(2)-Xlim(1))*(P2(1)-Xlim(1));

Y_conv(1)=Pos(2)+(Pos(4))/(Ylim(2)-Ylim(1))*(P1(2)-Ylim(1));

Y_conv(2)=Pos(2)+(Pos(4))/(Ylim(2)-Ylim(1))*(P2(2)-Ylim(1));

x=0:0.1:100;plot(x, sin(x));plot([-100 1000], P2(2)*[1 1]); plot(P2(1)*[1 1], [-100 100]);

plot(x, sin(x));plot([-100 1000], P1(2)*[1 1]); plot(P1(1)*[1 1], [-100 100])

xlim(Xlim)

ylim(Ylim)

annotation('arrow', X_conv, Y_conv)

##### 3 Comments

Walter Roberson
on 1 Jun 2021

### See Also

### Categories

### Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!