NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/gliderproc/trunk/MATLAB/plots/patchleg.m

Revision 495 (checked in by cbc, 12 years ago)

Initial import of Stark code.

Line 
1 function [leghandle,labelhandles]=patchleg(varargin)
2 %LEGEND Graph legend.
3 %   LEGEND(string1,string2,string3, ...) puts a legend on the current plot
4 %   using the specified strings as labels. LEGEND works on line graphs,
5 %   bar graphs, pie graphs, ribbon plots, etc.  You can label any
6 %   solid-colored patch or surface object.  The fontsize and fontname for
7 %   the legend strings matches the axes fontsize and fontname.
8 %
9 %   LEGEND(H,string1,string2,string3, ...) puts a legend on the plot
10 %   containing the handles in the vector H using the specified strings as
11 %   labels for the corresponding handles.
12 %
13 %   LEGEND(M), where M is a string matrix or cell array of strings, and
14 %   LEGEND(H,M) where H is a vector of handles to lines and patches also
15 %   works.
16 %
17 %   LEGEND(AX,...) puts a legend on the axes with handle AX.
18 %
19 %   LEGEND OFF removes the legend from the current axes.
20 %   LEGEND(AX,'off') removes the legend from the axis AX.
21 %
22 %   LEGH = LEGEND returns the handle to legend on the current axes or
23 %   empty if none exists.
24 %
25 %   LEGEND with no arguments refreshes all the legends in the current
26 %   figure (if any).  LEGEND(LEGH) refreshes the specified legend.
27 %
28 %   LEGEND(...,Pos) places the legend in the specified
29 %   location:
30 %       0 = Automatic "best" placement (least conflict with data)
31 %       1 = Upper right-hand corner (default)
32 %       2 = Upper left-hand corner
33 %       3 = Lower left-hand corner
34 %       4 = Lower right-hand corner
35 %      -1 = To the right of the plot
36 %
37 %   To move the legend, press the left mouse button on the legend and drag
38 %   to the desired location. Double clicking on a label allows you to edit
39 %   the label.
40 %
41 %   [LEGH,OBJH] = LEGEND(...) returns a handle LEGH to the legend axes and
42 %   vector OBJH containing the text, line, and patch handles.
43 %
44 %   LEGEND will try to install a ResizeFcn on the figure if it hasn't been
45 %   defined before.  This resize function will try to keep the legend the
46 %   same size.
47 %
48 %   Examples:
49 %       x = 0:.2:12;
50 %       plot(x,bessel(1,x),x,bessel(2,x),x,bessel(3,x));
51 %       legend('First','Second','Third');
52 %       legend('First','Second','Third',-1)
53 %
54 %       b = bar(rand(10,5),'stacked'); colormap(summer); hold on
55 %       x = plot(1:10,5*rand(10,1),'marker','square','markersize',12,...
56 %                'markeredgecolor','y','markerfacecolor',[.6 0 .6],...
57 %                'linestyle','-','color','r','linewidth',2); hold off
58 %       legend([b,x],'Carrots','Peas','Peppers','Green Beans',...
59 %                 'Cucumbers','Eggplant')       
60 %
61 %   See also PLOT.
62 %
63 % Calls: none
64
65 %   D. Thomas 5/6/93
66 %             9/6/95 
67 %   Rich Radke 6/17/96 Latest Update
68 %   Copyright (c) 1984-98 by The MathWorks, Inc.
69 %   $Revision: 5.53 $  $Date: 1998/12/10 22:07:24 $
70
71 %   Private syntax:
72 %
73 %     LEGEND('DeleteLegend') is called from the deleteFcn to remove the legend.
74 %     LEGEND('EditLegend',h) is called from MOVEAXIS to edit the legend labels.
75 %     LEGEND('ShowLegendPlot') is called from MOVEAXIS to set the gco to
76 %     the plot the legend goes with.
77 %     LEGEND('ResizeLegend') is called from the resizeFcn to resize the legend.
78 %
79 %   Obsolete syntax:
80 %
81 %     LEGEND(linetype1,string1,linetype2,string2, ...) specifies
82 %     the line types/colors for each label.
83 %     Linetypes can be any valid PLOT linetype specifying color,
84 %     marker type, and linestyle, such as 'g:o'. 
85
86 narg = nargin;
87 isresize(0);
88
89 %--------------------------
90 % Parse inputs
91 %--------------------------
92
93 % Determine the legend parent axes (ha) is specified
94 if narg > 0 & ~isempty(varargin{1}) & ishandle(varargin{1}) & ...
95         strcmp(get(varargin{1}(1),'type'),'axes') % legend(ax,...)
96     ha = varargin{1}(1);
97     varargin(1) = []; % Remove from list
98     narg = narg - 1;
99     if strcmp(get(ha,'Tag'),'legend') % Use the parent
100       ud = get(ha,'userdata');
101       if isfield(ud,'PlotHandle')
102          ha = ud.PlotHandle;
103       else
104         warning('Can''t put a legend on a legend.')
105         if nargout>0, leghandle = []; labelhandles = []; end
106         return
107       end
108     end
109 else
110   ha = [];
111 end
112
113 if narg==0 % legend
114   if nargout==1, % h = legend
115     if isempty(ha)
116       leghandle = find_legend(find_gca);
117     else
118       leghandle = find_legend(ha);
119     end
120   elseif nargout==0 % legend
121     if isempty(ha)
122       update_all_legends
123     else
124       update_legend(find_legend(ha));
125     end
126   else % [h,objh] = legend
127     if isempty(ha)
128       [leghandle,labelhandles] = find_legend(find_gca);
129     else
130       [leghandle,labelhandles] = find_legend(ha);
131     end
132   end
133   return
134 elseif narg==1 & strcmp(varargin{1},'DeleteLegend'), % legend('DeleteLegend')
135   % Should only be called by the deleteFcn
136   delete_legend(gcbo)
137   if nargout>0, error('Too many outputs.'); end
138   return
139 elseif narg==1 & strcmp(varargin{1},'ResizeLegend'), % legend('ResizeLegend')
140   isresize(1);
141   resize_all_legends(gcbf)
142   isresize(0);
143   if nargout>0, error('Too many outputs.'); end
144   return
145 elseif narg==1 & strcmp(varargin{1},'off'), % legend('off') or legend(AX,'off')
146   if isempty(ha)
147     delete_legend(find_legend(find_gca))
148   else
149     delete_legend(find_legend(ha))
150   end   
151   if nargout>0, error('Too many outputs.'); end
152   return
153 elseif narg==1 & strcmp(varargin{1},'ShowLegendPlot')
154   show_plot
155   return
156 elseif narg==2 & strcmp(varargin{1},'EditLegend')
157   edit_legend(varargin{2})
158   return
159 elseif narg==1 & islegend(varargin{1}) % legend(legh)
160   [hl,labelhandles] = update_legend(varargin{1});
161   if nargout>0, leghandle = hl; end
162   return
163 end
164
165 % Look for legendpos code
166 if isa(varargin{end},'double')
167   legendpos = varargin{end};
168   varargin(end) = [];
169 else
170   legendpos = [];
171 end
172
173 % Determine the active children (kids) and the strings (lstrings)
174 if narg < 1
175   error('Not enough input arguments.');
176 elseif ishandle(varargin{1}) % legend(h,strings,...)
177   kids = varargin{1};
178   if isempty(ha)
179     ha=get(varargin{1}(1),'Parent');
180     if ~strcmp(get(ha,'type'),'axes'),
181       error('Handle must be an axes or child of an axes.');
182     end
183   end
184   if narg==1, error('A string must be supplied for each handle.'); end
185   lstrings = getstrings(varargin(2:end));
186 else % legend(strings,...) or legend(linetype,string,...)
187   if isempty(ha), ha=find_gca; end
188   kids = getchildren(ha);
189   lstrings = getstrings(varargin);
190 end
191
192 % Set default legendpos
193 if isempty(legendpos)
194   if ~isequal(get(ha,'view'),[0 90])
195     legendpos = -1;  % To the right of axis is default for 3-D
196   else
197     legendpos = 1;   % Upper right is default for 2-D
198   end
199 end
200
201 % Remove any existing legend on this plot
202 hl = find_legend;
203 if ~isempty(hl),
204   ud = get(hl,{'userdata'});
205   for i=1:length(ud)
206     if isfield(ud{i},'PlotHandle') & ud{i}.PlotHandle == ha
207       delete_legend(hl)
208     end
209   end
210 end
211
212 if length(kids)==0,
213   warning('Plot empty.')
214   if nargout>0
215     leghandle = []; labelhandles = [];
216   end
217   return
218 end
219
220 [hl,labelhandles] = make_legend(ha,kids,lstrings,legendpos);
221
222 if nargout > 0,
223     leghandle=hl;
224 end
225
226
227 %--------------------------------
228 function [hl,hobjs] = find_legend(ha)
229 %FIND_LEGEND Return current legend handle or error out if none.
230 if nargin==0
231   hl = findobj(allchild(find_gcf),'Tag','legend');
232 else
233   hl = findobj(allchild(find_gcf(ha)),'Tag','legend');
234 end
235 hobjs = [];
236 if nargin>0
237   if length(ha)~=1
238     error('Requires a single axis handle.');
239   end
240   ud = get(hl,{'userdata'});
241   for i=1:length(ud)
242     if isfield(ud{i},'PlotHandle') & ud{i}.PlotHandle == ha
243       hl = hl(i);
244       hobjs = ud{i}.LabelHandles;
245       return
246     end
247   end
248   hl = []; % None found
249   hobjs = [];
250 end
251
252 %-------------------------------
253 function tf = isresize(setting)
254 persistent s
255 if nargin==1
256   s = setting;
257 else
258   tf = s;
259 end
260
261 %--------------------------------
262 function hf = find_gcf(ha)
263 %FIND_GCF Find gcf.
264 %   FIND_GCF Returns the callback figure if there is one otherwise
265 %   it returns the current figure.
266 if nargin==1 & strcmp(get(ha,'type'),'axes')
267   hf = get(ha,'parent');
268 else
269   if isresize
270     hf = gcbf;
271     if isempty(hf),
272       hf = gcf;
273     end
274   else
275     hf = gcf;
276   end
277 end
278
279 %---------------------------------
280 function ha = find_gca(ha)
281 %FIND_GCA Find gca (skipping legend)
282 if nargin==0
283 fig = find_gcf;
284 else
285 fig = find_gcf(ha);
286 end
287 ha = get(fig,'currentaxes');
288 if isempty(ha), ha = gca; end
289 if strcmp(get(ha,'tag'),'legend')
290   ud = get(ha,'userdata');
291   if isfield(ud,'PlotHandle')
292     ha = ud.PlotHandle;
293     % Make sure legend isn't isn't the gca
294     set(fig,'currentaxes',ud.PlotHandle)
295   end
296 end
297
298 %----------------------------------
299 function tf = islegend(h)
300 %ISLEGEND Return true if input is a legend handle
301 if length(h)==1,
302   tf = ishandle(h) & strcmp(get(h,'tag'),'legend');
303 else
304   tf = logical(0);
305 end
306
307
308 %-------------------------------
309 function [hl,labelhandles] = make_legend(ha,Kids,lstrings,legendpos,ud)
310 %MAKE_LEGEND Make legend given parent axes, kids, and strings
311 %
312 %   MAKE_LEGEND(...,ud) is called from the resizeFcn.  In this case
313 %   just update the position of the legend pieces instead of recreating
314 %   it from scratch.
315
316 hf = get(ha,'parent'); % Parent figure
317
318 % Get the legend info structure from the inputs
319 info = legend_info(ha,hf,Kids,lstrings);
320
321 % Remember current state
322 hfold = find_gcf(ha);
323 haold = find_gca(ha);
324 punits=get(hf,'units');
325 aunits=get(ha,'units');
326 if strncmp(get(hf,'NextPlot'),'replace',7),
327   set(hf,'NextPlot','add')
328   oldNextPlot = get(hf,'NextPlot');
329 else
330   oldNextPlot = '';
331 end
332 set(ha,'units','points');
333 set(hf,'units','points');
334
335 % Determine size of legend in figure points
336 fontn = get(ha,'fontname');
337 fonts = get(ha,'fontsize');
338 fonta = get(ha,'fontangle');
339 fontw = get(ha,'fontweight');
340 cap = get(ha,'Position');
341
342 % Symbols are the size of 3 numbers
343 h = text(0,0,'123','fontname',fontn,'fontsize',fonts,...
344          'fontangle',fonta,'fontweight',fontw);
345 set(h,'units','points','visible','off');
346 ext = get(h,'extent');
347 lsym = ext(3);
348 loffset = lsym/3;
349 delete(h);
350
351 % Make box big enough to handle longest string
352 h=text(0,0,{info.label},'fontname',fontn,'fontsize',fonts,...
353        'fontangle',fonta,'fontweight',fontw);
354 set(h,'units','points','visible','off');
355 ext = get(h,'extent');
356 width = ext(3);
357 height = ext(4)/size(get(h,'string'),1);
358 margin = height*0.075;
359 delete(h);
360
361 llen = width + loffset*3 + lsym;
362 lhgt = ext(4) + 2*margin;
363
364 % Reset axis if it had be resized to that getposition works
365 if nargin==5,
366   set(ha,'units','normalized','position',ud.PlotPosition)
367   set(ha,'units','points')
368 end
369  
370 [lpos,axpos] = getposition(ha,legendpos,llen,lhgt);
371
372 % Shrink axes if necessary
373 ud.PlotHandle = ha;
374 set(ha,'units','normalized')
375 ud.PlotPosition = get(ha,'position'); % Remember old axes position
376 set(ha,'units','points')
377 ud.legendpos = legendpos;
378 if ~isempty(axpos)
379   set(ha,'Position',axpos)
380 end
381 set(ha,'units','normalized')
382 ud.AxesPosition = get(ha,'position');
383
384 % Create legend object
385 if find_gcf ~= hf & nargin<5, figure(hf); end
386 if strcmp(get(ha,'color'),'none')
387   acolor = get(hf,'color');
388 else
389   acolor = get(ha,'color');
390 end
391
392 if nargin<5
393   % Create legend axes and DeleteProxy object (an
394   % invisible text object in target axes) so that the
395   % legend will get deleted correctly.
396   ud.DeleteProxy = text('parent',ha,'visible','off', ...
397                         'tag','LegendDeleteProxy',...
398                         'handlevisibility','off');
399   hl=axes('units','points','position',lpos,'box','on','drawmode', ...
400         'fast','nextplot','add','xtick',[-1],'ytick',[-1], ...
401         'xticklabel','','yticklabel','','xlim',[0 1],'ylim',[0 1], ...
402         'clipping','on','color',acolor,'tag','legend','view',[0 90],...
403         'climmode',get(ha,'climmode'),'clim',get(ha,'clim'),...
404         'deletefcn','legend(''DeleteLegend'')');
405   set(hl,'units','normalized')
406   setappdata(hl,'NonDataObject',[]); % Used by DATACHILDREN.M
407   ud.LegendPosition = get(hl,'position');
408   set(ud.DeleteProxy,'deletefcn','eval(''delete(get(gcbo,''''userdata''''))'','''')');
409   set(ud.DeleteProxy,'userdata',hl);
410 else
411   hl = ud.LegendHandle;
412   labelhandles = ud.LabelHandles;
413   set(hl,'units','points','position',lpos);
414   set(hl,'units','normalized')
415   ud.LegendPosition = get(hl,'position');
416 end
417  
418 texthandles = [];
419 objhandles = [];
420
421 nstack = length(info);
422 nrows = size(char(info.label),1);
423
424 % draw text one on chunk so that the text spacing is good
425 label = char(info.label);
426 top = (1-max(1,size(label,1)))/2;
427 if nargin<5
428   texthandles = text('parent',hl,'units','data',...
429        'position',[1-(width+loffset)/llen,1-(1-top)/(nrows+1)],...
430        'string',char(info.label),...
431        'fontname',fontn,'fontweight',fontw,...
432        'fontsize',fonts,'fontangle',fonta,...
433        'ButtonDownFcn','moveaxis');
434 else
435   texthandles = ud.LabelHandles(1);
436   set(texthandles,'units','data',...
437       'position',[1-(width+loffset)/llen,1-(1-top)/(nrows+1)],...
438       'fontname',fontn,'fontsize',fonts);
439 end
440      
441 ext = get(texthandles,'extent');
442 centers = linspace(ext(4)-ext(4)/nrows,0,nrows)+ext(4)/nrows/2 + 0.4*(1-ext(4));
443 edges = linspace(ext(4),0,nrows+1) + 0.4*(1-ext(4));
444 indent = [1 1 -1 -1 1] * ext(4)/nrows/7.5;
445
446 hpos = 2;
447 r = 1;
448 for i=1:nstack
449   % draw lines with markers like this: --*--
450   if strcmp(info(i).objtype,'line')
451      if ~strcmp(info(i).linetype,'none')
452         if nargin<5
453           p = line('parent',hl,'xdata',(loffset+[0 lsym])/llen,...
454                    'ydata',[centers(r) centers(r)],...
455                    'linestyle',info(i).linetype,...
456                    'marker','none',...
457                    'color',info(i).edgecol, ...
458                    'linewidth',info(i).lnwidth,...
459                    'ButtonDownFcn','moveaxis');
460         else
461           p = ud.LabelHandles(hpos);
462           set(p,'xdata',(loffset+[0 lsym])/llen,...
463                 'ydata',[centers(r) centers(r)]);
464           hpos = hpos+1;
465         end
466      else
467        p = [];
468      end
469      if ~strcmp(info(i).marker,'none')
470        if nargin<5
471          p = [p;line('parent',hl,'xdata',(loffset+lsym/2)/llen,...
472                     'ydata',centers(r),...
473                     'color',info(i).edgecol, ...
474                     'linestyle','none',...
475                     'linewidth',info(i).lnwidth,...
476                     'marker',info(i).marker,...
477                     'markeredgecolor',info(i).markedge,...
478                     'markerfacecolor',info(i).markface,...
479                     'markersize',info(i).marksize,...
480                     'ButtonDownFcn','moveaxis')];
481        else
482          p2 = ud.LabelHandles(hpos);
483          set(p2,'xdata',(loffset+lsym/2)/llen,...
484                'ydata',centers(r));
485          hpos = hpos+1;
486          p = [p;p2];
487        end
488      end
489
490   % draw patches
491   elseif strcmp(info(i).objtype,'patch')  | strcmp(info(i).objtype,'surface')
492      % Adjusting ydata to make a thinner box will produce nicer
493      % results if you use patches with markers.
494      if nargin<5
495        p = patch('parent',hl,'xdata',(loffset+[0 lsym lsym 0 0])/llen,...
496                   'ydata',[edges(r) edges(r) edges(r+1) edges(r+1) edges(r)]-indent,...
497                   'linestyle',info(i).linetype,...
498                   'edgecolor',info(i).edgecol, ...
499                   'facecolor',info(i).facecol,...
500                   'linewidth',info(i).lnwidth,...
501                   'marker',info(i).marker,...
502                   'markeredgecolor',info(i).markedge,...
503                   'markerfacecolor',info(i).markface,...
504                   'markersize',info(i).marksize,...
505                   'ButtonDownFcn','moveaxis');
506      else
507        p = ud.LabelHandles(hpos);
508        set(p,'xdata',(loffset+[0 lsym lsym 0 0])/llen,...
509              'ydata',[edges(r) edges(r) edges(r+1) edges(r+1) edges(r)]-indent);
510        hpos = hpos+1;
511      end
512      if strcmp(info(i).facecol,'flat') | strcmp(info(i).edgecol,'flat')
513         c = get(Kids(i),'cdata');
514         k = min(find(finite(c)));
515         if ~isempty(k)
516           set(p,'cdata',c(k)*ones(1,5),...
517                 'cdatamapping',get(Kids(i),'cdatamapping'));
518         end
519      end
520   end
521   objhandles = [objhandles;p];
522   r = r + max(1,size(info(i).label,1));
523 end
524
525 labelhandles = [texthandles;objhandles];
526
527 % Clean up a bit
528
529 set(hf,'currentaxes',haold)
530 set(hf,'units',punits)
531 set(ha,'units',aunits)
532 if (hfold ~= hf) & nargin<5, figure(hfold); end
533 if ~isempty(oldNextPlot)
534   set(hf,'nextplot',oldNextPlot)
535 end
536 ud.handles = Kids;
537 ud.lstrings = {info.label};
538 ud.LabelHandles = labelhandles;
539 ud.LegendHandle = hl;
540 set(hl,'ButtonDownFcn','moveaxis','interruptible','on', ...
541        'busyaction','queue','userdata',ud);
542
543 % Make legend resize itself
544 if isempty(get(hf,'resizefcn')),
545   set(hf,'resizefcn','legend(''ResizeLegend'')')
546 end
547
548 if nargin<5
549   PlaceLegendOnTop(hf,hl,ha)
550 end
551
552 %------------------------------
553 function PlaceLegendOnTop(hf,hl,ha)
554 %PlaceLengendOpTop  Make sure the legend is on top of its axes.
555 ord = findobj(allchild(hf),'flat','type','axes');
556 axpos = find(ord==ha);
557 legpos = find(ord==hl);
558 if legpos ~= axpos-1
559   axes(ord(axpos))
560   axes(ord(legpos))
561   for i=axpos-1:-1:1
562     if i ~= legpos
563       axes(ord(i));
564     end
565   end
566 end
567
568
569 %------------------------------
570 function info = legend_info(ha,hf,Kids,lstrings);
571 %LEGEND_INFO Get legend info from parent axes, Kids, and strings
572 %   INFO = LEGEND_INFO(HA,KIDS,STRINGS) returns a structure array containing
573 %      objtype  -- Type of object 'line', 'patch', or 'surface'
574 %      label    -- label string
575 %      linetype -- linetype;
576 %      edgecol  -- edge color
577 %      facecol  -- face color
578 %      lnwidth  -- line width
579 %      marker   -- marker
580 %      marksize -- markersize
581 %      markedge -- marker edge color
582 %      markface -- marker face color (not used for 'line')
583
584 defaultlinestyle = get(hf,'defaultlinelinestyle');
585 defaultlinecolor = get(hf,'defaultlinecolor');
586 defaultlinewidth = get(hf,'defaultlinelinewidth');
587 defaultlinemarker = get(hf,'defaultlinemarker');
588 defaultlinemarkersize = get(hf,'defaultlinemarkersize');
589 defaultlinemarkerfacecolor = get(hf,'defaultlinemarkerfacecolor');
590 defaultlinemarkeredgecolor = get(hf,'defaultlinemarkeredgecolor');
591 defaultpatchfacecolor = get(hf,'defaultpatchfacecolor');
592
593 linetype = {};
594 edgecol = {};
595 facecol = {};
596 lnwidth = {};
597 marker = {};
598 marksize = {};
599 markedge = {};
600 markface = {};
601
602 % These 8 variables are the important ones.  The only ambiguity is
603 % edgecol/facecol.  For lines, edgecol is the line color and facecol
604 % is unused.  For patches, edgecol/facecol mean the logical thing.
605
606 Kids = Kids(:);  %  Reshape so that we have a column vector of handles.
607 lstrings = lstrings(:);
608
609 % Check for valid handles
610 nonhandles = ~ishandle(Kids);
611 if any(nonhandles)
612 %  warning('Some invalid handles were ignored.')
613   Kids(nonhandles) = [];
614 end
615 if ~isempty(Kids)
616 badhandles = ~(strcmp(get(Kids,'type'),'patch') | ...
617                strcmp(get(Kids,'type'),'line')  | ...
618                strcmp(get(Kids,'type'),'surface'));
619 if any(badhandles)
620   warning(['Some handles to non-lines and/or non-solid color',...
621            ' objects were ignored.'])
622   Kids(badhandles) = [];
623 end
624 end
625
626 % Look for obsolete syntax label(...,LineSpec,Label,LineSpec,Label)
627 % To reduce the number of false hits, we require at least one
628 % line type in the list
629 obsolete = 0;
630 if rem(length(lstrings),2)==0,
631   for i=1:2:length(lstrings)
632     if isempty(lstrings{i}), % Empty lineSpec isn't obsolete syntax
633       obsolete = 0;
634       break
635     end
636     [L,C,M,msg] = colstyle(lstrings{i});
637     if ~isempty(msg), % If any error parsing LineSpec
638        obsolete = 0;
639        break
640      end
641     if ~isempty(L), obsolete = 1; end
642   end
643 end
644
645 if obsolete
646   warning(sprintf(['The syntax LEGEND(linetype1,string1,linetype2,',...
647            'string2, ...) \n is obsolete.  Use LEGEND(H,string1,',...
648            'string2, ...) instead, \n where H is a vector of handles ',...
649            'to the objects you wish to label.']))
650
651   % Every other argument is a linespec
652
653   % Right now we don't check to see if a corresponding linespec is
654   % actually present on the graph, we just draw it anyway as a
655   % simple line with properties color, linestyle, 1-char markertype.
656   % No frills like markersize, marker colors, etc.  Exception: if
657   % a patch is present with facecolor = 'rybcgkwm' and the syntax
658   % legend('g','label') is used, a patch shows up in the legend
659   % instead.  Since this whole functionality is being phased out and
660   % you can do better things using handles, the legend may not look
661   % as nice using this option.
662  
663   objtype = {};
664
665   % Check for an even number of strings
666   if rem(length(lstrings),2)~=0
667     error('Invalid legend syntax.')
668   end
669
670   for i=1:2:length(lstrings)       
671     lnstr=lstrings{i};
672     [lnt,lnc,lnm,msg] = colstyle(lnstr);
673
674     if (isempty(msg) & ~isempty(lnstr)) % Valid linespec
675       % Check for line style
676       if (isempty(lnt))
677         linetype=[linetype,{defaultlinestyle}];
678       else
679         linetype=[linetype,{lnt}];
680       end
681       % Check for line color
682       if (isempty(lnc))
683         edgecol=[edgecol,{defaultlinecolor}];
684         facecol=[facecol,{defaultpatchfacecolor}];
685         objtype = [objtype,{'line'}];
686       else   
687         colspec = ctorgb(lnc);
688         edgecol=[edgecol,{colspec}];
689         facecol=[facecol,{colspec}];
690         if ~isempty(findobj('type','patch','facecolor',colspec)) | ...
691            ~isempty(findobj('type','surface','facecolor',colspec))
692           objtype = [objtype,{'patch'}];
693         else
694           objtype = [objtype,{'line'}];
695         end
696       end
697       % Check for marker
698       if (isempty(lnm)),
699           marker=[marker,{defaultlinemarker}];
700       else
701           marker=[marker,{lnm}];
702       end
703       % Set remaining properties
704       lnwidth = [lnwidth,{defaultlinewidth}];
705       marksize = [marksize,{defaultlinemarkersize}];
706       markedge = [markedge,{defaultlinemarkeredgecolor}];
707       markface = [markface,{defaultlinemarkerfacecolor}];
708     else
709       % Set everything to defaults
710       linetype=[linetype,{defaultlinestyle}];
711       edgecol=[edgecol,{defaultlinecolor}];
712       facecol=[facecol,{defaultpatchfacecolor}];
713       marker=[marker,{defaultlinemarker}];
714       lnwidth = [lnwidth,{defaultlinewidth}];
715       marksize = [marksize,{defaultlinemarkersize}];
716       markedge = [markedge,{defaultlinemarkeredgecolor}];
717       markface = [markface,{defaultlinemarkerfacecolor}];
718       objtype = [objtype,{'line'}];
719     end
720   end
721   lstrings = lstrings(2:2:end);
722
723 else % Normal syntax
724   objtype = get(Kids,{'type'});
725   nk = length(Kids);
726   nstack = length(lstrings);
727   n = min(nstack,nk);
728
729   % Treat empty strings as a single space
730   for i=1:nstack
731     if isempty(lstrings{i})
732       lstrings{i} = ' ';
733     end
734   end
735  
736   % Truncate kids if necessary to match the number of strings
737   objtype = objtype(1:n);
738   Kids = Kids(1:n);
739
740   for i=1:n
741     linetype = [linetype,get(Kids(i),{'LineStyle'})];
742     if strcmp(objtype{i},'line')
743       edgecol = [edgecol,get(Kids(i),{'Color'})];           
744       facecol = [facecol,{'none'}];
745     elseif strcmp(objtype{i},'patch') | strcmp(objtype{i},'surface')
746       [e,f] = patchcol(Kids(i));
747       edgecol = [edgecol,{e}];
748       facecol = [facecol,{f}];
749     end
750     lnwidth = [lnwidth,get(Kids(i),{'LineWidth'})];
751     marker = [marker,get(Kids(i),{'Marker'})];
752     marksize = [marksize,get(Kids(i),{'MarkerSize'})];
753     markedge = [markedge,get(Kids(i),{'MarkerEdgeColor'})];
754     markface = [markface,get(Kids(i),{'MarkerFaceColor'})];
755   end
756
757   if n < nstack,     % More strings than handles
758     objtype(end+1:nstack) = {'none'};
759     linetype(end+1:nstack) = {'none'};
760     edgecol(end+1:nstack) = {'none'};
761     facecol(end+1:nstack) = {'none'};
762     lnwidth(end+1:nstack) = {defaultlinewidth};
763     marker(end+1:nstack) = {'none'};
764     marksize(end+1:nstack) = {defaultlinemarkersize};
765     markedge(end+1:nstack) = {'auto'};
766     markface(end+1:nstack) = {'auto'};
767   end
768 end
769
770 % Limit markersize to axes fontsize
771 fonts = get(ha,'fontsize');
772 marksize([marksize{:}]' > fonts & strcmp(objtype(:),'line')) = {fonts}; 
773 marksize([marksize{:}]' > fonts/2 & strcmp(objtype(:),'patch')) = {fonts/2}; 
774
775 % Package everything into the info structure
776 info = struct('objtype',objtype(:),'label',lstrings(:),...
777               'linetype',linetype(:),'edgecol',edgecol(:),...
778              'facecol',facecol(:),'lnwidth',lnwidth(:),'marker',marker(:),...
779              'marksize',marksize(:),'markedge',markedge(:),'markface',markface(:));
780  
781
782 %-----------------------------
783 function update_all_legends
784 %UPDATE_ALL_LEGENDS Update all legends on this figure
785 legh = find_legend;
786 for i=1:length(legh)
787    update_legend(legh(i));
788 end
789
790 %-------------------------------
791 function [hl,objh] = update_legend(legh)
792 %UPDATE_LEGEND Update an existing legend
793 if isempty(legh),
794   hl = [];
795   objh = [];
796   return
797 end
798 if length(legh)~=1,
799   error('Can only update one legend at a time.')
800 end
801
802 ud = get(legh,'userdata');
803 if ~isfield(ud,'LegendPosition')
804   warning('No legend to update.')
805   hl = []; objh = [];
806   return
807 end
808
809 moved = DidLegendMove(legh);
810
811 units = get(legh,'units');
812 set(legh,'units','points')
813 oldpos = get(legh,'position');
814
815 % Delete old legend
816 delete_legend(legh)
817
818 % Make a new one
819 if moved | length(ud.legendpos)==4
820   [hl,objh] = make_legend(ud.PlotHandle,ud.handles,ud.lstrings,oldpos);
821 else
822   [hl,objh] = make_legend(ud.PlotHandle,ud.handles,ud.lstrings,ud.legendpos);
823 end
824 set(hl,'units',units)
825
826 %----------------------------------------------
827 function moved = DidLegendMove(legh)
828 % Check to see if the legend has been moved
829 ud = get(legh,'userdata');
830 units = get(legh,'units');
831 set(legh,'units','normalized')
832 pos = get(legh,'position');
833 set(legh,'units','pixels')
834 tol = pos ./ get(legh,'position')/2;
835 if any(abs(ud.LegendPosition - pos) > max(tol(3:4)))
836   moved = 1;
837 else
838   moved = 0;
839 end
840 set(legh,'units',units)
841
842 %----------------------------------------------
843 function moved = DidAxesMove(legh)
844 % Check to see if the axes has been moved
845 ud = get(legh,'userdata');
846 ax = ud.PlotHandle;
847 if isfield(ud,'AxesPosition')
848   units = get(ax,'units');
849   set(ax,'units','normalized')
850   pos = get(ax,'position');
851   set(ax,'units','pixels')
852   tol = pos ./ get(ax,'position')/2;
853   if any(abs(ud.AxesPosition - pos) > max(tol(3:4)))
854     moved = 1;
855   else
856     moved = 0;
857   end
858   set(ax,'units',units)
859 else
860   moved = 0;
861 end
862
863 %----------------------------
864 function resize_all_legends(fig)
865 %RESIZE_ALL_LEGENDS Resize all legends in this figure
866 legh = findobj(allchild(fig),'Tag','legend');
867 for i=1:length(legh)
868    resize_legend(legh(i));
869 end
870
871 %----------------------------
872 function resize_legend(legh)
873 %RESIZE_LEGEND Resize all legend in this figure
874
875 ud = get(legh,'userdata');
876 units = get(legh,'units');
877 set(legh,'units','normalized')
878
879 if ~isfield(ud,'LegendPosition')
880   warning('No legend to update.')
881   hl = []; objh = [];
882   return
883 end
884
885 moved = DidLegendMove(legh);
886
887 set(legh,'units','points')
888 oldpos = get(legh,'position');
889
890 % Update the legend
891 if moved | length(ud.legendpos)==4
892   [hl,objh] = make_legend(ud.PlotHandle,ud.handles,ud.lstrings,oldpos,ud);
893 else
894   [hl,objh] = make_legend(ud.PlotHandle,ud.handles,ud.lstrings,ud.legendpos,ud);
895 end
896 set(hl,'units',units)
897
898
899 %----------------------------
900 function delete_legend(ax)
901 %DELETE_LEGEND Remove legend from plot
902 if isempty(ax), return, end
903 ax = ax(1);
904 hf = get(ax,'parent');
905
906 % Remove auto-resize
907 resizefcn = get(hf,'resizefcn');
908 if strcmp(resizefcn,'legend(''ResizeLegend'')')
909   set(hf,'resizefcn','')
910 end
911
912 ud = get(ax,'userdata');
913 if isfield(ud,'PlotHandle') & ishandle(ud.PlotHandle) & ...
914    isfield(ud,'PlotPosition') & ~isempty(ud.PlotPosition) & ...
915    ~DidAxesMove(ax)
916   units = get(ud.PlotHandle,'units');
917   set(ud.PlotHandle,'units','normalized','position',ud.PlotPosition)
918   set(ud.PlotHandle,'units',units)
919 end
920 if isfield(ud,'DeleteProxy') & ishandle(ud.DeleteProxy)
921   delete(ud.DeleteProxy)
922 end
923
924 %-------------------------------
925 function [lpos,axpos] = getposition(ha,legendpos,llen,lhgt)
926 %GETPOS Get position vector from legendpos code
927 stickytol=1;
928 cap=get(ha,'position');
929 edge = 5; % 5 Point edge
930
931 if length(legendpos)==4,
932   % Keep the top at the same place
933   Pos = [legendpos(1) legendpos(2)+legendpos(4)-lhgt];
934 else
935   switch legendpos,
936     case 0,
937        Pos = lscan(ha,llen,lhgt,0,stickytol,-1);
938     case 1,
939        Pos = [cap(1)+cap(3)-llen-edge cap(2)+cap(4)-lhgt-edge];
940     case 2,
941        Pos = [cap(1)+edge cap(2)+cap(4)-lhgt-edge];
942     case 3,
943        Pos = [cap(1)+edge cap(2)+edge];
944     case 4,
945        Pos = [cap(1)+cap(3)-llen-edge cap(2)+edge];
946     otherwise
947        Pos = -1;
948   end
949 end
950 if isequal(Pos,-1)
951   axpos=[cap(1) cap(2) cap(3)-llen-.03 cap(4)];
952   lpos=[cap(1)+cap(3)-llen+edge cap(4)+cap(2)-lhgt llen lhgt];
953   if any(axpos<0) | any(lpos<0),
954     warning('Insufficient space to draw legend.')
955     if any(axpos<0), axpos = []; end
956   end
957 else
958   axpos=[];
959   lpos=[Pos(1) Pos(2) llen lhgt];
960 end
961
962 %--------------------------------------------
963 function Pos = lscan(ha,wdt,hgt,tol,stickytol,hl)
964 %LSCAN  Scan for good legend location.
965
966 debug = 0; % Set to 1 for debugging
967
968 if nargin==6 & hl>0,
969   moved = DidLegendMove(legh);
970   if moved, sticky = 1; else sticky = 0; end
971 else
972   moved = 0;
973 end
974
975 % Calculate tile size
976 cap=get(ha,'Position'); % In Point coordinates
977 xlim=get(ha,'Xlim');
978 ylim=get(ha,'Ylim');
979 H=ylim(2)-ylim(1);
980 W=xlim(2)-xlim(1);
981
982 dh = 0.03*H;
983 dw = 0.03*W;
984 Hgt = hgt*H/cap(4);
985 Wdt = wdt*W/cap(3);
986 Thgt = H/max(1,floor(H/(Hgt+dh)));
987 Twdt = W/max(1,floor(W/(Wdt+dw)));
988 dh = (Thgt - Hgt)/2;
989 dw = (Twdt - Wdt)/2;
990
991 % Get data, points and text
992
993 Kids=get(ha,'children');
994 Xdata=[];Ydata=[];
995 for i=1:length(Kids),
996   type = get(Kids(i),'type');
997   if strcmp(type,'line')
998     xk = get(Kids(i),'Xdata');
999     yk = get(Kids(i),'Ydata');
1000     n = length(xk);
1001     if n < 100 & n > 1
1002       xk = interp1(xk,linspace(1,n,200));
1003       yk = interp1(yk,linspace(1,n,200));
1004     end
1005     Xdata=[Xdata,xk];
1006     Ydata=[Ydata,yk];
1007   elseif strcmp(type,'patch') | strcmp(type,'surface')
1008     xk = get(Kids(i),'Xdata');
1009     yk = get(Kids(i),'Ydata');
1010     Xdata=[Xdata,xk(:)'];
1011     Ydata=[Ydata,yk(:)'];
1012   elseif strcmp(get(Kids(i),'type'),'text'),
1013     tmpunits = get(Kids(i),'units');
1014     set(Kids(i),'units','data')
1015     tmp=get(Kids(i),'Position');
1016     ext=get(Kids(i),'Extent');
1017     set(Kids(i),'units',tmpunits);
1018     Xdata=[Xdata,[tmp(1) tmp(1)+ext(3)]];
1019     Ydata=[Ydata,[tmp(2) tmp(2)+ext(4)]];
1020   end
1021 end
1022 in = finite(Xdata) & finite(Ydata);
1023 Xdata = Xdata(in);
1024 Ydata = Ydata(in);
1025
1026 % Determine # of data points under each "tile"
1027 xp = (0:Twdt:W-Twdt) + xlim(1);
1028 yp = (0:Thgt:H-Thgt) + ylim(1);
1029 wtol = Twdt / 100;
1030 htol = Thgt / 100;
1031 for j=1:length(yp)
1032   if debug, line([xlim(1) xlim(2)],[yp(j) yp(j)],'handlevisibility','off'); end
1033   for i=1:length(xp)
1034     if debug, line([xp(i) xp(i)],[ylim(1) ylim(2)],'handlevisibility','off'); end
1035     pop(j,i) = sum(sum((Xdata > xp(i)-wtol) & (Xdata < xp(i)+Twdt+wtol) & ...
1036                        (Ydata > yp(j)-htol) & (Ydata < yp(j)+Thgt+htol)));   
1037   end
1038 end
1039
1040 if all(pop(:) == 0), pop(1) = 1; end
1041
1042 % Cover up fewest points.  After this while loop, pop will
1043 % be lowest furthest away from the data
1044 while any(pop(:) == 0)
1045   newpop = filter2(ones(3),pop);
1046   if all(newpop(:) ~= 0)
1047     break;
1048   end
1049   pop = newpop;
1050 end
1051 if debug,
1052   figure,
1053   surface('xdata',[xp xp(end)+Twdt],'ydata', [yp yp(end)+Thgt],...
1054           'zdata',zeros(length(yp)+1,length(xp)+1),...
1055           'cdata',pop)
1056   figure(gpf)
1057 end
1058 [j,i] = find(pop == min(pop(:)));
1059 xp =  xp - xlim(1) + dw;
1060 yp =  yp - ylim(1) + dh;
1061 Pos = [cap(1)+xp(i(end))*cap(3)/W
1062        cap(2)+yp(j(end))*cap(4)/H];
1063
1064 %--------------------------------
1065 function Kids = getchildren(ha)
1066 %GETCHILDREN Get children that can have legends
1067 %   Note: by default, lines get labeled before patches;
1068 %   patches get labeled before surfaces.
1069 Kids = flipud([findobj(ha,'type','surface') ;...
1070        findobj(ha,'type','patch') ; findobj(ha,'type','line')]);
1071
1072
1073 %----------------------------
1074 function s = getstrings(c)
1075 %GETSTRINGS Get strings from legend input
1076 %   S = GETSTRINGS(C) where C is a cell array containing the legend
1077 %   input arguments.  Handles three cases:
1078 %      (1) legend(M) -- string matrix
1079 %      (2) legend(C) -- cell array of strings
1080 %      (3) legend(string1,string2,string3,...)
1081 %   Returns a cell array of strings
1082 if length(c)==1 % legend(M) or legend(C)
1083   s = cellstr(c{1});
1084 elseif iscellstr(c)
1085   s = c;
1086 else
1087   error('Legend labels must be strings.');
1088 end
1089
1090
1091 %-----------------------------------
1092 function  out=ctorgb(arg)
1093 %CTORGB Convert color string to rgb value
1094 switch arg
1095   case 'y', out=[1 1 0];
1096   case 'm', out=[1 0 1];
1097   case 'c', out=[0 1 1];
1098   case 'r', out=[1 0 0];
1099   case 'g', out=[0 1 0];
1100   case 'b', out=[0 0 1];
1101   case 'w', out=[1 1 1];
1102   otherwise, out=[0 0 0];
1103 end
1104
1105
1106 %----------------------------------
1107 function  [edgecol,facecol] = patchcol(h)
1108 %PATCHCOL Return edge and facecolor from patch handle
1109 cdat = get(h,'Cdata');
1110 facecol = get(h,'FaceColor');
1111 if strcmp(facecol,'interp') | strcmp(facecol,'texturemap')
1112   if ~all(cdat == cdat(1))
1113      warning(['Legend not supported for patches with FaceColor = ''',facecol,''''])
1114   end
1115   facecol = 'flat';
1116 end
1117 if strcmp(facecol,'flat')
1118   if size(cdat,3) == 1       % Indexed Color
1119     k = find(finite(cdat));
1120     if isempty(k)
1121       facecol = 'none';
1122     end
1123   else                       % RGB values
1124     facecol = reshape(cdat(1,1,:),1,3);
1125   end
1126 end
1127
1128 edgecol = get(h,'EdgeColor');
1129 if strcmp(edgecol,'interp')
1130   if ~all(cdat == cdat(1))
1131      warning('Legend not supported for patches with EdgeColor = ''interp''.')
1132   end 
1133   edgecol = 'flat';
1134 end
1135 if strcmp(edgecol,'flat')
1136   if size(cdat,3) == 1      % Indexed Color
1137     k = find(finite(cdat));
1138     if isempty(k)
1139       edgecol = 'none';
1140     end
1141   else                      % RGB values
1142     edgecol = reshape(cdat(1,1,:),1,3);
1143   end
1144 end
1145
1146
1147 %------------------------
1148 function edit_legend(gco)
1149 %Edit a legend
1150
1151 if ~strcmp(get(gco,'type'),'text'), return, end
1152
1153 % Determine which string was clicked on
1154 units = get(gco,'units');
1155 set(gco,'units','data')
1156 cp = get(gca,'currentpoint');
1157 ext = get(gco,'extent');
1158 nstrings = size(get(gco,'string'),1);
1159
1160 % The k-th string (from the top) was clicked on
1161 k = floor((ext(4) - cp(1,2))/ext(4)*nstrings) + 1;
1162 legh = get(gco,'parent');
1163 ud = get(legh,'userdata');
1164 nrows = cellfun('size',ud.lstrings,1);
1165 crows = cumsum(nrows);
1166
1167 % Determine which string in the cell array was clicked on
1168 active_string = floor( ...
1169             interp1([0 cumsum(nrows)+1],[1:length(nrows) length(nrows)],k));
1170 if isnan(active_string), return, end
1171
1172 % Disable legend buttondownfcn's
1173 savehandle = findobj('buttondownfcn','moveaxis');
1174 set(savehandle,'buttondownfcn','')
1175
1176 % Make a editable string on top of the legend string
1177 pos = get(gco,'position');
1178 y = ext(4) - (crows(active_string)-nrows(active_string)/2)*ext(4)/nstrings;
1179 pos(2) = ext(2) + y;
1180 TextHandle = copyobj(gco,gca);
1181 set(TextHandle,'string',char(ud.lstrings{active_string}),'position',pos, ...
1182                'Editing','on')
1183 waitfor(TextHandle,'Editing');
1184
1185 % Protect against the handles being destroyed during the waitfor
1186 if ishandle(TextHandle) & ishandle(legh) & ishandle(savehandle)
1187   ud.lstrings{active_string} = get(TextHandle,'String');
1188   delete(TextHandle)
1189   set(legh,'UserData',ud)
1190   set(gco,'units',units)
1191
1192   % Enable legend buttondfcn's
1193   set(savehandle,'buttondownfcn','moveaxis')
1194   update_legend(legh);
1195 end
1196
1197 %-----------------------------
1198 function show_plot
1199 %Set the axes this legend goes with to the current axes
1200
1201 if strcmp(get(gca,'tag'),'legend')
1202   ud = get(gca,'userdata');
1203   if isfield(ud,'PlotHandle')
1204     set(find_gcf,'currentaxes',ud.PlotHandle)
1205   end
1206 end
1207
Note: See TracBrowser for help on using the browser.