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 |
|
---|