function [h,yy,zz] = arrow(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8, ...
        arg9,arg10,arg11,arg12,arg13,arg14,arg15,arg16, ...
        arg17,arg18,arg19,arg20,arg21,arg22,arg23,arg24)
    % ARROW  Draw a line with an arrowhead.
    %
    %  ARROW(Start,Stop) draws a line with an arrow pointing from the point
    %                    Start to point Stop (the points should be vectors
    %                    of length 2 or 3, or matrices with 2 or 3 columns), and
    %                    returns the graphics handle of the arrow(s).
    %
    %  ARROW DEMO & ARROW DEMO2 show 3-D & 2-D demos of the capabilities of ARROW.
    %
    %  ARROW may be called with a normal argument list or a property-based list.
    %        ARROW(Start,Stop,Length,BaseAngle,TipAngle,Width,Page,CrossDir) is
    %        the full normal argument list, where all but the Start and Stop
    %        points are optional.  If you need to specify a later argument (e.g.,
    %        Page) but want default values of earlier ones (e.g., TipAngle),
    %        pass an empty matrix for the earlier ones (e.g., TipAngle=[]).
    %
    %  ARROW('Property1',PropVal1,'Property2',PropVal2,...) creates arrows with the
    %        given properties, using default values for any not specified or given
    %        as 'default'.  Some properties used for line and patch objects are
    %        used in a modified fashion, others are passed directly to LINE, PATCH,
    %        or SET.  For a detailed properties explanation, call ARROW PROPERTIES.
    %
    %    Start          The starting points.                        B
    %    Stop           The end points.                            /|\           ^
    %    Length         Length of the arrowhead in pixels.        /|||\          |
    %    BaseAngle      Base angle in degrees (ADE).             //|||\\        L|
    %    TipAngle       Tip angle in degrees (ABC).             ///|||\\\       e|
    %    Width          Width of the base in pixels.           ////|||\\\\      n|
    %    Page           Use hardcopy proportions.             /////|D|\\\\\     g|
    %    CrossDir       Vector || to arrowhead plane.        ////  |||  \\\\    t|
    %    NormalDir      Vector out of arrowhead plane.      ///    |||    \\\   h|
    %    Ends           Which end has an arrowhead.        //<----->||      \\   |
    %    ObjectHandles  Vector of handles to update.      /   base |||        \  V
    %    LineStyle      The linestyle of the arrow.      E    angle||<-------->C
    %    LineWidth      Line thicknesses.                          |||tipangle
    %    FaceColor      FaceColor of patch arrows.                 |||
    %    EdgeColor      EdgeColor/Color of patch/line arrows.      |||
    %    Color          Set FaceColor & EdgeColor properties.   -->|A|<-- width
    %
    %  ARROW(H,'Prop1',PropVal1,...), where H is a vector of handles to previously-
    %        created arrows and/or line objects, will update the previously-created
    %        arrows according to the current view and any specified properties, and
    %        will convert two-point line objects to corresponding arrows.  Note
    %        that ARROW(H) will update the arrows if the current view has changed.
    %
    %  A property list can follow any specified normal argument list, e.g.,
    %  ARROW([1 2 3],[0 0 0],36,'BaseAngle',60) creates an arrow from (1,2,3) to
    %  the origin, with an arrowhead of length 36 pixels and 60-degree base angle.
    %
    %  The basic arguments or properties can generally be vectorized to create
    %  multiple arrows with the same call.  This is done by passing a property
    %  with one row per arrow, or, if all arrows are to have the same property
    %  value, just one row may be specified.
    %
    %  You may want to execute AXIS(AXIS) before calling ARROW so it doesn't change
    %  the axes on you; ARROW determines the sizes of arrow components BEFORE the
    %  arrow is plotted, so if ARROW changes axis limits, arrows may be malformed.
    %
    %  ARROW uses features of Matlab 4.2c and later, so earlier versions may be
    %  incompatible; call ARROW VERSION for more details.

    % Copyright (c)1995, Erik A. Johnson <johnsone@uxh.cso.uiuc.edu>, 11/13/95

    % Revision history:
    %   11/13/95  EAJ  Corrected handling for 'reverse' axis directions
    %   10/06/95  EAJ  Corrected occasional conflict with SUBPLOT

    % Many thanks to Keith Rogers <kerog@ai.mit.edu> for his many excellent
    % suggestions and beta testing.  Check out his shareware package MATDRAW.
    % He has permission to distribute ARROW with MATDRAW.

    % Are we doing the demo?
    c = newline;
    if (nargin==1)
        if (ischar(arg1))
            arg1 = lower([arg1 '                ']);
            if (strcmp(arg1(1:4),'prop'))
                disp([c ...
                    'ARROW Properties:  Default values are given in [square brackets], and other' c ...
                    '                   acceptable equivalent property names are in (parenthesis).' c c ...
                    '  Start           The starting points. For N arrows,            B' c ...
                    '                  this should be a Nx2 or Nx3 matrix.          /|\           ^' c ...
                    '  Stop            The end points. For N arrows, this          /|||\          |' c ...
                    '                  should be a Nx2 or Nx3 matrix.             //|||\\        L|' c ...
                    '  Length          Length of the arrowhead (in pixels on     ///|||\\\       e|' c ...
                    '                  screen, points on a page). [16] (Len)    ////|||\\\\      n|' c ...
                    '  BaseAngle       Angle (degrees) of the base angle       /////|D|\\\\\     g|' c ...
                    '                  ADE.  For a simple stick arrow, use    ////  |||  \\\\    t|' c ...
                    '                  BaseAngle=TipAngle. [90] (Base)       ///    |||    \\\   h|' c ...
                    '  TipAngle        Angle (degrees) of tip angle ABC.    //<----->||      \\   |' c ...
                    '                  [16] (Tip)                          /   base |||        \  V' c ...
                    '  Width           Width of the base in pixels.  Not  E   angle ||<-------->C' c ...
                    '                  the ''LineWidth'' prop. [0] (Wid)              |||tipangle' c ...
                    '  Page            If provided, non-empty, and not NaN,         |||' c ...
                    '                  this causes ARROW to use hardcopy            |||' c ...
                    '                  rather than onscreen proportions.             A' c ...
                    '                  This is important if screen aspect        -->   <-- width' c ...
                    '                  ratio and hardcopy aspect ratio are    ----CrossDir---->' c ...
                    '                  vastly different. []' c...
                    '  CrossDir        A vector giving the direction towards which the fletches' c ...
                    '                  on the arrow should go.  [computed such that it is perpen-' c ...
                    '                  dicular to both the arrow direction and the view direction' c ...
                    '                  (i.e., as if it was pasted on a normal 2-D graph)]  (Note' c ...
                    '                  that CrossDir is a vector; if an axis is plotted on a log' c ...
                    '                  scale, then the corresponding component of CrossDir must' c ...
                    '                  also be set appropriately, i.e., to 1 for no change in' c ...
                    '                  that direction, >1 for a positive change, >0 and <1 for' c ...
                    '                  negative change.)' c ...
                    '  NormalDir       A vector normal to the fletch direction (CrossDir is then' c ...
                    '                  computed by the vector cross product {Line}x{NormalDir}). []' c ...
                    '  Ends            Set which end has an arrowhead.  Valid values are ''none'',' c ...
                    '                  ''stop'', ''start'', and ''both''. [''stop''] (End)' c...
                    '  ObjectHandles   Vector of handles to previously-created arrows to be' c ...
                    '                  updated or line objects to be converted to arrows.' c ...
                    '                  [] (Object,Handle)' c ...
                    '  LineStyle       The linestyle of the arrow.  If anything other than ''-'',' c ...
                    '                  the arrow will be drawn with a line object, otherwise it' c ...
                    '                  will be drawn with a patch.  [''-''] (LineS)' c ...
                    '  LineWidth       Same as used in SET commands, but may be passed a vector' c ...
                    '                  for multiple arrows. [default of the line or patch]' c ...
                    '  FaceColor       Set the FaceColor of patch arrows.  May be one of' c ...
                    '                  ''ymcrgbwkfn'' (f=flat,n=none) or a column-vector colorspec' c ...
                    '                  (or Nx3 matrix for N arrows). [1 1 1] (FaceC)' c ...
                    '  EdgeColor       Set the EdgeColor of patch arrows and Color of line' c ...
                    '                  arrows. [1 1 1] (EdgeC)' c ...
                    '  Color           Sets both FaceColor and EdgeColor properties.' c ...
                    '                  Included for compatibility with line objects.' c c ...
                    '  ARROW(''Start'',P,''Stop'',[]) or ARROW(''Start'',[],''Stop'',P), with size(P)=[N 3]' c ...
                    '  will create N-1 arrows, just like ARROW(''Start'',P1,''Stop'',P2), where' c c ...
                    '          / x1 y1 z1 \         /  x1   y1   z1  \         / x2 y2 z2 \' c ...
                    '          | .  .  .  |         |  .    .    .   |         | .  .  .  |' c ...
                    '      P = | .  .  .  |    P1 = |  .    .    .   |    P2 = | .  .  .  |' c ...
                    '          | .  .  .  |         |  .    .    .   |         | .  .  .  |' c ...
                    '          \ xN yN zN /         \ xN-1 yN-1 zN-1 /         \ xN yN zN /' c]);
            elseif (strcmp(arg1(1:4),'vers'))
                disp([c ...
                    'ARROW Version:  There are two compatibility problems for ARROW in Matlab' c ...
                    '                versions earlier than 4.2c:' c c c ...
                    '   1) In Matlab <4.2, the ''Tag'' property does not exist.  ARROW uses this' c ...
                    '      ''Tag'' to identify arrow objects for subsequent calls to ARROW.' c c ...
                    '      Solution: (a) delete all instances of the string' c c ...
                    '                         ,''Tag'',ArrowTag' c c ...
                    '                (b) and replace all instances of the string' c c ...
                    '                         strcmp(get(oh,''Tag''),ArrowTag)' c c ...
                    '                    with the string' c c ...
                    '                         all(size(ud)==[1 15])' c c c ...
                    '   2) In Matlab <4.2c, FINDOBJ is buggy (it can cause Matlab to crash if' c ...
                    '      more than 100 objects are returned).  ARROW uses FINDOBJ to make sure' c ...
                    '      that any handles it receives are truly handles to existing objects.' c c ...
                    '      Solution: replace the line' c c ...
                    '                         objs = findobj;' c c ...
                    '                with the lines' c c ...
                    '                         objs=0; k=1;' c ...
                    '                         while (k<=length(objs)),' c ...
                    '                             objs = [objs; get(objs(k),''Children'')];' c ...
                    '                             if (strcmp(get(objs(k),''Type''),''axes'')),' c ...
                    '                                 objs=[objs;get(objs(k),''XLabel''); ...' c ...
                    '                                            get(objs(k),''YLabel''); ...' c ...
                    '                                            get(objs(k),''ZLabel''); ...' c ...
                    '                                            get(objs(k),''Title'')];' c ...
                    '                             end;' c ...
                    '                             k=k+1;' c ...
                    '                         end;' c c]);
            elseif (strcmp(arg1(1:4),'demo'))
                % demo
                % create the data
                [x,y,z] = peaks;
                [ddd,iii]=max(z(:));
                axlim = [min(x(:)) max(x(:)) min(y(:)) max(y(:)) min(z(:)) max(z(:))];

                % modify it by inserting some NaN's
                [m,n] = size(z);
                m = floor(m/2);
                n = floor(n/2);
                z(1:m,1:n) = NaN*ones(m,n);

                % graph it
                clf('reset');
                hs=surf(x,y,z);
                xlabel('x'); ylabel('y');

                if (~strcmp(arg1(1:5),'demo2'))
                    % set the view
                    axis(axlim);
                    zlabel('z');
                    %shading('interp'); set(hs,'EdgeColor','k');
                    view(viewmtx(-37.5,30,20));
                    title('Demo of the capabilities of the ARROW function in 3-D');

                    % Normal yellow arrow
                    h1 = arrow([axlim(1) axlim(4) 4],[-.8 1.2 4], ...
                        'EdgeColor','y','FaceColor','y');

                    % Normal white arrow, clipped by the surface
                    h2 = arrow(axlim([1 4 6]),[0 2 4]);
                    t=text(-2.4,2.7,7.7,'arrow clipped by surf');

                    % Baseangle<90
                    h3 = arrow([3 .125 3.5],[1.375 0.125 3.5],30,50);
                    t2=text(3.1,.125,3.5,'local maximum');

                    % Baseangle<90, fill and edge colors different
                    h4 = arrow(axlim(1:2:5)*.5,[0 0 0],36,60,25, ...
                        'EdgeColor','b','FaceColor','c');
                    t3=text(axlim(1)*.5,axlim(3)*.5,axlim(5)*.5-.75,'origin');
                    set(t3,'HorizontalAlignment','center');

                    % Baseangle>90, black fill
                    h5 = arrow([-2.9 2.9 3],[-1.3 .4 3.2],30,120,[],6, ...
                        'EdgeColor','r','FaceColor','k','LineWidth',2);

                    % Baseangle>90, no fill
                    h6 = arrow([-2.9 2.9 1.3],[-1.3 .4 1.5],30,120,[],6, ...
                        'EdgeColor','r','FaceColor','none','LineWidth',2);

                    % Stick arrow
                    h7 = arrow([-1.6 -1.65 -6.5],[0 -1.65 -6.5],[],16,16);
                    t4=text(-1.5,-1.65,-7.25,'global mininum');
                    set(t4,'HorizontalAlignment','center');

                    % Normal, black fill
                    h8 = arrow([-1.4 0 -7.2],[-1.4 0 -3],'FaceColor','k');
                    t5=text(-1.5,0,-7.75,'local minimum');
                    set(t5,'HorizontalAlignment','center');

                    % Gray fill, crossdir specified
                    h9 = arrow([-3 2.2 -6],[-3 2.2 -.05],36,[],27,6,[],[0 -1 0], ...
                        'EdgeColor','w','FaceColor',.2*[1 1 1]);

                    % a series of normal arrows, linearly spaced, crossdir specified
                    h10y=(0:4)'/3;
                    h10 = arrow([-3*ones(size(h10y)) h10y -6.5*ones(size(h10y))], ...
                        [-3*ones(size(h10y)) h10y -.05*ones(size(h10y))], ...
                        12,[],[],[],[],[0 -1 0]);

                    % a series of normal arrows, linearly spaced
                    h11x=(1:.33:2.8)';
                    h11 = arrow([h11x -3*ones(size(h11x)) 6.5*ones(size(h11x))], ...
                        [h11x -3*ones(size(h11x)) -.05*ones(size(h11x))]);

                    % series of black-filled arrows, radially oriented, crossdir specified
                    h12x=2; h12y=-3; h12z=axlim(5)/2; h12xr=1; h12zr=h12z; ir=.15;or=.81;
                    h12t=(0:11)'/6*pi;
                    h12 = arrow([h12x+h12xr*cos(h12t)*ir h12y*ones(size(h12t))       ...
                        h12z+h12zr*sin(h12t)*ir],[h12x+h12xr*cos(h12t)*or    ...
                        h12y*ones(size(h12t)) h12z+h12zr*sin(h12t)*or],      ...
                        10,[],[],[],[],                                      ...
                        [-h12xr*sin(h12t) zeros(size(h12t)) h12zr*cos(h12t)],...
                        'FaceColor','none','EdgeColor','m');

                    % series of normal arrows, tangentially oriented, crossdir specified
                    or13=.91; h13t=(0:.5:12)'/6*pi;
                    h13 = arrow([h12x+h12xr*cos(h13t)*or13 ...
                        h12y*ones(size(h13t))     ...
                        h12z+h12zr*sin(h13t)*or13],[],6);

                    % arrow with no line ==> oriented downwards
                    h14 = arrow([3 3 .100001],[3 3 .1],30);
                    t6=text(3,3,3.6,'no line'); set(t6,'HorizontalAlignment','center');

                    % arrow with -- linestyle
                    h15 = arrow([-.5 -3 -3],[1 -3 -3],'LineStyle','--','EdgeColor','g');

                    if (nargout>=1), h=[h1;h2;h3;h4;h5;h6;h7;h8;h9;h10;h11;h12;h13;h14;h15]; end
                else
                    set(hs,'YData',10.^get(hs,'YData'));
                    shading('interp');
                    view(2);
                    title('Demo of the capabilities of the ARROW function in 2-D');
                    hold on; [C,H]=contour(x,y,z,20); hold off;
                    for k=H', set(k,'ZData',(axlim(6)+1)*ones(size(get(k,'XData'))),...
                            'YData',10.^get(k,'YData'),'Color','k'); end
                    set(gca,'YScale','log');
                    axis([axlim(1:2) 10.^axlim(3:4)]);

                    % Normal yellow arrow
                    h1 = arrow([axlim(1) 10^axlim(4) axlim(6)+2],[x(iii) 10^y(iii) axlim(6)+2], ...
                        'EdgeColor','y','FaceColor','y');

                    % three arrows with varying fill, width, and baseangle
                    h2 = arrow([-3 10^(-3) 10; -3 10^(-1.5) 10; -1.5 10^(-3) 10], ...
                        [-.03 10^(-.03) 10; -.03 10^(-1.5) 10; -1.5 10^(-.03) 10], ...
                        24,[90;60;120],[],[0;0;4]);
                    set(h2(2),'EdgeColor','g','FaceColor','c');
                    set(h2(3),'EdgeColor','m','FaceColor','r');
                    if (nargout>=1), h=[h1;h2]; end
                end
            else
                error(['ARROW got an unknown single-argument string ''' deblank(arg1) '''.']);
            end
            return;
        end
    end

    % Check # of arguments
    if (nargin==0), help arrow ; return;
    elseif (nargout>3), error('ARROW produces at most 3 output arguments.');
    end

    % find first property number
    firstprop = nargin+1;
    if (nargin<=3), % to speed things up a bit
        if (nargin==1)
        elseif (ischar(arg1)), firstprop=1;
        elseif (ischar(arg2)), firstprop=2;
        elseif (nargin==3)
            if (ischar(arg3)), firstprop=3; end
        end
    else
        for k=1:nargin
            curarg = eval(['arg' num2str(k)]);
            if (ischar(curarg))
                firstprop = k;
                break;
            end
        end
    end

    % check property list
    if (firstprop<=nargin)
        for k=firstprop:2:nargin
            curarg = eval(['arg' num2str(k)]);
            if ((~ischar(curarg))|(min(size(curarg))~=1))
                error('ARROW requires that a property name be a single string.');
            end
        end
        if (rem(nargin-firstprop,2)~=1)
            error(['ARROW requires that the property ''' eval(['arg' num2str(nargin)]) ...
                ''' be paired with a property value.']);
        end
    end

    % default output
    if (nargout>0), h=[]; end
    if (nargout>1), yy=[]; end
    if (nargout>2), zz=[]; end

    % set values to empty matrices
    start      = [];
    stop       = [];
    len        = [];
    baseangle  = [];
    tipangle   = [];
    wid        = [];
    page       = [];
    crossdir   = [];
    ends       = [];
    linewidth  = [];
    linestyle  = [];
    edgecolor  = [];
    facecolor  = [];
    ax         = [];
    oldh       = [];
    defstart      = [NaN NaN NaN];
    defstop       = [NaN NaN NaN];
    deflen        = 16;
    defbaseangle  = 90;
    deftipangle   = 16;
    defwid        = 0;
    defpage       = 0;
    defcrossdir   = [NaN NaN NaN];
    defends       = 1;
    deflinewidth  = NaN;
    deflinestyle  = '- ';
    defedgecolor  = [1 1 1];
    deffacecolor  = [1 1 1];
    defoldh       = [];

    % The 'Tag' we'll put on our arrows
    ArrowTag = 'Arrow';

    % check for oldstyle arguments
    if (firstprop>2)
        % if old style arguments, get them
        if     (firstprop==3), start=arg1; stop=arg2;
        elseif (firstprop==4), start=arg1; stop=arg2; len=arg3(:);
        elseif (firstprop==5), start=arg1; stop=arg2; len=arg3(:); baseangle=arg4(:);
        elseif (firstprop==6), start=arg1; stop=arg2; len=arg3(:); baseangle=arg4(:); tipangle=arg5(:);
        elseif (firstprop==7), start=arg1; stop=arg2; len=arg3(:); baseangle=arg4(:); tipangle=arg5(:); wid=arg6(:);
        elseif (firstprop==8), start=arg1; stop=arg2; len=arg3(:); baseangle=arg4(:); tipangle=arg5(:); wid=arg6(:); page=arg7(:);
        elseif (firstprop==9), start=arg1; stop=arg2; len=arg3(:); baseangle=arg4(:); tipangle=arg5(:); wid=arg6(:); page=arg7(:); crossdir=arg8;
        else, error('ARROW takes at most 8 non-property arguments.');
        end
    elseif (firstprop==2)
        % assume arg1 is a set of handles
        oldh = arg1(:);
    end

    % parse property pairs
    extraprops=char([]);
    for k=firstprop:2:nargin
        prop = eval(['arg' num2str(k)]);
        val  = eval(['arg' num2str(k+1)]);
        prop = [lower(prop(:)') '      '];
        if     (all(prop(1:5)=='start')),      start      = val;
        elseif (all(prop(1:4)=='stop')),       stop       = val;
        elseif (all(prop(1:3)=='len')),        len        = val(:);
        elseif (all(prop(1:4)=='base')),       baseangle  = val(:);
        elseif (all(prop(1:3)=='tip')),        tipangle   = val(:);
        elseif (all(prop(1:3)=='wid')),        wid        = val(:);
        elseif (all(prop(1:4)=='page')),       page       = val;
        elseif (all(prop(1:5)=='cross')),      crossdir   = val;
        elseif (all(prop(1:4)=='norm')),       if (ischar(val)), crossdir=val; else, crossdir=val*sqrt(-1); end
        elseif (all(prop(1:3)=='end')),        ends       = val;
        elseif (all(prop(1:5)=='linew')),      linewidth  = val(:);
        elseif (all(prop(1:5)=='lines')),      linestyle  = val;
        elseif (all(prop(1:5)=='color')),      edgecolor  = val; facecolor=val;
        elseif (all(prop(1:5)=='edgec')),      edgecolor  = val;
        elseif (all(prop(1:5)=='facec')),      facecolor  = val;
        elseif (all(prop(1:6)=='object')),     oldh       = val(:);
        elseif (all(prop(1:6)=='handle')),     oldh       = val(:);
        elseif (all(prop(1:5)=='userd')),      %ignore it
        else, extraprops=[extraprops ',arg' num2str(k) ',arg' num2str(k+1)];
        end
    end

    % Check if we got 'default' values
    if (ischar(start     )),  s=lower([start(:)'      '   ']); if (all(s(1:3)=='def')), start      = defstart;      else, error(['ARROW does not recognize ''' start(:)'     ''' as a valid ''Start'' string.']); end; end
    if (ischar(stop      )),  s=lower([stop(:)'       '   ']); if (all(s(1:3)=='def')), stop       = defstop;       else, error(['ARROW does not recognize ''' stop(:)'      ''' as a valid ''Stop'' string.']); end; end
    if (ischar(len       )),  s=lower([len(:)'        '   ']); if (all(s(1:3)=='def')), len        = deflen;        else, error(['ARROW does not recognize ''' len(:)'       ''' as a valid ''Length'' string.']); end; end
    if (ischar(baseangle )),  s=lower([baseangle(:)'  '   ']); if (all(s(1:3)=='def')), baseangle  = defbaseangle;  else, error(['ARROW does not recognize ''' baseangle(:)' ''' as a valid ''BaseAngle'' string.']); end; end
    if (ischar(tipangle  )),  s=lower([tipangle(:)'   '   ']); if (all(s(1:3)=='def')), tipangle   = deftipangle;   else, error(['ARROW does not recognize ''' tipangle(:)'  ''' as a valid ''TipAngle'' string.']); end; end
    if (ischar(wid       )),  s=lower([wid(:)'        '   ']); if (all(s(1:3)=='def')), wid        = defwid;        else, error(['ARROW does not recognize ''' wid(:)'       ''' as a valid ''Width'' string.']); end; end
    if (ischar(crossdir  )),  s=lower([crossdir(:)'   '   ']); if (all(s(1:3)=='def')), crossdir   = defcrossdir;   else, error(['ARROW does not recognize ''' crossdir(:)'  ''' as a valid ''CrossDir'' or ''NormalDir'' string.']); end; end
    if (ischar(page      )),  s=lower([page(:)'       '   ']); if (all(s(1:3)=='def')), page       = defpage;       else, error(['ARROW does not recognize ''' page(:)'      ''' as a valid ''Page'' string.']); end; end
    if (ischar(ends      )),  s=lower([ends(:)'       '   ']); if (all(s(1:3)=='def')), ends       = defends;       end; end
    if (ischar(linewidth )),  s=lower([linewidth(:)'  '   ']); if (all(s(1:3)=='def')), linewidth  = deflinewidth;  else, error(['ARROW does not recognize ''' linewidth(:)' ''' as a valid ''LineWidth'' string.']); end; end
    if (ischar(linestyle )),  s=lower([linestyle(:)'  '   ']); if (all(s(1:3)=='def')), linestyle  = deflinestyle;  end; end
    if (ischar(edgecolor )),  s=lower([edgecolor(:)'  '   ']); if (all(s(1:3)=='def')), edgecolor  = defedgecolor;  end; end
    if (ischar(facecolor )),  s=lower([facecolor(:)'  '   ']); if (all(s(1:3)=='def')), facecolor  = deffacecolor;  end; end
    if (ischar(oldh      )),  s=lower([oldh(:)'       '   ']); if (all(s(1:3)=='def')), oldh       = [];            else, error(['ARROW does not recognize ''' oldh(:)'      ''' as a valid ''ObjectHandles'' string.']); end; end

    % check transpose on arguments; convert strings to numbers
    if (((size(start   ,1)==2)|(size(start   ,1)==3))&((size(start   ,2)==1)|(size(start   ,2)>3))), start    = start';    end
    if (((size(stop    ,1)==2)|(size(stop    ,1)==3))&((size(stop    ,2)==1)|(size(stop    ,2)>3))), stop     = stop';     end
    if (((size(crossdir,1)==2)|(size(crossdir,1)==3))&((size(crossdir,2)==1)|(size(crossdir,2)>3))), crossdir = crossdir'; end
    if ((size(linestyle,2)>2)&(size(linestyle,1)<=2)), linestyle=linestyle'; end
    if (all(size(edgecolor)))
        if (ischar(edgecolor))
            col = lower(edgecolor(:,1));
            edgecolor = zeros(length(col),3);
            ii=find(col=='y'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[1 1 0]; end
            ii=find(col=='m'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[1 0 1]; end
            ii=find(col=='c'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[0 1 1]; end
            ii=find(col=='r'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[1 0 0]; end
            ii=find(col=='g'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[0 1 0]; end
            ii=find(col=='b'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[0 0 1]; end
            ii=find(col=='w'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[1 1 1]; end
            ii=find(col=='k'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[0 0 0]; end
            ii=find(col=='f'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[1 1 1]*inf; end
            ii=find(col=='n'); if (all(size(ii))), edgecolor(ii,:)=ones(length(ii),1)*[1 1 1]*(-inf); end
        elseif ((size(edgecolor,2)~=3)&(size(edgecolor,1)==3))
            edgecolor=edgecolor';
        elseif (size(edgecolor,2)~=3)
            error('ARROW requires that color specifications must be a ?x3 RGB matrix.');
        end
    end
    if (all(size(facecolor)))
        if (ischar(facecolor))
            col = lower(facecolor(:,1));
            facecolor = zeros(length(col),3);
            ii=find(col=='y'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[1 1 0]; end
            ii=find(col=='m'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[1 0 1]; end
            ii=find(col=='c'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[0 1 1]; end
            ii=find(col=='r'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[1 0 0]; end
            ii=find(col=='g'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[0 1 0]; end
            ii=find(col=='b'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[0 0 1]; end
            ii=find(col=='w'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[1 1 1]; end
            ii=find(col=='k'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[0 0 0]; end
            ii=find(col=='f'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[1 1 1]*inf; end
            ii=find(col=='n'); if (all(size(ii))), facecolor(ii,:)=ones(length(ii),1)*[1 1 1]*(-inf); end
        elseif ((size(facecolor,2)~=3)&(size(facecolor,1)==3))
            facecolor=facecolor';
        elseif (size(facecolor,2)~=3)
            error('ARROW requires that color specifications must be a ?x3 RGB matrix.');
        end
    end
    if (all(size(ends)))
        if (ischar(ends))
            endsorig = ends;
            col = lower([ends(:,1:min(3,size(ends,2))) ones(size(ends,1),max(0,3-size(ends,2)))*' ']);
            ends = NaN*ones(size(ends,1),1);
            oo = ones(1,size(ends,1));
            ii=find(all(col'==['non']'*oo)'); if (all(size(ii))), ends(ii)=ones(length(ii),1)*0; end
            ii=find(all(col'==['sto']'*oo)'); if (all(size(ii))), ends(ii)=ones(length(ii),1)*1; end
            ii=find(all(col'==['sta']'*oo)'); if (all(size(ii))), ends(ii)=ones(length(ii),1)*2; end
            ii=find(all(col'==['bot']'*oo)'); if (all(size(ii))), ends(ii)=ones(length(ii),1)*3; end
            if (any(isnan(ends)))
                ii = min(find(isnan(ends)));
                error(['ARROW does not recognize ''' deblank(endsorig(ii,:)) ''' as a valid ''Ends'' value.']);
            end
        else
            ends = ends(:);
        end
    end
    oldh = oldh(:);

    % check object handles
    if (all(size(oldh)))
        oldh = oldh.';
        objs=0; k=1;
        while (k<=length(objs))
            objs = [objs; get(objs(k),'Children')];
            if (strcmp(get(objs(k),'Type'),'axes'))
                objs=[objs;get(objs(k),'XLabel'); ...
                    get(objs(k),'YLabel'); ...
                    get(objs(k),'ZLabel'); ...
                    get(objs(k),'Title')];
            end
            k=k+1;
        end
        if isempty(objs), error('ARROW found no graphics handles.');
        elseif (length(objs)==1), objs=[objs;objs]; end
        if (~all(any(objs(:,ones(1,length(oldh)))==oldh(ones(length(objs),1),:))))
            error('ARROW got invalid object handles.');
        end
        oldh = oldh.';
    end

    % Check for an empty Start or Stop (but not both) with no object handles
    if ((~all(size(oldh)))&(all(size(start))~=all(size(stop))))
        if (~all(size(start))), start=stop; end
        ii = find(all(diff(start)'==0)');
        if (size(start,1)==1)
            stop = start;
        elseif (length(ii)==size(start,1)-1)
            stop = start(1,:);
            start = stop;
        else
            if (all(size(ii)))
                jj = (1:size(start,1))';
                jj(ii) = zeros(length(ii),1);
                jj = jj(find(jj>0));
                start = start(jj,:);
            end
            stop = start(2:size(start,1),:);
            start = start(1:size(start,1)-1,:);
        end
    end

    % largest argument length
    argsizes = [length(oldh) size(start,1) size(stop,1)                ...
        length(len) length(baseangle) length(tipangle)         ...
        length(wid) length(page) size(crossdir,1) length(ends) ...
        length(linewidth) size(edgecolor,1) size(facecolor,1)];
    args=['length(ObjectHandle)  '; ...
        '#rows(Start)          '; ...
        '#rows(Stop)           '; ...
        'length(Length)        '; ...
        'length(BaseAngle)     '; ...
        'length(TipAngle)      '; ...
        'length(Width)         '; ...
        'length(Page)          '; ...
        '#rows(CrossDir)       '; ...
        '#rows(Ends)           '; ...
        'length(LineWidth)     '; ...
        '#colors in EdgeColor  '; ...
        '#colors in FaceColor  '];
    if (any(imag(crossdir(:))~=0))
        args(9,:) = '#rows(NormalDir)      ';
    end
    if (~all(size(oldh)))
        narrows = max(argsizes);
    else
        narrows = length(oldh);
    end

    % Check size of arguments
    ii = find((argsizes~=0)&(argsizes~=1)&(argsizes~=narrows));
    if (all(size(ii)))
        s = args(ii',:);
        while ((size(s,2)>1)&((abs(s(:,size(s,2)))==0)|(abs(s(:,size(s,2)))==abs(' '))))
            s = s(:,1:size(s,2)-1);
        end
        s = [ones(length(ii),1)*'ARROW requires that  ' s ...
            ones(length(ii),1)*['  equal the # of arrows (' num2str(narrows) ').' c]];
        s = s';
        s = s(:)';
        s = s(1:length(s)-1);
        error(char(s));
    end

    % check element length in Start, Stop, and CrossDir
    if (all(size(start)))
        if (size(start,2)==2)
            start = [start NaN*ones(size(start,1),1)];
        elseif (size(start,2)~=3)
            error('ARROW requires 2- or 3-element Start points.');
        end
    end
    if (all(size(stop)))
        if (size(stop,2)==2)
            stop = [stop NaN*ones(size(stop,1),1)];
        elseif (size(stop,2)~=3)
            error('ARROW requires 2- or 3-element Stop points.');
        end
    end
    if (all(size(crossdir)))
        if (size(crossdir,2)<3)
            crossdir = [crossdir NaN*ones(size(crossdir,1),3-size(crossdir,2))];
        elseif (size(crossdir,2)~=3)
            if (all(imag(crossdir(:))==0))
                error('ARROW requires 2- or 3-element CrossDir vectors.');
            else
                error('ARROW requires 2- or 3-element NormalDir vectors.');
            end
        end
    end

    % fill empty arguments
    if (~all(size(start     ))),   start      = [NaN NaN NaN];      end
    if (~all(size(stop      ))),   stop       = [NaN NaN NaN];      end
    if (~all(size(len       ))),   len        = NaN;                end
    if (~all(size(baseangle ))),   baseangle  = NaN;                end
    if (~all(size(tipangle  ))),   tipangle   = NaN;                end
    if (~all(size(wid       ))),   wid        = NaN;                end
    if (~all(size(page      ))),   page       = NaN;                end
    if (~all(size(crossdir  ))),   crossdir   = [NaN NaN NaN];      end
    if (~all(size(ends      ))),   ends       = NaN;                end
    if (~all(size(linewidth ))),   linewidth  = NaN;                end
    if (~all(size(linestyle ))),   linestyle  = char([NaN]);      end
    if (~all(size(edgecolor ))),   edgecolor  = [NaN NaN NaN];      end
    if (~all(size(facecolor ))),   facecolor  = [NaN NaN NaN];      end

    % expand single-column arguments
    o = ones(narrows,1);
    if (size(start     ,1)==1),   start      = o * start     ;   end
    if (size(stop      ,1)==1),   stop       = o * stop      ;   end
    if (length(len       )==1),   len        = o * len       ;   end
    if (length(baseangle )==1),   baseangle  = o * baseangle ;   end
    if (length(tipangle  )==1),   tipangle   = o * tipangle  ;   end
    if (length(wid       )==1),   wid        = o * wid       ;   end
    if (length(page      )==1),   page       = o * page      ;   end
    if (size(crossdir  ,1)==1),   crossdir   = o * crossdir  ;   end
    if (length(ends      )==1),   ends       = o * ends      ;   end
    if (length(linewidth )==1),   linewidth  = o * linewidth ;   end
    if (size(linestyle, 1)==1),   linestyle  = o * linestyle ;   end
    if (size(edgecolor, 1)==1),   edgecolor  = o * edgecolor ;   end
    if (size(facecolor, 1)==1),   facecolor  = o * facecolor ;   end
    ax = o * gca;
    if (size(linestyle, 2)==1),   linestyle  = char([linestyle o*' ']); end
    linestyle = char(linestyle);

    % if we've got handles, get the defaults from the handles
    oldlinewidth = NaN*ones(narrows,1);
    oldlinestyle = char(zeros(narrows,2));
    oldedgecolor = NaN*ones(narrows,3);
    oldfacecolor = NaN*ones(narrows,3);
    if (all(size(oldh)))
        fromline = zeros(narrows,1);
        for k=1:narrows
            oh = oldh(k);
            ud = get(oh,'UserData');
            isarrow = strcmp(get(oh,'Tag'),ArrowTag);
            ohtype = get(oh,'Type');
            ispatch = strcmp(ohtype,'patch');
            isline  = strcmp(ohtype,'line');
            if (isarrow|isline)
                % arrow UserData format: [start' stop' len base tip wid page crossdir' ends]
                if (isarrow)
                    start0 = ud(1:3);
                    stop0  = ud(4:6);
                    if (isnan(len(k))),           len(k)        = ud( 7);   end
                    if (isnan(baseangle(k))),     baseangle(k)  = ud( 8);   end
                    if (isnan(tipangle(k))),      tipangle(k)   = ud( 9);   end
                    if (isnan(wid(k))),           wid(k)        = ud(10);   end
                    if (isnan(page(k))),          page(k)       = ud(11);   end
                    if (isnan(crossdir(k,1))),    crossdir(k,1) = ud(12);   end
                    if (isnan(crossdir(k,2))),    crossdir(k,2) = ud(13);   end
                    if (isnan(crossdir(k,3))),    crossdir(k,3) = ud(14);   end
                    if (isnan(ends(k))),          ends(k)       = ud(15);   end
                end
                if (isline)
                    fromline(k) = 1;
                    if (isarrow)
                        fc = -inf*[1 1 1];
                    else
                        fc = get(oh,'Color');
                        x  = get(oh,'XData');
                        y  = get(oh,'YData');
                        z  = get(oh,'ZData');
                        if (any(size(x)~=[1 2])|any(size(y)~=[1 2]))
                            error('ARROW only converts two-point lines.');
                        end
                        if (~all(size(z))), z=NaN*ones(size(x)); end
                        start0 = [x(1) y(1) z(1)];
                        stop0  = [x(2) y(2) z(2)];
                    end
                    ec = get(oh,'Color');
                    ls = [get(oh,'LineStyle') '  ']; ls=char(ls(1:2));
                    lw = get(oh,'LineWidth');
                else, % an arrow patch
                    fc = get(oh,'FaceColor');if (ischar(fc))
                        if (strcmp(fc,'none')), fc=-inf*[1 1 1];
                        elseif (strcmp(fc,'flat')), fc=inf*[1 1 1];
                        else, fc=[1 1 1]; end
                    end
                    ec = get(oh,'EdgeColor');if (ischar(ec))
                        if (strcmp(ec,'none')), ec=-inf*[1 1 1];
                        elseif (strcmp(ec,'flat')), ec=inf*[1 1 1];
                        else, ec=[1 1 1]; end
                    end
                    ls = char('- ');
                    lw = get(oh,'LineWidth');
                end
                ax(k) = get(oh,'Parent');
            else
                error(['ARROW cannot convert ' ohtype ' objects.']);
            end
            oldlinewidth(k)   = lw;
            oldlinestyle(k,:) = ls;
            oldedgecolor(k,:) = ec;
            oldfacecolor(k,:) = fc;
            ii=find(isnan(start(k,:)));      if (all(size(ii))),  start(k,ii)=start0(ii);  end
            ii=find(isnan(stop( k,:)));      if (all(size(ii))),  stop( k,ii)=stop0( ii);  end
            if (isnan(linewidth(k))),        linewidth(k)     = lw;  end
            if (isnan(linestyle(k,1))),      linestyle(k,1:2) = ls;  end
            if (any(isnan(facecolor(k,:)))), facecolor(k,:)   = fc;  end
            if (any(isnan(edgecolor(k,:)))), edgecolor(k,:)   = ec;  end
        end
    else
        fromline = [];
    end

    % set up the UserData data
    % (do it here so it is not corrupted by log10's and such)
    ud = [start stop len baseangle tipangle wid page crossdir ends];

    % Set Page defaults
    page = (~isnan(page))&(page);

    % Get axes limits, range, min; correct for aspect ratio and log scale
    axm      = zeros(3,narrows);
    axr      = zeros(3,narrows);
    axrev    = zeros(3,narrows);
    ap       = zeros(2,narrows);
    xyzlog   = zeros(3,narrows);
    limmin   = zeros(2,narrows);
    limrange = zeros(2,narrows);
    oneax = all(ax==ax(1));
    if (oneax)
        T = zeros(4,4);
        invT = zeros(4,4);
    else
        T        = zeros(16,narrows);
        invT     = zeros(16,narrows);
    end
    axnotdone = ones(size(ax));
    while (any(axnotdone))
        ii = min(find(axnotdone));
        curax = ax(ii);
        curpage = page(ii);
        % get axes limits and aspect ratio
        axl = [get(curax,'XLim'); get(curax,'YLim'); get(curax,'ZLim')];
        ar = get(curax,'AspectRatio');
        % get axes size in pixels (points)
        u = get(curax,'Units');
        axposoldunits = get(curax,'Position');
        if (curpage)
            curfig = get(curax,'Parent');
            pu = get(curfig,'PaperUnits');
            set(curfig,'PaperUnits','points');
            pp = get(curfig,'PaperPosition');
            set(curfig,'PaperUnits',pu);
            set(curax,'Units','normalized');
            curap = get(curax,'Position');
            curap = pp.*curap;
        else
            set(curax,'Units','pixels');
            curap = get(curax,'Position');
        end
        set(curax,'Units',u);
        set(curax,'Position',axposoldunits);
        % adjust limits for log scale on axes
        curxyzlog = [strcmp(get(curax,'XScale'),'log'); ...
            strcmp(get(curax,'YScale'),'log'); ...
            strcmp(get(curax,'ZScale'),'log')];
        if (any(curxyzlog))
            ii = find([curxyzlog;curxyzlog]);
            if (any(axl(ii)<=0))
                error('ARROW does not support non-positive limits on log-scaled axes.');
            else
                axl(ii) = log10(axl(ii));
            end
        end
        % correct for 'reverse' direction on axes;
        curreverse = [strcmp(get(curax,'XDir'),'reverse'); ...
            strcmp(get(curax,'YDir'),'reverse'); ...
            strcmp(get(curax,'ZDir'),'reverse')];
        ii = find(curreverse);
        if (all(size(ii)))
            axl(ii,[1 2])=-axl(ii,[2 1]);
        end
        % correct for aspect ratio
        if (~isnan(ar(1)))
            if (curap(3) < ar(1)*curap(4))
                curap(2) = curap(2) + (curap(4)-curap(3)/ar(1))/2;
                curap(4) = curap(3)/ar(1);
            else
                curap(1) = curap(1) + (curap(3)-curap(4)*ar(1))/2;
                curap(3) = curap(4)*ar(1);
            end
        end
        % correct for 'equal'
        % may only want to do this for 2-D views, but seems right for 3-D also
        if (~isnan(ar(2)))
            if ((curap(3)/(axl(1,2)-axl(1,1)))/(curap(4)/(axl(2,2)-axl(2,1)))>ar(2))
                incr = curap(3)*(axl(2,2)-axl(2,1))/(curap(4)*ar(2)) - (axl(1,2)-axl(1,1));
                axl(1,:) = axl(1,:) + incr/2*[-1 1];
            else
                incr = ar(2)*(axl(1,2)-axl(1,1))*curap(4)/curap(3) - (axl(2,2)-axl(2,1));
                axl(2,:) = axl(2,:) + incr/2*[-1 1];
            end
        end
        % compute the range of 2-D values
        curT = get(curax,'Xform');
        lim = curT*[0 1 0 1 0 1 0 1;0 0 1 1 0 0 1 1;0 0 0 0 1 1 1 1;1 1 1 1 1 1 1 1];
        lim = lim(1:2,:)./([1;1]*lim(4,:));
        curlimmin = min(lim')';
        curlimrange = max(lim')' - curlimmin;
        curinvT = inv(curT);
        if (~oneax)
            curT = curT.';
            curinvT = curinvT.';
            curT = curT(:);
            curinvT = curinvT(:);
        end
        % check which arrows to which cur corresponds
        ii = find((ax==curax)&(page==curpage));
        oo = ones(1,length(ii));
        axr(:,ii)      = diff(axl')' * oo;
        axm(:,ii)      = axl(:,1)    * oo;
        axrev(:,ii)    = curreverse  * oo;
        ap(:,ii)       = curap(3:4)' * oo;
        xyzlog(:,ii)   = curxyzlog   * oo;
        limmin(:,ii)   = curlimmin   * oo;
        limrange(:,ii) = curlimrange * oo;
        if (oneax)
            T    = curT;
            invT = curinvT;
        else
            T(:,ii)    = curT    * oo;
            invT(:,ii) = curinvT * oo;
        end
        axnotdone(ii) = zeros(1,length(ii));
    end

    % correct for log scales
    curxyzlog = xyzlog.';
    ii = find(curxyzlog(:));
    if (all(size(ii)))
        start(   ii) = real(log10(start(   ii)));
        stop(    ii) = real(log10(stop(    ii)));
        if (all(imag(crossdir(ii))==0))
            crossdir(ii) = real(log10(crossdir(ii)));
        else
            jj = find(imag(crossdir(ii))==0);
            if (all(size(jj))), crossdir(jj) = real(log10(crossdir(jj))); end
            jj = find(imag(crossdir(ii))~=0);
            if (all(size(jj))), crossdir(jj) = real(log10(imag(crossdir(jj))))*sqrt(-1); end
        end
    end

    % correct for reverse directions
    ii = find(axrev.');
    if (all(size(ii)))
        start(   ii) = -start(   ii);
        stop(    ii) = -stop(    ii);
        crossdir(ii) = -crossdir(ii);
    end

    % take care of defaults, page was done above
    ii=find(isnan(start(:)       ));  if (all(size(ii))),  start(ii)       = axm(ii)+axr(ii)/2;                end
    ii=find(isnan(stop(:)        ));  if (all(size(ii))),  stop(ii)        = axm(ii)+axr(ii)/2;                end
    ii=find(isnan(crossdir(:)    ));  if (all(size(ii))),  crossdir(ii)    = zeros(length(ii),1);              end
    ii=find(isnan(len            ));  if (all(size(ii))),  len(ii)         = ones(length(ii),1)*deflen;        end
    ii=find(isnan(baseangle      ));  if (all(size(ii))),  baseangle(ii)   = ones(length(ii),1)*defbaseangle;  end
    ii=find(isnan(tipangle       ));  if (all(size(ii))),  tipangle(ii)    = ones(length(ii),1)*deftipangle;   end
    ii=find(isnan(wid            ));  if (all(size(ii))),  wid(ii)         = ones(length(ii),1)*defwid;        end
    ii=find(isnan(ends           ));  if (all(size(ii))),  ends(ii)        = ones(length(ii),1)*defends;       end
    ii=find(isnan(linewidth      ));  if (all(size(ii))),  linewidth(ii)   = ones(length(ii),1)*deflinewidth;  end
    ii=find(any(isnan(edgecolor')));  if (all(size(ii))),  edgecolor(ii,:) = ones(length(ii),1)*defedgecolor;  end
    ii=find(any(isnan(facecolor')));  if (all(size(ii))),  facecolor(ii,:) = ones(length(ii),1)*deffacecolor;  end
    ii=find(isnan(linestyle(:,1) ));  if (all(size(ii))),  linestyle(ii,:) = ones(length(ii),1)*deflinestyle;  end
    ii=find(isnan(linestyle(:,2) ));  if (all(size(ii))),  linestyle(ii,2) = ones(length(ii),1)*' ';           end; %just in case

    % transpose all values
    start     = start.';
    stop      = stop.';
    len       = len.';
    baseangle = baseangle.';
    tipangle  = tipangle.';
    wid       = wid.';
    page      = page.';
    crossdir  = crossdir.';
    ends      = ends.';
    linewidth = linewidth.';
    linestyle = linestyle.';
    facecolor = facecolor.';
    edgecolor = edgecolor.';
    fromline  = fromline.';
    ax        = ax.';
    oldlinewidth = oldlinewidth.';
    oldlinestyle = oldlinestyle.';
    oldedgecolor = oldedgecolor.';
    oldfacecolor = oldfacecolor.';

    % given x, a 3xN matrix of points in 3-space;
    % want to convert to X, the corresponding 4xN 2-space matrix
    %
    %   tmp1=[(x-axm)./axr; ones(1,size(x,1))];
    %   if (oneax), X=T*tmp1;
    %   else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T.*tmp1;
    %         tmp2=zeros(4,4*N); tmp2(:)=tmp1(:);
    %         X=zeros(4,N); X(:)=sum(tmp2)'; end
    %   X = X ./ (X(:,4)*ones(1,4));

    % for all points with start==stop, start=stop-(verysmallvalue)*(up-direction);
    ii = find(all(start==stop));
    if (all(size(ii)))
        % find an arrowdir vertical on screen and perpendicular to viewer
        %	transform to 2-D
        tmp1 = [(stop(:,ii)-axm(:,ii))./axr(:,ii);ones(1,length(ii))];
        if (oneax), twoD=T*tmp1;
        else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T(:,ii).*tmp1;
            tmp2=zeros(4,4*length(ii)); tmp2(:)=tmp1(:);
            twoD=zeros(4,length(ii)); twoD(:)=sum(tmp2)'; end
        twoD=twoD./(ones(4,1)*twoD(4,:));
        %	move the start point down just slightly
        tmp1 = twoD + [0;-1/1000;0;0]*(limrange(2,ii)./ap(2,ii));
        %	transform back to 3-D
        if (oneax), threeD=invT*tmp1;
        else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT(:,ii).*tmp1;
            tmp2=zeros(4,4*length(ii)); tmp2(:)=tmp1(:);
            threeD=zeros(4,length(ii)); threeD(:)=sum(tmp2)'; end
        start(:,ii) = (threeD(1:3,:)./(ones(3,1)*threeD(4,:))).*axr(:,ii)+axm(:,ii);
    end

    % compute along-arrow points
    %	transform Start points
    tmp1=[(start-axm)./axr;ones(1,narrows)];
    if (oneax), X0=T*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        X0=zeros(4,narrows); X0(:)=sum(tmp2)'; end
    X0=X0./(ones(4,1)*X0(4,:));
    %	transform Stop points
    tmp1=[(stop-axm)./axr;ones(1,narrows)];
    if (oneax), Xf=T*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        Xf=zeros(4,narrows); Xf(:)=sum(tmp2)'; end
    Xf=Xf./(ones(4,1)*Xf(4,:));
    %	compute pixel distance between points
    D = sqrt(sum(((Xf(1:2,:)-X0(1:2,:)).*(ap./limrange)).^2));
    %	compute and modify along-arrow distances
    len1 = len;
    len2 = len - (len.*tan(tipangle/180*pi)-wid/2).*tan((90-baseangle)/180*pi);
    slen0 = zeros(1,narrows);
    slen1 = len1 .* ((ends==2)|(ends==3));
    slen2 = len2 .* ((ends==2)|(ends==3));
    len0 = zeros(1,narrows);
    len1  = len1 .* ((ends==1)|(ends==3));
    len2  = len2 .* ((ends==1)|(ends==3));
    %	for no start arrowhead
    ii=find((ends==1)&(D<len2));
    if (all(size(ii)))
        slen0(ii) = D(ii)-len2(ii);
    end
    %	for no end arrowhead
    ii=find((ends==2)&(D<slen2));
    if (all(size(ii)))
        len0(ii) = D(ii)-slen2(ii);
    end
    len1  = len1  + len0;
    len2  = len2  + len0;
    slen1 = slen1 + slen0;
    slen2 = slen2 + slen0;
    %	compute stoppoints
    tmp1=X0.*(ones(4,1)*(len0./D))+Xf.*(ones(4,1)*(1-len0./D));
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end
    stoppoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
    %	compute tippoints
    tmp1=X0.*(ones(4,1)*(len1./D))+Xf.*(ones(4,1)*(1-len1./D));
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end
    tippoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
    %	compute basepoints
    tmp1=X0.*(ones(4,1)*(len2./D))+Xf.*(ones(4,1)*(1-len2./D));
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end
    basepoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
    %	compute startpoints
    tmp1=X0.*(ones(4,1)*(1-slen0./D))+Xf.*(ones(4,1)*(slen0./D));
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end
    startpoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
    %	compute stippoints
    tmp1=X0.*(ones(4,1)*(1-slen1./D))+Xf.*(ones(4,1)*(slen1./D));
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end
    stippoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
    %	compute sbasepoints
    tmp1=X0.*(ones(4,1)*(1-slen2./D))+Xf.*(ones(4,1)*(slen2./D));
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
        tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end
    sbasepoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;

    % compute cross-arrow directions for arrows with NormalDir specified
    if (any(imag(crossdir(:))~=0))
        ii = find(any(imag(crossdir)~=0));
        crossdir(:,ii) = cross((stop(:,ii)-start(:,ii))./axr(:,ii), ...
            imag(crossdir(:,ii))).*axr(:,ii);
    end

    % compute cross-arrow directions
    basecross  = crossdir + basepoint;
    tipcross   = crossdir + tippoint;
    sbasecross = crossdir + sbasepoint;
    stipcross  = crossdir + stippoint;
    ii = find(all(crossdir==0)|any(isnan(crossdir)));
    if (all(size(ii)))
        numii = length(ii);
        %	transform start points
        tmp1 = [basepoint(:,ii) tippoint(:,ii) sbasepoint(:,ii) stippoint(:,ii)];
        tmp1 = (tmp1-axm(:,[ii ii ii ii])) ./ axr(:,[ii ii ii ii]);
        tmp1 = [tmp1; ones(1,4*numii)];
        if (oneax), X0=T*tmp1;
        else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T(:,[ii ii ii ii]).*tmp1;
            tmp2=zeros(4,16*numii); tmp2(:)=tmp1(:);
            X0=zeros(4,4*numii); X0(:)=sum(tmp2)'; end
        X0=X0./(ones(4,1)*X0(4,:));
        %	transform stop points
        tmp1 = [(2*stop(:,ii)-start(:,ii)-axm(:,ii))./axr(:,ii);ones(1,numii)];
        tmp1 = [tmp1 tmp1 tmp1 tmp1];
        if (oneax), Xf=T*tmp1;
        else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T(:,[ii ii ii ii]).*tmp1;
            tmp2=zeros(4,16*numii); tmp2(:)=tmp1(:);
            Xf=zeros(4,4*numii); Xf(:)=sum(tmp2)'; end
        Xf=Xf./(ones(4,1)*Xf(4,:));
        %	compute perpendicular directions
        pixfact = ((limrange(1,ii)./limrange(2,ii)).*(ap(2,ii)./ap(1,ii))).^2;
        pixfact = [pixfact pixfact pixfact pixfact];
        pixfact = [pixfact;1./pixfact];
        [dummyval,jj] = max(abs(Xf(1:2,:)-X0(1:2,:)));
        jj1 = ((1:4)'*ones(1,length(jj))==ones(4,1)*jj);
        jj2 = ((1:4)'*ones(1,length(jj))==ones(4,1)*(3-jj));
        jj3 = jj1(1:2,:);
        Xp = X0;
        Xp(jj2) = X0(jj2) + ones(sum(jj2(:)),1);
        Xp(jj1) = X0(jj1) - (Xf(jj2)-X0(jj2))./(Xf(jj1)-X0(jj1)) .* pixfact(jj3);
        %	inverse transform the cross points
        if (oneax), Xp=invT*Xp;
        else, tmp1=[Xp;Xp;Xp;Xp]; tmp1=invT(:,[ii ii ii ii]).*tmp1;
            tmp2=zeros(4,16*numii); tmp2(:)=tmp1(:);
            Xp=zeros(4,4*numii); Xp(:)=sum(tmp2)'; end
        Xp=(Xp(1:3,:)./(ones(3,1)*Xp(4,:))).*axr(:,[ii ii ii ii])+axm(:,[ii ii ii ii]);
        basecross(:,ii)  = Xp(:,0*numii+(1:numii));
        tipcross(:,ii)   = Xp(:,1*numii+(1:numii));
        sbasecross(:,ii) = Xp(:,2*numii+(1:numii));
        stipcross(:,ii)  = Xp(:,3*numii+(1:numii));
    end

    % compute all points
    %	compute start points
    axm11 = [axm axm axm axm axm axm axm axm axm axm axm];
    axr11 = [axr axr axr axr axr axr axr axr axr axr axr];
    st = [stoppoint tippoint basepoint sbasepoint stippoint startpoint stippoint sbasepoint basepoint tippoint stoppoint];
    tmp1 = (st - axm11) ./ axr11;
    tmp1 = [tmp1; ones(1,size(tmp1,2))];
    if (oneax), X0=T*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=[T T T T T T T T T T T].*tmp1;
        tmp2=zeros(4,44*narrows); tmp2(:)=tmp1(:);
        X0=zeros(4,11*narrows); X0(:)=sum(tmp2)'; end
    X0=X0./(ones(4,1)*X0(4,:));
    %	compute stop points
    tmp1 = ([start tipcross basecross sbasecross stipcross stop stipcross sbasecross basecross tipcross start] ...
        - axm11) ./ axr11;
    tmp1 = [tmp1; ones(1,size(tmp1,2))];
    if (oneax), Xf=T*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=[T T T T T T T T T T T].*tmp1;
        tmp2=zeros(4,44*narrows); tmp2(:)=tmp1(:);
        Xf=zeros(4,11*narrows); Xf(:)=sum(tmp2)'; end
    Xf=Xf./(ones(4,1)*Xf(4,:));
    %	compute lengths
    len0  = len.*((ends==1)|(ends==3)).*tan(tipangle/180*pi);
    slen0 = len.*((ends==2)|(ends==3)).*tan(tipangle/180*pi);
    le = [zeros(1,narrows) len0 wid/2 wid/2 slen0 zeros(1,narrows) -slen0 -wid/2 -wid/2 -len0 zeros(1,narrows)];
    aprange = ap./limrange;
    aprange = [aprange aprange aprange aprange aprange aprange aprange aprange aprange aprange aprange];
    D = sqrt(sum(((Xf(1:2,:)-X0(1:2,:)).*aprange).^2));
    Dii=find(D==0); if (all(size(Dii))), D=D+(D==0); le(Dii)=zeros(1,length(Dii)); end; %should fix DivideByZero warnings
    tmp1 = X0.*(ones(4,1)*(1-le./D)) + Xf.*(ones(4,1)*(le./D));
    %	inverse transform
    if (oneax), tmp3=invT*tmp1;
    else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=[invT invT invT invT invT invT invT invT invT invT invT].*tmp1;
        tmp2=zeros(4,44*narrows); tmp2(:)=tmp1(:);
        tmp3=zeros(4,11*narrows); tmp3(:)=sum(tmp2)'; end
    pts = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)) .* axr11 + axm11;

    % correct for ones where the crossdir was specified
    ii = find(~(all(crossdir==0)|any(isnan(crossdir))));
    if (all(size(ii)))
        D1 = [pts(:,1*narrows+ii)-pts(:,9*narrows+ii) ...
            pts(:,2*narrows+ii)-pts(:,8*narrows+ii) ...
            pts(:,3*narrows+ii)-pts(:,7*narrows+ii) ...
            pts(:,4*narrows+ii)-pts(:,6*narrows+ii) ...
            pts(:,6*narrows+ii)-pts(:,4*narrows+ii) ...
            pts(:,7*narrows+ii)-pts(:,3*narrows+ii) ...
            pts(:,8*narrows+ii)-pts(:,2*narrows+ii) ...
            pts(:,9*narrows+ii)-pts(:,1*narrows+ii)]/2;
        ii = ii'*ones(1,8) + ones(length(ii),1)*[1:4 6:9]*narrows;
        ii = ii(:)';
        pts(:,ii) = st(:,ii) + D1;
    end

    % readjust for reverse directions
    tmp1=[axrev axrev axrev axrev axrev axrev axrev axrev axrev axrev axrev];
    ii = find(tmp1(:)); if (all(size(ii))), pts(ii)=-pts(ii); end

    % readjust for log scale on axes
    tmp1=[xyzlog xyzlog xyzlog xyzlog xyzlog xyzlog xyzlog xyzlog xyzlog xyzlog xyzlog];
    ii = find(tmp1(:)); if (all(size(ii))), pts(ii)=10.^pts(ii); end

    % compute the x,y,z coordinates of the patches;
    ii = narrows*(0:10)'*ones(1,narrows) + ones(11,1)*(1:narrows);
    ii = ii(:)';
    x = zeros(11,narrows);
    y = zeros(11,narrows);
    z = zeros(11,narrows);
    x(:) = pts(1,ii)';
    y(:) = pts(2,ii)';
    z(:) = pts(3,ii)';

    % do the output
    if (nargout<=1)
        %	% create or modify the patches
        p = (linestyle(1,:)=='-')&(linestyle(2,:)==' ');
        if (all(size(oldh)))
            H = oldh;
        else
            fromline = p;
            H = zeros(narrows,1);
        end
        %	% new patches
        ii = find(p&fromline);
        if (all(size(ii)))
            if (all(size(oldh))), delete(oldh(ii)); end
            H(ii) = patch(x(:,ii),y(:,ii),z(:,ii),zeros(11,length(ii)),'Tag',ArrowTag);
        end
        %	% new lines
        ii = find((~p)&(~fromline));
        if (all(size(ii)))
            if (all(size(oldh))), delete(oldh(ii)); end
            H(ii) = line(x(:,ii),y(:,ii),z(:,ii),'Tag',ArrowTag);
        end
        %	% additional properties
        for k=1:narrows
            s = 'set(H(k),''UserData'',ud(k,:)';
            if (p(k)~=fromline(k)), % modifying the data
                s = [s ',''XData'',x(:,k)'',''YData'',y(:,k)'',''ZData'',z(:,k)'''];
            end
            if ((~isnan(linewidth(k)))&(linewidth(k)~=oldlinewidth(k)))
                s=[s ',''LineWidth'',linewidth(k)'];
            end
            if (p(k)), % a patch
                if (any(edgecolor(:,k)~=oldedgecolor(:,k))|(fromline(k)))
                    if (edgecolor(1,k)==inf)
                        s=[s ',''EdgeColor'',''flat'''];
                    elseif (edgecolor(1,k)==-inf)
                        s=[s ',''EdgeColor'',''none'''];
                    else
                        s=[s ',''EdgeColor'',edgecolor(:,k)'''];
                    end
                end
                if (any(facecolor(:,k)~=oldfacecolor(:,k))|(fromline(k)))
                    if (facecolor(1,k)==inf)
                        s=[s ',''FaceColor'',''flat'''];
                    elseif (facecolor(1,k)==-inf)
                        s=[s ',''FaceColor'',''none'''];
                    else
                        s=[s ',''FaceColor'',facecolor(:,k)'''];
                    end
                end
            else, % a line
                s = [s ',''LineStyle'',''' deblank(char(linestyle(:,k)')) ''''];
                if (any(facecolor(:,k)~=oldfacecolor(:,k))|any(edgecolor(:,k)~=oldedgecolor(:,k))), % a line
                    if (isinf(edgecolor(1,k))&(facecolor(1,k)~=-inf))
                        s = [s ',''Color'',facecolor(:,k)'''];
                    else
                        s = [s ',''Color'',edgecolor(:,k)'''];
                    end
                end
            end
            eval([s extraprops ');']);
        end
        % set the output
        if (nargout>0), h=H; end
    else
        % don't create the patch, just return the data
        h=x;
        yy=y;
        zz=z;
    end


function varargout = dcm2metaimage( str, res, scale, crop, ext, file )
% DCM2METAIMAGE  Read a batch of DICOM files, collate them and save as a
% single MetaImage volume (one .mha and one .raw file)
%
% DCM2METAIMAGE(STR)
%
%   STR is a string with the path and output mha header file name. For
%   example,
%
%     STR='/home/john/data/study01/study01.mha'; % linux
%     STR='C:\data\study01\study01.mha';         % windows
%
%   It is assumed that the MetaImage file (.mha) will be created from the
%   DICOM files found in the target directory.
%
% DCM2METAIMAGE(STR, RES, SCALE, CROP, EXT, FILE)
%
%   RES is a 3-vector with the pixel size in the x-, y- and z-coordinates.
%   If RES is not provided, the function tries to read the values from the
%   DICOM header. If the resolution values cannot be read, by default
%   RES=[1 1 1].
%
%   SCALE is a factor to reduce the size of each frame (the number of
%   frames doesn't change). By default, SCALE=1.
%
%   CROP is a vector with values [xmin, xmax, ymin, ymax, zmin, zmax].
%   These values allow to crop the data volume. It follows the same
%   convention as Seg3D [1]. For example, if xmin=0, xmax=3, then voxels 1,
%   2 and 3 are selected.
%   Note that the output MetaImage file will have an offset header so that
%   the data retains its true coordinates.
%
%   EXT is a string with the file extension of the images. By default,
%   EXT='tif'.
%
%   FILE is the list of image files. By default, all the files in the
%   target directory are read, but FILE allows to change the system listing
%   order, select a subset of files, etc. This is useful, for example, when
%   the system listing order does not correspond to the order in which you
%   want to collate the slices. FILE is expected to have the same struct
%   format as the output of function DIR().
%
%   If you have filenames like
%
%    MISAS_DT_22_10_2009.MR.HEAD_GENERAL.6.10.2009.10.23.16.02.38.562500.9699412.DCM
%    MISAS_DT_22_10_2009.MR.HEAD_GENERAL.6.11.2009.10.23.16.02.38.562500.9699451.DCM
%    MISAS_DT_22_10_2009.MR.HEAD_GENERAL.6.1.2009.10.23.16.02.38.562500.9699056.DCM
%    MISAS_DT_22_10_2009.MR.HEAD_GENERAL.6.12.2009.10.23.16.02.38.562500.9699490.DCM
%
%   where the slice order is given by the field between the "6" and the
%   "2009", then you can use Gerardus function sortdirbynum() to order the
%   slices correctly. Example:
%
%   >> str1 = 'MISAS_DT_22_10_2009.MR.HEAD_GENERAL.6.*.DCM';
%   >> str2 = 'MISAS_DT_22_10_2009.MR.HEAD_GENERAL.6.mha';
%   >> file = sortdirbynum( str1, 5, '.' );
%   >> dcm2metaimage( str2, [], [], [], 'DCM', file );
%
% IM = DCM2METAIMAGE(...);
%
%   IM is the image volume.

% Author: Ramon Casero <rcasero@gmail.com>
% Copyright © 2009-2010 University of Oxford
% Version: 0.1.1
%
% University of Oxford means the Chancellor, Masters and Scholars of
% the University of Oxford, having an administrative office at
% Wellington Square, Oxford OX1 2JD, UK.
%
% This file is part of Gerardus.
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details. The offer of this
% program under the terms of the License is subject to the License
% being interpreted in accordance with English Law and subject to any
% action against the University of Oxford being under the jurisdiction
% of the English Courts.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% check arguments
error( nargchk( 1, 6, nargin, 'struct' ) );
error( nargoutchk( 0, 1, nargout, 'struct' ) );

% defaults
% if ( nargin < 2 || isempty( res ) )
%     res = [ 1, 1, 1 ];
% end
% "res" default set below
if ( nargin < 3 || isempty( scale ) )
    scale = 1;
end
if ( nargin < 4 || isempty( crop ) )
    crop = [];
end
if ( nargin < 5 || isempty( ext ) )
    ext = 'tif';
end
if ( nargin < 6 || isempty( file ) )
    file = [];
end

% check arguments
if ( scale <= 0 || scale > 1 )
    error( 'SCALE must be in (0, 1]' );
end

% get path to the data files
[ dirdata, name ] = fileparts( str );
if (isempty(dirdata))
    dirdata = '.';
end

% get list of image files
if isempty( file )
    file = dir( [ dirdata filesep '*.' ext ] );
end

% if resolution is not provided, try to read it from DICOM file
if ( nargin < 2 || isempty( res ) )
    % get DICOM header
    info = dicominfo( [ dirdata filesep file( 1 ).name ] );

    % if resolution is in the DICOM header, read it...
    if ( isfield( info, 'SliceThickness' ) && isfield( info, 'PixelSpacing' ) )
        res = [ info.PixelSpacing' info.SliceThickness ];
    else % ... if not, then fall back to default resolution
        res = [ 1, 1, 1 ];
    end
end

% adjust the image resolution to the scaling factor
res( 1:2 ) = res( 1:2 ) / scale;

% load first slice
frame = dicomread( [ dirdata filesep file( 1 ).name ] );

% remove alpha channel or other layers if present
frame = frame( :, :, 1 );

% init volume to load data
im = zeros( [ size( frame )*scale length( file ) ], class( frame ) );

% load slices of whole volume
im( :, :, 1 ) = imresize( frame, scale, 'bilinear' );
for I = 2:length( file )
    frame = dicomread( [ dirdata filesep file( I ).name ] );
    frame = frame( :, :, 1 );
    % resize frames
    im( :, :, I ) = imresize( frame, scale, 'bilinear' );
end

% crop volume (this is less efficient than initialising the volume as
% cropped, and then cropping each frame, but it makes the code simpler)
%
% The "+1" is necessary to follow the Seg3D convention
if ~isempty( crop )
    im = im( ...
        crop( 1 )+1:crop( 2 ), ...
        crop( 3 )+1:crop( 4 ), ...
        crop( 5 )+1:crop( 6 ) ...
        );
end

% compute offset in the coordinates of the cropped volume
if isempty( crop )
    offset = [ 0.0 0.0 0.0 ];
else
    offset = crop( [ 1 3 5 ] ) .* res;
end

% write mha file
writemetaimagefile([dirdata filesep name '.mha'], im, res, offset);

% avoid outputing the image volume unless the user has requested it
% explicitly
if ( nargout == 0 )
    varargout = [];
else
    varargout = { im };
end


% Sample object to test data persistence functions and user controls.

% Copyright 2008-2009 Levente Hunyadi
classdef SampleObject
    properties
        % A scalar of the standard MatLab type.
        RealDoubleScalar = pi;
        % A matrix of the standard MatLab type.
        RealDoubleMatrix = [1.5,2.5;3.5,4.5];
        RealSingleMatrix = single([1.5,2.5;3.5,4.5]);
        ComplexDoubleMatrix = [1.5,2.5i;3.5i,4.5];
        ComplexDoubleScalar = 1.5 + 2.5i;
        IntegerScalar = int32(32);
        IntegerMatrix = int32([1,2;3,4]);
        Logical = true;
        LogicalMatrix = [true,false;false,true];
        String = 'this is a sample string';
        CellArrayOfStrings = {'this','is','a','cell','array','of','strings'};
        Caption = 'SampleObject';
        % An unchangeable property.
        % Whenever the setter method of this property is used to modify the
        % value, an exception is thrown.
        Unchangeable = 'unchangeable';
        NestedObject = SampleNestedObject;
    end
    properties (Constant)
        Re = 6.378e6;  % Earth radius (m)
        RealDoubleConstantMatrix = [1.5,2.5;3.5,4.5];
    end
    properties (Dependent)
        DependentProperty;
        ReadOnlyDependentProperty;
    end
    properties (Hidden)
        HiddenProperty = 'hidden';
    end
    properties (Transient)
        TransientProperty = 'transient';
    end
    properties (Access = private)
        PrivateProperty = 'private';
    end
    properties (Access = protected)
        ProtectedProperty = 'protected';
    end
    methods
        function Method(self)
            disp(self.PrivateProperty);
        end

        function self = set.Caption(self, caption)
            caption = deblank(caption);
            fprintf('Caption has been changed from %s to %s.\n', self.Caption, caption);
            self.Caption = caption;
        end

        function value = get.DependentProperty(self)
            value = self.Caption;
        end

        function self = set.DependentProperty(self, value)
            self.Caption = value;
        end

        function value = get.ReadOnlyDependentProperty(self)
            value = [ self.Caption ' - ' self.Caption ];
        end

        function self = set.Unchangeable(self, value) %#ok<INUSL>
            error('SampleObject:InvalidOperation', 'Cannot change property value to %s', value);
        end
    end
end


function calpak_test51 ( )

%*****************************************************************************80
%
%% CALPAK_TEST51 tests WEEKDAY_TO_NAME_REPUBLICAN.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    10 April 2013
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'CALPAK_TEST51\n' );
  fprintf ( 1, '  For the Republican calendar,\n' );
  fprintf ( 1, '  WEEKDAY_TO_NAME_REPUBLICAN names the days of the week.\n' );
  fprintf ( 1, '\n' );

  for i = 1 : 10
    s = weekday_to_name_republican ( i );
    fprintf ( 1, '  %2d  %s\n', i, s );
  end

  return
end


function out = get(f, prop)
%GET   GET method for the DELTAFUN class.
%   P = GET(F, PROP) returns the property P specified in the string PROP from
%   the DELTAFUN object F.  The string PROP may be the name of a DELTAFUN
%   property (see the DELTAFUN classdef file for a list) or any of the
%   following strings:
%       'DELTAS'       - Return the locations and magnitude of delta functions
%                        as a single matrix with the first row corresponding to
%                        the locations of the delta functions, and the
%                        magnitudes appended below the first row.
%   If PROP is a string other than those specified above, GET(F, PROP) returns
%   the result of GET(F.FUNPART, PROP).
%
% See also DELTAFUN.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

switch prop
    case fieldnames(f)
        out = f.(prop);
    case 'deltas'
        out = [f.deltaLoc ; f.deltaMag];
    otherwise
        out = get(f.funPart, prop);
end

end


function [sFile, ChannelMat] = in_fopen_tdt(DataFile)
% IN_FOPEN_TDT Open recordings saved in the Tucker Davis Technologies format.
%
% The importer needs the folder that the files are in. K Nasiotis selected one type
% of files to work as the "raw file" - (.Tbk)

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Author: Konstantinos Nasiotis 2019-2020

% Not available in the compiled version
if bst_iscompiled()
    error('Reading TDT files is not available in the compiled version of Brainstorm.');
end
% Install/load TDT-SDK library
[isInstalled, errMsg] = bst_plugin('Install', 'tdt-sdk');
if ~isInstalled
    error(errMsg);
end

 %% ===== GET FILES =====
% Get base dataset folder
[DataFolder, FileName] = bst_fileparts(DataFile);

hdr.BaseFolder = DataFolder;

 %% ===== FILE COMMENT =====
% Comment: BaseFolder
Comment = DataFolder;

%% ===== READ DATA HEADERS =====
bst_progress('text', 'TDT: Reading headers...');

% Load one second segment to see what type of signals exist in this dataset
% Use as general sampling rate the rate of the HIGHEST sampled signal
% The signals that have a lower sampling rate will be interpolated to match
% the general sampling rate

headers = TDTbin2mat(DataFolder, 'HEADERS', 1);

data = TDTbin2mat(DataFolder, 'T1', 0, 'T2', 1); % 1 second segment
all_streams = fieldnames(data.streams);

%% Get rid of streams that are pNe and SynC

all_streams = all_streams(~ismember(all_streams,{'pNe1','pNe2','pNe3','pNe4','SynC'}));

% The sampling rates present are the weirdest numbers I have ever seen:
% e.g. Fs = 3051.7578125 Hz !!!
% Those numbers create problems when loading segments of data.
% The segment loading is in TimeBounds, not SampleBounds that makes it even
% worse with those sampling rates
stream_info = struct;

LFP_label_exists = 0;

ii = 1;
for iStream = 1:length(all_streams)
    stream_info(iStream).label          = all_streams{iStream};
    stream_info(iStream).fs             = data.streams.(all_streams{iStream}).fs;
    stream_info(iStream).total_channels = size(data.streams.(all_streams{iStream}).data,1);
    stream_info(iStream).channelIndices = ii:ii+size(data.streams.(all_streams{iStream}).data,1)-1;

    ii = ii + size(data.streams.(all_streams{iStream}).data,1);

    % Brainstorm needs a single sampling rate. I assign the one on the LFPs

    theLFPlabel = 'LFP';
    if strfind(all_streams{iStream},theLFPlabel)
        data_new = TDTbin2mat(DataFolder, 'STORE', all_streams{iStream},'T1', 0, 'T2', 1); % 1 second segment

        general_sampling_rate = data_new.streams.(all_streams{iStream}).fs;
        LFP_label_exists = 1;
    end
    popup_labels{iStream} = [all_streams{iStream} ' - ' num2str(stream_info(iStream).fs) ' Hz'];
end

if ~LFP_label_exists
    [indx,tf] = listdlg('PromptString',{'Select the label of the electrophysiological signal that is present in this dataset',...
    'If more streams are present, they will be resampled to match the Fs of this stream.',''},...
    'SelectionMode','single','ListString', popup_labels);

    data_new = TDTbin2mat(DataFolder, 'STORE', all_streams{indx},'T1', 0, 'T2', 1); % 1 second segment

    general_sampling_rate = data_new.streams.(all_streams{indx}).fs;
    LFP_label_exists = 1;

    if isempty(data_new.streams)
        error('The selected stream is empty');
    end

    if isempty(indx)
        error('No stream was selected');
    end
end

nChannels = sum([stream_info.total_channels]);

 %% ===== CREATE BRAINSTORM SFILE STRUCTURE =====
% Initialize returned file structure
sFile = db_template('sfile');

% Add information read from header
sFile.prop.sfreq   =  general_sampling_rate;
sFile.byteorder    = 'l';
sFile.filename     = DataFolder;
sFile.format       = 'EEG-TDT';
sFile.device       = 'Tucker Davis Technologies';
sFile.header.tdt   = headers;
sFile.comment      = FileName;
sFile.prop.times   = [0, headers.stopTime-headers.startTime];
sFile.prop.nAvg    = 1;
% No info on bad channels
sFile.channelflag  = ones(nChannels, 1);

sFile.header.stream_info = stream_info;

%% ===== CREATE EMPTY CHANNEL FILE =====
ChannelMat = db_template('channelmat');
ChannelMat.Comment = 'TDT channels';
ChannelMat.Channel = repmat(db_template('channeldesc'), [1, nChannels]);

ii = 0;
for iStream = 1:length(all_streams)
     for iChannel = 1:stream_info(iStream).total_channels
         ii = ii+1;
         if ~(stream_info(iStream).total_channels==1)
            ChannelMat.Channel(ii).Name = [all_streams{iStream} '_' num2str(iChannel)];
         else
             ChannelMat.Channel(ii).Name= [all_streams{iStream}];
         end
         ChannelMat.Channel(ii).Loc     = [0;0;0];

         ChannelMat.Channel(ii).Group   = all_streams{iStream};

         if strfind(stream_info(iStream).label, theLFPlabel)
            ChannelMat.Channel(ii).Type = 'EEG'; % Not all are EEGs - NOT SURE WHAT TO PUT HERE - AS A STARTING POINT, I PUT WHATEVER IS ONLY ONE CHANNEL SET IT AS Misc
         else
            ChannelMat.Channel(ii).Type = all_streams{iStream};
         end
         ChannelMat.Channel(ii).Orient  = [];
         ChannelMat.Channel(ii).Weight  = 1;
         ChannelMat.Channel(ii).Comment = [];
     end
end

%% Check for acquisition events

bst_progress('text', 'TDT: Collecting acquisition events...');

disp('Getting Acquisition System events')
NO_data = TDTbin2mat(DataFolder, 'TYPE', 2); % Just load epocs / events

are_there_events = ~isempty(NO_data.epocs);

if are_there_events

    all_event_Labels = fieldnames(NO_data.epocs);

    iindex = 0;

    for iEvent = 1:length(all_event_Labels)

        if sum(ismember({'Tick','Swep','Swe+'},NO_data.epocs.(all_event_Labels{iEvent}).name))~=0
            iindex = iindex + 1;

            events(iindex).label      = NO_data.epocs.(all_event_Labels{iEvent}).name;
            events(iindex).color      = rand(1,3);
            events(iindex).epochs     = ones(1,length(NO_data.epocs.(all_event_Labels{iEvent}).onset))  ;
            events(iindex).times      = NO_data.epocs.(all_event_Labels{iEvent}).onset';
            events(iindex).reactTimes = [];
            events(iindex).select     = 1;
            events(iindex).channels   = [];
            events(iindex).notes      = [];
        else
            conditions_in_event = unique(NO_data.epocs.(all_event_Labels{iEvent}).data);

            for iCondition = 1:length(conditions_in_event)

                selected_Events_for_condition = find(NO_data.epocs.(all_event_Labels{iEvent}).data == conditions_in_event(iCondition));

                iindex = iindex+1;

                if strcmp(all_event_Labels{iEvent}, 'Note')
                    events(iindex).label  = NO_data.epocs.Note.notes{iCondition};
                else
                    events(iindex).label  = [NO_data.epocs.(all_event_Labels{iEvent}).name num2str(conditions_in_event(iCondition))];
                end
                events(iindex).color      = rand(1,3);
                events(iindex).epochs     = ones(1,length(selected_Events_for_condition))  ;
                events(iindex).times      = NO_data.epocs.(all_event_Labels{iEvent}).onset(selected_Events_for_condition)';
                events(iindex).reactTimes = [];
                events(iindex).select     = 1;
                events(iindex).channels   = [];
                events(iindex).notes      = [];
            end
        end
    end
end

%% Check for spike events

check_for_spikes = 1;

if check_for_spikes
    bst_progress('text', 'TDT: Collecting spiking events...');
    NO_data = TDTbin2mat(DataFolder, 'TYPE', 3); % Just load spikes
    are_there_spikes = ~isempty(NO_data.snips);
else
    are_there_spikes = 0;
end

%%%%%%%%
disp('***************************************************')
disp('CHECK THE SPIKES. THEY ARE ONLY ASSIGNED ON RIG TWO')
disp('***************************************************')
%%%%%%%%

if are_there_spikes

    if  ~exist ('events','var')
        events = struct;
        last_event_index = 0;
    else
        last_event_index = length(events);
    end

    all_spike_event_Labels = fieldnames(NO_data.snips);

    for iSpikeDetectedField = 1:length(all_spike_event_Labels)
%         channels_are_EEG = find(strcmp({ChannelMat.Channel.Type}, 'EEG'));

        try
            channels_are_EEG_on_selected_RIG = find(strcmp({ChannelMat.Channel.Type}, 'EEG') & strcmp({ChannelMat.Channel.Group}, ['LFP' num2str(iSpikeDetectedField)]));
        catch
            error('There is an assumption here that LFP1 corresponds to eNe1, LFP2 to eNe2 and so on. If that''s not the case on your system please contact the forum')
        end

        for iChannel = 1:length(channels_are_EEG_on_selected_RIG)

            NeuronIDs = unique(NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).sortcode(find(NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).chan == iChannel)));

            if length(NeuronIDs)>1 && sum(ismember(NeuronIDs,0))~=0
                warning('There are Sorted AND Unsorted Spikes in this Dataset - Probably the selection for Online sorting was made mid-Recording - THE UNSORTED SPIKES WILL BE IGNORED')
                NeuronIDs = NeuronIDs(~ismember(NeuronIDs,[0,31]));
            else
                NeuronIDs = NeuronIDs(~ismember(NeuronIDs, 31));
            end

            for iNeuron = 1:length(NeuronIDs)

                if NeuronIDs(iNeuron) ~= 31 && (length(NeuronIDs) > 1 && NeuronIDs(iNeuron) ~= 0) % MARYSE MENTIONED THAT .DATA = 31 INDICATES ARTIFACTS - NOISE

                    last_event_index = last_event_index + 1;

                    if length(NeuronIDs) == 1
                        SpikesOfThatNeuronOnChannel_Indices = find(NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).chan == iChannel & NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).sortcode == 0); % Unsorted
                        events(last_event_index).label = ['Spikes Channel ' ChannelMat.Channel(channels_are_EEG_on_selected_RIG(iChannel)).Name];
                    else
                        SpikesOfThatNeuronOnChannel_Indices = find(NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).chan == iChannel & NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).sortcode == NeuronIDs(iNeuron)); % Sorted
                        events(last_event_index).label = ['Spikes Channel ' ChannelMat.Channel(channels_are_EEG_on_selected_RIG(iChannel)).Name ' |' num2str(NeuronIDs(iNeuron)) '|'];
                    end

                    events(last_event_index).color      = rand(1,3);
                    events(last_event_index).epochs     = ones(1,length(SpikesOfThatNeuronOnChannel_Indices));
                    events(last_event_index).times      = NO_data.snips.(all_spike_event_Labels{iSpikeDetectedField}).ts(SpikesOfThatNeuronOnChannel_Indices)';
                    events(last_event_index).reactTimes = [];
                    events(last_event_index).select     = 1;
                    events(last_event_index).channels   = repmat({{ChannelMat.Channel(channels_are_EEG_on_selected_RIG(iChannel)).Name}}, 1, size(events(last_event_index).times, 2));
                    events(last_event_index).notes      = [];
                end
            end
        end
    end
end

% Import this list
sFile = import_events(sFile, [], events);

end


function util_playMov(h, w, options, Y1, Y2,sz)
% UTIL_PLAYMOV Play a video file which is stored by rows in a 2-D matrix.
% DESC util_playMov(h, w, options, Y1, Y2,sz)
% Utility to play video files (a sequence of frames) where each row corresponds
% to one frame, serialized by columns. Each frame must then be reshaped before plotted
% and for this the function must receice its actual dimensions as an argument.
% Sanity warning: This function has been augmented in many steps and is quite messy!!!
% ARG h: the true height of the frame
% ARG w: the true width of the frame
% ARG options: further options for the function
% ARG: Y1 the matrix which keeps the video by rows
% ARG: As Y2, in case the user wants to see two videos side by side
% (as long as both have the same dimensions)
% ARG: sz: The presented frame's dimensions (cf. the true dimensions) is
% divided by this number if the argument is present.
%
% COPYRIGHT: Andreas C. Damianou, 2011
%
% SEEALSO : preprocessVideo.m, saveMovieScript.m

% VARGPLVM

if ~nargin
    fprintf(1,'# Usage: \nplayMov(h,w,[p <p_diff>],Y1,<Y2>,<figSizeFactor>)\n');
    return
end

% The following is only used if options.saveImages exists and is 1.
if ~isfield(options,'imFormat')
    imFormat = 'jpg';
else
    imFormat = options.imFormat;
end

scrsz = get(0,'ScreenSize');
fntSz=18;
 try
    if ~isstruct(options)
        p = options; % pause after each frame
    else
        if ~isfield(options,'p')
            options.p = []; % pause after each frame ([] means that next frame is displayed after pressing any key)
        end
        p = options.p;
        % indices: a vector v in {0,1} with size equal to the number of frames,
        % for which v(i)==1 means it corresponds to a training
        % frame, otherwise to a test or generated frame.
        if isfield(options, 'indices')
            indices = options.indices;
        end

        % Save movie as an avi file, also given some more options
        if isfield(options, 'saveMovie') && options.saveMovie
            saveMovie=1;
            if ~isfield(options,'movieName') || ~isfield(options,'fps')
                error('You need to provide the movieName and the fps in the options structure\n');
            end
            aviobj = avifile(options.movieName, 'fps', options.fps); % e.g: 4
            if isfield(options,'quality')
                aviobj.Quality = options.quality;
            end
            if isfield(options,'compression') && ~isempty(options.compression) % e.g. 'Cinepak'
                aviobj.Compression = options.compression;
            end
        end

        % sz will cause each frame's size to be reduced by that number.
        if isfield(options, 'sz')
             scrsz(3) = scrsz(3)/options.sz;
             scrsz(4) = scrsz(4)/options.sz;
             fntSz = fntSz/options.sz;
        end

        % Like above (sz) but you can re-size the frame with different
        % scales for the horizontal and vertical axis.
         if isfield(options, 'sz1') && isfield(options, 'sz2')
             scrsz(3) = options.sz1;
             scrsz(4) = options.sz2;
        end
    end
    if exist('sz') && sz
        scrsz(3) = scrsz(3)/sz;
        scrsz(4) = scrsz(4)/sz;
    end

    figure('Position',[scrsz(3)/4.86 scrsz(4)/6.666 1.2*scrsz(3)/1.6457 0.6*scrsz(4)/1.4682])

    % if sz is zero pause to allow user to fix manually the size
    if exist('sz') && ~sz
        pause
    end

    % Oversampling
    if nargin > 4 && (length(p)==2)
        % if p is 2-D then the first element says how much to pause for the
        % second matrix and the second elem how many frames of the second to play in
        % between from the first matrix
        k=1;
        try
            for i=1:size(Y2,1)
                subplot(1,2,1);
                fr=reshape(Y2(i,:),h,w);
                imagesc(fr);
                colormap('gray');
                title(['frame' num2str(i)])
                text(scrsz(4)/6.666,0,['frame' num2str(i)],'HorizontalAlignment','center','VerticalAlignment','top','BackgroundColor',[.7 .9 .7], 'FontSize',fntSz);
                for j=1:p(2)
                    subplot(1,2,2);
                    fr=reshape(Y1(k,:),h,w);
                    k = k+1;
                    imagesc(fr);
                    colormap('gray');
                    drawnow;
                    pause(p(1))
                end
                if isfield(options, 'saveMovie') && options.saveMovie
                    frame = getframe ( gca );
                    aviobj = addframe ( aviobj, frame );
                end
             if isfield(options, 'saveImages')
                 frame = getframe ( gca );
                   imwrite (frame.cdata,[options.saveImages num2str(i) '.' imFormat]);
                end
            end
        catch e
            e.message
        end
    elseif nargin >4 && (length(p) < 2) % Two movies
        for i=1:size(Y1,1)
            subplot(1,2,1);
            fr=reshape(Y1(i,:),h,w);
            imagesc(fr);
            colormap('gray');
            subplot(1,2,2);
            fr=reshape(Y2(i,:),h,w);
            imagesc(fr);
            colormap('gray');
            title(['frame' num2str(i)])
           % text(scrsz(4)/6.666,0,['frame' num2str(i)],'HorizontalAlignment','center','VerticalAlignment','top','BackgroundColor',[.7 .9 .7], 'FontSize',fntSz);
            if isempty(p)
                pause
            else
                pause(p);
            end
            if isfield(options, 'saveMovie') && options.saveMovie
                frame = getframe ( gca );
                aviobj = addframe ( aviobj, frame );
            end
             if isfield(options, 'saveImages')
                 frame = getframe ( gca );
                   imwrite (frame.cdata,[options.saveImages num2str(i) '.' imFormat]);
                end
        end
    elseif nargin <=4 % One movie
        for i=1:size(Y1,1)
            fr=reshape(Y1(i,:),h,w);
            if isfield(options, 'addLogo')
              %  possible arguments for the colormap: 64, 256, etc.
               % h1=imshow(grs2rgb(uint8(fr), gray(64)),'Border','tight');
                 h1=imshow(grs2rgb(uint8(fr), colormap('gray')),'Border','tight'); % Default: gray(64)
            else
                 imagesc(fr);
                 colormap('gray');
            end
            drawnow;
            if exist('indices')
                if indices(i)
                    title(['Frame: ' num2str(i) ' (Training)'], 'Color','b','FontSize',fntSz)
                    text(scrsz(4)/6.666,0,['frame' num2str(i) ' (Training)'],'VerticalAlignment','top','BackgroundColor',[.7 .9 .7], 'FontSize',fntSz);
                else
                    title(['Frame: ' num2str(i) ' (Generated)'], 'Color','r','FontSize',fntSz)
                    text(scrsz(4)/6.666,0,['frame' num2str(i) ' (Generated)'],'VerticalAlignment','top','BackgroundColor',[1 0 0], 'FontSize',fntSz);
                end
            else
                title(['frame' num2str(i)])
               % text(scrsz(4)/6.666,0,['frame' num2str(i)],'HorizontalAlignment','center','VerticalAlignment','top','BackgroundColor',[.7 .9 .7], 'FontSize',fntSz);
            end
            if isfield(options,'addLogo')
               % The following only for png and export_fig
               % set(gcf, 'color', 'none'); % Set the figure background to transparent
               % set(gca, 'color', 'none'); % Set the axes background to transparent
                hold on; h2=imshow(options.addLogo,'Border','tight'); axis off;
                if isfield(options,'logoAlpha')
                    set(h2,'AlphaData',options.logoAlpha);
                end
                hold off;
            end

            if isempty(p)
                pause
            else
                pause(p);
            end
            if isfield(options, 'saveMovie') && options.saveMovie
                frame = getframe ( gca );
                aviobj = addframe ( aviobj, frame );
            end
            if isfield(options, 'saveImages')
                frame = getframe ( gca );
                imageNo = num2str(i);
                % Pad the image number with leading zeros so that all
                % images have the same length in their filename (useful for
                % sorting frames).
                lengthAll = length(num2str(size(Y1,1)));
                padding = lengthAll - length(imageNo);
                imageNo = [repmat(['0'],1,padding) imageNo];
             %   if isfield(options, 'addLogo')
                    if ~isfield(options, 'saveImagesQuality')
                        options.saveImagesQuality = 75;
                    end
                    if ~isfield(options, 'saveImagesMagn')
                        options.saveImagesMagn =1;
                    end
                    if strcmp(imFormat,'manual')
                        % pause and wait for the user to save it manually
                        fprintf('User saves: %s \n', [options.saveImages imageNo '.' imFormat]);
                        pause
                    else
                         % Use export_fig which crops the borders from each
                        % frame.
                        % if ~strcmp(imFormat, 'eps')
                            export_fig([options.saveImages imageNo], ['-' imFormat], ['-q' num2str(options.saveImagesQuality)], ...
                                ['-m' num2str(options.saveImagesMagn)]);
                        %else
                        %    print('-depsc', [options.saveImages imageNo '.' imFormat]);
                        %    system(['epstopdf ' options.saveImages imageNo '.' imFormat]);
                        %end
                    end
              %  else
               %    imwrite (frame.cdata,[options.saveImages imageNo '.' imFormat]);
                %end
            end
        end
    else
        fprintf(1,'playMov(h,w,p,Y1,Y2)\n')
    end

 catch e
     e.getReport
     if isfield(options, 'saveMovie') && options.saveMovie
         aviobj = close (aviobj);
     end
 end

if isfield(options, 'saveMovie') && options.saveMovie
    aviobj = close ( aviobj );
end


function score = visualize(board,goal,solutionVector)
solutionVector = validateSolutionVector(solutionVector, board);
boardToScore  = runSolutionVector(solutionVector, board, goal, true);
score = sum(boardToScore(:)) + sum(solutionVector);


function l = exp_length(x)
% Get the number of elements (parameters) in any expression.
% Length = exp_length(Expression)
%
% In:
%   Expression  : any expression
%
% Out:
%   Length      : the expression's length; highest allowed index allowed for exp_part.
%
% See also:
%   exp_head, exp_part, exp_parts
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-04-26

if isfield(x,{'head','parts'})
    % struct with fields .head & .parts
    l = length(x.parts);
elseif isfield(x,'tracking') && isfield(x.tracking,'expression')
    % impure expressions viewed as expressions
    l = exp_length(x.tracking.expression);
else
    % most MATLAB data structures are not further broken down when reflected into the expression
    % system
    l = 1;
end


function [ graph ] = bidirectional_graph()

ends = neighbour_joint_list();
starts = 1:length(ends);

graph = [starts, ends; ...
         ends,   starts];
graph = graph';
end


function q = vertcat(varargin)
% implements [q1;q2;q3..]

q = cat(1,varargin{:});


function disp(f)
%DISP  Display a BALLFUNV to the command line.

% Copyright 2019 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

loose = strcmp(get(0,'FormatSpacing'),'loose');

% Compact version:
if ( isempty(f) )
    fprintf('empty ballfunv\n\n')
    return
end

disp('   ballfunv object containing')
if ( loose )
    fprintf('\n');
end

F = f.comp;
for j = 1:3
    disp(F{j});
end

end


function start_num = p02_start_num ( )

%*****************************************************************************80
%
%% P02_START_NUM returns the number of starting points for problem 2.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 May 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer START_NUM, the number of starting points.
%
  start_num = 4;

  return
end


function msm_to_mm_array_integer ( output_filename, a, symmetry )

%*****************************************************************************80
%
%% MSM_TO_MM_ARRAY_INTEGER writes a "matrix array integer" Matrix Market file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 November 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the name of the file to which the information
%    should be written.
%
%    Input, sparse matrix A, the NROW by NCOL matrix, stored in MATLAB sparse
%    matrix format, which is to be written to the file.
%
%    Input, string SYMMETRY, describes the symmetry type, as 'GENERAL',
%    'SYMMETRIC', 'SKEW-SYMMETRIC' or 'HERMITIAN'.
%
  if ( s_eqi ( symmetry, 'GENERAL' ) )
    msm_to_mm_array_integer_general ( output_filename, a );
  elseif ( s_eqi ( symmetry, 'SYMMETRIC' ) )
    msm_to_mm_array_integer_symmetric ( output_filename, a );
  elseif ( s_eqi ( symmetry, 'SKEW-SYMMETRIC' ) )
    msm_to_mm_array_integer_skewsymmetric ( output_filename, a );
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MSM_TO_MM_ARRAY_INTEGER - Fatal error!\n' );
    fprintf ( 1, '  Illegal symmetry option.\n' );
    error ( 'MSM_TO_MM_ARRAY_INTEGER - Fatal error!' );
  end

  return
end