annotate aim-mat/tools/arrow.m @ 4:537f939baef0 tip

various bug fixes and changed copyright message
author Stefan Bleeck <bleeck@gmail.com>
date Tue, 16 Aug 2011 14:37:17 +0100
parents 74dedb26614d
children
rev   line source
tomwalters@0 1 function [h,yy,zz] = arrow(varargin)
tomwalters@0 2 % ARROW Draw a line with an arrowhead.
tomwalters@0 3 %
tomwalters@0 4 % ARROW(Start,Stop) draws a line with an arrow from Start to Stop (points
tomwalters@0 5 % should be vectors of length 2 or 3, or matrices with 2 or 3
tomwalters@0 6 % columns), and returns the graphics handle of the arrow(s).
tomwalters@0 7 %
tomwalters@0 8 % ARROW uses the mouse (click-drag) to create an arrow.
tomwalters@0 9 %
tomwalters@0 10 % ARROW DEMO & ARROW DEMO2 show 3-D & 2-D demos of the capabilities of ARROW.
tomwalters@0 11 %
tomwalters@0 12 % ARROW may be called with a normal argument list or a property-based list.
tomwalters@0 13 % ARROW(Start,Stop,Length,BaseAngle,TipAngle,Width,Page,CrossDir) is
tomwalters@0 14 % the full normal argument list, where all but the Start and Stop
tomwalters@0 15 % points are optional. If you need to specify a later argument (e.g.,
tomwalters@0 16 % Page) but want default values of earlier ones (e.g., TipAngle),
tomwalters@0 17 % pass an empty matrix for the earlier ones (e.g., TipAngle=[]).
tomwalters@0 18 %
tomwalters@0 19 % ARROW('Property1',PropVal1,'Property2',PropVal2,...) creates arrows with the
tomwalters@0 20 % given properties, using default values for any unspecified or given as
tomwalters@0 21 % 'default' or NaN. Some properties used for line and patch objects are
tomwalters@0 22 % used in a modified fashion, others are passed directly to LINE, PATCH,
tomwalters@0 23 % or SET. For a detailed properties explanation, call ARROW PROPERTIES.
tomwalters@0 24 %
tomwalters@0 25 % Start The starting points. B
tomwalters@0 26 % Stop The end points. /|\ ^
tomwalters@0 27 % Length Length of the arrowhead in pixels. /|||\ |
tomwalters@0 28 % BaseAngle Base angle in degrees (ADE). //|||\\ L|
tomwalters@0 29 % TipAngle Tip angle in degrees (ABC). ///|||\\\ e|
tomwalters@0 30 % Width Width of the base in pixels. ////|||\\\\ n|
tomwalters@0 31 % Page Use hardcopy proportions. /////|D|\\\\\ g|
tomwalters@0 32 % CrossDir Vector || to arrowhead plane. //// ||| \\\\ t|
tomwalters@0 33 % NormalDir Vector out of arrowhead plane. /// ||| \\\ h|
tomwalters@0 34 % Ends Which end has an arrowhead. //<----->|| \\ |
tomwalters@0 35 % ObjectHandles Vector of handles to update. / base ||| \ V
tomwalters@0 36 % E angle||<-------->C
tomwalters@0 37 % ARROW(H,'Prop1',PropVal1,...), where H is a |||tipangle
tomwalters@0 38 % vector of handles to previously-created arrows |||
tomwalters@0 39 % and/or line objects, will update the previously- |||
tomwalters@0 40 % created arrows according to the current view -->|A|<-- width
tomwalters@0 41 % and any specified properties, and will convert
tomwalters@0 42 % two-point line objects to corresponding arrows. ARROW(H) will update
tomwalters@0 43 % the arrows if the current view has changed. Root, figure, or axes
tomwalters@0 44 % handles included in H are replaced by all descendant Arrow objects.
tomwalters@0 45 %
tomwalters@0 46 % A property list can follow any specified normal argument list, e.g.,
tomwalters@0 47 % ARROW([1 2 3],[0 0 0],36,'BaseAngle',60) creates an arrow from (1,2,3) to
tomwalters@0 48 % the origin, with an arrowhead of length 36 pixels and 60-degree base angle.
tomwalters@0 49 %
tomwalters@0 50 % The basic arguments or properties can generally be vectorized to create
tomwalters@0 51 % multiple arrows with the same call. This is done by passing a property
tomwalters@0 52 % with one row per arrow, or, if all arrows are to have the same property
tomwalters@0 53 % value, just one row may be specified.
tomwalters@0 54 %
tomwalters@0 55 % You may want to execute AXIS(AXIS) before calling ARROW so it doesn't change
tomwalters@0 56 % the axes on you; ARROW determines the sizes of arrow components BEFORE the
tomwalters@0 57 % arrow is plotted, so if ARROW changes axis limits, arrows may be malformed.
tomwalters@0 58 %
tomwalters@0 59 % This version of ARROW uses features of MATLAB 5 and is incompatible with
tomwalters@0 60 % earlier MATLAB versions (ARROW for MATLAB 4.2c is available separately);
tomwalters@0 61 % some problems with perspective plots still exist.
tomwalters@0 62 % Copyright (c)1995-2002, Dr. Erik A. Johnson <JohnsonE@usc.edu>, 11/15/02
tomwalters@0 63 % Revision history:
tomwalters@0 64 % 11/15/02 EAJ Accomodate how MATLAB 6.5 handles NaN and logicals
tomwalters@0 65 % 7/28/02 EAJ Tried (but failed) work-around for MATLAB 6.x / OpenGL bug
tomwalters@0 66 % if zero 'Width' or not double-ended
tomwalters@0 67 % 11/10/99 EAJ Add logical() to eliminate zero index problem in MATLAB 5.3.
tomwalters@0 68 % 11/10/99 EAJ Corrected warning if axis limits changed on multiple axes.
tomwalters@0 69 % 11/10/99 EAJ Update e-mail address.
tomwalters@0 70 % 2/10/99 EAJ Some documentation updating.
tomwalters@0 71 % 2/24/98 EAJ Fixed bug if Start~=Stop but both colinear with viewpoint.
tomwalters@0 72 % 8/14/97 EAJ Added workaround for MATLAB 5.1 scalar logical transpose bug.
tomwalters@0 73 % 7/21/97 EAJ Fixed a few misc bugs.
tomwalters@0 74 % 7/14/97 EAJ Make arrow([],'Prop',...) do nothing (no old handles)
tomwalters@0 75 % 6/23/97 EAJ MATLAB 5 compatible version, release.
tomwalters@0 76 % 5/27/97 EAJ Added Line Arrows back in. Corrected a few bugs.
tomwalters@0 77 % 5/26/97 EAJ Changed missing Start/Stop to mouse-selected arrows.
tomwalters@0 78 % 5/19/97 EAJ MATLAB 5 compatible version, beta.
tomwalters@0 79 % 4/13/97 EAJ MATLAB 5 compatible version, alpha.
tomwalters@0 80 % 1/31/97 EAJ Fixed bug with multiple arrows and unspecified Z coords.
tomwalters@0 81 % 12/05/96 EAJ Fixed one more bug with log plots and NormalDir specified
tomwalters@0 82 % 10/24/96 EAJ Fixed bug with log plots and NormalDir specified
tomwalters@0 83 % 11/13/95 EAJ Corrected handling for 'reverse' axis directions
tomwalters@0 84 % 10/06/95 EAJ Corrected occasional conflict with SUBPLOT
tomwalters@0 85 % 4/24/95 EAJ A major rewrite.
tomwalters@0 86 % Fall 94 EAJ Original code.
tomwalters@0 87 % Things to be done:
tomwalters@0 88 % - segment parsing, computing, and plotting into separate subfunctions
tomwalters@0 89 % - change computing from Xform to Camera paradigms
tomwalters@0 90 % + this will help especially with 3-D perspective plots
tomwalters@0 91 % + if the WarpToFill section works right, remove warning code
tomwalters@0 92 % + when perpsective works properly, remove perspective warning code
tomwalters@0 93 % - add cell property values and struct property name/values (like get/set)
tomwalters@0 94 % - get rid of NaN as the "default" data label
tomwalters@0 95 % + perhaps change userdata to a struct and don't include (or leave
tomwalters@0 96 % empty) the values specified as default; or use a cell containing
tomwalters@0 97 % an empty matrix for a default value
tomwalters@0 98 % - add functionality of GET to retrieve current values of ARROW properties
tomwalters@0 99 % Many thanks to Keith Rogers <kerog@ai.mit.com> for his many excellent
tomwalters@0 100 % suggestions and beta testing. Check out his shareware package MATDRAW
tomwalters@0 101 % (at ftp://ftp.mathworks.com/pub/contrib/v5/graphics/matdraw/) -- he has
tomwalters@0 102 % permission to distribute ARROW with MATDRAW.
tomwalters@0 103 % Permission is granted to distribute ARROW with the toolboxes for the book
tomwalters@0 104 % "Solving Solid Mechanics Problems with MATLAB 5", by F. Golnaraghi et al.
tomwalters@0 105 % (Prentice Hall, 1999).
tomwalters@0 106 % global variable initialization
tomwalters@0 107 global ARROW_PERSP_WARN ARROW_STRETCH_WARN ARROW_AXLIMITS
tomwalters@0 108 if isempty(ARROW_PERSP_WARN ), ARROW_PERSP_WARN =1; end;
tomwalters@0 109 if isempty(ARROW_STRETCH_WARN), ARROW_STRETCH_WARN=1; end;
tomwalters@0 110 % Handle callbacks
tomwalters@0 111 if (nargin>0 & isstr(varargin{1}) & strcmp(lower(varargin{1}),'callback')),
tomwalters@0 112 arrow_callback(varargin{2:end}); return;
tomwalters@0 113 end;
tomwalters@0 114 % Are we doing the demo?
tomwalters@0 115 c = sprintf('\n');
tomwalters@0 116 if (nargin==1 & isstr(varargin{1})),
tomwalters@0 117 arg1 = lower(varargin{1});
tomwalters@0 118 if strncmp(arg1,'prop',4), arrow_props;
tomwalters@0 119 elseif strncmp(arg1,'demo',4)
tomwalters@0 120 clf reset
tomwalters@0 121 demo_info = arrow_demo;
tomwalters@0 122 if ~strncmp(arg1,'demo2',5),
tomwalters@0 123 hh=arrow_demo3(demo_info);
tomwalters@0 124 else,
tomwalters@0 125 hh=arrow_demo2(demo_info);
tomwalters@0 126 end;
tomwalters@0 127 if (nargout>=1), h=hh; end;
tomwalters@0 128 elseif strncmp(arg1,'fixlimits',3),
tomwalters@0 129 arrow_fixlimits(ARROW_AXLIMITS);
tomwalters@0 130 ARROW_AXLIMITS=[];
tomwalters@0 131 elseif strncmp(arg1,'help',4),
tomwalters@0 132 disp(help(mfilename));
tomwalters@0 133 else,
tomwalters@0 134 error([upper(mfilename) ' got an unknown single-argument string ''' deblank(arg1) '''.']);
tomwalters@0 135 end;
tomwalters@0 136 return;
tomwalters@0 137 end;
tomwalters@0 138 % Check # of arguments
tomwalters@0 139 if (nargout>3), error([upper(mfilename) ' produces at most 3 output arguments.']); end;
tomwalters@0 140 % find first property number
tomwalters@0 141 firstprop = nargin+1;
tomwalters@0 142 for k=1:length(varargin), if ~isnumeric(varargin{k}), firstprop=k; break; end; end;
tomwalters@0 143 lastnumeric = firstprop-1;
tomwalters@0 144 % check property list
tomwalters@0 145 if (firstprop<=nargin),
tomwalters@0 146 for k=firstprop:2:nargin,
tomwalters@0 147 curarg = varargin{k};
tomwalters@0 148 if ~isstr(curarg) | sum(size(curarg)>1)>1,
tomwalters@0 149 error([upper(mfilename) ' requires that a property name be a single string.']);
tomwalters@0 150 end;
tomwalters@0 151 end;
tomwalters@0 152 if (rem(nargin-firstprop,2)~=1),
tomwalters@0 153 error([upper(mfilename) ' requires that the property ''' ...
tomwalters@0 154 varargin{nargin} ''' be paired with a property value.']);
tomwalters@0 155 end;
tomwalters@0 156 end;
tomwalters@0 157 % default output
tomwalters@0 158 if (nargout>0), h=[]; end;
tomwalters@0 159 if (nargout>1), yy=[]; end;
tomwalters@0 160 if (nargout>2), zz=[]; end;
tomwalters@0 161 % set values to empty matrices
tomwalters@0 162 start = [];
tomwalters@0 163 stop = [];
tomwalters@0 164 len = [];
tomwalters@0 165 baseangle = [];
tomwalters@0 166 tipangle = [];
tomwalters@0 167 wid = [];
tomwalters@0 168 page = [];
tomwalters@0 169 crossdir = [];
tomwalters@0 170 ends = [];
tomwalters@0 171 ax = [];
tomwalters@0 172 oldh = [];
tomwalters@0 173 ispatch = [];
tomwalters@0 174 defstart = [NaN NaN NaN];
tomwalters@0 175 defstop = [NaN NaN NaN];
tomwalters@0 176 deflen = 16;
tomwalters@0 177 defbaseangle = 90;
tomwalters@0 178 deftipangle = 16;
tomwalters@0 179 defwid = 0;
tomwalters@0 180 defpage = 0;
tomwalters@0 181 defcrossdir = [NaN NaN NaN];
tomwalters@0 182 defends = 1;
tomwalters@0 183 defoldh = [];
tomwalters@0 184 defispatch = 1;
tomwalters@0 185 % The 'Tag' we'll put on our arrows
tomwalters@0 186 ArrowTag = 'Arrow';
tomwalters@0 187 % check for oldstyle arguments
tomwalters@0 188 if (firstprop==2),
tomwalters@0 189 % assume arg1 is a set of handles
tomwalters@0 190 oldh = varargin{1}(:);
tomwalters@0 191 if isempty(oldh), return; end;
tomwalters@0 192 elseif (firstprop>9),
tomwalters@0 193 error([upper(mfilename) ' takes at most 8 non-property arguments.']);
tomwalters@0 194 elseif (firstprop>2),
tomwalters@0 195 s = str2mat('start','stop','len','baseangle','tipangle','wid','page','crossdir');
tomwalters@0 196 for k=1:firstprop-1, eval([deblank(s(k,:)) '=varargin{k};']); end;
tomwalters@0 197 end;
tomwalters@0 198 % parse property pairs
tomwalters@0 199 extraprops={};
tomwalters@0 200 for k=firstprop:2:nargin,
tomwalters@0 201 prop = varargin{k};
tomwalters@0 202 val = varargin{k+1};
tomwalters@0 203 prop = [lower(prop(:)') ' '];
tomwalters@0 204 if strncmp(prop,'start' ,5), start = val;
tomwalters@0 205 elseif strncmp(prop,'stop' ,4), stop = val;
tomwalters@0 206 elseif strncmp(prop,'len' ,3), len = val(:);
tomwalters@0 207 elseif strncmp(prop,'base' ,4), baseangle = val(:);
tomwalters@0 208 elseif strncmp(prop,'tip' ,3), tipangle = val(:);
tomwalters@0 209 elseif strncmp(prop,'wid' ,3), wid = val(:);
tomwalters@0 210 elseif strncmp(prop,'page' ,4), page = val;
tomwalters@0 211 elseif strncmp(prop,'cross' ,5), crossdir = val;
tomwalters@0 212 elseif strncmp(prop,'norm' ,4), if (isstr(val)), crossdir=val; else, crossdir=val*sqrt(-1); end;
tomwalters@0 213 elseif strncmp(prop,'end' ,3), ends = val;
tomwalters@0 214 elseif strncmp(prop,'object',6), oldh = val(:);
tomwalters@0 215 elseif strncmp(prop,'handle',6), oldh = val(:);
tomwalters@0 216 elseif strncmp(prop,'type' ,4), ispatch = val;
tomwalters@0 217 elseif strncmp(prop,'userd' ,5), %ignore it
tomwalters@0 218 else,
tomwalters@0 219 % make sure it is a valid patch or line property
tomwalters@0 220 eval('get(0,[''DefaultPatch'' varargin{k}]);err=0;','err=1;'); errstr=lasterr;
tomwalters@0 221 if (err), eval('get(0,[''DefaultLine'' varargin{k}]);err=0;','err=1;'); end;
tomwalters@0 222 if (err),
tomwalters@0 223 errstr(1:max(find(errstr==setstr(13)|errstr==setstr(10)))) = '';
tomwalters@0 224 error([upper(mfilename) ' got ' errstr]);
tomwalters@0 225 end;
tomwalters@0 226 extraprops={extraprops{:},varargin{k},val};
tomwalters@0 227 end;
tomwalters@0 228 end;
tomwalters@0 229 % Check if we got 'default' values
tomwalters@0 230 start = arrow_defcheck(start ,defstart ,'Start' );
tomwalters@0 231 stop = arrow_defcheck(stop ,defstop ,'Stop' );
tomwalters@0 232 len = arrow_defcheck(len ,deflen ,'Length' );
tomwalters@0 233 baseangle = arrow_defcheck(baseangle,defbaseangle,'BaseAngle' );
tomwalters@0 234 tipangle = arrow_defcheck(tipangle ,deftipangle ,'TipAngle' );
tomwalters@0 235 wid = arrow_defcheck(wid ,defwid ,'Width' );
tomwalters@0 236 crossdir = arrow_defcheck(crossdir ,defcrossdir ,'CrossDir' );
tomwalters@0 237 page = arrow_defcheck(page ,defpage ,'Page' );
tomwalters@0 238 ends = arrow_defcheck(ends ,defends ,'' );
tomwalters@0 239 oldh = arrow_defcheck(oldh ,[] ,'ObjectHandles');
tomwalters@0 240 ispatch = arrow_defcheck(ispatch ,defispatch ,'' );
tomwalters@0 241 % check transpose on arguments
tomwalters@0 242 [m,n]=size(start ); if any(m==[2 3])&(n==1|n>3), start = start'; end;
tomwalters@0 243 [m,n]=size(stop ); if any(m==[2 3])&(n==1|n>3), stop = stop'; end;
tomwalters@0 244 [m,n]=size(crossdir); if any(m==[2 3])&(n==1|n>3), crossdir = crossdir'; end;
tomwalters@0 245 % convert strings to numbers
tomwalters@0 246 if ~isempty(ends) & isstr(ends),
tomwalters@0 247 endsorig = ends;
tomwalters@0 248 [m,n] = size(ends);
tomwalters@0 249 col = lower([ends(:,1:min(3,n)) ones(m,max(0,3-n))*' ']);
tomwalters@0 250 ends = NaN*ones(m,1);
tomwalters@0 251 oo = ones(1,m);
tomwalters@0 252 ii=find(all(col'==['non']'*oo)'); if ~isempty(ii), ends(ii)=ones(length(ii),1)*0; end;
tomwalters@0 253 ii=find(all(col'==['sto']'*oo)'); if ~isempty(ii), ends(ii)=ones(length(ii),1)*1; end;
tomwalters@0 254 ii=find(all(col'==['sta']'*oo)'); if ~isempty(ii), ends(ii)=ones(length(ii),1)*2; end;
tomwalters@0 255 ii=find(all(col'==['bot']'*oo)'); if ~isempty(ii), ends(ii)=ones(length(ii),1)*3; end;
tomwalters@0 256 if any(isnan(ends)),
tomwalters@0 257 ii = min(find(isnan(ends)));
tomwalters@0 258 error([upper(mfilename) ' does not recognize ''' deblank(endsorig(ii,:)) ''' as a valid ''Ends'' value.']);
tomwalters@0 259 end;
tomwalters@0 260 else,
tomwalters@0 261 ends = ends(:);
tomwalters@0 262 end;
tomwalters@0 263 if ~isempty(ispatch) & isstr(ispatch),
tomwalters@0 264 col = lower(ispatch(:,1));
tomwalters@0 265 patchchar='p'; linechar='l'; defchar=' ';
tomwalters@0 266 mask = col~=patchchar & col~=linechar & col~=defchar;
tomwalters@0 267 if any(mask),
tomwalters@0 268 error([upper(mfilename) ' does not recognize ''' deblank(ispatch(min(find(mask)),:)) ''' as a valid ''Type'' value.']);
tomwalters@0 269 end;
tomwalters@0 270 ispatch = (col==patchchar)*1 + (col==linechar)*0 + (col==defchar)*defispatch;
tomwalters@0 271 else,
tomwalters@0 272 ispatch = ispatch(:);
tomwalters@0 273 end;
tomwalters@0 274 oldh = oldh(:);
tomwalters@0 275 % check object handles
tomwalters@0 276 if ~all(ishandle(oldh)), error([upper(mfilename) ' got invalid object handles.']); end;
tomwalters@0 277 % expand root, figure, and axes handles
tomwalters@0 278 if ~isempty(oldh),
tomwalters@0 279 ohtype = get(oldh,'Type');
tomwalters@0 280 mask = strcmp(ohtype,'root') | strcmp(ohtype,'figure') | strcmp(ohtype,'axes');
tomwalters@0 281 if any(mask),
tomwalters@0 282 oldh = num2cell(oldh);
tomwalters@0 283 for ii=find(mask)',
tomwalters@0 284 oldh(ii) = {findobj(oldh{ii},'Tag',ArrowTag)};
tomwalters@0 285 end;
tomwalters@0 286 oldh = cat(1,oldh{:});
tomwalters@0 287 if isempty(oldh), return; end; % no arrows to modify, so just leave
tomwalters@0 288 end;
tomwalters@0 289 end;
tomwalters@0 290 % largest argument length
tomwalters@0 291 [mstart,junk]=size(start); [mstop,junk]=size(stop); [mcrossdir,junk]=size(crossdir);
tomwalters@0 292 argsizes = [length(oldh) mstart mstop ...
tomwalters@0 293 length(len) length(baseangle) length(tipangle) ...
tomwalters@0 294 length(wid) length(page) mcrossdir length(ends) ];
tomwalters@0 295 args=['length(ObjectHandle) '; ...
tomwalters@0 296 '#rows(Start) '; ...
tomwalters@0 297 '#rows(Stop) '; ...
tomwalters@0 298 'length(Length) '; ...
tomwalters@0 299 'length(BaseAngle) '; ...
tomwalters@0 300 'length(TipAngle) '; ...
tomwalters@0 301 'length(Width) '; ...
tomwalters@0 302 'length(Page) '; ...
tomwalters@0 303 '#rows(CrossDir) '; ...
tomwalters@0 304 '#rows(Ends) '];
tomwalters@0 305 if (any(imag(crossdir(:))~=0)),
tomwalters@0 306 args(9,:) = '#rows(NormalDir) ';
tomwalters@0 307 end;
tomwalters@0 308 if isempty(oldh),
tomwalters@0 309 narrows = max(argsizes);
tomwalters@0 310 else,
tomwalters@0 311 narrows = length(oldh);
tomwalters@0 312 end;
tomwalters@0 313 if (narrows<=0), narrows=1; end;
tomwalters@0 314 % Check size of arguments
tomwalters@0 315 ii = find((argsizes~=0)&(argsizes~=1)&(argsizes~=narrows));
tomwalters@0 316 if ~isempty(ii),
tomwalters@0 317 s = args(ii',:);
tomwalters@0 318 while ((size(s,2)>1)&((abs(s(:,size(s,2)))==0)|(abs(s(:,size(s,2)))==abs(' ')))),
tomwalters@0 319 s = s(:,1:size(s,2)-1);
tomwalters@0 320 end;
tomwalters@0 321 s = [ones(length(ii),1)*[upper(mfilename) ' requires that '] s ...
tomwalters@0 322 ones(length(ii),1)*[' equal the # of arrows (' num2str(narrows) ').' c]];
tomwalters@0 323 s = s';
tomwalters@0 324 s = s(:)';
tomwalters@0 325 s = s(1:length(s)-1);
tomwalters@0 326 error(setstr(s));
tomwalters@0 327 end;
tomwalters@0 328 % check element length in Start, Stop, and CrossDir
tomwalters@0 329 if ~isempty(start),
tomwalters@0 330 [m,n] = size(start);
tomwalters@0 331 if (n==2),
tomwalters@0 332 start = [start NaN*ones(m,1)];
tomwalters@0 333 elseif (n~=3),
tomwalters@0 334 error([upper(mfilename) ' requires 2- or 3-element Start points.']);
tomwalters@0 335 end;
tomwalters@0 336 end;
tomwalters@0 337 if ~isempty(stop),
tomwalters@0 338 [m,n] = size(stop);
tomwalters@0 339 if (n==2),
tomwalters@0 340 stop = [stop NaN*ones(m,1)];
tomwalters@0 341 elseif (n~=3),
tomwalters@0 342 error([upper(mfilename) ' requires 2- or 3-element Stop points.']);
tomwalters@0 343 end;
tomwalters@0 344 end;
tomwalters@0 345 if ~isempty(crossdir),
tomwalters@0 346 [m,n] = size(crossdir);
tomwalters@0 347 if (n<3),
tomwalters@0 348 crossdir = [crossdir NaN*ones(m,3-n)];
tomwalters@0 349 elseif (n~=3),
tomwalters@0 350 if (all(imag(crossdir(:))==0)),
tomwalters@0 351 error([upper(mfilename) ' requires 2- or 3-element CrossDir vectors.']);
tomwalters@0 352 else,
tomwalters@0 353 error([upper(mfilename) ' requires 2- or 3-element NormalDir vectors.']);
tomwalters@0 354 end;
tomwalters@0 355 end;
tomwalters@0 356 end;
tomwalters@0 357 % fill empty arguments
tomwalters@0 358 if isempty(start ), start = [Inf Inf Inf]; end;
tomwalters@0 359 if isempty(stop ), stop = [Inf Inf Inf]; end;
tomwalters@0 360 if isempty(len ), len = Inf; end;
tomwalters@0 361 if isempty(baseangle ), baseangle = Inf; end;
tomwalters@0 362 if isempty(tipangle ), tipangle = Inf; end;
tomwalters@0 363 if isempty(wid ), wid = Inf; end;
tomwalters@0 364 if isempty(page ), page = Inf; end;
tomwalters@0 365 if isempty(crossdir ), crossdir = [Inf Inf Inf]; end;
tomwalters@0 366 if isempty(ends ), ends = Inf; end;
tomwalters@0 367 if isempty(ispatch ), ispatch = Inf; end;
tomwalters@0 368 % expand single-column arguments
tomwalters@0 369 o = ones(narrows,1);
tomwalters@0 370 if (size(start ,1)==1), start = o * start ; end;
tomwalters@0 371 if (size(stop ,1)==1), stop = o * stop ; end;
tomwalters@0 372 if (length(len )==1), len = o * len ; end;
tomwalters@0 373 if (length(baseangle )==1), baseangle = o * baseangle ; end;
tomwalters@0 374 if (length(tipangle )==1), tipangle = o * tipangle ; end;
tomwalters@0 375 if (length(wid )==1), wid = o * wid ; end;
tomwalters@0 376 if (length(page )==1), page = o * page ; end;
tomwalters@0 377 if (size(crossdir ,1)==1), crossdir = o * crossdir ; end;
tomwalters@0 378 if (length(ends )==1), ends = o * ends ; end;
tomwalters@0 379 if (length(ispatch )==1), ispatch = o * ispatch ; end;
tomwalters@0 380 ax = o * gca;
tomwalters@0 381 % if we've got handles, get the defaults from the handles
tomwalters@0 382 if ~isempty(oldh),
tomwalters@0 383 for k=1:narrows,
tomwalters@0 384 oh = oldh(k);
tomwalters@0 385 ud = get(oh,'UserData');
tomwalters@0 386 ax(k) = get(oh,'Parent');
tomwalters@0 387 ohtype = get(oh,'Type');
tomwalters@0 388 if strcmp(get(oh,'Tag'),ArrowTag), % if it's an arrow already
tomwalters@0 389 if isinf(ispatch(k)), ispatch(k)=strcmp(ohtype,'patch'); end;
tomwalters@0 390 % arrow UserData format: [start' stop' len base tip wid page crossdir' ends]
tomwalters@0 391 start0 = ud(1:3);
tomwalters@0 392 stop0 = ud(4:6);
tomwalters@0 393 if (isinf(len(k))), len(k) = ud( 7); end;
tomwalters@0 394 if (isinf(baseangle(k))), baseangle(k) = ud( 8); end;
tomwalters@0 395 if (isinf(tipangle(k))), tipangle(k) = ud( 9); end;
tomwalters@0 396 if (isinf(wid(k))), wid(k) = ud(10); end;
tomwalters@0 397 if (isinf(page(k))), page(k) = ud(11); end;
tomwalters@0 398 if (isinf(crossdir(k,1))), crossdir(k,1) = ud(12); end;
tomwalters@0 399 if (isinf(crossdir(k,2))), crossdir(k,2) = ud(13); end;
tomwalters@0 400 if (isinf(crossdir(k,3))), crossdir(k,3) = ud(14); end;
tomwalters@0 401 if (isinf(ends(k))), ends(k) = ud(15); end;
tomwalters@0 402 elseif strcmp(ohtype,'line')|strcmp(ohtype,'patch'), % it's a non-arrow line or patch
tomwalters@0 403 convLineToPatch = 1; %set to make arrow patches when converting from lines.
tomwalters@0 404 if isinf(ispatch(k)), ispatch(k)=convLineToPatch|strcmp(ohtype,'patch'); end;
tomwalters@0 405 x=get(oh,'XData'); x=x(~isnan(x(:))); if isempty(x), x=NaN; end;
tomwalters@0 406 y=get(oh,'YData'); y=y(~isnan(y(:))); if isempty(y), y=NaN; end;
tomwalters@0 407 z=get(oh,'ZData'); z=z(~isnan(z(:))); if isempty(z), z=NaN; end;
tomwalters@0 408 start0 = [x(1) y(1) z(1) ];
tomwalters@0 409 stop0 = [x(end) y(end) z(end)];
tomwalters@0 410 else,
tomwalters@0 411 error([upper(mfilename) ' cannot convert ' ohtype ' objects.']);
tomwalters@0 412 end;
tomwalters@0 413 ii=find(isinf(start(k,:))); if ~isempty(ii), start(k,ii)=start0(ii); end;
tomwalters@0 414 ii=find(isinf(stop( k,:))); if ~isempty(ii), stop( k,ii)=stop0( ii); end;
tomwalters@0 415 end;
tomwalters@0 416 end;
tomwalters@0 417 % convert Inf's to NaN's
tomwalters@0 418 start( isinf(start )) = NaN;
tomwalters@0 419 stop( isinf(stop )) = NaN;
tomwalters@0 420 len( isinf(len )) = NaN;
tomwalters@0 421 baseangle( isinf(baseangle)) = NaN;
tomwalters@0 422 tipangle( isinf(tipangle )) = NaN;
tomwalters@0 423 wid( isinf(wid )) = NaN;
tomwalters@0 424 page( isinf(page )) = NaN;
tomwalters@0 425 crossdir( isinf(crossdir )) = NaN;
tomwalters@0 426 ends( isinf(ends )) = NaN;
tomwalters@0 427 ispatch( isinf(ispatch )) = NaN;
tomwalters@0 428 % set up the UserData data (here so not corrupted by log10's and such)
tomwalters@0 429 ud = [start stop len baseangle tipangle wid page crossdir ends];
tomwalters@0 430 % Set Page defaults
tomwalters@0 431 page = ~isnan(page) & trueornan(page);
tomwalters@0 432 % Get axes limits, range, min; correct for aspect ratio and log scale
tomwalters@0 433 axm = zeros(3,narrows);
tomwalters@0 434 axr = zeros(3,narrows);
tomwalters@0 435 axrev = zeros(3,narrows);
tomwalters@0 436 ap = zeros(2,narrows);
tomwalters@0 437 xyzlog = zeros(3,narrows);
tomwalters@0 438 limmin = zeros(2,narrows);
tomwalters@0 439 limrange = zeros(2,narrows);
tomwalters@0 440 oldaxlims = zeros(narrows,7);
tomwalters@0 441 oneax = all(ax==ax(1));
tomwalters@0 442 if (oneax),
tomwalters@0 443 T = zeros(4,4);
tomwalters@0 444 invT = zeros(4,4);
tomwalters@0 445 else,
tomwalters@0 446 T = zeros(16,narrows);
tomwalters@0 447 invT = zeros(16,narrows);
tomwalters@0 448 end;
tomwalters@0 449 axnotdone = logical(ones(size(ax)));
tomwalters@0 450 while (any(axnotdone)),
tomwalters@0 451 ii = min(find(axnotdone));
tomwalters@0 452 curax = ax(ii);
tomwalters@0 453 curpage = page(ii);
tomwalters@0 454 % get axes limits and aspect ratio
tomwalters@0 455 axl = [get(curax,'XLim'); get(curax,'YLim'); get(curax,'ZLim')];
tomwalters@0 456 oldaxlims(min(find(oldaxlims(:,1)==0)),:) = [curax reshape(axl',1,6)];
tomwalters@0 457 % get axes size in pixels (points)
tomwalters@0 458 u = get(curax,'Units');
tomwalters@0 459 axposoldunits = get(curax,'Position');
tomwalters@0 460 really_curpage = curpage & strcmp(u,'normalized');
tomwalters@0 461 if (really_curpage),
tomwalters@0 462 curfig = get(curax,'Parent');
tomwalters@0 463 pu = get(curfig,'PaperUnits');
tomwalters@0 464 set(curfig,'PaperUnits','points');
tomwalters@0 465 pp = get(curfig,'PaperPosition');
tomwalters@0 466 set(curfig,'PaperUnits',pu);
tomwalters@0 467 set(curax,'Units','pixels');
tomwalters@0 468 curapscreen = get(curax,'Position');
tomwalters@0 469 set(curax,'Units','normalized');
tomwalters@0 470 curap = pp.*get(curax,'Position');
tomwalters@0 471 else,
tomwalters@0 472 set(curax,'Units','pixels');
tomwalters@0 473 curapscreen = get(curax,'Position');
tomwalters@0 474 curap = curapscreen;
tomwalters@0 475 end;
tomwalters@0 476 set(curax,'Units',u);
tomwalters@0 477 set(curax,'Position',axposoldunits);
tomwalters@0 478 % handle non-stretched axes position
tomwalters@0 479 str_stretch = { 'DataAspectRatioMode' ; ...
tomwalters@0 480 'PlotBoxAspectRatioMode' ; ...
tomwalters@0 481 'CameraViewAngleMode' };
tomwalters@0 482 str_camera = { 'CameraPositionMode' ; ...
tomwalters@0 483 'CameraTargetMode' ; ...
tomwalters@0 484 'CameraViewAngleMode' ; ...
tomwalters@0 485 'CameraUpVectorMode' };
tomwalters@0 486 notstretched = strcmp(get(curax,str_stretch),'manual');
tomwalters@0 487 manualcamera = strcmp(get(curax,str_camera),'manual');
tomwalters@0 488 if ~arrow_WarpToFill(notstretched,manualcamera,curax),
tomwalters@0 489 % give a warning that this has not been thoroughly tested
tomwalters@0 490 if 0 & ARROW_STRETCH_WARN,
tomwalters@0 491 ARROW_STRETCH_WARN = 0;
tomwalters@0 492 strs = {str_stretch{1:2},str_camera{:}};
tomwalters@0 493 strs = [char(ones(length(strs),1)*sprintf('\n ')) char(strs)]';
tomwalters@0 494 warning([upper(mfilename) ' may not yet work quite right ' ...
tomwalters@0 495 'if any of the following are ''manual'':' strs(:).']);
tomwalters@0 496 end;
tomwalters@0 497 % find the true pixel size of the actual axes
tomwalters@0 498 texttmp = text(axl(1,[1 2 2 1 1 2 2 1]), ...
tomwalters@0 499 axl(2,[1 1 2 2 1 1 2 2]), ...
tomwalters@0 500 axl(3,[1 1 1 1 2 2 2 2]),'');
tomwalters@0 501 set(texttmp,'Units','points');
tomwalters@0 502 textpos = get(texttmp,'Position');
tomwalters@0 503 delete(texttmp);
tomwalters@0 504 textpos = cat(1,textpos{:});
tomwalters@0 505 textpos = max(textpos(:,1:2)) - min(textpos(:,1:2));
tomwalters@0 506 % adjust the axes position
tomwalters@0 507 if (really_curpage),
tomwalters@0 508 % adjust to printed size
tomwalters@0 509 textpos = textpos * min(curap(3:4)./textpos);
tomwalters@0 510 curap = [curap(1:2)+(curap(3:4)-textpos)/2 textpos];
tomwalters@0 511 else,
tomwalters@0 512 % adjust for pixel roundoff
tomwalters@0 513 textpos = textpos * min(curapscreen(3:4)./textpos);
tomwalters@0 514 curap = [curap(1:2)+(curap(3:4)-textpos)/2 textpos];
tomwalters@0 515 end;
tomwalters@0 516 end;
tomwalters@0 517 if ARROW_PERSP_WARN & ~strcmp(get(curax,'Projection'),'orthographic'),
tomwalters@0 518 ARROW_PERSP_WARN = 0;
tomwalters@0 519 warning([upper(mfilename) ' does not yet work right for 3-D perspective projection.']);
tomwalters@0 520 end;
tomwalters@0 521 % adjust limits for log scale on axes
tomwalters@0 522 curxyzlog = [strcmp(get(curax,'XScale'),'log'); ...
tomwalters@0 523 strcmp(get(curax,'YScale'),'log'); ...
tomwalters@0 524 strcmp(get(curax,'ZScale'),'log')];
tomwalters@0 525 if (any(curxyzlog)),
tomwalters@0 526 ii = find([curxyzlog;curxyzlog]);
tomwalters@0 527 if (any(axl(ii)<=0)),
tomwalters@0 528 error([upper(mfilename) ' does not support non-positive limits on log-scaled axes.']);
tomwalters@0 529 else,
tomwalters@0 530 axl(ii) = log10(axl(ii));
tomwalters@0 531 end;
tomwalters@0 532 end;
tomwalters@0 533 % correct for 'reverse' direction on axes;
tomwalters@0 534 curreverse = [strcmp(get(curax,'XDir'),'reverse'); ...
tomwalters@0 535 strcmp(get(curax,'YDir'),'reverse'); ...
tomwalters@0 536 strcmp(get(curax,'ZDir'),'reverse')];
tomwalters@0 537 ii = find(curreverse);
tomwalters@0 538 if ~isempty(ii),
tomwalters@0 539 axl(ii,[1 2])=-axl(ii,[2 1]);
tomwalters@0 540 end;
tomwalters@0 541 % compute the range of 2-D values
tomwalters@0 542 curT = get(curax,'Xform');
tomwalters@0 543 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];
tomwalters@0 544 lim = lim(1:2,:)./([1;1]*lim(4,:));
tomwalters@0 545 curlimmin = min(lim')';
tomwalters@0 546 curlimrange = max(lim')' - curlimmin;
tomwalters@0 547 curinvT = inv(curT);
tomwalters@0 548 if (~oneax),
tomwalters@0 549 curT = curT.';
tomwalters@0 550 curinvT = curinvT.';
tomwalters@0 551 curT = curT(:);
tomwalters@0 552 curinvT = curinvT(:);
tomwalters@0 553 end;
tomwalters@0 554 % check which arrows to which cur corresponds
tomwalters@0 555 ii = find((ax==curax)&(page==curpage));
tomwalters@0 556 oo = ones(1,length(ii));
tomwalters@0 557 axr(:,ii) = diff(axl')' * oo;
tomwalters@0 558 axm(:,ii) = axl(:,1) * oo;
tomwalters@0 559 axrev(:,ii) = curreverse * oo;
tomwalters@0 560 ap(:,ii) = curap(3:4)' * oo;
tomwalters@0 561 xyzlog(:,ii) = curxyzlog * oo;
tomwalters@0 562 limmin(:,ii) = curlimmin * oo;
tomwalters@0 563 limrange(:,ii) = curlimrange * oo;
tomwalters@0 564 if (oneax),
tomwalters@0 565 T = curT;
tomwalters@0 566 invT = curinvT;
tomwalters@0 567 else,
tomwalters@0 568 T(:,ii) = curT * oo;
tomwalters@0 569 invT(:,ii) = curinvT * oo;
tomwalters@0 570 end;
tomwalters@0 571 axnotdone(ii) = zeros(1,length(ii));
tomwalters@0 572 end;
tomwalters@0 573 oldaxlims(oldaxlims(:,1)==0,:)=[];
tomwalters@0 574 % correct for log scales
tomwalters@0 575 curxyzlog = xyzlog.';
tomwalters@0 576 ii = find(curxyzlog(:));
tomwalters@0 577 if ~isempty(ii),
tomwalters@0 578 start( ii) = real(log10(start( ii)));
tomwalters@0 579 stop( ii) = real(log10(stop( ii)));
tomwalters@0 580 if (all(imag(crossdir)==0)), % pulled (ii) subscript on crossdir, 12/5/96 eaj
tomwalters@0 581 crossdir(ii) = real(log10(crossdir(ii)));
tomwalters@0 582 end;
tomwalters@0 583 end;
tomwalters@0 584 % correct for reverse directions
tomwalters@0 585 ii = find(axrev.');
tomwalters@0 586 if ~isempty(ii),
tomwalters@0 587 start( ii) = -start( ii);
tomwalters@0 588 stop( ii) = -stop( ii);
tomwalters@0 589 crossdir(ii) = -crossdir(ii);
tomwalters@0 590 end;
tomwalters@0 591 % transpose start/stop values
tomwalters@0 592 start = start.';
tomwalters@0 593 stop = stop.';
tomwalters@0 594 % take care of defaults, page was done above
tomwalters@0 595 ii=find(isnan(start(:) )); if ~isempty(ii), start(ii) = axm(ii)+axr(ii)/2; end;
tomwalters@0 596 ii=find(isnan(stop(:) )); if ~isempty(ii), stop(ii) = axm(ii)+axr(ii)/2; end;
tomwalters@0 597 ii=find(isnan(crossdir(:) )); if ~isempty(ii), crossdir(ii) = zeros(length(ii),1); end;
tomwalters@0 598 ii=find(isnan(len )); if ~isempty(ii), len(ii) = ones(length(ii),1)*deflen; end;
tomwalters@0 599 ii=find(isnan(baseangle )); if ~isempty(ii), baseangle(ii) = ones(length(ii),1)*defbaseangle; end;
tomwalters@0 600 ii=find(isnan(tipangle )); if ~isempty(ii), tipangle(ii) = ones(length(ii),1)*deftipangle; end;
tomwalters@0 601 ii=find(isnan(wid )); if ~isempty(ii), wid(ii) = ones(length(ii),1)*defwid; end;
tomwalters@0 602 ii=find(isnan(ends )); if ~isempty(ii), ends(ii) = ones(length(ii),1)*defends; end;
tomwalters@0 603 % transpose rest of values
tomwalters@0 604 len = len.';
tomwalters@0 605 baseangle = baseangle.';
tomwalters@0 606 tipangle = tipangle.';
tomwalters@0 607 wid = wid.';
tomwalters@0 608 page = page.';
tomwalters@0 609 crossdir = crossdir.';
tomwalters@0 610 ends = ends.';
tomwalters@0 611 ax = ax.';
tomwalters@0 612 % given x, a 3xN matrix of points in 3-space;
tomwalters@0 613 % want to convert to X, the corresponding 4xN 2-space matrix
tomwalters@0 614 %
tomwalters@0 615 % tmp1=[(x-axm)./axr; ones(1,size(x,1))];
tomwalters@0 616 % if (oneax), X=T*tmp1;
tomwalters@0 617 % else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T.*tmp1;
tomwalters@0 618 % tmp2=zeros(4,4*N); tmp2(:)=tmp1(:);
tomwalters@0 619 % X=zeros(4,N); X(:)=sum(tmp2)'; end;
tomwalters@0 620 % X = X ./ (ones(4,1)*X(4,:));
tomwalters@0 621 % for all points with start==stop, start=stop-(verysmallvalue)*(up-direction);
tomwalters@0 622 ii = find(all(start==stop));
tomwalters@0 623 if ~isempty(ii),
tomwalters@0 624 % find an arrowdir vertical on screen and perpendicular to viewer
tomwalters@0 625 % transform to 2-D
tomwalters@0 626 tmp1 = [(stop(:,ii)-axm(:,ii))./axr(:,ii);ones(1,length(ii))];
tomwalters@0 627 if (oneax), twoD=T*tmp1;
tomwalters@0 628 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T(:,ii).*tmp1;
tomwalters@0 629 tmp2=zeros(4,4*length(ii)); tmp2(:)=tmp1(:);
tomwalters@0 630 twoD=zeros(4,length(ii)); twoD(:)=sum(tmp2)'; end;
tomwalters@0 631 twoD=twoD./(ones(4,1)*twoD(4,:));
tomwalters@0 632 % move the start point down just slightly
tomwalters@0 633 tmp1 = twoD + [0;-1/1000;0;0]*(limrange(2,ii)./ap(2,ii));
tomwalters@0 634 % transform back to 3-D
tomwalters@0 635 if (oneax), threeD=invT*tmp1;
tomwalters@0 636 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT(:,ii).*tmp1;
tomwalters@0 637 tmp2=zeros(4,4*length(ii)); tmp2(:)=tmp1(:);
tomwalters@0 638 threeD=zeros(4,length(ii)); threeD(:)=sum(tmp2)'; end;
tomwalters@0 639 start(:,ii) = (threeD(1:3,:)./(ones(3,1)*threeD(4,:))).*axr(:,ii)+axm(:,ii);
tomwalters@0 640 end;
tomwalters@0 641 % compute along-arrow points
tomwalters@0 642 % transform Start points
tomwalters@0 643 tmp1=[(start-axm)./axr;ones(1,narrows)];
tomwalters@0 644 if (oneax), X0=T*tmp1;
tomwalters@0 645 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T.*tmp1;
tomwalters@0 646 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 647 X0=zeros(4,narrows); X0(:)=sum(tmp2)'; end;
tomwalters@0 648 X0=X0./(ones(4,1)*X0(4,:));
tomwalters@0 649 % transform Stop points
tomwalters@0 650 tmp1=[(stop-axm)./axr;ones(1,narrows)];
tomwalters@0 651 if (oneax), Xf=T*tmp1;
tomwalters@0 652 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T.*tmp1;
tomwalters@0 653 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 654 Xf=zeros(4,narrows); Xf(:)=sum(tmp2)'; end;
tomwalters@0 655 Xf=Xf./(ones(4,1)*Xf(4,:));
tomwalters@0 656 % compute pixel distance between points
tomwalters@0 657 D = sqrt(sum(((Xf(1:2,:)-X0(1:2,:)).*(ap./limrange)).^2));
tomwalters@0 658 D = D + (D==0); %eaj new 2/24/98
tomwalters@0 659 % compute and modify along-arrow distances
tomwalters@0 660 len1 = len;
tomwalters@0 661 len2 = len - (len.*tan(tipangle/180*pi)-wid/2).*tan((90-baseangle)/180*pi);
tomwalters@0 662 slen0 = zeros(1,narrows);
tomwalters@0 663 slen1 = len1 .* ((ends==2)|(ends==3));
tomwalters@0 664 slen2 = len2 .* ((ends==2)|(ends==3));
tomwalters@0 665 len0 = zeros(1,narrows);
tomwalters@0 666 len1 = len1 .* ((ends==1)|(ends==3));
tomwalters@0 667 len2 = len2 .* ((ends==1)|(ends==3));
tomwalters@0 668 % for no start arrowhead
tomwalters@0 669 ii=find((ends==1)&(D<len2));
tomwalters@0 670 if ~isempty(ii),
tomwalters@0 671 slen0(ii) = D(ii)-len2(ii);
tomwalters@0 672 end;
tomwalters@0 673 % for no end arrowhead
tomwalters@0 674 ii=find((ends==2)&(D<slen2));
tomwalters@0 675 if ~isempty(ii),
tomwalters@0 676 len0(ii) = D(ii)-slen2(ii);
tomwalters@0 677 end;
tomwalters@0 678 len1 = len1 + len0;
tomwalters@0 679 len2 = len2 + len0;
tomwalters@0 680 slen1 = slen1 + slen0;
tomwalters@0 681 slen2 = slen2 + slen0;
tomwalters@0 682 % note: the division by D below will probably not be accurate if both
tomwalters@0 683 % of the following are true:
tomwalters@0 684 % 1. the ratio of the line length to the arrowhead
tomwalters@0 685 % length is large
tomwalters@0 686 % 2. the view is highly perspective.
tomwalters@0 687 % compute stoppoints
tomwalters@0 688 tmp1=X0.*(ones(4,1)*(len0./D))+Xf.*(ones(4,1)*(1-len0./D));
tomwalters@0 689 if (oneax), tmp3=invT*tmp1;
tomwalters@0 690 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
tomwalters@0 691 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 692 tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 693 stoppoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
tomwalters@0 694 % compute tippoints
tomwalters@0 695 tmp1=X0.*(ones(4,1)*(len1./D))+Xf.*(ones(4,1)*(1-len1./D));
tomwalters@0 696 if (oneax), tmp3=invT*tmp1;
tomwalters@0 697 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
tomwalters@0 698 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 699 tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 700 tippoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
tomwalters@0 701 % compute basepoints
tomwalters@0 702 tmp1=X0.*(ones(4,1)*(len2./D))+Xf.*(ones(4,1)*(1-len2./D));
tomwalters@0 703 if (oneax), tmp3=invT*tmp1;
tomwalters@0 704 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
tomwalters@0 705 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 706 tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 707 basepoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
tomwalters@0 708 % compute startpoints
tomwalters@0 709 tmp1=X0.*(ones(4,1)*(1-slen0./D))+Xf.*(ones(4,1)*(slen0./D));
tomwalters@0 710 if (oneax), tmp3=invT*tmp1;
tomwalters@0 711 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
tomwalters@0 712 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 713 tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 714 startpoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
tomwalters@0 715 % compute stippoints
tomwalters@0 716 tmp1=X0.*(ones(4,1)*(1-slen1./D))+Xf.*(ones(4,1)*(slen1./D));
tomwalters@0 717 if (oneax), tmp3=invT*tmp1;
tomwalters@0 718 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
tomwalters@0 719 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 720 tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 721 stippoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
tomwalters@0 722 % compute sbasepoints
tomwalters@0 723 tmp1=X0.*(ones(4,1)*(1-slen2./D))+Xf.*(ones(4,1)*(slen2./D));
tomwalters@0 724 if (oneax), tmp3=invT*tmp1;
tomwalters@0 725 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=invT.*tmp1;
tomwalters@0 726 tmp2=zeros(4,4*narrows); tmp2(:)=tmp1(:);
tomwalters@0 727 tmp3=zeros(4,narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 728 sbasepoint = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)).*axr+axm;
tomwalters@0 729 % compute cross-arrow directions for arrows with NormalDir specified
tomwalters@0 730 if (any(imag(crossdir(:))~=0)),
tomwalters@0 731 ii = find(any(imag(crossdir)~=0));
tomwalters@0 732 crossdir(:,ii) = cross((stop(:,ii)-start(:,ii))./axr(:,ii), ...
tomwalters@0 733 imag(crossdir(:,ii))).*axr(:,ii);
tomwalters@0 734 end;
tomwalters@0 735 % compute cross-arrow directions
tomwalters@0 736 basecross = crossdir + basepoint;
tomwalters@0 737 tipcross = crossdir + tippoint;
tomwalters@0 738 sbasecross = crossdir + sbasepoint;
tomwalters@0 739 stipcross = crossdir + stippoint;
tomwalters@0 740 ii = find(all(crossdir==0)|any(isnan(crossdir)));
tomwalters@0 741 if ~isempty(ii),
tomwalters@0 742 numii = length(ii);
tomwalters@0 743 % transform start points
tomwalters@0 744 tmp1 = [basepoint(:,ii) tippoint(:,ii) sbasepoint(:,ii) stippoint(:,ii)];
tomwalters@0 745 tmp1 = (tmp1-axm(:,[ii ii ii ii])) ./ axr(:,[ii ii ii ii]);
tomwalters@0 746 tmp1 = [tmp1; ones(1,4*numii)];
tomwalters@0 747 if (oneax), X0=T*tmp1;
tomwalters@0 748 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T(:,[ii ii ii ii]).*tmp1;
tomwalters@0 749 tmp2=zeros(4,16*numii); tmp2(:)=tmp1(:);
tomwalters@0 750 X0=zeros(4,4*numii); X0(:)=sum(tmp2)'; end;
tomwalters@0 751 X0=X0./(ones(4,1)*X0(4,:));
tomwalters@0 752 % transform stop points
tomwalters@0 753 tmp1 = [(2*stop(:,ii)-start(:,ii)-axm(:,ii))./axr(:,ii);ones(1,numii)];
tomwalters@0 754 tmp1 = [tmp1 tmp1 tmp1 tmp1];
tomwalters@0 755 if (oneax), Xf=T*tmp1;
tomwalters@0 756 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=T(:,[ii ii ii ii]).*tmp1;
tomwalters@0 757 tmp2=zeros(4,16*numii); tmp2(:)=tmp1(:);
tomwalters@0 758 Xf=zeros(4,4*numii); Xf(:)=sum(tmp2)'; end;
tomwalters@0 759 Xf=Xf./(ones(4,1)*Xf(4,:));
tomwalters@0 760 % compute perpendicular directions
tomwalters@0 761 pixfact = ((limrange(1,ii)./limrange(2,ii)).*(ap(2,ii)./ap(1,ii))).^2;
tomwalters@0 762 pixfact = [pixfact pixfact pixfact pixfact];
tomwalters@0 763 pixfact = [pixfact;1./pixfact];
tomwalters@0 764 [dummyval,jj] = max(abs(Xf(1:2,:)-X0(1:2,:)));
tomwalters@0 765 jj1 = ((1:4)'*ones(1,length(jj))==ones(4,1)*jj);
tomwalters@0 766 jj2 = ((1:4)'*ones(1,length(jj))==ones(4,1)*(3-jj));
tomwalters@0 767 jj3 = jj1(1:2,:);
tomwalters@0 768 Xf(jj1)=Xf(jj1)+(Xf(jj1)-X0(jj1)==0); %eaj new 2/24/98
tomwalters@0 769 Xp = X0;
tomwalters@0 770 Xp(jj2) = X0(jj2) + ones(sum(jj2(:)),1);
tomwalters@0 771 Xp(jj1) = X0(jj1) - (Xf(jj2)-X0(jj2))./(Xf(jj1)-X0(jj1)) .* pixfact(jj3);
tomwalters@0 772 % inverse transform the cross points
tomwalters@0 773 if (oneax), Xp=invT*Xp;
tomwalters@0 774 else, tmp1=[Xp;Xp;Xp;Xp]; tmp1=invT(:,[ii ii ii ii]).*tmp1;
tomwalters@0 775 tmp2=zeros(4,16*numii); tmp2(:)=tmp1(:);
tomwalters@0 776 Xp=zeros(4,4*numii); Xp(:)=sum(tmp2)'; end;
tomwalters@0 777 Xp=(Xp(1:3,:)./(ones(3,1)*Xp(4,:))).*axr(:,[ii ii ii ii])+axm(:,[ii ii ii ii]);
tomwalters@0 778 basecross(:,ii) = Xp(:,0*numii+(1:numii));
tomwalters@0 779 tipcross(:,ii) = Xp(:,1*numii+(1:numii));
tomwalters@0 780 sbasecross(:,ii) = Xp(:,2*numii+(1:numii));
tomwalters@0 781 stipcross(:,ii) = Xp(:,3*numii+(1:numii));
tomwalters@0 782 end;
tomwalters@0 783 % compute all points
tomwalters@0 784 % compute start points
tomwalters@0 785 axm11 = [axm axm axm axm axm axm axm axm axm axm axm];
tomwalters@0 786 axr11 = [axr axr axr axr axr axr axr axr axr axr axr];
tomwalters@0 787 st = [stoppoint tippoint basepoint sbasepoint stippoint startpoint stippoint sbasepoint basepoint tippoint stoppoint];
tomwalters@0 788 tmp1 = (st - axm11) ./ axr11;
tomwalters@0 789 tmp1 = [tmp1; ones(1,size(tmp1,2))];
tomwalters@0 790 if (oneax), X0=T*tmp1;
tomwalters@0 791 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=[T T T T T T T T T T T].*tmp1;
tomwalters@0 792 tmp2=zeros(4,44*narrows); tmp2(:)=tmp1(:);
tomwalters@0 793 X0=zeros(4,11*narrows); X0(:)=sum(tmp2)'; end;
tomwalters@0 794 X0=X0./(ones(4,1)*X0(4,:));
tomwalters@0 795 % compute stop points
tomwalters@0 796 tmp1 = ([start tipcross basecross sbasecross stipcross stop stipcross sbasecross basecross tipcross start] ...
tomwalters@0 797 - axm11) ./ axr11;
tomwalters@0 798 tmp1 = [tmp1; ones(1,size(tmp1,2))];
tomwalters@0 799 if (oneax), Xf=T*tmp1;
tomwalters@0 800 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=[T T T T T T T T T T T].*tmp1;
tomwalters@0 801 tmp2=zeros(4,44*narrows); tmp2(:)=tmp1(:);
tomwalters@0 802 Xf=zeros(4,11*narrows); Xf(:)=sum(tmp2)'; end;
tomwalters@0 803 Xf=Xf./(ones(4,1)*Xf(4,:));
tomwalters@0 804 % compute lengths
tomwalters@0 805 len0 = len.*((ends==1)|(ends==3)).*tan(tipangle/180*pi);
tomwalters@0 806 slen0 = len.*((ends==2)|(ends==3)).*tan(tipangle/180*pi);
tomwalters@0 807 le = [zeros(1,narrows) len0 wid/2 wid/2 slen0 zeros(1,narrows) -slen0 -wid/2 -wid/2 -len0 zeros(1,narrows)];
tomwalters@0 808 aprange = ap./limrange;
tomwalters@0 809 aprange = [aprange aprange aprange aprange aprange aprange aprange aprange aprange aprange aprange];
tomwalters@0 810 D = sqrt(sum(((Xf(1:2,:)-X0(1:2,:)).*aprange).^2));
tomwalters@0 811 Dii=find(D==0); if ~isempty(Dii), D=D+(D==0); le(Dii)=zeros(1,length(Dii)); end; %should fix DivideByZero warnings
tomwalters@0 812 tmp1 = X0.*(ones(4,1)*(1-le./D)) + Xf.*(ones(4,1)*(le./D));
tomwalters@0 813 % inverse transform
tomwalters@0 814 if (oneax), tmp3=invT*tmp1;
tomwalters@0 815 else, tmp1=[tmp1;tmp1;tmp1;tmp1]; tmp1=[invT invT invT invT invT invT invT invT invT invT invT].*tmp1;
tomwalters@0 816 tmp2=zeros(4,44*narrows); tmp2(:)=tmp1(:);
tomwalters@0 817 tmp3=zeros(4,11*narrows); tmp3(:)=sum(tmp2)'; end;
tomwalters@0 818 pts = tmp3(1:3,:)./(ones(3,1)*tmp3(4,:)) .* axr11 + axm11;
tomwalters@0 819 % correct for ones where the crossdir was specified
tomwalters@0 820 ii = find(~(all(crossdir==0)|any(isnan(crossdir))));
tomwalters@0 821 if ~isempty(ii),
tomwalters@0 822 D1 = [pts(:,1*narrows+ii)-pts(:,9*narrows+ii) ...
tomwalters@0 823 pts(:,2*narrows+ii)-pts(:,8*narrows+ii) ...
tomwalters@0 824 pts(:,3*narrows+ii)-pts(:,7*narrows+ii) ...
tomwalters@0 825 pts(:,4*narrows+ii)-pts(:,6*narrows+ii) ...
tomwalters@0 826 pts(:,6*narrows+ii)-pts(:,4*narrows+ii) ...
tomwalters@0 827 pts(:,7*narrows+ii)-pts(:,3*narrows+ii) ...
tomwalters@0 828 pts(:,8*narrows+ii)-pts(:,2*narrows+ii) ...
tomwalters@0 829 pts(:,9*narrows+ii)-pts(:,1*narrows+ii)]/2;
tomwalters@0 830 ii = ii'*ones(1,8) + ones(length(ii),1)*[1:4 6:9]*narrows;
tomwalters@0 831 ii = ii(:)';
tomwalters@0 832 pts(:,ii) = st(:,ii) + D1;
tomwalters@0 833 end;
tomwalters@0 834 % readjust for reverse directions
tomwalters@0 835 iicols=(1:narrows)'; iicols=iicols(:,ones(1,11)); iicols=iicols(:).';
tomwalters@0 836 tmp1=axrev(:,iicols);
tomwalters@0 837 ii = find(tmp1(:)); if ~isempty(ii), pts(ii)=-pts(ii); end;
tomwalters@0 838 % readjust for log scale on axes
tomwalters@0 839 tmp1=xyzlog(:,iicols);
tomwalters@0 840 ii = find(tmp1(:)); if ~isempty(ii), pts(ii)=10.^pts(ii); end;
tomwalters@0 841 % compute the x,y,z coordinates of the patches;
tomwalters@0 842 ii = narrows*(0:10)'*ones(1,narrows) + ones(11,1)*(1:narrows);
tomwalters@0 843 ii = ii(:)';
tomwalters@0 844 x = zeros(11,narrows);
tomwalters@0 845 y = zeros(11,narrows);
tomwalters@0 846 z = zeros(11,narrows);
tomwalters@0 847 x(:) = pts(1,ii)';
tomwalters@0 848 y(:) = pts(2,ii)';
tomwalters@0 849 z(:) = pts(3,ii)';
tomwalters@0 850 % do the output
tomwalters@0 851 if (nargout<=1),
tomwalters@0 852 % % create or modify the patches
tomwalters@0 853 newpatch = trueornan(ispatch) & (isempty(oldh)|~strcmp(get(oldh,'Type'),'patch'));
tomwalters@0 854 newline = ~trueornan(ispatch) & (isempty(oldh)|~strcmp(get(oldh,'Type'),'line'));
tomwalters@0 855 if isempty(oldh), H=zeros(narrows,1); else, H=oldh; end;
tomwalters@0 856 % % make or modify the arrows
tomwalters@0 857 for k=1:narrows,
tomwalters@0 858 if all(isnan(ud(k,[3 6])))&arrow_is2DXY(ax(k)), zz=[]; else, zz=z(:,k); end;
tomwalters@0 859 % work around a MATLAB 6.x OpenGL bug -- 7/28/02
tomwalters@0 860 xx=x(:,k); yy=y(:,k);
tomwalters@0 861 mask=any([ones(1,2+size(zz,2));diff([xx yy zz],[],1)],2);
tomwalters@0 862 xx=xx(mask); yy=yy(mask); if ~isempty(zz), zz=zz(mask); end;
tomwalters@0 863 % plot the patch or line
tomwalters@0 864 xyz = {'XData',xx,'YData',yy,'ZData',zz,'Tag',ArrowTag};
tomwalters@0 865 if newpatch(k)|newline(k),
tomwalters@0 866 if newpatch(k),
tomwalters@0 867 H(k) = patch(xyz{:});
tomwalters@0 868 else,
tomwalters@0 869 H(k) = line(xyz{:});
tomwalters@0 870 end;
tomwalters@0 871 if ~isempty(oldh), arrow_copyprops(oldh(k),H(k)); end;
tomwalters@0 872 else,
tomwalters@0 873 if ispatch(k), xyz={xyz{:},'CData',[]}; end;
tomwalters@0 874 set(H(k),xyz{:});
tomwalters@0 875 end;
tomwalters@0 876 end;
tomwalters@0 877 if ~isempty(oldh), delete(oldh(oldh~=H)); end;
tomwalters@0 878 % % additional properties
tomwalters@0 879 set(H,'Clipping','off');
tomwalters@0 880 set(H,{'UserData'},num2cell(ud,2));
tomwalters@0 881 if (length(extraprops)>0), set(H,extraprops{:}); end;
tomwalters@0 882 % handle choosing arrow Start and/or Stop locations if unspecified
tomwalters@0 883 [H,oldaxlims,errstr] = arrow_clicks(H,ud,x,y,z,ax,oldaxlims);
tomwalters@0 884 if ~isempty(errstr), error([upper(mfilename) ' got ' errstr]); end;
tomwalters@0 885 % set the output
tomwalters@0 886 if (nargout>0), h=H; end;
tomwalters@0 887 % make sure the axis limits did not change
tomwalters@0 888 if isempty(oldaxlims),
tomwalters@0 889 ARROW_AXLIMITS = [];
tomwalters@0 890 else,
tomwalters@0 891 lims = get(oldaxlims(:,1),{'XLim','YLim','ZLim'})';
tomwalters@0 892 lims = reshape(cat(2,lims{:}),6,size(lims,2));
tomwalters@0 893 mask = arrow_is2DXY(oldaxlims(:,1));
tomwalters@0 894 oldaxlims(mask,6:7) = lims(5:6,mask)';
tomwalters@0 895 ARROW_AXLIMITS = oldaxlims(find(any(oldaxlims(:,2:7)'~=lims)),:);
tomwalters@0 896 if ~isempty(ARROW_AXLIMITS),
tomwalters@0 897 warning(arrow_warnlimits(ARROW_AXLIMITS,narrows));
tomwalters@0 898 end;
tomwalters@0 899 end;
tomwalters@0 900 else,
tomwalters@0 901 % don't create the patch, just return the data
tomwalters@0 902 h=x;
tomwalters@0 903 yy=y;
tomwalters@0 904 zz=z;
tomwalters@0 905 end;
tomwalters@0 906 function out = arrow_defcheck(in,def,prop)
tomwalters@0 907 % check if we got 'default' values
tomwalters@0 908 out = in;
tomwalters@0 909 if ~isstr(in), return; end;
tomwalters@0 910 if size(in,1)==1 & strncmp(lower(in),'def',3),
tomwalters@0 911 out = def;
tomwalters@0 912 elseif ~isempty(prop),
tomwalters@0 913 error([upper(mfilename) ' does not recognize ''' in(:)' ''' as a valid ''' prop ''' string.']);
tomwalters@0 914 end;
tomwalters@0 915 function [H,oldaxlims,errstr] = arrow_clicks(H,ud,x,y,z,ax,oldaxlims)
tomwalters@0 916 % handle choosing arrow Start and/or Stop locations if necessary
tomwalters@0 917 errstr = '';
tomwalters@0 918 if isempty(H)|isempty(ud)|isempty(x), return; end;
tomwalters@0 919 % determine which (if any) need Start and/or Stop
tomwalters@0 920 needStart = all(isnan(ud(:,1:3)'))';
tomwalters@0 921 needStop = all(isnan(ud(:,4:6)'))';
tomwalters@0 922 mask = any(needStart|needStop);
tomwalters@0 923 if ~any(mask), return; end;
tomwalters@0 924 ud(~mask,:)=[]; ax(:,~mask)=[];
tomwalters@0 925 x(:,~mask)=[]; y(:,~mask)=[]; z(:,~mask)=[];
tomwalters@0 926 % make them invisible for the time being
tomwalters@0 927 set(H,'Visible','off');
tomwalters@0 928 % save the current axes and limits modes; set to manual for the time being
tomwalters@0 929 oldAx = gca;
tomwalters@0 930 limModes=get(ax(:),{'XLimMode','YLimMode','ZLimMode'});
tomwalters@0 931 set(ax(:),{'XLimMode','YLimMode','ZLimMode'},{'manual','manual','manual'});
tomwalters@0 932 % loop over each arrow that requires attention
tomwalters@0 933 jj = find(mask);
tomwalters@0 934 for ii=1:length(jj),
tomwalters@0 935 h = H(jj(ii));
tomwalters@0 936 axes(ax(ii));
tomwalters@0 937 % figure out correct call
tomwalters@0 938 if needStart(ii), prop='Start'; else, prop='Stop'; end;
tomwalters@0 939 [wasInterrupted,errstr] = arrow_click(needStart(ii)&needStop(ii),h,prop,ax(ii));
tomwalters@0 940 % handle errors and control-C
tomwalters@0 941 if wasInterrupted,
tomwalters@0 942 delete(H(jj(ii:end)));
tomwalters@0 943 H(jj(ii:end))=[];
tomwalters@0 944 oldaxlims(jj(ii:end),:)=[];
tomwalters@0 945 break;
tomwalters@0 946 end;
tomwalters@0 947 end;
tomwalters@0 948 % restore the axes and limit modes
tomwalters@0 949 axes(oldAx);
tomwalters@0 950 set(ax(:),{'XLimMode','YLimMode','ZLimMode'},limModes);
tomwalters@0 951 function [wasInterrupted,errstr] = arrow_click(lockStart,H,prop,ax)
tomwalters@0 952 % handle the clicks for one arrow
tomwalters@0 953 fig = get(ax,'Parent');
tomwalters@0 954 % save some things
tomwalters@0 955 oldFigProps = {'Pointer','WindowButtonMotionFcn','WindowButtonUpFcn'};
tomwalters@0 956 oldFigValue = get(fig,oldFigProps);
tomwalters@0 957 oldArrowProps = {'EraseMode'};
tomwalters@0 958 oldArrowValue = get(H,oldArrowProps);
tomwalters@0 959 set(H,'EraseMode','background'); %because 'xor' makes shaft invisible unless Width>1
tomwalters@0 960 global ARROW_CLICK_H ARROW_CLICK_PROP ARROW_CLICK_AX ARROW_CLICK_USE_Z
tomwalters@0 961 ARROW_CLICK_H=H; ARROW_CLICK_PROP=prop; ARROW_CLICK_AX=ax;
tomwalters@0 962 ARROW_CLICK_USE_Z=~arrow_is2DXY(ax)|~arrow_planarkids(ax);
tomwalters@0 963 set(fig,'Pointer','crosshair');
tomwalters@0 964 % set up the WindowButtonMotion so we can see the arrow while moving around
tomwalters@0 965 set(fig,'WindowButtonUpFcn','set(gcf,''WindowButtonUpFcn'','''')', ...
tomwalters@0 966 'WindowButtonMotionFcn','');
tomwalters@0 967 if ~lockStart,
tomwalters@0 968 set(H,'Visible','on');
tomwalters@0 969 set(fig,'WindowButtonMotionFcn',[mfilename '(''callback'',''motion'');']);
tomwalters@0 970 end;
tomwalters@0 971 % wait for the button to be pressed
tomwalters@0 972 [wasKeyPress,wasInterrupted,errstr] = arrow_wfbdown(fig);
tomwalters@0 973 % if we wanted to click-drag, set the Start point
tomwalters@0 974 if lockStart & ~wasInterrupted,
tomwalters@0 975 pt = arrow_point(ARROW_CLICK_AX,ARROW_CLICK_USE_Z);
tomwalters@0 976 feval(mfilename,H,'Start',pt,'Stop',pt);
tomwalters@0 977 set(H,'Visible','on');
tomwalters@0 978 ARROW_CLICK_PROP='Stop';
tomwalters@0 979 set(fig,'WindowButtonMotionFcn',[mfilename '(''callback'',''motion'');']);
tomwalters@0 980 % wait for the mouse button to be released
tomwalters@0 981 eval('waitfor(fig,''WindowButtonUpFcn'','''');','wasInterrupted=1;');
tomwalters@0 982 if wasInterrupted, errstr=lasterr; end;
tomwalters@0 983 end;
tomwalters@0 984 if ~wasInterrupted, feval(mfilename,'callback','motion'); end;
tomwalters@0 985 % restore some things
tomwalters@0 986 set(gcf,oldFigProps,oldFigValue);
tomwalters@0 987 set(H,oldArrowProps,oldArrowValue);
tomwalters@0 988 function arrow_callback(varargin)
tomwalters@0 989 % handle redrawing callbacks
tomwalters@0 990 if nargin==0, return; end;
tomwalters@0 991 str = varargin{1};
tomwalters@0 992 if ~isstr(str), error([upper(mfilename) ' got an invalid Callback command.']); end;
tomwalters@0 993 s = lower(str);
tomwalters@0 994 if strcmp(s,'motion'),
tomwalters@0 995 % motion callback
tomwalters@0 996 global ARROW_CLICK_H ARROW_CLICK_PROP ARROW_CLICK_AX ARROW_CLICK_USE_Z
tomwalters@0 997 feval(mfilename,ARROW_CLICK_H,ARROW_CLICK_PROP,arrow_point(ARROW_CLICK_AX,ARROW_CLICK_USE_Z));
tomwalters@0 998 drawnow;
tomwalters@0 999 else,
tomwalters@0 1000 error([upper(mfilename) ' does not recognize ''' str(:).' ''' as a valid Callback option.']);
tomwalters@0 1001 end;
tomwalters@0 1002 function out = arrow_point(ax,use_z)
tomwalters@0 1003 % return the point on the given axes
tomwalters@0 1004 if nargin==0, ax=gca; end;
tomwalters@0 1005 if nargin<2, use_z=~arrow_is2DXY(ax)|~arrow_planarkids(ax); end;
tomwalters@0 1006 out = get(ax,'CurrentPoint');
tomwalters@0 1007 out = out(1,:);
tomwalters@0 1008 if ~use_z, out=out(1:2); end;
tomwalters@0 1009 function [wasKeyPress,wasInterrupted,errstr] = arrow_wfbdown(fig)
tomwalters@0 1010 % wait for button down ignoring object ButtonDownFcn's
tomwalters@0 1011 if nargin==0, fig=gcf; end;
tomwalters@0 1012 errstr = '';
tomwalters@0 1013 % save ButtonDownFcn values
tomwalters@0 1014 objs = findobj(fig);
tomwalters@0 1015 buttonDownFcns = get(objs,'ButtonDownFcn');
tomwalters@0 1016 mask=~strcmp(buttonDownFcns,''); objs=objs(mask); buttonDownFcns=buttonDownFcns(mask);
tomwalters@0 1017 set(objs,'ButtonDownFcn','');
tomwalters@0 1018 % save other figure values
tomwalters@0 1019 figProps = {'KeyPressFcn','WindowButtonDownFcn'};
tomwalters@0 1020 figValue = get(fig,figProps);
tomwalters@0 1021 % do the real work
tomwalters@0 1022 set(fig,'KeyPressFcn','set(gcf,''KeyPressFcn'','''',''WindowButtonDownFcn'','''');', ...
tomwalters@0 1023 'WindowButtonDownFcn','set(gcf,''WindowButtonDownFcn'','''')');
tomwalters@0 1024 lasterr('');
tomwalters@0 1025 wasInterrupted=0; eval('waitfor(fig,''WindowButtonDownFcn'','''');','wasInterrupted=1;');
tomwalters@0 1026 wasKeyPress = ~wasInterrupted & strcmp(get(fig,'KeyPressFcn'),'');
tomwalters@0 1027 if wasInterrupted, errstr=lasterr; end;
tomwalters@0 1028 % restore ButtonDownFcn and other figure values
tomwalters@0 1029 set(objs,'ButtonDownFcn',buttonDownFcns);
tomwalters@0 1030 set(fig,figProps,figValue);
tomwalters@0 1031 function [out,is2D] = arrow_is2DXY(ax)
tomwalters@0 1032 % check if axes are 2-D X-Y plots
tomwalters@0 1033 % may not work for modified camera angles, etc.
tomwalters@0 1034 out = logical(zeros(size(ax))); % 2-D X-Y plots
tomwalters@0 1035 is2D = out; % any 2-D plots
tomwalters@0 1036 views = get(ax(:),{'View'});
tomwalters@0 1037 views = cat(1,views{:});
tomwalters@0 1038 out(:) = abs(views(:,2))==90;
tomwalters@0 1039 is2D(:) = out(:) | all(rem(views',90)==0)';
tomwalters@0 1040 function out = arrow_planarkids(ax)
tomwalters@0 1041 % check if axes descendents all have empty ZData (lines,patches,surfaces)
tomwalters@0 1042 out = logical(ones(size(ax)));
tomwalters@0 1043 allkids = get(ax(:),{'Children'});
tomwalters@0 1044 for k=1:length(allkids),
tomwalters@0 1045 kids = get([findobj(allkids{k},'flat','Type','line')
tomwalters@0 1046 findobj(allkids{k},'flat','Type','patch')
tomwalters@0 1047 findobj(allkids{k},'flat','Type','surface')],{'ZData'});
tomwalters@0 1048 for j=1:length(kids),
tomwalters@0 1049 if ~isempty(kids{j}), out(k)=logical(0); break; end;
tomwalters@0 1050 end;
tomwalters@0 1051 end;
tomwalters@0 1052 function arrow_fixlimits(axlimits)
tomwalters@0 1053 % reset the axis limits as necessary
tomwalters@0 1054 if isempty(axlimits), disp([upper(mfilename) ' does not remember any axis limits to reset.']); end;
tomwalters@0 1055 for k=1:size(axlimits,1),
tomwalters@0 1056 if any(get(axlimits(k,1),'XLim')~=axlimits(k,2:3)), set(axlimits(k,1),'XLim',axlimits(k,2:3)); end;
tomwalters@0 1057 if any(get(axlimits(k,1),'YLim')~=axlimits(k,4:5)), set(axlimits(k,1),'YLim',axlimits(k,4:5)); end;
tomwalters@0 1058 if any(get(axlimits(k,1),'ZLim')~=axlimits(k,6:7)), set(axlimits(k,1),'ZLim',axlimits(k,6:7)); end;
tomwalters@0 1059 end;
tomwalters@0 1060 function out = arrow_WarpToFill(notstretched,manualcamera,curax)
tomwalters@0 1061 % check if we are in "WarpToFill" mode.
tomwalters@0 1062 out = strcmp(get(curax,'WarpToFill'),'on');
tomwalters@0 1063 % 'WarpToFill' is undocumented, so may need to replace this by
tomwalters@0 1064 % out = ~( any(notstretched) & any(manualcamera) );
tomwalters@0 1065 function out = arrow_warnlimits(axlimits,narrows)
tomwalters@0 1066 % create a warning message if we've changed the axis limits
tomwalters@0 1067 msg = '';
tomwalters@0 1068 switch (size(axlimits,1))
tomwalters@0 1069 case 1, msg='';
tomwalters@0 1070 case 2, msg='on two axes ';
tomwalters@0 1071 otherwise, msg='on several axes ';
tomwalters@0 1072 end;
tomwalters@0 1073 msg = [upper(mfilename) ' changed the axis limits ' msg ...
tomwalters@0 1074 'when adding the arrow'];
tomwalters@0 1075 if (narrows>1), msg=[msg 's']; end;
tomwalters@0 1076 out = [msg '.' sprintf('\n') ' Call ' upper(mfilename) ...
tomwalters@0 1077 ' FIXLIMITS to reset them now.'];
tomwalters@0 1078 function arrow_copyprops(fm,to)
tomwalters@0 1079 % copy line properties to patches
tomwalters@0 1080 props = {'EraseMode','LineStyle','LineWidth','Marker','MarkerSize',...
tomwalters@0 1081 'MarkerEdgeColor','MarkerFaceColor','ButtonDownFcn', ...
tomwalters@0 1082 'Clipping','DeleteFcn','BusyAction','HandleVisibility', ...
tomwalters@0 1083 'Selected','SelectionHighlight','Visible'};
tomwalters@0 1084 lineprops = {'Color', props{:}};
tomwalters@0 1085 patchprops = {'EdgeColor',props{:}};
tomwalters@0 1086 patch2props = {'FaceColor',patchprops{:}};
tomwalters@0 1087 fmpatch = strcmp(get(fm,'Type'),'patch');
tomwalters@0 1088 topatch = strcmp(get(to,'Type'),'patch');
tomwalters@0 1089 set(to( fmpatch& topatch),patch2props,get(fm( fmpatch& topatch),patch2props)); %p->p
tomwalters@0 1090 set(to(~fmpatch&~topatch),lineprops, get(fm(~fmpatch&~topatch),lineprops )); %l->l
tomwalters@0 1091 set(to( fmpatch&~topatch),lineprops, get(fm( fmpatch&~topatch),patchprops )); %p->l
tomwalters@0 1092 set(to(~fmpatch& topatch),patchprops, get(fm(~fmpatch& topatch),lineprops) ,'FaceColor','none'); %l->p
tomwalters@0 1093 function arrow_props
tomwalters@0 1094 % display further help info about ARROW properties
tomwalters@0 1095 c = sprintf('\n');
tomwalters@0 1096 disp([c ...
tomwalters@0 1097 'ARROW Properties: Default values are given in [square brackets], and other' c ...
tomwalters@0 1098 ' acceptable equivalent property names are in (parenthesis).' c c ...
tomwalters@0 1099 ' Start The starting points. For N arrows, B' c ...
tomwalters@0 1100 ' this should be a Nx2 or Nx3 matrix. /|\ ^' c ...
tomwalters@0 1101 ' Stop The end points. For N arrows, this /|||\ |' c ...
tomwalters@0 1102 ' should be a Nx2 or Nx3 matrix. //|||\\ L|' c ...
tomwalters@0 1103 ' Length Length of the arrowhead (in pixels on ///|||\\\ e|' c ...
tomwalters@0 1104 ' screen, points on a page). [16] (Len) ////|||\\\\ n|' c ...
tomwalters@0 1105 ' BaseAngle Angle (degrees) of the base angle /////|D|\\\\\ g|' c ...
tomwalters@0 1106 ' ADE. For a simple stick arrow, use //// ||| \\\\ t|' c ...
tomwalters@0 1107 ' BaseAngle=TipAngle. [90] (Base) /// ||| \\\ h|' c ...
tomwalters@0 1108 ' TipAngle Angle (degrees) of tip angle ABC. //<----->|| \\ |' c ...
tomwalters@0 1109 ' [16] (Tip) / base ||| \ V' c ...
tomwalters@0 1110 ' Width Width of the base in pixels. Not E angle ||<-------->C' c ...
tomwalters@0 1111 ' the ''LineWidth'' prop. [0] (Wid) |||tipangle' c ...
tomwalters@0 1112 ' Page If provided, non-empty, and not NaN, |||' c ...
tomwalters@0 1113 ' this causes ARROW to use hardcopy |||' c ...
tomwalters@0 1114 ' rather than onscreen proportions. A' c ...
tomwalters@0 1115 ' This is important if screen aspect --> <-- width' c ...
tomwalters@0 1116 ' ratio and hardcopy aspect ratio are ----CrossDir---->' c ...
tomwalters@0 1117 ' vastly different. []' c...
tomwalters@0 1118 ' CrossDir A vector giving the direction towards which the fletches' c ...
tomwalters@0 1119 ' on the arrow should go. [computed such that it is perpen-' c ...
tomwalters@0 1120 ' dicular to both the arrow direction and the view direction' c ...
tomwalters@0 1121 ' (i.e., as if it was pasted on a normal 2-D graph)] (Note' c ...
tomwalters@0 1122 ' that CrossDir is a vector. Also note that if an axis is' c ...
tomwalters@0 1123 ' plotted on a log scale, then the corresponding component' c ...
tomwalters@0 1124 ' of CrossDir must also be set appropriately, i.e., to 1 for' c ...
tomwalters@0 1125 ' no change in that direction, >1 for a positive change, >0' c ...
tomwalters@0 1126 ' and <1 for negative change.)' c ...
tomwalters@0 1127 ' NormalDir A vector normal to the fletch direction (CrossDir is then' c ...
tomwalters@0 1128 ' computed by the vector cross product [Line]x[NormalDir]). []' c ...
tomwalters@0 1129 ' (Note that NormalDir is a vector. Unlike CrossDir,' c ...
tomwalters@0 1130 ' NormalDir is used as is regardless of log-scaled axes.)' c ...
tomwalters@0 1131 ' Ends Set which end has an arrowhead. Valid values are ''none'',' c ...
tomwalters@0 1132 ' ''stop'', ''start'', and ''both''. [''stop''] (End)' c...
tomwalters@0 1133 ' ObjectHandles Vector of handles to previously-created arrows to be' c ...
tomwalters@0 1134 ' updated or line objects to be converted to arrows.' c ...
tomwalters@0 1135 ' [] (Object,Handle)' c ]);
tomwalters@0 1136 function out = arrow_demo
tomwalters@0 1137 % demo
tomwalters@0 1138 % create the data
tomwalters@0 1139 [x,y,z] = peaks;
tomwalters@0 1140 [ddd,out.iii]=max(z(:));
tomwalters@0 1141 out.axlim = [min(x(:)) max(x(:)) min(y(:)) max(y(:)) min(z(:)) max(z(:))];
tomwalters@0 1142
tomwalters@0 1143 % modify it by inserting some NaN's
tomwalters@0 1144 [m,n] = size(z);
tomwalters@0 1145 m = floor(m/2);
tomwalters@0 1146 n = floor(n/2);
tomwalters@0 1147 z(1:m,1:n) = NaN*ones(m,n);
tomwalters@0 1148
tomwalters@0 1149 % graph it
tomwalters@0 1150 clf('reset');
tomwalters@0 1151 out.hs=surf(x,y,z);
tomwalters@0 1152 out.x=x; out.y=y; out.z=z;
tomwalters@0 1153 xlabel('x'); ylabel('y');
tomwalters@0 1154
tomwalters@0 1155 function h = arrow_demo3(in)
tomwalters@0 1156 % set the view
tomwalters@0 1157 axlim = in.axlim;
tomwalters@0 1158 axis(axlim);
tomwalters@0 1159 zlabel('z');
tomwalters@0 1160 %set(in.hs,'FaceColor','interp');
tomwalters@0 1161 view(viewmtx(-37.5,30,20));
tomwalters@0 1162 title(['Demo of the capabilities of the ARROW function in 3-D']);
tomwalters@0 1163
tomwalters@0 1164 % Normal blue arrow
tomwalters@0 1165 h1 = feval(mfilename,[axlim(1) axlim(4) 4],[-.8 1.2 4], ...
tomwalters@0 1166 'EdgeColor','b','FaceColor','b');
tomwalters@0 1167
tomwalters@0 1168 % Normal white arrow, clipped by the surface
tomwalters@0 1169 h2 = feval(mfilename,axlim([1 4 6]),[0 2 4]);
tomwalters@0 1170 t=text(-2.4,2.7,7.7,'arrow clipped by surf');
tomwalters@0 1171
tomwalters@0 1172 % Baseangle<90
tomwalters@0 1173 h3 = feval(mfilename,[3 .125 3.5],[1.375 0.125 3.5],30,50);
tomwalters@0 1174 t2=text(3.1,.125,3.5,'local maximum');
tomwalters@0 1175
tomwalters@0 1176 % Baseangle<90, fill and edge colors different
tomwalters@0 1177 h4 = feval(mfilename,axlim(1:2:5)*.5,[0 0 0],36,60,25, ...
tomwalters@0 1178 'EdgeColor','b','FaceColor','c');
tomwalters@0 1179 t3=text(axlim(1)*.5,axlim(3)*.5,axlim(5)*.5-.75,'origin');
tomwalters@0 1180 set(t3,'HorizontalAlignment','center');
tomwalters@0 1181
tomwalters@0 1182 % Baseangle>90, black fill
tomwalters@0 1183 h5 = feval(mfilename,[-2.9 2.9 3],[-1.3 .4 3.2],30,120,[],6, ...
tomwalters@0 1184 'EdgeColor','r','FaceColor','k','LineWidth',2);
tomwalters@0 1185
tomwalters@0 1186 % Baseangle>90, no fill
tomwalters@0 1187 h6 = feval(mfilename,[-2.9 2.9 1.3],[-1.3 .4 1.5],30,120,[],6, ...
tomwalters@0 1188 'EdgeColor','r','FaceColor','none','LineWidth',2);
tomwalters@0 1189
tomwalters@0 1190 % Stick arrow
tomwalters@0 1191 h7 = feval(mfilename,[-1.6 -1.65 -6.5],[0 -1.65 -6.5],[],16,16);
tomwalters@0 1192 t4=text(-1.5,-1.65,-7.25,'global mininum');
tomwalters@0 1193 set(t4,'HorizontalAlignment','center');
tomwalters@0 1194
tomwalters@0 1195 % Normal, black fill
tomwalters@0 1196 h8 = feval(mfilename,[-1.4 0 -7.2],[-1.4 0 -3],'FaceColor','k');
tomwalters@0 1197 t5=text(-1.5,0,-7.75,'local minimum');
tomwalters@0 1198 set(t5,'HorizontalAlignment','center');
tomwalters@0 1199
tomwalters@0 1200 % Gray fill, crossdir specified, 'LineStyle' --
tomwalters@0 1201 h9 = feval(mfilename,[-3 2.2 -6],[-3 2.2 -.05],36,[],27,6,[],[0 -1 0], ...
tomwalters@0 1202 'EdgeColor','k','FaceColor',.75*[1 1 1],'LineStyle','--');
tomwalters@0 1203
tomwalters@0 1204 % a series of normal arrows, linearly spaced, crossdir specified
tomwalters@0 1205 h10y=(0:4)'/3;
tomwalters@0 1206 h10 = feval(mfilename,[-3*ones(size(h10y)) h10y -6.5*ones(size(h10y))], ...
tomwalters@0 1207 [-3*ones(size(h10y)) h10y -.05*ones(size(h10y))], ...
tomwalters@0 1208 12,[],[],[],[],[0 -1 0]);
tomwalters@0 1209
tomwalters@0 1210 % a series of normal arrows, linearly spaced
tomwalters@0 1211 h11x=(1:.33:2.8)';
tomwalters@0 1212 h11 = feval(mfilename,[h11x -3*ones(size(h11x)) 6.5*ones(size(h11x))], ...
tomwalters@0 1213 [h11x -3*ones(size(h11x)) -.05*ones(size(h11x))]);
tomwalters@0 1214
tomwalters@0 1215 % series of magenta arrows, radially oriented, crossdir specified
tomwalters@0 1216 h12x=2; h12y=-3; h12z=axlim(5)/2; h12xr=1; h12zr=h12z; ir=.15;or=.81;
tomwalters@0 1217 h12t=(0:11)'/6*pi;
tomwalters@0 1218 h12 = feval(mfilename, ...
tomwalters@0 1219 [h12x+h12xr*cos(h12t)*ir h12y*ones(size(h12t)) ...
tomwalters@0 1220 h12z+h12zr*sin(h12t)*ir],[h12x+h12xr*cos(h12t)*or ...
tomwalters@0 1221 h12y*ones(size(h12t)) h12z+h12zr*sin(h12t)*or], ...
tomwalters@0 1222 10,[],[],[],[], ...
tomwalters@0 1223 [-h12xr*sin(h12t) zeros(size(h12t)) h12zr*cos(h12t)],...
tomwalters@0 1224 'FaceColor','none','EdgeColor','m');
tomwalters@0 1225
tomwalters@0 1226 % series of normal arrows, tangentially oriented, crossdir specified
tomwalters@0 1227 or13=.91; h13t=(0:.5:12)'/6*pi;
tomwalters@0 1228 locs = [h12x+h12xr*cos(h13t)*or13 h12y*ones(size(h13t)) h12z+h12zr*sin(h13t)*or13];
tomwalters@0 1229 h13 = feval(mfilename,locs(1:end-1,:),locs(2:end,:),6);
tomwalters@0 1230
tomwalters@0 1231 % arrow with no line ==> oriented downwards
tomwalters@0 1232 h14 = feval(mfilename,[3 3 .100001],[3 3 .1],30);
tomwalters@0 1233 t6=text(3,3,3.6,'no line'); set(t6,'HorizontalAlignment','center');
tomwalters@0 1234
tomwalters@0 1235 % arrow with arrowheads at both ends
tomwalters@0 1236 h15 = feval(mfilename,[-.5 -3 -3],[1 -3 -3],'Ends','both','FaceColor','g', ...
tomwalters@0 1237 'Length',20,'Width',3,'CrossDir',[0 0 1],'TipAngle',25);
tomwalters@0 1238
tomwalters@0 1239 h=[h1;h2;h3;h4;h5;h6;h7;h8;h9;h10;h11;h12;h13;h14;h15];
tomwalters@0 1240 function h = arrow_demo2(in)
tomwalters@0 1241 axlim = in.axlim;
tomwalters@0 1242 dolog = 1;
tomwalters@0 1243 if (dolog), set(in.hs,'YData',10.^get(in.hs,'YData')); end;
tomwalters@0 1244 shading('interp');
tomwalters@0 1245 view(2);
tomwalters@0 1246 title(['Demo of the capabilities of the ARROW function in 2-D']);
tomwalters@0 1247 hold on; [C,H]=contour(in.x,in.y,in.z,20,'-'); hold off;
tomwalters@0 1248 for k=H',
tomwalters@0 1249 set(k,'ZData',(axlim(6)+1)*ones(size(get(k,'XData'))),'Color','k');
tomwalters@0 1250 if (dolog), set(k,'YData',10.^get(k,'YData')); end;
tomwalters@0 1251 end;
tomwalters@0 1252 if (dolog), axis([axlim(1:2) 10.^axlim(3:4)]); set(gca,'YScale','log');
tomwalters@0 1253 else, axis(axlim(1:4)); end;
tomwalters@0 1254
tomwalters@0 1255 % Normal blue arrow
tomwalters@0 1256 start = [axlim(1) axlim(4) axlim(6)+2];
tomwalters@0 1257 stop = [in.x(in.iii) in.y(in.iii) axlim(6)+2];
tomwalters@0 1258 if (dolog), start(:,2)=10.^start(:,2); stop(:,2)=10.^stop(:,2); end;
tomwalters@0 1259 h1 = feval(mfilename,start,stop,'EdgeColor','b','FaceColor','b');
tomwalters@0 1260
tomwalters@0 1261 % three arrows with varying fill, width, and baseangle
tomwalters@0 1262 start = [-3 -3 10; -3 -1.5 10; -1.5 -3 10];
tomwalters@0 1263 stop = [-.03 -.03 10; -.03 -1.5 10; -1.5 -.03 10];
tomwalters@0 1264 if (dolog), start(:,2)=10.^start(:,2); stop(:,2)=10.^stop(:,2); end;
tomwalters@0 1265 h2 = feval(mfilename,start,stop,24,[90;60;120],[],[0;0;4],'Ends',str2mat('both','stop','stop'));
tomwalters@0 1266 set(h2(2),'EdgeColor',[0 .35 0],'FaceColor',[0 .85 .85]);
tomwalters@0 1267 set(h2(3),'EdgeColor','r','FaceColor',[1 .5 1]);
tomwalters@0 1268 h=[h1;h2];
tomwalters@0 1269 function out = trueornan(x)
tomwalters@0 1270 if isempty(x),
tomwalters@0 1271 out=x;
tomwalters@0 1272 else,
tomwalters@0 1273 out = isnan(x);
tomwalters@0 1274 out(~out) = x(~out);
tomwalters@0 1275 end;