% ARROWH Draws a solid 2D arrow head in current plot.
% ARROWH(X,Y,COLOR,SIZE,LOCATION) draws a solid arrow head into
% the current plot to indicate a direction. X and Y must contain
% a pair of x and y coordinates ([x1 x2],[y1 y2]) of two points:
% The first point is only used to tell (in conjunction with the
% second one) the direction and orientation of the arrow -- it
% will point from the first towards the second.
% The head of the arrow will be located in the second point. An
% example of use is plot([0 2],[0 4]); ARROWH([0 1],[0 2],'b')
% You may also give two vectors of same length > 2. The routine
% will then choose two consecutive points from "about" the middle
% of each vectors. Useful if you don't want to worry each time
% about where to put the arrows on a trajectory. If x1 and x2
% are the vectors x1(t) and x2(t), simply put ARROWH(x1,x2,'r')
% to have the right direction indicated in your x2 = f(x1) phase
% \ |
% Please note that the following optional arguments need -- if
% you want to use them -- to be given in that exact order.
% The COLOR argument is exactely the same as for plots, eg. 'r';
% if not given, blue is default.
% The SIZE argument allows you to tune the size of the arrows.
% The LOCAITON argument only applies, if entire solution vectors
% have been passed on. With this argument you can indicate where
% abouts inside those vectors to take the two points from.
% Can be a vector, if you want to have more than one arrow drawn.
% Both arguments, SIZE and LOCATION must be given in percent,
% where 100 means standard size, 50 means half size, respectively
% 100 means end of the vector, 48 means about middle, 0 beginning.
% Note that those "locations" correspond to the cardinal position
% "inside" the vector, say "index-wise".
% This routine is mainely intended to be used for indicating
% "directions" on trajectories -- just give two consecutive times
% and the corresponding values of a flux and the proper direction
% of the trajectory will be shown on the plot. You may also pass
% on two solution vectors, as described above.
% Note, that the arrow only looks good on the specific axis
% settings when the routine was actually started. If you zoom in
% afterwards, the triangle gets distorted.
% Examples of use:
% x1 = [0:.2:2]; x2 = [0:.2:2]; plot(x1,x2); hold on;
% arrowh(x1,x2,'r',100,20); % passing on entire vectors
% arrowh([0 1],[0 1],'g',300); % passing on 2 points
% Author: Florian Knorn
% Email: firstname.lastname@example.org
% Version: 1.10
% Filedate: Dec 1st, 2005
% History: 1.10 - Buxfix
% 1.09 - Possibility to chose *several* locations
% 1.08 - Possibility to chose location
% 1.07 - Choice of color
% 1.06 - Bug fixes
% 1.00 - Release
% ToDos: - More specific shaping-possibilities,
% - Keep proportions when zooming or resizing;
% has to be done with callback functions, I guess.
% Bugs: None discovered yet, those discovered were fixed
% Thanks: I haven't used the function in ages, but the
% last time I modified something in a hurry, I
% introduced a stupid bug, which Kesh Ikum was so
% kind to point out ;-) Thanks!
% If you have suggestions for this program, if it doesn't work for
% your "situation" or if you change something in it - please send
% me an email! This is my very first "public" program and I'd like
% to improve it where I can -- your help is kindely appreciated!
% Thank you!
if nargin < 2
error('Please give enough coordinates !');
if (length(x) < 2) || (length(y) < 2),
error('X and Y vectors must each have "length" >= 2 !');
if (x(1) == x(2)) && (y(1) == y(2)),
error('Points superimposed - cannot determine direction !');
if nargin < 3
clr = 'b';
if nargin < 4
ArSize = 100 / 10000; %-- 10000 is an arbitrary value...
ArSize = ArSize / 10000;
if nargin < 5
Where = 50;
%-- determine and remember the hold status, toggle if necessary
WasHold = 1;
WasHold = 0;
%-- start for-loop in case several arrows are wanted
for Loop = 1:length(Where),
%-- if vectors "longer" then 2 are given we're dealing with time series
if (length(x) == length(y)) && (length(x) > 2),
j = floor(length(x)*Where(Loop)/100); %-- determine that location
if j >= length(x), j = length(x) - 1; end
if j == 0, j = 1; end
x1 = x(j); x2 = x(j+1); y1 = y(j); y2 = y(j+1);
else %-- just two points given - take those
x1 = x(1); x2 = x(2); y1 = y(1); y2 = y(2);
%-- get axe ranges and their norm
OriginalAxis = axis;
Xextend = abs(OriginalAxis(2)-OriginalAxis(1));
Yextend = abs(OriginalAxis(4)-OriginalAxis(3));
%-- determine angle for the rotation of the triangle
if x2 == x1, %-- line vertical, no need to calculate slope
if y2 > y1,
p = pi/2;
else %-- line not vertical, go ahead and calculate slope
%-- using normed differences (looks better like that)
m = ( (y2 - y1)/Yextend ) / ( (x2 - x1)/Xextend );
if x2 > x1, %-- now calculate the resulting angle
p = atan(m);
p = atan(m) + pi;
%-- the arrow is made of a transformed "template triangle".
%-- it will be created, rotated, moved, resized and shifted.
%-- the template triangle (it points "east", centered in (0,0)):
xt = [1 -sin(pi/6) -sin(pi/6)];
yt = [0 cos(pi/6) -cos(pi/6)];
%-- rotate it by the angle determined above:
xd(i) = cos(p)*xt(i) - sin(p)*yt(i);
yd(i) = sin(p)*xt(i) + cos(p)*yt(i);
%-- move the triangle so that its "head" lays in (0,0):
xd = xd - cos(p);
yd = yd - sin(p);
%-- stretch/deform the triangle to look good on the current axes:
xd = xd*Xextend*ArSize;
yd = yd*Yextend*ArSize;
%-- move the triangle to the location where it's needed
xd = xd + x2;
yd = yd + y2;
%-- draw the actual triangle
end % Loops
%-- restore original axe ranges and hold status
%-- work done. good bye.