comparison toolboxes/FullBNT-1.0.7/GraphViz/arrow.m @ 0:e9a9cd732c1e tip

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