No BSD License  

Highlights from
Draw 3D arrows

image thumbnail
from Draw 3D arrows by Changshun Deng
A function to draw 3D-arrows.

varargout=arrow3d(start,stop,ang,ltype,p,n)
function varargout=arrow3d(start,stop,ang,ltype,p,n)
%   Plot 3d Arrows
% 
%   This functin is used to draw 3D-arrows. The size of arrow head is
%   decided by the minimum distance between start and stop.
%   
%   
%   Bug Fixed:
%   1. arrow3d([ 0 0 -1 ], [ 0 0 -2]) points the wrong way 
%      Found by Pavel Grinfeld(pg@math.drexel.edu)
%      Fixed By: WaitingForMe 2006/7/24
% 
%   The functin will return the handles of arrow bodies and arrow heads   
%
%   Usage:
%       [hline,hhead]=arrow3d(start,stop)
%       [hline,hhead]=arrow3d(start,stop,ang)
%       [hline,hhead]=arrow3d(start,stop,ang,linetype)
%       [hline,hhead]=arrow3d(start,stop,ang,linetype,p)
%       [hline,hhead]=arrow3d(start,stop,ang,linetype,p,n)
% 
%   Start:      The starting points.
%   Stop :      The end points.
%   Ang  :      The arrow head angle
%   linetype:   Choose the body type, you can set this to be 'line' or 
%               'cylinder', 'cylinder' is the default value
%   p    :      P(1) is the ratio of arrow head height to the distance
%               between start and stop points
%               P(2) is the ratio of arrow body
%   n    :      The arrow head has n(1) equally spaced points around its circumference.
%               The arrow body has n(2) equally spaced points around its circumference.
%
%   Example:
%       t=linspace(0,4*pi,40);
%       x=cos(t);y=sin(t);z=0.2*t;
%       p=[x' y' z'];
%       p1=p(1:end-1,:);p2=p(2:end,:);
%       arrow3d(p1,p2,15,'cylinder',[0.5,0.5]);axis equal;grid on;
% 
%   Author: Changshun Deng
%   Email: heroaq_2002@163.com
%   WEB-Log: http://waitingforme.yculblog.com
%   30/8/2005 


%Check start and stop point data
%the column number must be 3, and the row number must greater than 0
%Set default data
if nargin<3 ang=30; end
if nargin<4 ltype='cylinder'; end
if nargin<5 p=[0.25,0.1]; end
if nargin<6 n=[20,10]; end
if size(start,1)<1 | size(stop,1)<1
    error([upper(mfilename) ': start point data or stop piont data must be a m*3 matrix']);
end
if size(start,2)~=3 | size(stop,2)~=3
    error([upper(mfilename) ': start point data or stop piont data must be a m*3 matrix']);
end
%p(1) is the ratio of arrow head height to the distance between start and end points
%p(2) is the ratio of cylinder radial to arrow head height
%Check if p is between 0 and 1
if ~all(p<=1 & p>0)
    error([upper(mfilename) ': p must between 0 and 1']);
end
%n(1) is the patch number of arrow head
%n(2) is the patch number of cylinder
%Check if n a positive integer, and n must greater than 2
n=ceil(n);
if ~all(n>2)
    error([mfilename ': patch number of arrow head and cylinder must greater than 3']);
end
%Calculate the direction vectors between start and end points
dvec=stop-start;
%Calculate the distances between start and end points
dis=sqrt(sum(dvec.^2,2));
%The height of arrow head
hv=min(dis)*p(1);
%Generate the initial line data of 3d arrows
init_start=zeros(size(start));
init_stop=[zeros(size(dis)) zeros(size(dis)) (dis-hv).*ones(size(dis))];
%Calculate Rotate angles of the lines
cosrang=acos(dvec(:,3)./dis)*180/pi;
%Calculate normal vector between arrow line and Z-axis
nvec=[-dvec(:,2) dvec(:,1) zeros(size(dis))];
%draw lines of arrows
if ~ishold
    hold on;
    view(3);
    SET_HOLD_OFF=true;
else
    SET_HOLD_OFF=false;
end
hlines=[];
if strcmp(ltype,'line')
    for i=1:length(dis)
        %Rotate end point
        [rx,ry,rz]=rotatedata(init_stop(i,1),init_stop(i,2),init_stop(i,3),nvec(i,:),cosrang(i),[0,0,0]);
        hlines(i)=line([start(i,1);start(i,1)+rx],[start(i,2);start(i,2)+ry],[start(i,3);start(i,3)+rz]);
    end
    hlgrd=[];
else
    for i=1:length(dis)
        r=hv*tan(ang./180.*pi).*p(2);
        [xi,yi,zi] = cylinder(r.*[1,1],n(2));
        zi=zi.*(dis(i)-hv);
        %escape the error if the arrow is in z-direction
        %if the arrow is in z-direction then the nvector result zeros to
        %make a error!
        %Fix this bug! 2006/07/24
        %Thanks to Pavel Grinfeld(pg@math.drexel.edu)
        if all(nvec(i,:)==0)
            nvec(i,:)=[0,1,0];
        end
        [rx,ry,rz] = rotatedata(xi,yi,zi,nvec(i,:),cosrang(i),[0,0,0]);
        cx=start(i,1)+rx;cy=start(i,2)+ry;cz=start(i,3)+rz;
        hlines(i)=surf(cx,cy,cz,'edgecolor','none','facecolor',[145,90,7]/255);
        hlgrd(i)=patch(cx(1,:),cy(1,:),cz(1,:),[145,90,7]/255);
    end
end
%Generate the initial arrow head data of 3d arrwos
hheads=[];
hgrd=[];
pv=dis-hv;
%draw heads of arrows
for i=1:length(dis)
    %Generate initial taper data
    [xi,yi,zi] = cylinder([tan(ang/180*pi),0],n(1));
    xi=xi*hv;yi=yi*hv;zi=zi*hv+pv(i);
    %Rotate the taper
    [rx,ry,rz] = rotatedata(xi,yi,zi,nvec(i,:),cosrang(i),[0,0,0]);
    cx=start(i,1)+rx;cy=start(i,2)+ry;cz=start(i,3)+rz;
    hheads(i)=surf(cx,cy,cz,'edgecolor','none','facecolor',[11 131 222]/255);
    %Draw the underside of taper
    hhgrd(i)=patch(cx(1,:),cy(1,:),cz(1,:),[7 86 145]/255);
end
if SET_HOLD_OFF
    hold off;
end
if nargout>1 varargout{2}=[hheads;hhgrd]; end
if nargout>0 varargout{1}=[hlines;hlgrd]; end

Contact us at files@mathworks.com