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