263 lines
12 KiB
Matlab
Executable File
263 lines
12 KiB
Matlab
Executable File
function [legend_h,object_h,plot_h,text_strings] = columnlegend(numcolumns, str, varargin)
|
|
%
|
|
% columnlegend creates a legend with a specified number of columns.
|
|
%
|
|
% columnlegend(numcolumns, str, varargin)
|
|
% numcolumns - number of columns in the legend
|
|
% str - cell array of strings for the legend
|
|
%
|
|
% Additional Input Options:
|
|
% columnlegend(..., 'location', loc)
|
|
% loc - location variable for legend, default is 'NorthEast'
|
|
% possible values: 'NorthWest', 'NorthEast', 'SouthEast', 'SouthWest',
|
|
% 'NorthOutside', 'SouthOutside',
|
|
% 'NortheastOutside', 'SoutheastOutside'
|
|
%
|
|
% columnlegend(numcolumns, str, 'padding', 0.5); % add 50% vertical padding between legend entries (relative to original height)
|
|
%
|
|
% columnlegend(..., 'boxon')
|
|
% columnlegend(..., 'boxoff')
|
|
% set legend bounding box on/off
|
|
%
|
|
% example:
|
|
% plot(bsxfun(@times, [0:9]',[1:10]));
|
|
% columnlegend(3, cellstr(num2str([1:10]')), 'location','northwest');
|
|
%
|
|
%
|
|
% Author: Simon Henin <shenin@gc.cuny.edu>
|
|
%
|
|
% 4/09/2013 - Fixed bug with 3 entries / 3 columns
|
|
% 4/09/2013 - Added bounding box option as per @Durga Lal Shrestha (fileexchage)
|
|
% 11 May 2010 - 1.2 Add instructions for printing figure with columns
|
|
% 08 Feb 2011 - 1.4 Added functionality when using markers.
|
|
% 31 Oct 2015 - Updates for compatibility with 2015a, Adds minor improvements as per user suggestions
|
|
% 07 Nov 2016 - Bug fixes, added functionality for bar plots, added all valid legend locations
|
|
% 07 Jun 2017 - Added a quick padding feature that creates additional
|
|
% vertical space between legend entries.
|
|
location = 'NorthEast';
|
|
boxon = false; legend_h = false; padding = 0; extravars = [];
|
|
for i=1:2:length(varargin)
|
|
switch lower(varargin{i})
|
|
case 'location'
|
|
location = varargin{i+1};
|
|
i=i+2;
|
|
case 'boxon'
|
|
boxon = true;
|
|
case 'boxoff'
|
|
boxon = false;
|
|
case 'legend'
|
|
legend_h = varargin{i+1};
|
|
i=i+2;
|
|
case 'object'
|
|
object_h = varargin{i+1};
|
|
i=i+2;
|
|
case 'padding'
|
|
padding = varargin{i+1};
|
|
i=i+2;
|
|
otherwise
|
|
extravars{end+1} = varargin{i};
|
|
extravars{end+1} = varargin{i+1};
|
|
end
|
|
end
|
|
if legend_h == false
|
|
%create the legend
|
|
if ~isempty(extravars)
|
|
[legend_h,object_h,plot_h,text_strings] = legend(str, extravars{:});
|
|
else
|
|
[legend_h,object_h,plot_h,text_strings] = legend(str);
|
|
end
|
|
end
|
|
%some variables
|
|
numlines = length(str);
|
|
numpercolumn = ceil(numlines/numcolumns);
|
|
%get old width, new width and scale factor
|
|
set(legend_h, 'units', 'normalized');
|
|
set(gca, 'units', 'normalized');
|
|
pos = get(legend_h, 'position');
|
|
width = numcolumns*pos(3);
|
|
newheight = (pos(4)/numlines)*numpercolumn;
|
|
rescale = pos(3)/width;
|
|
%get some old values so we can scale everything later
|
|
type = get(object_h(numlines+1), 'type');
|
|
switch type
|
|
case {'line'}
|
|
xdata = get(object_h(numlines+1), 'xdata');
|
|
ydata1 = get(object_h(numlines+1), 'ydata');
|
|
ydata2 = get(object_h(numlines+3), 'ydata');
|
|
|
|
%we'll use these later to align things appropriately
|
|
sheight = ydata1(1)-ydata2(1) + (ydata1(1)-ydata2(1))*padding; % height between data lines
|
|
height = ydata1(1) + (ydata1(1)-ydata2(1))*padding*2; % height of the box. Used to top margin offset
|
|
line_width = (xdata(2)-xdata(1))*rescale; % rescaled linewidth to match original
|
|
spacer = xdata(1)*rescale; % rescaled spacer used for margins
|
|
case {'hggroup'}
|
|
text_pos = get(object_h(1), 'position');
|
|
child = get(object_h(numlines+1), 'children');
|
|
% determine if bar or errorbar
|
|
subtype = get(child, 'Type');
|
|
if strcmpi(subtype, 'patch') % bar
|
|
vertices_1 = get(child, 'vertices');
|
|
child = get(object_h(numlines+2), 'children');
|
|
vertices_2 = get(child, 'vertices');
|
|
sheight = vertices_1(2,2)-vertices_1(1,2);
|
|
height = vertices_1(2,2);
|
|
line_width = (vertices_1(3,1)-vertices_1(1,1))*rescale; % rescaled linewidth to match original
|
|
spacer = vertices_1(1,2)-vertices_2(2,2); % rescaled spacer used for margins
|
|
text_space = (text_pos(1)-vertices_1(4,1))./numcolumns;
|
|
elseif strcmpi(subtype, 'hggroup') % errorbar
|
|
child1 = get(get(object_h(numlines+1), 'children'), 'children');
|
|
child1 = child1(2);
|
|
child2 = get(get(object_h(numlines+2), 'children'), 'children');
|
|
child2 = child2(2);
|
|
xdata = get(child1, 'xdata');
|
|
ydata1 = get(child1, 'ydata');
|
|
ydata2 = get(child2, 'ydata');
|
|
%we'll use these later to align things appropriately
|
|
sheight = ydata1(1)-ydata2(1) + (ydata1(1)-ydata2(1))*padding; % height between data lines
|
|
height = ydata1(1) + (ydata1(1)-ydata2(1))*padding*2; % height of the box. Used to top margin offset
|
|
line_width = (xdata(2)-xdata(1))*rescale; % rescaled linewidth to match original
|
|
spacer = xdata(1)*rescale; % rescaled spacer used for margins
|
|
end
|
|
end
|
|
%put the legend on the upper left corner to make initial adjustments easier
|
|
% set(gca, 'units', 'pixels');
|
|
loci = get(gca, 'position');
|
|
set(legend_h, 'position', [loci(1) pos(2) width pos(4)]);
|
|
col = -1;
|
|
for i=1:numlines
|
|
if (mod(i,numpercolumn)==1 || (numpercolumn == 1))
|
|
col = col+1;
|
|
end
|
|
|
|
if i==1
|
|
linenum = i+numlines;
|
|
else
|
|
if strcmp(type, 'line')
|
|
linenum = linenum+2;
|
|
else
|
|
linenum = linenum+1;
|
|
end
|
|
end
|
|
labelnum = i;
|
|
|
|
position = mod(i,numpercolumn);
|
|
if position == 0
|
|
position = numpercolumn;
|
|
end
|
|
|
|
switch type,
|
|
case {'line'}
|
|
%realign the labels
|
|
set(object_h(linenum), 'ydata', [(height-(position-1)*sheight) (height-(position-1)*sheight)]);
|
|
set(object_h(linenum), 'xdata', [col/numcolumns+spacer col/numcolumns+spacer+line_width]);
|
|
|
|
set(object_h(linenum+1), 'ydata', [height-(position-1)*sheight height-(position-1)*sheight]);
|
|
set(object_h(linenum+1), 'xdata', [col/numcolumns+spacer*3.5 col/numcolumns+spacer*3.5]);
|
|
|
|
set(object_h(labelnum), 'position', [col/numcolumns+spacer*2+line_width height-(position-1)*sheight]);
|
|
case {'hggroup'},
|
|
if exist('subtype', 'var')&strcmpi(subtype, 'patch'),
|
|
child = get(object_h(linenum), 'children');
|
|
v = get(child, 'vertices');
|
|
%x-positions
|
|
v([1:2 5],1) = col/numcolumns+spacer;
|
|
v(3:4,1) = col/numcolumns+spacer+line_width;
|
|
% y-positions
|
|
v([1 4 5],2) = (height-(position-1)*sheight-(position-1)*spacer);
|
|
v([2 3], 2) = v(1,2)+sheight;
|
|
set(child, 'vertices', v);
|
|
set(object_h(labelnum), 'position', [v(3,1)+text_space v(1,2)+(v(2,2)-v(1,2))/2 v(3,1)-v(1,1)]);
|
|
else
|
|
child = get(get(object_h(linenum), 'children'), 'children');
|
|
set(child(2), 'ydata', [(height-(position-1)*sheight) (height-(position-1)*sheight)]);
|
|
set(child(2), 'xdata', [col/numcolumns+spacer col/numcolumns+spacer+line_width]);
|
|
|
|
% set(object_h(linenum+1), 'ydata', [height-(position-1)*sheight height-(position-1)*sheight]);
|
|
% set(object_h(linenum+1), 'xdata', [col/numcolumns+spacer*3.5 col/numcolumns+spacer*3.5]);
|
|
|
|
set(object_h(labelnum), 'position', [col/numcolumns+spacer*2+line_width height-(position-1)*sheight]);
|
|
end
|
|
end
|
|
|
|
end
|
|
%unfortunately, it is not possible to force the box to be smaller than the
|
|
%original height, therefore, turn it off and set background color to none
|
|
%so that it no longer appears
|
|
set(legend_h, 'Color', 'None', 'Box', 'off');
|
|
%let's put it where you want it
|
|
fig_pos = get(gca, 'position');
|
|
pos = get(legend_h, 'position');
|
|
padding = 0.01; % padding, in normalized units
|
|
% if location is some variation on south, then we need to take into account
|
|
% the new height
|
|
if strfind(location, 'south'),
|
|
h_diff = pos(4)-newheight;
|
|
pos(4) = newheight;
|
|
end
|
|
switch lower(location),
|
|
case {'northeast'}
|
|
set(legend_h, 'position', [pos(1)+fig_pos(3)-pos(3)-padding pos(2) pos(3) pos(4)]);
|
|
case {'northwest'}
|
|
set(legend_h, 'position', [pos(1)+padding pos(2) pos(3) pos(4)]);
|
|
case {'southeast'}
|
|
pos(4) = newheight;
|
|
set(legend_h, 'position', [pos(1)+fig_pos(3)-pos(3)-padding fig_pos(2)-pos(4)/2+pos(4)/4 pos(3) pos(4)]);
|
|
case {'southwest'}
|
|
set(legend_h, 'position', [fig_pos(1)+padding fig_pos(2)-pos(4)/2+pos(4)/4 pos(3) pos(4)]);
|
|
case {'northeastoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]-[0 0 pos(3) 0]);
|
|
set(legend_h, 'position', [pos(1)+fig_pos(3)-pos(3) pos(2) pos(3) pos(4)]);
|
|
case {'northwestoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]+[pos(3) 0 -pos(3) 0]);
|
|
set(legend_h, 'position', [fig_pos(1)-fig_pos(3)*.1 pos(2) pos(3) pos(4)]); % -10% figurewidth to account for axis labels
|
|
case {'north'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(legend_h, 'position', [fig_pos(1)+fig_pos(3)/2-pos(3)/2 fig_pos(2)+(fig_pos(4)-pos(4))-padding pos(3) pos(4)]);
|
|
case {'northoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]-[0 0 0 pos(4)]);
|
|
% set(legend_h, 'position', [fig_pos(1)+fig_pos(3)/2-pos(3)/2 fig_pos(2)+(fig_pos(4)-pos(4)) pos(3) pos(4)]);
|
|
set(legend_h, 'position', [fig_pos(1)+fig_pos(3)/2-pos(3)/2 fig_pos(2)+fig_pos(4)-pos(4) pos(3) pos(4)]);
|
|
case {'south'}
|
|
y_pos = fig_pos(2)-h_diff+pos(4);
|
|
set(legend_h, 'position', [fig_pos(1)+fig_pos(3)/2-pos(3)/2 y_pos pos(3) pos(4)]);
|
|
case {'southoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]-[0 -pos(4) 0 pos(4)]);
|
|
% set(legend_h, 'position', [fig_pos(1)+fig_pos(3)/2-pos(3)/2 fig_pos(2)-pos(4)-pos(3)*0.1 pos(3) pos(4)]);
|
|
set(legend_h, 'position', [fig_pos(1)+fig_pos(3)/2-pos(3)/2 0 pos(3) pos(4)]);
|
|
case {'eastoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]-[0 0 pos(3) 0]);
|
|
set(legend_h, 'position', [pos(1)+fig_pos(3)-pos(3) fig_pos(2)+fig_pos(4)/2-pos(4)/2 pos(3) pos(4)]);
|
|
case {'southeastoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]-[0 0 pos(3) 0]);
|
|
set(legend_h, 'position', [pos(1)+fig_pos(3)-pos(3) fig_pos(2)-pos(4)/4 pos(3) pos(4)]);
|
|
case {'westoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]+[pos(3) 0 -pos(3) 0]);
|
|
set(legend_h, 'position', [fig_pos(1)-fig_pos(3)*.1 fig_pos(2)+fig_pos(4)/2-pos(4)/2 pos(3) pos(4)]); % -10% figurewidth to account for axis labels
|
|
case {'southwestoutside'}
|
|
% need to resize axes to allow legend to fit in figure window
|
|
set(gca, 'position', [fig_pos]+[pos(3) 0 -pos(3) 0]);
|
|
set(legend_h, 'position', [fig_pos(1)-fig_pos(3)*.1 fig_pos(2)-pos(4)/4 pos(3) pos(4)]); % -10% figurewidth to account for axis labels
|
|
end
|
|
% display box around legend
|
|
if boxon
|
|
drawnow; % make sure everyhting is drawn in place first.
|
|
% set(legend_h, 'units', 'normalized');
|
|
pos = get(legend_h, 'position');
|
|
orgHeight = pos(4);
|
|
pos(4) = (orgHeight/numlines)*numpercolumn;
|
|
pos(2)=pos(2) + orgHeight-pos(4) - pos(4)*0.05;
|
|
pos(1) = pos(1)+pos(1)*0.01;
|
|
annotation('rectangle',pos, 'linewidth', 1)
|
|
end
|
|
% re-set to normalized so that things scale properly
|
|
set(legend_h, 'units', 'normalized');
|
|
set(gca, 'units', 'normalized');
|
|
|
|
end |