comparison scripts/plot/appearance/legend.m @ 17572:7bb76a22cde1

maint: Split scripts/plot directory into 4 pieces. scripts/gui : user-interface functions scripts/plot/appearance : functions controlling plot appearance scripts/plot/draw : plotting functions which produce graphs scripts/plot/util : low-level plotting functions and utilities. * scripts/gui/guidata.m, scripts/gui/guihandles.m, scripts/gui/module.mk, scripts/gui/private/__file_filter__.m, scripts/gui/private/__fltk_file_filter__.m, scripts/gui/private/__is_function__.m, scripts/gui/private/__uigetdir_fltk__.m, scripts/gui/private/__uigetfile_fltk__.m, scripts/gui/private/__uiobject_split_args__.m, scripts/gui/private/__uiputfile_fltk__.m, scripts/gui/uicontextmenu.m, scripts/gui/uicontrol.m, scripts/gui/uigetdir.m, scripts/gui/uigetfile.m, scripts/gui/uimenu.m, scripts/gui/uipanel.m, scripts/gui/uipushtool.m, scripts/gui/uiputfile.m, scripts/gui/uiresume.m, scripts/gui/uitoggletool.m, scripts/gui/uitoolbar.m, scripts/gui/uiwait.m, scripts/gui/waitbar.m, scripts/gui/waitforbuttonpress.m: Moved from scripts/plot to scripts/gui * scripts/plot/appearance/__clabel__.m, scripts/plot/appearance/__getlegenddata__.m, scripts/plot/appearance/axis.m, scripts/plot/appearance/box.m, scripts/plot/appearance/caxis.m, scripts/plot/appearance/clabel.m, scripts/plot/appearance/daspect.m, scripts/plot/appearance/diffuse.m, scripts/plot/appearance/grid.m, scripts/plot/appearance/gtext.m, scripts/plot/appearance/hidden.m, scripts/plot/appearance/legend.m, scripts/plot/appearance/orient.m, scripts/plot/appearance/pbaspect.m, scripts/plot/appearance/private/__axis_label__.m, scripts/plot/appearance/private/__axis_limits__.m, scripts/plot/appearance/shading.m, scripts/plot/appearance/specular.m, scripts/plot/appearance/text.m, scripts/plot/appearance/title.m, scripts/plot/appearance/view.m, scripts/plot/appearance/whitebg.m, scripts/plot/appearance/xlabel.m, scripts/plot/appearance/xlim.m, scripts/plot/appearance/ylabel.m, scripts/plot/appearance/ylim.m, scripts/plot/appearance/zlabel.m, scripts/plot/appearance/zlim.m: Moved from scripts/plot to subdir appearance. * scripts/plot/draw/area.m, scripts/plot/draw/bar.m, scripts/plot/draw/barh.m, scripts/plot/draw/colorbar.m, scripts/plot/draw/comet.m, scripts/plot/draw/comet3.m, scripts/plot/draw/compass.m, scripts/plot/draw/contour.m, scripts/plot/draw/contour3.m, scripts/plot/draw/contourc.m, scripts/plot/draw/contourf.m, scripts/plot/draw/cylinder.m, scripts/plot/draw/ellipsoid.m, scripts/plot/draw/errorbar.m, scripts/plot/draw/ezcontour.m, scripts/plot/draw/ezcontourf.m, scripts/plot/draw/ezmesh.m, scripts/plot/draw/ezmeshc.m, scripts/plot/draw/ezplot.m, scripts/plot/draw/ezplot3.m, scripts/plot/draw/ezpolar.m, scripts/plot/draw/ezsurf.m, scripts/plot/draw/ezsurfc.m, scripts/plot/draw/feather.m, scripts/plot/draw/fill.m, scripts/plot/draw/fplot.m, scripts/plot/draw/hist.m, scripts/plot/draw/isocolors.m, scripts/plot/draw/isonormals.m, scripts/plot/draw/isosurface.m, scripts/plot/draw/line.m, scripts/plot/draw/loglog.m, scripts/plot/draw/loglogerr.m, scripts/plot/draw/mesh.m, scripts/plot/draw/meshc.m, scripts/plot/draw/meshz.m, scripts/plot/draw/pareto.m, scripts/plot/draw/patch.m, scripts/plot/draw/pcolor.m, scripts/plot/draw/peaks.m, scripts/plot/draw/pie.m, scripts/plot/draw/pie3.m, scripts/plot/draw/plot.m, scripts/plot/draw/plot3.m, scripts/plot/draw/plotmatrix.m, scripts/plot/draw/plotyy.m, scripts/plot/draw/polar.m, scripts/plot/draw/private/__add_datasource__.m, scripts/plot/draw/private/__bar__.m, scripts/plot/draw/private/__contour__.m, scripts/plot/draw/private/__errcomm__.m, scripts/plot/draw/private/__errplot__.m, scripts/plot/draw/private/__ezplot__.m, scripts/plot/draw/private/__interp_cube__.m, scripts/plot/draw/private/__line__.m, scripts/plot/draw/private/__marching_cube__.m, scripts/plot/draw/private/__patch__.m, scripts/plot/draw/private/__pie__.m, scripts/plot/draw/private/__plt__.m, scripts/plot/draw/private/__quiver__.m, scripts/plot/draw/private/__scatter__.m, scripts/plot/draw/private/__stem__.m, scripts/plot/draw/quiver.m, scripts/plot/draw/quiver3.m, scripts/plot/draw/rectangle.m, scripts/plot/draw/ribbon.m, scripts/plot/draw/rose.m, scripts/plot/draw/scatter.m, scripts/plot/draw/scatter3.m, scripts/plot/draw/semilogx.m, scripts/plot/draw/semilogxerr.m, scripts/plot/draw/semilogy.m, scripts/plot/draw/semilogyerr.m, scripts/plot/draw/shrinkfaces.m, scripts/plot/draw/slice.m, scripts/plot/draw/sombrero.m, scripts/plot/draw/sphere.m, scripts/plot/draw/stairs.m, scripts/plot/draw/stem.m, scripts/plot/draw/stem3.m, scripts/plot/draw/stemleaf.m, scripts/plot/draw/surf.m, scripts/plot/draw/surface.m, scripts/plot/draw/surfc.m, scripts/plot/draw/surfl.m, scripts/plot/draw/surfnorm.m, scripts/plot/draw/tetramesh.m, scripts/plot/draw/trimesh.m, scripts/plot/draw/triplot.m, scripts/plot/draw/trisurf.m, scripts/plot/draw/waterfall.m: Moved from plot/ to subdir draw. * scripts/plot/util/__actual_axis_position__.m, scripts/plot/util/__default_plot_options__.m, scripts/plot/util/__gnuplot_drawnow__.m, scripts/plot/util/__next_line_color__.m, scripts/plot/util/__next_line_style__.m, scripts/plot/util/__plt_get_axis_arg__.m, scripts/plot/util/__pltopt__.m, scripts/plot/util/allchild.m, scripts/plot/util/ancestor.m, scripts/plot/util/axes.m, scripts/plot/util/cla.m, scripts/plot/util/clf.m, scripts/plot/util/close.m, scripts/plot/util/closereq.m, scripts/plot/util/colstyle.m, scripts/plot/util/copyobj.m, scripts/plot/util/figure.m, scripts/plot/util/findall.m, scripts/plot/util/findfigs.m, scripts/plot/util/findobj.m, scripts/plot/util/gca.m, scripts/plot/util/gcbf.m, scripts/plot/util/gcbo.m, scripts/plot/util/gcf.m, scripts/plot/util/gco.m, scripts/plot/util/ginput.m, scripts/plot/util/gnuplot_binary.in, scripts/plot/util/graphics_toolkit.m, scripts/plot/util/hdl2struct.m, scripts/plot/util/hggroup.m, scripts/plot/util/hold.m, scripts/plot/util/isaxes.m, scripts/plot/util/isfigure.m, scripts/plot/util/ishghandle.m, scripts/plot/util/ishold.m, scripts/plot/util/isprop.m, scripts/plot/util/linkprop.m, scripts/plot/util/meshgrid.m, scripts/plot/util/ndgrid.m, scripts/plot/util/newplot.m, scripts/plot/util/print.m, scripts/plot/util/printd.m, scripts/plot/util/private/__add_default_menu__.m, scripts/plot/util/private/__fltk_ginput__.m, scripts/plot/util/private/__fltk_print__.m, scripts/plot/util/private/__ghostscript__.m, scripts/plot/util/private/__gnuplot_get_var__.m, scripts/plot/util/private/__gnuplot_ginput__.m, scripts/plot/util/private/__gnuplot_has_feature__.m, scripts/plot/util/private/__gnuplot_has_terminal__.m, scripts/plot/util/private/__gnuplot_open_stream__.m, scripts/plot/util/private/__gnuplot_print__.m, scripts/plot/util/private/__gnuplot_version__.m, scripts/plot/util/private/__go_draw_axes__.m, scripts/plot/util/private/__go_draw_figure__.m, scripts/plot/util/private/__print_parse_opts__.m, scripts/plot/util/private/__tight_eps_bbox__.m, scripts/plot/util/refresh.m, scripts/plot/util/refreshdata.m, scripts/plot/util/saveas.m, scripts/plot/util/shg.m, scripts/plot/util/struct2hdl.m, scripts/plot/util/subplot.m: Moved from plot to subdir util. * etc/HACKING: Updated directory structure info. * scripts/Makefile.am, scripts/plot/appearance/module.mk, scripts/plot/draw/module.mk, scripts/plot/util/module.mk: Added new directories to build system.
author Rik <rik@octave.org>
date Fri, 04 Oct 2013 17:09:08 -0700
parents scripts/plot/legend.m@225ec9a0222a
children c14e5af64de4
comparison
equal deleted inserted replaced
17571:6e4ea5c8a4bb 17572:7bb76a22cde1
1 ## Copyright (C) 2010-2012 David Bateman
2 ##
3 ## This file is part of Octave.
4 ##
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
9 ##
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ## General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING. If not, see
17 ## <http://www.gnu.org/licenses/>.
18
19 ## -*- texinfo -*-
20 ## @deftypefn {Function File} {} legend (@var{str1}, @var{str2}, @dots{})
21 ## @deftypefnx {Function File} {} legend (@var{matstr})
22 ## @deftypefnx {Function File} {} legend (@var{cellstr})
23 ## @deftypefnx {Function File} {} legend (@dots{}, "location", @var{pos})
24 ## @deftypefnx {Function File} {} legend (@dots{}, "orientation", @var{orient})
25 ## @deftypefnx {Function File} {} legend (@var{hax}, @dots{})
26 ## @deftypefnx {Function File} {} legend (@var{hobjs}, @dots{})
27 ## @deftypefnx {Function File} {} legend (@var{hax}, @var{hobjs}, @dots{})
28 ## @deftypefnx {Function File} {} legend ("@var{option}")
29 ## @deftypefnx {Function File} {[@var{hleg}, @var{hleg_obj}, @var{hplot}, @var{labels}] =} legend (@dots{})
30 ##
31 ## Display a legend for the current axes using the specified strings as labels.
32 ##
33 ## Legend entries may be specified as individual character string arguments,
34 ## a character array, or a cell array of character strings.
35 ##
36 ## If the first argument @var{hax} is an axes handle, then plot into this axis,
37 ## rather than the current axes returned by @code{gca}. If the handles,
38 ## @var{hobjs}, are not specified then the legend's strings will be associated
39 ## with the axes' descendants. @code{legend} works on line graphs,
40 ## bar graphs, etc. A plot must exist before legend is called.
41 ##
42 ## The optional parameter @var{pos} specifies the location of the legend
43 ## as follows:
44 ##
45 ## @multitable @columnfractions 0.06 0.14 0.80
46 ## @headitem @tab pos @tab location of the legend
47 ## @item @tab north @tab center top
48 ## @item @tab south @tab center bottom
49 ## @item @tab east @tab right center
50 ## @item @tab west @tab left center
51 ## @item @tab northeast @tab right top (default)
52 ## @item @tab northwest @tab left top
53 ## @item @tab southeast @tab right bottom
54 ## @item @tab southwest @tab left bottom
55 ## @item
56 ## @item @tab outside @tab can be appended to any location string
57 ## @end multitable
58 ##
59 ## The optional parameter @var{orient} determines if the key elements
60 ## are placed vertically or horizontally. The allowed values are
61 ## @qcode{"vertical"} (default) or @qcode{"horizontal"}.
62 ##
63 ## The following customizations are available using @var{option}:
64 ##
65 ## @table @asis
66 ## @item @qcode{"show"}
67 ## Show legend on the plot
68 ##
69 ## @item @qcode{"hide"}
70 ## Hide legend on the plot
71 ##
72 ## @item @qcode{"toggle"}
73 ## Toggles between @qcode{"hide"} and @qcode{"show"}
74 ##
75 ## @item @qcode{"boxon"}
76 ## Show a box around legend (default)
77 ##
78 ## @item @qcode{"boxoff"}
79 ## Hide the box around legend
80 ##
81 ## @item @qcode{"right"}
82 ## Place label text to the right of the keys (default)
83 ##
84 ## @item @qcode{"left"}
85 ## Place label text to the left of the keys
86 ##
87 ## @item @qcode{"off"}
88 ## Delete the legend object
89 ## @end table
90 ##
91 ## The optional output values are
92 ##
93 ## @table @var
94 ## @item hleg
95 ## The graphics handle of the legend object.
96 ##
97 ## @item hleg_obj
98 ## Graphics handles to the text and line objects which make up the legend.
99 ##
100 ## @item hplot
101 ## Graphics handles to the plot objects which were used in making the legend.
102 ##
103 ## @item labels
104 ## A cell array of strings of the labels in the legend.
105 ## @end table
106 ##
107 ## The legend label text is either provided in the call to @code{legend} or
108 ## is taken from the DisplayName property of graphics objects. If no
109 ## labels or DisplayNames are available, then the label text is simply
110 ## @qcode{"data1"}, @qcode{"data2"}, @dots{}, @nospell{@qcode{"dataN"}}.
111 ##
112 ## Implementation Note: A legend is implemented as an additional axes object
113 ## of the current figure with the @qcode{"tag"} set to @qcode{"legend"}.
114 ## Properties of the legend object may be manipulated directly by using
115 ## @code{set}.
116 ## @end deftypefn
117
118 function [hleg, hleg_obj, hplot, labels] = legend (varargin)
119
120 if (nargin > 0
121 && (! ishandle (varargin{1})
122 || (strcmp (get (varargin{1}, "type"), "axes")
123 && ! strcmp (get (varargin{1}, "tag"), "legend"))))
124 [ca, varargin, nargin] = __plt_get_axis_arg__ ("legend", varargin{:});
125 if (isempty (ca))
126 ca = gca ();
127 endif
128 fig = get (ca, "parent");
129 else
130 fig = get (0, "currentfigure");
131 if (isempty (fig))
132 fig = gcf ();
133 endif
134 ca = gca ();
135 endif
136
137 ## Special handling for plotyy which has two axes objects
138 if (ishandle (ca) && isprop (ca, "__plotyy_axes__"))
139 plty = get (ca, "__plotyy_axes__");
140 if (isscalar (plty) && ishandle (plty))
141 ca = [ca, plty];
142 elseif (iscell (plty))
143 ca = [ca, plty{:}];
144 elseif (all (ishandle (plty)))
145 ca = [ca, plty(:).'];
146 else
147 error ("legend.m: This should not happen. File a bug report.");
148 endif
149 ## Remove duplicates while preserving order
150 [~, n] = unique (ca);
151 ca = ca(sort (n));
152 endif
153
154 if (nargin > 0 && all (ishandle (varargin{1})))
155 kids = flipud (varargin{1}(:));
156 varargin(1) = [];
157 else
158 kids = ca;
159 kids(strcmp (get (ca, "tag"), "legend")) = [];
160 if (isscalar (kids))
161 kids = get (kids, "children")(:);
162 else
163 kids = flipud ([get(kids, "children"){:}](:));
164 endif
165 endif
166 nargs = numel (varargin);
167 nkids = numel (kids);
168
169 orientation = "default";
170 location = "default";
171 show = "create";
172 textpos = "default";
173 box = "default";
174
175 ## Process old way of specifying location with a number rather than a string.
176 if (nargs > 0)
177 pos = varargin{nargs};
178 if (isnumeric (pos) && isscalar (pos) && pos == fix (pos))
179 if (pos >= -1 && pos <= 4)
180 location = [{"northeastoutside", "best", "northeast",
181 "northwest", "southwest", "southeast"}] {pos + 2};
182 nargs--;
183 else
184 error ("legend: invalid location specified");
185 endif
186 endif
187 endif
188
189 ## Find location and orientation property/value pairs
190 while (nargs > 1)
191 pos = varargin{nargs-1};
192 str = varargin{nargs};
193 if (strcmpi (pos, "location") && ischar (str))
194 location = lower (str);
195 nargs -= 2;
196 elseif (strcmpi (pos, "orientation") && ischar (str))
197 orientation = lower (str);
198 nargs -= 2;
199 else
200 break;
201 endif
202 endwhile
203
204 ## Validate the orientation
205 switch (orientation)
206 case {"vertical", "horizontal", "default"}
207 ## These are all accepted orientations.
208 otherwise
209 error ("legend: unrecognized legend orientation");
210 endswitch
211
212 ## Validate the location type
213 outside = false;
214 inout = strfind (location, "outside");
215 if (! isempty (inout))
216 outside = true;
217 location = location(1:inout-1);
218 else
219 outside = false;
220 endif
221
222 switch (location)
223 case {"north", "south", "east", "west", "northeast", "northwest", ...
224 "southeast", "southwest", "default"}
225 case "best"
226 warning ("legend: 'best' not yet implemented for location specifier\n");
227 location = "northeast";
228 otherwise
229 error ("legend: unrecognized legend location");
230 endswitch
231
232 ## Find any existing legend object on figure
233 hlegend = [];
234 fkids = get (fig, "children");
235 for i = 1 : numel (fkids)
236 if ( strcmp (get (fkids(i), "type"), "axes")
237 && strcmp (get (fkids(i), "tag"), "legend"))
238 udata = get (fkids(i), "userdata");
239 if (any (udata.handle == ca))
240 hlegend = fkids(i);
241 break;
242 endif
243 endif
244 endfor
245
246 if (nargs == 1)
247 arg = varargin{1};
248 if (ischar (arg))
249 if (rows (arg) == 1)
250 str = tolower (strtrim (arg));
251 switch (str)
252 case "off"
253 delete (hlegend);
254 return;
255 case "hide"
256 show = "off";
257 nargs--;
258 case "show"
259 if (! isempty (hlegend))
260 show = "on";
261 else
262 show = "create";
263 textpos = "right";
264 endif
265 nargs--;
266 case "toggle"
267 if (isempty (hlegend))
268 show = "create";
269 textpos = "right";
270 elseif (strcmp (get (hlegend, "visible"), "off"))
271 show = "on";
272 else
273 show = "off";
274 endif
275 nargs--;
276 case "boxon"
277 box = "on";
278 nargs--;
279 case "boxoff"
280 box = "off";
281 nargs--;
282 case "left"
283 textpos = "left";
284 nargs--;
285 case "right"
286 textpos = "right";
287 nargs--;
288 endswitch
289 else
290 ## Character matrix of labels
291 varargin = cellstr (arg);
292 nargs = numel (varargin);
293 endif
294 elseif (iscellstr (arg))
295 ## Cell array of labels
296 varargin = arg;
297 nargs = numel (varargin);
298 else
299 error ("legend: expecting argument to be a character string");
300 endif
301 elseif (nargs > 1 && iscellstr (varargin{1}))
302 ## Cell array of labels followed by property/value pairs
303 varargin = {varargin{1}{:}, varargin{2:end}};
304 nargs = numel (varargin);
305 endif
306
307 have_labels = (nargs > 0);
308
309 if (strcmp (show, "off"))
310 if (! isempty (hlegend))
311 set (findobj (hlegend), "visible", "off");
312 hlegend = [];
313 endif
314 hobjects = [];
315 hplots = [];
316 text_strings = {};
317 elseif (strcmp (show, "on"))
318 if (! isempty (hlegend))
319 set (findobj (hlegend), "visible", "on");
320 ## NOTE - Matlab sets both "visible", and "box" to "on"
321 set (hlegend, "visible", get (hlegend, "box"));
322 else
323 hobjects = [];
324 hplots = [];
325 text_strings = {};
326 endif
327 elseif (strcmp (box, "on"))
328 if (! isempty (hlegend))
329 set (hlegend, "box", "on", "visible", "on");
330 endif
331 elseif (strcmp (box, "off"))
332 if (! isempty (hlegend))
333 set (hlegend, "box", "off", "visible", "off");
334 endif
335 elseif (! have_labels && ! (strcmp (location, "default") &&
336 strcmp (orientation, "default")))
337 ## Changing location or orientation of existing legend
338 if (! isempty (hlegend))
339 if (strcmp (location, "default"))
340 set (hlegend, "orientation", orientation);
341 elseif (strcmp (orientation, "default"))
342 if (outside)
343 set (hlegend, "location", [location "outside"]);
344 else
345 set (hlegend, "location", location);
346 endif
347 else
348 if (outside)
349 set (hlegend, "location", [location "outside"],
350 "orientation", orientation);
351 else
352 set (hlegend, "location", location,
353 "orientation", orientation);
354 endif
355 endif
356 endif
357 else
358 ## Create new legend
359 hobjects = [];
360 hplots = [];
361 text_strings = {};
362
363 if (have_labels)
364 ## Check for valid data that can be labeled.
365 have_data = false;
366 have_dname = false;
367 for k = 1 : nkids
368 typ = get (kids(k), "type");
369 if (any (strcmp (typ, {"line", "patch", "surface", "hggroup"})))
370 have_data = true;
371 break;
372 endif
373 endfor
374
375 if (! have_data)
376 warning ("legend: plot data is empty; setting key labels has no effect");
377 endif
378 else
379 ## No labels. Search for DisplayName property.
380 have_dname = false;
381 for k = 1 : nkids
382 hkid = kids(k);
383 typ = get (hkid, "type");
384 if (any (strcmp (typ, {"line", "patch", "surface"})))
385 if (! isempty (get (hkid, "displayname")))
386 have_dname = true;
387 break;
388 endif
389 elseif (strcmp (typ, "hggroup"))
390 hgkids = get (hkid, "children");
391 for j = 1 : length (hgkids)
392 try
393 dname = get (hgkids(j), "DisplayName");
394 if (! isempty (dname))
395 have_dname = true;
396 break; # break from j-loop over hgkids
397 endif
398 end_try_catch
399 endfor
400 if (have_dname)
401 break; # break from k loop over nkids
402 endif
403 endif # elseif hggroup
404 endfor # for loop k = 1 : nkids
405 endif # else branch of if (have_labels)
406
407 if (have_labels || ! have_dname)
408 k = nkids;
409 if (! have_labels)
410 varargin = arrayfun (@(x) sprintf ("data%d", x), [1:nkids]',
411 "uniformoutput", false);
412 have_labels = true;
413 nargs = nkids;
414 endif
415 for i = 1 : nargs
416 arg = varargin{i};
417 if (ischar (arg))
418 typ = get (kids(k), "type");
419 while (k > 0
420 && ! any (strcmp (typ, {"line","patch","surface","hggroup"})))
421 typ = get (kids(--k), "type");
422 endwhile
423 if (k > 0)
424 if (strcmp (get (kids(k), "type"), "hggroup"))
425 hgkids = get (kids(k), "children");
426 for j = 1 : length (hgkids)
427 hgobj = get (hgkids(j));
428 if (isfield (hgobj, "displayname"))
429 if (have_labels)
430 set (hgkids(j), "displayname", arg);
431 endif
432 hplots(end+1) = hgkids(j);
433 text_strings(end+1) = arg;
434 break;
435 endif
436 endfor
437 else
438 if (have_labels)
439 set (kids(k), "displayname", arg);
440 endif
441 hplots(end+1) = kids(k);
442 text_strings(end+1) = arg;
443 endif
444
445 if (--k == 0)
446 break;
447 endif
448 else
449 break; # k = 0, no further handles to process
450 endif
451 else
452 error ("legend: expecting argument to be a character string");
453 endif
454 endfor
455 if (have_labels && i < nargs)
456 warning ("legend: ignoring extra labels");
457 endif
458 else
459 ## No labels specified but objects have DisplayName property set.
460 k = nkids;
461 while (k > 0)
462 typ = get (kids(k), "type");
463 while (k > 1
464 && ! any (strcmp (typ, {"line","patch","surface","hggroup"})))
465 typ = get (kids(--k), "type");
466 endwhile
467 if (! any (strcmp (typ, {"line","patch","surface","hggroup"})))
468 break;
469 endif
470 if (k > 0)
471 if (strcmp (get (kids(k), "type"), "hggroup"))
472 hgkids = get (kids(k), "children");
473 for j = 1 : length (hgkids)
474 hgobj = get (hgkids(j));
475 if (isfield (hgobj, "displayname")
476 && ! isempty (hgobj.displayname))
477 hplots(end+1) = hgkids(j);
478 text_strings(end+1) = hgobj.displayname;
479 break;
480 endif
481 endfor
482 else
483 if (! isempty (get (kids(k), "displayname")))
484 hplots(end+1) = kids(k);
485 text_strings(end+1) = get (kids(k), "displayname");
486 endif
487 endif
488 if (--k == 0)
489 break;
490 endif
491 endif
492 endwhile
493 endif
494
495 if (isempty (hplots))
496 if (! isempty (hlegend))
497 fkids = get (fig, "children");
498 delete (fkids(fkids == hlegend));
499 hlegend = [];
500 hobjects = [];
501 hplots = [];
502 text_strings = {};
503 endif
504 else
505 ## Preserve the old legend if it exists
506 if (! isempty (hlegend))
507 if (strcmp (textpos, "default"))
508 textpos = get (hlegend, "textposition");
509 endif
510 if (strcmp (location, "default"))
511 location = get (hlegend, "location");
512 inout = strfind (location, "outside");
513 if (! isempty (inout))
514 outside = true;
515 location = location(1:inout-1);
516 else
517 outside = false;
518 endif
519 endif
520 if (strcmp (orientation, "default"))
521 orientation = get (hlegend, "orientation");
522 endif
523 box = get (hlegend, "box");
524 else
525 if (strcmp (textpos, "default"))
526 textpos = "right";
527 endif
528 if (strcmp (location, "default"))
529 location = "northeast";
530 endif
531 if (strcmp (orientation, "default"))
532 orientation = "vertical";
533 endif
534 box = "on";
535 endif
536
537 ## Get axis size and fontsize in points.
538 ## Rely on listener to handle coversion.
539 units = get (ca(1), "units");
540 unwind_protect
541 set (ca(1), "units", "points");
542 set (ca(1), "fontunits", "points");
543 if (isempty (hlegend) || ! isprop (hlegend, "unmodified_axes_position"))
544 unmodified_axes_position = get (ca(1), "position");
545 unmodified_axes_outerposition = get (ca(1), "outerposition");
546 else
547 unmodified_axes_position = get (hlegend, "unmodified_axes_position");
548 unmodified_axes_outerposition = get (hlegend, ...
549 "unmodified_axes_outerposition");
550 endif
551 ca_pos = unmodified_axes_position;
552 ca_outpos = unmodified_axes_outerposition;
553 tightinset = get (ca(1), "tightinset");
554 for i = 2 : numel (ca)
555 tightinset = max (tightinset, get (ca(i), "tightinset"));
556 endfor
557 unwind_protect_cleanup
558 set (ca(1), "units", units);
559 end_unwind_protect
560
561 ## Padding between legend entries horizontally and vertically
562 xpad = 2;
563 ypad = 2;
564
565 linelength = 15;
566
567 ## Create the axis first
568 curaxes = get (fig, "currentaxes");
569 unwind_protect
570 ud = ancestor (hplots, "axes");
571 if (! isscalar (ud))
572 ud = unique ([ud{:}]);
573 endif
574 if (isempty (hlegend))
575 addprops = true;
576 hlegend = axes ("tag", "legend", "userdata", struct ("handle", ud),
577 "box", box,
578 "xtick", [], "ytick", [],
579 "xlim", [0, 1], "ylim", [0, 1],
580 "visible", ifelse (strcmp (box, "on"), "on", "off"),
581 "activepositionproperty", "position",
582 "interpreter", "tex");
583 ## Inherit properties from current axis
584 ## "fontunits" shoud be first because it affects interpretation
585 ## of "fontsize" property
586 proplist = {"fontunits", "fontangle", "fontname", "fontsize", ...
587 "fontweight"};
588 ca_props = get (ca(1), proplist);
589 set (hlegend, proplist, ca_props);
590 else
591 addprops = false;
592 axes (hlegend);
593 delete (get (hlegend, "children"));
594 endif
595 if (addprops)
596 addproperty ("edgecolor", hlegend, "color", [0, 0, 0]);
597 addproperty ("textcolor", hlegend, "color", [0, 0, 0]);
598 locations = {"north", "south", "east", "west", ...
599 "{northeast}", "southeast", "northwest", "southwest", ...
600 "northoutside", "southoutside", ...
601 "eastoutside", "westoutside", ...
602 "northeastoutside", "southeastoutside", ...
603 "northwestoutside", "southwestoutside"};
604 addproperty ("location", hlegend, "radio", strjoin (locations, "|"));
605 addproperty ("orientation", hlegend, "radio",
606 "{vertical}|horizontal");
607 addproperty ("string", hlegend, "any", text_strings);
608 addproperty ("textposition", hlegend, "radio", "left|{right}");
609 endif
610 ## Inherit visual properties from legend object
611 fontunits = get (hlegend, "fontunits");
612 fontangle = get (hlegend, "fontangle");
613 fontname = get (hlegend, "fontname");
614 fontsize = get (hlegend, "fontsize");
615 fontweight = get (hlegend, "fontweight");
616 interpreter = get (hlegend, "interpreter");
617 textcolor = get (hlegend, "textcolor");
618 ## Add text label to the axis first, checking their extents
619 nentries = numel (hplots);
620 texthandle = [];
621 maxwidth = 0;
622 maxheight = 0;
623 for k = 1 : nentries
624 halign = ifelse (strcmp (textpos, "right"), "left", "right");
625 texthandle(end+1) = text (0, 0, text_strings{k},
626 "color", textcolor,
627 "horizontalalignment", halign,
628 "interpreter", interpreter,
629 "fontunits", fontunits,
630 "fontangle", fontangle,
631 "fontname", fontname,
632 "fontsize", fontsize,
633 "fontweight", fontweight,
634 "userdata", hplots(k));
635 units = get (texthandle(end), "units");
636 unwind_protect
637 set (texthandle(end), "units", "points");
638 extents = get (texthandle(end), "extent");
639 maxwidth = max (maxwidth, extents(3));
640 maxheight = max (maxheight, extents(4));
641 unwind_protect_cleanup
642 set (texthandle(end), "units", units);
643 end_unwind_protect
644 endfor
645
646 num1 = nentries;
647 if (strcmp (orientation, "vertical"))
648 height = nentries * (ypad + maxheight);
649 if (outside)
650 if (height > ca_pos(4))
651 ## Avoid shrinking the height of the axis to zero if outside
652 num1 = ca_pos(4) / (maxheight + ypad) / 2;
653 endif
654 else
655 if (height > 0.9 * ca_pos(4))
656 num1 = 0.9 * ca_pos(4) / (maxheight + ypad);
657 endif
658 endif
659 else
660 width = nentries * (ypad + maxwidth);
661 if (outside)
662 if (width > ca_pos(3))
663 ## Avoid shrinking the width of the axis to zero if outside
664 num1 = ca_pos(3) / (maxwidth + ypad) / 2;
665 endif
666 else
667 if (width > 0.9 * ca_pos(3))
668 num1 = 0.9 * ca_pos(3) / (maxwidth + ypad);
669 endif
670 endif
671 endif
672 num2 = ceil (nentries / num1);
673
674 xstep = 3 * xpad + (maxwidth + linelength);
675 if (strcmp (textpos, "right"))
676 xoffset = xpad;
677 txoffset = 2 * xpad + linelength;
678 else
679 xoffset = 2 * xpad + maxwidth;
680 txoffset = xpad + maxwidth;
681 endif
682 ystep = (ypad + maxheight);
683 yoffset = ystep / 2;
684
685 ## Place the legend in the desired location
686 if (strcmp (orientation, "vertical"))
687 lpos = [0, 0, num2 * xstep, num1 * ystep];
688 else
689 lpos = [0, 0, num1 * xstep, num2 * ystep];
690 endif
691
692 gnuplot = strcmp (get (fig, "__graphics_toolkit__"), "gnuplot");
693 if (gnuplot)
694 ## Gnuplot places the key (legend) at edge of the figure window.
695 ## OpenGL places the legend box at edge of the unmodified axes
696 ## position.
697 if (isempty (strfind (location, "east")))
698 gnuplot_offset = unmodified_axes_outerposition(1) ...
699 + unmodified_axes_outerposition(3) ...
700 - unmodified_axes_position(1) ...
701 - unmodified_axes_position(3);
702 else
703 gnuplot_offset = unmodified_axes_position(1) ...
704 - unmodified_axes_outerposition(1);
705 endif
706 ## FIXME: The "fontsize" is added to match the behavior of OpenGL.
707 ## This implies that a change in fontsize should trigger a listener
708 ## to update the legend. The "2" was determined using a long legend
709 ## key in the absence of any subplots.
710 gnuplot_offset = gnuplot_offset - 2 * fontsize;
711 else
712 gnuplot_offset = 0;
713 endif
714
715 ## For legend's outside the associated axes postion,
716 ## align their edge to the unmodified_axes_outerpostion,
717 ## and adjust the axes postion accordingly.
718 switch (location)
719 case "north"
720 if (outside)
721 lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
722 ca_outpos(2) + ca_outpos(4) - lpos(4) - ypad, lpos(3), ...
723 lpos(4)];
724
725 new_pos = [ca_pos(1), ca_pos(2), ca_pos(3), ca_pos(4) - lpos(4)];
726 else
727 lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
728 ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
729 endif
730 case "south"
731 if (outside)
732 lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
733 ca_outpos(2) + ypad, lpos(3), lpos(4)];
734 new_pos = [ca_pos(1), lpos(2) + lpos(4) + 2 * ypad ...
735 + tightinset(2), ca_pos(3), ...
736 ca_pos(4) - lpos(4)];
737 else
738 lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
739 ca_pos(2) + ypad, lpos(3), lpos(4)];
740 endif
741 case "east"
742 if (outside)
743 lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
744 ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
745 new_pos = [ca_pos(1), ca_pos(2), ...
746 lpos(1) - 2 * xpad - ca_pos(1) - tightinset(3), ...
747 ca_pos(4)];
748 new_pos(3) = new_pos(3) + gnuplot_offset;
749 else
750 lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
751 ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
752 endif
753 case "west"
754 if (outside)
755 lpos = [ca_outpos(1) + ypad, ...
756 ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, ...
757 lpos(3), lpos(4)];
758 new_pos = [lpos(1) + lpos(3) + 2 * xpad + tightinset(1), ...
759 ca_pos(2), ca_pos(3) - lpos(3) - 2 * xpad, ca_pos(4)];
760 new_pos(1) = new_pos(1) - gnuplot_offset;
761 new_pos(3) = new_pos(3) + gnuplot_offset;
762 else
763 lpos = [ca_pos(1) + ypad, ...
764 ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
765 endif
766 case "northeast"
767 if (outside)
768 lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
769 ca_pos(2) + ca_pos(4) - lpos(4), lpos(3), lpos(4)];
770 new_pos = [ca_pos(1), ca_pos(2), ...
771 lpos(1) - 2 * xpad - tightinset(3) - ca_pos(1), ...
772 ca_pos(4)];
773 new_pos(3) = new_pos(3) + gnuplot_offset;
774 else
775 lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
776 ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
777 endif
778 case "northwest"
779 if (outside)
780 lpos = [ca_outpos(1) + ypad , ca_pos(2) + ca_pos(4) - lpos(4), ...
781 lpos(3), lpos(4)];
782 new_pos = [lpos(1) + lpos(3) + 2 * xpad + tightinset(1), ...
783 ca_pos(2), ca_pos(3) - lpos(3) - 2 * xpad, ca_pos(4)];
784 new_pos(1) = new_pos(1) - gnuplot_offset;
785 new_pos(3) = new_pos(3) + gnuplot_offset;
786 else
787 lpos = [ca_pos(1) + ypad, ...
788 ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), lpos(4)];
789 endif
790 case "southeast"
791 if (outside)
792 lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3) - ypad, ...
793 ca_pos(2), lpos(3), lpos(4)];
794 new_pos = [ca_pos(1), ca_pos(2), ...
795 lpos(1) - 2 * xpad - ca_pos(1) - tightinset(3), ...
796 ca_pos(4)];
797 new_pos(3) = new_pos(3) + gnuplot_offset;
798 else
799 lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
800 ca_pos(2) + ypad, lpos(3), lpos(4)];
801 endif
802 case "southwest"
803 if (outside)
804 lpos = [ca_outpos(1) + ypad, ca_pos(2), lpos(3), lpos(4)];
805 new_pos = [lpos(1) + lpos(3) + 2 * xpad + tightinset(1), ...
806 ca_pos(2), ca_pos(3) - lpos(3) - 2 * xpad, ca_pos(4)];
807 new_pos(1) = new_pos(1) - gnuplot_offset;
808 new_pos(3) = new_pos(3) + gnuplot_offset;
809 else
810 lpos = [ca_pos(1) + ypad, ca_pos(2) + ypad, lpos(3), lpos(4)];
811 endif
812 endswitch
813
814 units = get (hlegend, "units");
815 unwind_protect
816 set (hlegend, "units", "points");
817 set (hlegend, "position", lpos);
818 unwind_protect_cleanup
819 set (hlegend, "units", units);
820 end_unwind_protect
821
822 ## Now write the line segments and place the text objects correctly
823 xk = 0;
824 yk = 0;
825 for k = 1 : numel (hplots)
826 hobjects(end+1) = texthandle(k);
827 switch (get (hplots(k), "type"))
828
829 case "line"
830 color = get (hplots(k), "color");
831 style = get (hplots(k), "linestyle");
832 if (! strcmp (style, "none"))
833 l1 = line ("xdata", ([xoffset, xoffset + linelength] + xk * xstep) / lpos(3),
834 "ydata", [1, 1] .* (lpos(4) - yoffset - yk * ystep) / lpos(4),
835 "color", color, "linestyle", style,
836 "marker", "none",
837 "userdata", hplots(k));
838 hobjects(end+1) = l1;
839 endif
840 marker = get (hplots(k), "marker");
841 if (! strcmp (marker, "none"))
842 l1 = line ("xdata", (xoffset + 0.5 * linelength + xk * xstep) / lpos(3),
843 "ydata", (lpos(4) - yoffset - yk * ystep) / lpos(4),
844 "color", color, "linestyle", "none",
845 "marker", marker,
846 "markeredgecolor",get (hplots(k), "markeredgecolor"),
847 "markerfacecolor",get (hplots(k), "markerfacecolor"),
848 "markersize", get (hplots(k), "markersize"),
849 "userdata", hplots(k));
850 hobjects(end+1) = l1;
851 endif
852
853 if (addprops)
854 addlistener (hplots(k), "color",
855 {@updateline, hlegend, linelength, false});
856 addlistener (hplots(k), "linestyle",
857 {@updateline, hlegend, linelength, false});
858 addlistener (hplots(k), "marker",
859 {@updateline, hlegend, linelength, false});
860 addlistener (hplots(k), "markeredgecolor",
861 {@updateline, hlegend, linelength, false});
862 addlistener (hplots(k), "markerfacecolor",
863 {@updateline, hlegend, linelength, false});
864 addlistener (hplots(k), "markersize",
865 {@updateline, hlegend, linelength, false});
866 addlistener (hplots(k), "displayname",
867 {@updateline, hlegend, linelength, true});
868 endif
869
870 case "patch"
871 facecolor = get (hplots(k), "facecolor");
872 edgecolor = get (hplots(k), "edgecolor");
873 cdata = get (hplots(k), "cdata");
874 if (! strcmp (facecolor, "none") || ! strcmp (edgecolor, "none"))
875 p1 = patch ("xdata", ([0, linelength, linelength, 0] +
876 xoffset + xk * xstep) / lpos(3),
877 "ydata", (lpos(4) - yoffset -
878 [yk-0.3, yk-0.3, yk+0.3, yk+0.3] .* ystep) / lpos(4),
879 "facecolor", facecolor, "edgecolor", edgecolor,
880 "cdata", cdata, "userdata", hplots(k));
881 hobjects(end+1) = p1;
882 endif
883 ## FIXME: Probably need listeners, as for line objects
884
885 case "surface"
886 facecolor = get (hplots(k), "facecolor");
887 edgecolor = get (hplots(k), "edgecolor");
888 cdata = sum (get (ca(1), "clim")) / 2;
889 if (! strcmp (facecolor, "none") || ! strcmp (edgecolor, "none"))
890 p1 = patch ("xdata", ([0, linelength, linelength, 0] +
891 xoffset + xk * xstep) / lpos(3),
892 "ydata", (lpos(4) - yoffset -
893 [yk-0.3, yk-0.3, yk+0.3, yk+0.3] .* ystep) / lpos(4),
894 "facecolor", facecolor, "edgecolor", edgecolor,
895 "cdata", cdata, "userdata", hplots(k));
896 hobjects(end+1) = p1;
897 endif
898 ## FIXME: Probably need listeners, as for line objects
899
900 endswitch
901
902 set (texthandle(k), "position",
903 [(txoffset + xk * xstep) / lpos(3), ...
904 (lpos(4) - yoffset - yk * ystep) / lpos(4)]);
905 if (strcmp (orientation, "vertical"))
906 yk++;
907 if (yk > num1)
908 yk = 0;
909 xk++;
910 endif
911 else
912 xk++;
913 if (xk > num1)
914 xk = 0;
915 yk++;
916 endif
917 endif
918 endfor
919
920 ## Add an invisible text object to original axis
921 ## that when it is destroyed will remove the legend
922 props = {"parent", ca(1), "tag", "legend", ...
923 "handlevisibility", "off", "visible", "off", ...
924 "xliminclude", "off", "yliminclude", "off"};
925 t1 = findall (ca(1), "tag", "legend", "type", "text");
926 if (isempty (t1))
927 t1 = text (0, 0, "", props{:});
928 set (t1, "deletefcn", {@deletelegend1, hlegend});
929 endif
930 if (isprop (hlegend, "unmodified_axes_position"))
931 set (hlegend, "unmodified_axes_position",
932 unmodified_axes_position,
933 "unmodified_axes_outerposition",
934 unmodified_axes_outerposition);
935 else
936 addproperty ("unmodified_axes_position", hlegend,
937 "data", unmodified_axes_position);
938 addproperty ("unmodified_axes_outerposition", hlegend,
939 "data", unmodified_axes_outerposition);
940 endif
941
942 ## Resize the axis that the legend is attached to if the legend is
943 ## "outside" the plot and create a listener to resize axis to original
944 ## size if the legend is deleted, hidden, or shown.
945 if (outside)
946 for i = 1 : numel (ca)
947 units = get (ca(i), "units");
948 unwind_protect
949 set (ca(i), "units", "points");
950 if (gnuplot && numel (ca) == 1)
951 ## Let Gnuplot handle the positioning of the keybox.
952 ## This violates strict Matlab compatibility, but reliably
953 ## renders an esthetic result.
954 set (ca(i), "position", unmodified_axes_position);
955 set (ca(i), "activepositionproperty", "outerposition")
956 else
957 ## numel (ca) > 1 for axes overlays (like plotyy)
958 set (ca(i), "position", new_pos);
959 endif
960 unwind_protect_cleanup
961 set (ca(i), "units", units);
962 end_unwind_protect
963 endfor
964
965 set (hlegend, "deletefcn", {@deletelegend2, ca, ...
966 unmodified_axes_position, ...
967 unmodified_axes_outerposition, ...
968 t1, hplots});
969 addlistener (hlegend, "visible", {@hideshowlegend, ca, ...
970 unmodified_axes_position, ...
971 new_pos});
972 else
973 set (hlegend, "deletefcn", {@deletelegend2, ca, [], [], t1, hplots});
974 endif
975
976 if (! addprops)
977 ## Remove listeners on existing legend temporarily to stop recursion.
978 dellistener (hlegend, "location");
979 dellistener (hlegend, "orientation");
980 dellistener (hlegend, "string");
981 dellistener (hlegend, "textposition");
982 endif
983
984 if (! addprops)
985 set (hlegend, "string", text_strings);
986 endif
987
988 if (outside)
989 set (hlegend, "location", [location "outside"],
990 "orientation", orientation, "textposition", textpos);
991 else
992 set (hlegend, "location", location, "orientation", orientation,
993 "textposition", textpos);
994 endif
995
996 if (addprops)
997 addlistener (hlegend, "edgecolor", @updatelegendtext);
998 addlistener (hlegend, "fontangle", @updatelegendtext);
999 addlistener (hlegend, "fontname", @updatelegendtext);
1000 addlistener (hlegend, "fontweight", @updatelegendtext);
1001 addlistener (hlegend, "textcolor", @updatelegendtext);
1002 ## Properties which could change size of box, such as fontsize,
1003 ## require legend to be redrawn.
1004 addlistener (hlegend, "fontsize", @updatelegend);
1005 addlistener (hlegend, "fontunits", @updatelegend);
1006 addlistener (hlegend, "interpreter", @updatelegend);
1007 addlistener (hlegend, "location", @updatelegend);
1008 addlistener (hlegend, "orientation", @updatelegend);
1009 addlistener (hlegend, "string", @updatelegend);
1010 addlistener (hlegend, "textposition", @updatelegend);
1011 ## FIXME: need to add listeners for tightinset and position
1012 ## addlistener (ca, "tightinset", @update????);
1013 ## addlistener (ca, "position", @update????);
1014 else
1015 ## Restore certain listeners
1016 addlistener (hlegend, "location", @updatelegend);
1017 addlistener (hlegend, "orientation", @updatelegend);
1018 addlistener (hlegend, "string", @updatelegend);
1019 addlistener (hlegend, "textposition", @updatelegend);
1020 endif
1021 unwind_protect_cleanup
1022 set (fig, "currentaxes", curaxes);
1023 end_unwind_protect
1024 endif
1025 endif
1026
1027 if (nargout > 0)
1028 hleg = hlegend;
1029 hleg_obj = hobjects;
1030 hplot = hplots;
1031 labels = text_strings;
1032 endif
1033
1034 endfunction
1035
1036 function updatelegend (h, ~)
1037 persistent recursive = false;
1038
1039 if (! recursive)
1040 recursive = true;
1041 unwind_protect
1042 hax = getfield (get (h, "userdata"), "handle");
1043 [hplots, ~] = __getlegenddata__ (h);
1044 position = get (h, "unmodified_axes_position");
1045 outerposition = get (h, "unmodified_axes_outerposition");
1046 units = get (hax, "units");
1047 set (hax, "units", "points");
1048 switch (get (hax, "activepositionproperty"))
1049 case "position"
1050 set (hax, "outerposition", outerposition);
1051 set (hax, "position", position);
1052 case "outerposition"
1053 set (hax, "position", position);
1054 set (hax, "outerposition", outerposition);
1055 endswitch
1056 set (hax, "units", units);
1057 h = legend (hax, hplots, get (h, "string"));
1058 unwind_protect_cleanup
1059 recursive = false;
1060 end_unwind_protect
1061 endif
1062
1063 endfunction
1064
1065 function updatelegendtext (h, ~)
1066 kids = get (h, "children");
1067 htext = [];
1068 for i = 1:numel (kids)
1069 if (strcmp (get (kids(i), "type"), "text"))
1070 htext(end+1) = kids(i);
1071 endif
1072 endfor
1073
1074 tprops = {"fontangle", "fontname", "fontweight", "color"};
1075 lprops = {"fontangle", "fontname", "fontweight", "textcolor"};
1076 set (htext, tprops, get (h, lprops));
1077
1078 ec = get (h, "edgecolor");
1079 set (h, "xcolor", ec, "ycolor", ec);
1080 endfunction
1081
1082 function hideshowlegend (h, ~, ca, pos1, pos2)
1083 isvisible = strcmp (get (h, "visible"), "off");
1084 if (! isvisible)
1085 kids = get (h, "children");
1086 if (any (! strcmp (get (kids, "visible"), "off")))
1087 isvisible = true;
1088 endif
1089 endif
1090
1091 for i = 1 : numel (ca)
1092 if (isaxes (ca(i))
1093 && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"),"off"))
1094 && strcmp (get (ca(i), "beingdeleted"), "off"))
1095 units = get (ca(i), "units");
1096 unwind_protect
1097 set (ca(i), "units", "points");
1098 if (isvisible)
1099 set (ca(i), "position", pos2);
1100 else
1101 set (ca(i), "position", pos1);
1102 endif
1103 unwind_protect_cleanup
1104 set (ca(i), "units", units);
1105 end_unwind_protect
1106 endif
1107 endfor
1108 endfunction
1109
1110 function deletelegend1 (h, ~, ca)
1111 if (isaxes (ca)
1112 && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off"))
1113 && strcmp (get (ca, "beingdeleted"), "off"))
1114 delete (ca);
1115 endif
1116 endfunction
1117
1118 function deletelegend2 (h, ~, ca, pos, outpos, t1, hplots)
1119 for i = 1 : numel (ca)
1120 if (isaxes (ca(i))
1121 && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off"))
1122 && strcmp (get (ca(i), "beingdeleted"), "off"))
1123 if (! isempty (pos) && ! isempty (outpos))
1124 units = get (ca(i), "units");
1125 unwind_protect
1126 set (ca(i), "units", "points");
1127 set (ca(i), "position", pos, "deletefcn", "");
1128 unwind_protect_cleanup
1129 set (ca(i), "units", units);
1130 end_unwind_protect
1131 endif
1132 endif
1133 endfor
1134 set (t1, "deletefcn", "");
1135 delete (t1);
1136 for i = 1 : numel (hplots)
1137 if (ishandle (hplots(i)) && strcmp (get (hplots(i), "type"), "line"))
1138 dellistener (hplots(i), "color");
1139 dellistener (hplots(i), "linestyle");
1140 dellistener (hplots(i), "marker");
1141 dellistener (hplots(i), "markeredgecolor");
1142 dellistener (hplots(i), "markerfacecolor");
1143 dellistener (hplots(i), "markersize");
1144 dellistener (hplots(i), "displayname");
1145 endif
1146 endfor
1147 endfunction
1148
1149 function updateline (h, ~, hlegend, linelength, update_name)
1150
1151 if (update_name)
1152 ## When string changes, have to rebuild legend completely
1153 [hplots, text_strings] = __getlegenddata__ (hlegend);
1154 legend (hplots, text_strings);
1155 else
1156 kids = get (hlegend, "children");
1157 ll = lm = [];
1158 for i = 1 : numel (kids)
1159 if (get (kids(i), "userdata") == h
1160 && strcmp (get (kids(i), "type"), "line"))
1161 if (strcmp (get (kids(i), "marker"), "none"))
1162 ll = kids(i);
1163 else
1164 lm = kids(i);
1165 endif
1166 endif
1167 endfor
1168
1169 [linestyle, marker, displayname] = ...
1170 get (h, {"linestyle", "marker", "displayname"}){:};
1171
1172 if (! isempty (ll))
1173 [xpos1, ypos1] = get (ll, {"xdata", "ydata"}){:};
1174 xpos2 = sum (xpos1) / 2;
1175 ypos2 = ypos1(1);
1176 delete (ll);
1177 if (! isempty (lm))
1178 delete (lm);
1179 endif
1180 else
1181 [xpos2, ypos2] = get (lm, {"xdata", "ydata"}){:};
1182 xpos1 = xpos2 + [-0.5, 0.5] * linelength;
1183 ypos1 = [ypos2, ypos2];
1184 delete (lm);
1185 endif
1186
1187 if (! strcmp (linestyle, "none"))
1188 line ("xdata", xpos1, "ydata", ypos1, "color", get (h, "color"),
1189 "linestyle", get (h, "linestyle"), "marker", "none",
1190 "userdata", h, "parent", hlegend);
1191 endif
1192 if (! strcmp (marker, "none"))
1193 line ("xdata", xpos2, "ydata", ypos2, "color", get (h, "color"),
1194 "marker", marker, "markeredgecolor", get (h, "markeredgecolor"),
1195 "markerfacecolor", get (h, "markerfacecolor"),
1196 "markersize", get (h, "markersize"), "linestyle", "none",
1197 "userdata", h, "parent", hlegend);
1198 endif
1199 endif
1200 endfunction
1201
1202
1203 %!demo
1204 %! clf;
1205 %! plot (rand (2));
1206 %! title ('legend called with cellstr and string inputs for labels');
1207 %! h = legend ({'foo'}, 'bar');
1208 %! legend location northeastoutside
1209 %! set (h, 'fontsize', 20);
1210
1211 %!demo
1212 %! clf;
1213 %! plot (rand (3));
1214 %! title ('legend() without inputs creates default labels');
1215 %! h = legend ();
1216
1217 %!demo
1218 %! clf;
1219 %! x = 0:1;
1220 %! plot (x,x,';I am Blue;', x,2*x, x,3*x,';I am Red;');
1221 %! legend location northeastoutside
1222 %! ## Placing legend inside should return axes to original size
1223 %! legend location northeast
1224 %! title ('Blue and Red keys, with Green missing');
1225
1226 %!demo
1227 %! clf;
1228 %! plot (1:10, 1:10, 1:10, fliplr (1:10));
1229 %! title ('incline is blue and decline is green');
1230 %! legend ({'I am blue', 'I am green'}, 'location', 'east');
1231 %! legend hide
1232 %! legend show
1233
1234 %!demo
1235 %! clf;
1236 %! plot (1:10, 1:10, 1:10, fliplr (1:10));
1237 %! title ('Legend with keys in horizontal orientation');
1238 %! legend ({'I am blue', 'I am green'}, ...
1239 %! 'location', 'east', 'orientation', 'horizontal');
1240 %! legend boxoff
1241 %! legend boxon
1242
1243 %!demo
1244 %! clf;
1245 %! plot (1:10, 1:10, 1:10, fliplr (1:10));
1246 %! title ('Legend with box off');
1247 %! legend ({'I am blue', 'I am green'}, 'location', 'east');
1248 %! legend boxoff
1249
1250 %!demo
1251 %! clf;
1252 %! plot (1:10, 1:10, 1:10, fliplr (1:10));
1253 %! title ('Legend with text to the left of key');
1254 %! legend ({'I am blue', 'I am green'}, 'location', 'east');
1255 %! legend left
1256
1257 %!demo
1258 %! clf;
1259 %! plot (1:10, 1:10, 1:10, fliplr (1:10));
1260 %! title ({'Use properties to place legend text to the left of key', ...
1261 %! 'Legend text color is magenta'});
1262 %! h = legend ({'I am blue', 'I am green'}, 'location', 'east');
1263 %! legend ('right');
1264 %! set (h, 'textposition', 'left');
1265 %! set (h, 'textcolor', [1 0 1]);
1266
1267 %!demo
1268 %! clf;
1269 %! plot (1:10, 1:10, 1:10, fliplr (1:10));
1270 %! title ('Legend is hidden')
1271 %! legend ({'I am blue', 'I am green'}, 'location', 'east');
1272 %! legend hide
1273
1274 %!demo
1275 %! clf;
1276 %! x = 0:1;
1277 %! plot (x,x,';I am Blue;', x,2*x,';I am Green;', x,3*x,';I am Red;');
1278 %! title ({'Labels are embedded in call to plot', ...
1279 %! 'Legend is hidden and then shown'});
1280 %! legend boxon
1281 %! legend hide
1282 %! legend show
1283
1284 %!demo
1285 %! clf;
1286 %! x = 0:1;
1287 %! plot (x, x, ';\alpha;', ...
1288 %! x, 2*x, ';\beta=2\alpha;', ...
1289 %! x, 3*x, ';\gamma=3\alpha;');
1290 %! h = legend ();
1291 %! set (h, 'interpreter', 'tex');
1292 %! title ('Labels with interpreted Greek text');
1293
1294 %!demo
1295 %! clf;
1296 %! plot (rand (2));
1297 %! title ('Labels with TeX interpreter turned off');
1298 %! h = legend ('Hello_World', 'foo^bar');
1299 %! set (h, 'interpreter', 'none');
1300
1301 %!demo
1302 %! clf;
1303 %! plot (1:10, 1:10);
1304 %! title ('a very long label can sometimes cause problems');
1305 %! legend ('hello very big world', 'location', 'northeastoutside');
1306
1307 %!demo
1308 %! clf;
1309 %! labels = {};
1310 %! colororder = get (gca, 'colororder');
1311 %! for i = 1:5
1312 %! h = plot (1:100, i + rand (100,1)); hold on;
1313 %! set (h, 'color', colororder(i,:));
1314 %! labels = {labels{:}, ['Signal ', num2str(i)]};
1315 %! end
1316 %! hold off;
1317 %! title ({'Signals with random offset and uniform noise';
1318 %! 'Legend shown below and outside of plot'});
1319 %! xlabel ('Sample Nr [k]'); ylabel ('Amplitude [V]');
1320 %! legend (labels, 'location', 'southoutside');
1321
1322 %!demo
1323 %! clf;
1324 %! x = linspace (0, 10);
1325 %! plot (x, x);
1326 %! hold on;
1327 %! stem (x, x.^2, 'g');
1328 %! title ('First created object gets first label');
1329 %! legend ('linear');
1330 %! hold off;
1331
1332 %!demo
1333 %! clf;
1334 %! x = linspace (0, 10);
1335 %! plot (x, x, x, x.^2);
1336 %! title ('First created object gets first label');
1337 %! legend ('linear');
1338
1339 %!demo
1340 %! clf;
1341 %! x = linspace (0, 10);
1342 %! plot (x, x, x, x.^2);
1343 %! title ('Labels are applied in order of object creation');
1344 %! legend ('linear', 'quadratic');
1345
1346 %!demo
1347 %! clf;
1348 %! rand_2x3_data1 = [0.341447, 0.171220, 0.284370; 0.039773, 0.731725, 0.779382];
1349 %! bar (rand_2x3_data1);
1350 %! ylim ([0 1.0]);
1351 %! title ('legend() works for bar graphs (hggroups)');
1352 %! legend ({'1st Bar', '2nd Bar', '3rd Bar'});
1353
1354 %!demo
1355 %! clf;
1356 %! rand_2x3_data2 = [0.44804, 0.84368, 0.23012; 0.72311, 0.58335, 0.90531];
1357 %! bar (rand_2x3_data2);
1358 %! ylim ([0 1.2]);
1359 %! title ('legend() works for bar graphs (hggroups)');
1360 %! legend ('1st Bar', '2nd Bar', '3rd Bar');
1361 %! legend right;
1362
1363 %!demo
1364 %! clf;
1365 %! x = 0:0.1:7;
1366 %! h = plot (x,sin(x), x,cos(x), x,sin(x.^2/10), x,cos(x.^2/10));
1367 %! title ('Only the sin() objects have keylabels');
1368 %! legend (h([1, 3]), {'sin (x)', 'sin (x^2/10)'}, 'location', 'southwest');
1369
1370 %!demo
1371 %! clf;
1372 %! x = 0:0.1:10;
1373 %! plot (x, sin (x), ';sin (x);');
1374 %! hold all;
1375 %! plot (x, cos (x), ';cos (x);');
1376 %! hold off;
1377 %! title ('legend constructed from multiple plot calls');
1378
1379 %!demo
1380 %! clf;
1381 %! x = 0:0.1:10;
1382 %! plot (x, sin (x), ';sin (x);');
1383 %! hold all;
1384 %! plot (x, cos (x), ';cos (x);');
1385 %! hold off;
1386 %! title ('Specified label text overrides previous labels');
1387 %! legend ({'Sine', 'Cosine'}, 'location', 'northeastoutside');
1388
1389 %!demo
1390 %! clf;
1391 %! x = 0:10;
1392 %! plot (x, rand (11));
1393 %! xlabel ('Indices');
1394 %! ylabel ('Random Values');
1395 %! title ('Legend ''off'' deletes the legend');
1396 %! legend (cellstr (num2str ((1:10)')), 'location', 'northeastoutside');
1397 %! legend off;
1398 %! axis ([0, 10, 0 1]);
1399
1400 %!demo
1401 %! clf;
1402 %! x = (1:5)';
1403 %! subplot (2,2,1);
1404 %! plot (x, rand (numel (x)));
1405 %! legend (cellstr (num2str (x)), 'location', 'northwestoutside');
1406 %! subplot (2,2,2);
1407 %! plot (x, rand (numel (x)));
1408 %! legend (cellstr (num2str (x)), 'location', 'northeastoutside');
1409 %! subplot (2,2,3);
1410 %! plot (x, rand (numel (x)));
1411 %! legend (cellstr (num2str (x)), 'location', 'southwestoutside');
1412 %! subplot (2,2,4);
1413 %! plot (x, rand (numel (x)));
1414 %! legend (cellstr (num2str (x)), 'location', 'southeastoutside');
1415
1416 %!demo
1417 %! clf;
1418 %! plot (rand (2));
1419 %! title ('legend() will warn if extra labels are specified');
1420 %! legend ('Hello', 'World', 'interpreter', 'foobar');
1421
1422 %!demo
1423 %! clf;
1424 %! x = 0:10;
1425 %! y1 = rand (size (x));
1426 %! y2 = rand (size (x));
1427 %! [ax, h1, h2] = plotyy (x, y1, x, y2);
1428 %! title ('plotyy legend test #1: Blue and Green labels');
1429 %! legend ([h1, h2], {'Blue', 'Green'}, 'location', 'south');
1430
1431 %!demo
1432 %! clf;
1433 %! x = 0:10;
1434 %! y1 = rand (size (x));
1435 %! y2 = rand (size (x));
1436 %! [ax, h1, h2] = plotyy (x, y1, x, y2);
1437 %! title ('plotyy legend test #2: Blue and Green labels');
1438 %! legend ({'Blue', 'Green'}, 'location', 'south');
1439
1440 %!demo
1441 %! clf;
1442 %! x = 0:10;
1443 %! y1 = rand (size (x));
1444 %! y2 = rand (size (x));
1445 %! [ax, h1, h2] = plotyy (x, y1, x, y2);
1446 %! title ('plotyy legend test #3: Blue and Green labels');
1447 %! legend ('Blue', 'Green', 'location', 'south');
1448
1449 %!demo % bug 36408
1450 %! clf;
1451 %! option = 'right';
1452 %! subplot (3,1,1);
1453 %! plot (rand (1,4));
1454 %! xlabel xlabel;
1455 %! ylabel ylabel;
1456 %! title ('Subplots should adjust to the legend placed outside');
1457 %! legend ({'1'}, 'location', 'northeastoutside');
1458 %! legend (option);
1459 %! subplot (3,1,2);
1460 %! plot (rand (1,4));
1461 %! xlabel xlabel;
1462 %! ylabel ylabel;
1463 %! legend ({'1234567890'}, 'location', 'eastoutside');
1464 %! legend (option);
1465 %! subplot (3,1,3);
1466 %! plot (rand (1,4));
1467 %! xlabel xlabel;
1468 %! ylabel ylabel;
1469 %! legend ({'12345678901234567890'}, 'location', 'southeastoutside');
1470 %! legend (option);
1471
1472 %!demo % bug 36408
1473 %! clf;
1474 %! option = 'right';
1475 %! subplot (3,1,1);
1476 %! plot (rand (1,4));
1477 %! title ('Subplots should adjust to the legend placed outside');
1478 %! legend ({'1'}, 'location', 'northwestoutside');
1479 %! legend (option);
1480 %! subplot (3,1,2);
1481 %! plot (rand (1,4));
1482 %! legend ({'1234567890'}, 'location', 'westoutside');
1483 %! legend (option);
1484 %! subplot (3,1,3);
1485 %! plot (rand (1,4));
1486 %! legend ({'12345678901234567890'}, 'location', 'southwestoutside');
1487 %! legend (option);
1488
1489 %!demo % bug 36408
1490 %! clf;
1491 %! option = 'right';
1492 %! subplot (3,1,1);
1493 %! plot (rand (1,4));
1494 %! set (gca (), 'yaxislocation', 'right');
1495 %! xlabel ('xlabel');
1496 %! ylabel ('ylabel');
1497 %! title ('Subplots should adjust to the legend placed outside');
1498 %! legend ({'1'}, 'location', 'northeastoutside');
1499 %! legend (option);
1500 %! subplot (3,1,2);
1501 %! plot (rand (1,4));
1502 %! set (gca (), 'yaxislocation', 'right');
1503 %! xlabel ('xlabel');
1504 %! ylabel ('ylabel');
1505 %! legend ({'1234567890'}, 'location', 'eastoutside');
1506 %! legend (option);
1507 %! subplot (3,1,3);
1508 %! plot (rand (1,4));
1509 %! set (gca (), 'yaxislocation', 'right');
1510 %! xlabel ('xlabel');
1511 %! ylabel ('ylabel');
1512 %! legend ({'12345678901234567890'}, 'location', 'southeastoutside');
1513 %! legend (option);
1514
1515 %!demo % bug 36408
1516 %! clf;
1517 %! option = 'right';
1518 %! subplot (3,1,1);
1519 %! plot (rand (1,4));
1520 %! set (gca (), 'yaxislocation', 'right');
1521 %! xlabel ('xlabel');
1522 %! ylabel ('ylabel');
1523 %! title ('Subplots should adjust to the legend placed outside');
1524 %! legend ({'1'}, 'location', 'northwestoutside');
1525 %! legend (option);
1526 %! subplot (3,1,2);
1527 %! plot (rand (1,4));
1528 %! set (gca (), 'yaxislocation', 'right');
1529 %! xlabel ('xlabel');
1530 %! ylabel ('ylabel');
1531 %! legend ({'1234567890'}, 'location', 'westoutside');
1532 %! legend (option);
1533 %! subplot (3,1,3);
1534 %! plot (rand (1,4));
1535 %! set (gca (), 'yaxislocation', 'right');
1536 %! xlabel ('xlabel');
1537 %! ylabel ('ylabel');
1538 %! legend ({'12345678901234567890'}, 'location', 'southwestoutside');
1539 %! legend (option);
1540
1541 %!demo % bug 36408;
1542 %! clf;
1543 %! option = 'right';
1544 %! subplot (3,1,1);
1545 %! plot (rand (1,4));
1546 %! set (gca (), 'xaxislocation', 'top');
1547 %! xlabel ('xlabel');
1548 %! ylabel ('ylabel');
1549 %! title ('Subplots should adjust to the legend placed outside');
1550 %! legend ({'1'}, 'location', 'northwestoutside');
1551 %! legend (option);
1552 %! subplot (3,1,2);
1553 %! plot (rand (1,4));
1554 %! set (gca (), 'xaxislocation', 'top');
1555 %! xlabel ('xlabel');
1556 %! ylabel ('ylabel');
1557 %! legend ({'1234567890'}, 'location', 'westoutside');
1558 %! legend (option);
1559 %! subplot (3,1,3);
1560 %! plot (rand (1,4));
1561 %! set (gca (), 'xaxislocation', 'top');
1562 %! xlabel ('xlabel');
1563 %! ylabel ('ylabel');
1564 %! legend ({'12345678901234567890'}, 'location', 'southwestoutside');
1565 %! legend (option);
1566
1567 %!demo % bug 39697
1568 %! clf;
1569 %! plot (1:10);
1570 %! legend ('Legend Text');
1571 %! title ({'Multi-line', 'titles', 'are a', 'problem'});
1572
1573 %!demo
1574 %! clf;
1575 %! colormap (cool (64));
1576 %! surf (peaks ());
1577 %! legend ('peaks()')
1578 %! title ('legend() works for surface objects too');
1579
1580 %!test
1581 %! toolkit = graphics_toolkit ("gnuplot");
1582 %! h = figure ("visible", "off");
1583 %! unwind_protect
1584 %! position = get (h, "position");
1585 %! plot (rand (3));
1586 %! legend ();
1587 %! filename = sprintf ("%s.eps", tmpnam ());
1588 %! print (filename);
1589 %! unlink (filename);
1590 %! assert (get (h, "position"), position);
1591 %! unwind_protect_cleanup
1592 %! close (h);
1593 %! graphics_toolkit (toolkit);
1594 %! end_unwind_protect
1595