image thumbnail

Distribute figures

by

 

15 Jun 2012 (Updated )

This function can distribute all open figures on different parts of the screen(s).

distFig(varargin)
function distFig(varargin)
% ===== Syntax ============================================================
%
% distFig(...,'Screen',Value)
% distFig(...,'Position',Value)
% 
% distFig(...,'Rows',Value)
% distFig(...,'Columns',Value)
% 
% distFig(...,'Not',Value)
% distFig(...,'Only',Value)
% distFig(...,'Offset',Value)
% 
% distFig(...,'Extra',Value)
% distFig(...,'Menu',Value)
% distFig(...,'Transpose',Value)
% distFig(...,'Simulink',Value)
% distFig(...,'Tight',Value)
%
% ===== Description =======================================================
%
% distFig(...,'Screen',Value) assigns where the figures will be
% distributed. 
% Synonums for 'Screen' are 'Scr'. Value can be:
% 'C' / 'Center' (Default)
% 'W' / 'West' / 'L' / 'Left'
% 'E' / 'East' / 'R' / 'Right'
% 'N' / 'North' / 'T' / 'Top'
% 'S' / 'South' / 'B' / 'Bottom'
% 'Main' / 'Primary'
% 'Ext' / 'External' / 'Secondary'
%
% distFig(...,'Position',Value) assigns in which part of the screen the
% figures will be distributed.
% Synonums for 'Position' are 'Pos'. Value can be:
% 'C' / 'Center' (Default)
% 'W' / 'West' / 'L' / 'Left'
% 'E' / 'East' / 'R' / 'Right'
% 'N' / 'North' / 'T' / 'Top'
% 'S' / 'South' / 'B' / 'Bottom'
% 'NW'
% 'NE'
% 'SW'
% 'SE'
%
% distFig(...,'Rows',Value) assigns how many rows the figures will be
% distributed on.
% Synonums for 'Rows' are 'R'. Value can be:
% 1...n
% 'Auto' / -1
% 'Auto' indicates that it automatically calculates the number of required
% rows.
% 
% distFig(...,'Columns',Value) assigns how many rows the figures will be
% distributed on.
% Synonums for 'Columns' are 'Cols'. Value can be:
% 1...n
% 'Auto' / -1
% 'Auto' indicates that it automatically calculates the number of required
% columns.
%
% distFig(...,'Not',Value) excludes specified figures from the distribution
% list. Value must be an array with the excluded figure numbers. The
% default value is [].
%
% distFig(...,'Only',Value) does only distrubute specified figures. Value
% must be an array with the figure which will be destributed. The
% default value is [].
%
% distFig(...,'Offset',Value) can be used to shift all figures on the
% distribution list. Value must be an integer larger or equal to zero. The
% default offset value is 0.
% 
% distFig(...,'Extra',Value) is used to handle the extra figures, which
% might not fit into 'Rows' * 'Cols'. For instance, 5 figure are to be
% distributed on a 2x2 = 4 area. The fifth figure is then places on top of
% the first figure if 'Extra' is 'restart'. It can be ignored by setting
% 'Extra' to 'ignore', in which the fifth figure will not be distributed.
% 
% distFig(...,'Menu',Value) can be used to modify the menu bar. 
% Value can be: 
% 'figure' (Default)
% 'none'
% 
% distFig(...,'Transpose',Value) can be used to tranpose the order of
% distributing. The values must be logical and the default value is false.
% 
% distFig(...,'Simulink',Value) can be used to handle simulink figures.
% Value can be:
% 'include' (Default)
% 'exclude'
% 'only'
% If value is true, simulink figures will be distributed along with the
% other figures. If value if 'exclude' simulink figures will be
% excluded from the distribution list. If value is 'only', only the 
% simulink figures will be distributed and not the ordinary figures.
% 
% distFig(...,'Tight',Value) can be used to stretch the figure to the 
% border of the figure, so no unnecessary "whitespace" is present. 
% Value must be logical and the default value if false.
%
% ===== Examples ==========================================================
%
% distFig();
% This will distribute all open figures on the primary screen in two rows.
%
% distFig('Screen','Left','Position','East','Only',[1,2,4])
% This will only distrubute figure 1, 2 and 4 on the right part of the left
% screen.
%
% distFig('Offset',2,'Not',[1,2])
% This will distribute all figure but figure 1 and 2 in the same pattern as
% distFig(), but figure 1 and 2 will not be distributed, but instead there will
% be blank spots where they would have been distributed.

% =========================================================================
% ===== Version ===========================================================
% =========================================================================

% Anders Schou Simonsen, AndersSSimonsen@GMail.com
% Version 5.0
% 20/12-2014

% =========================================================================
% ===== Default values ====================================================
% =========================================================================

Rows = -1;
Cols = -1;
Scr = 'C';
Pos = 'C';
Tight = false;
Menu = 'figure';
Transpose = false;
Extra = 'restart';
Offset = 0;
Not = [];
Only = [];
Simulink = 'include';

% =========================================================================
% ===== Get inputs ========================================================
% =========================================================================

if (mod(nargin,2) ~= 0)
	error('Uneven number of inputs! Returning...');
	return;
end

for i = 1:2:nargin
	if (ischar(varargin{i+1}))
		varargin{i+1} = lower(varargin{i+1});
	end
	switch lower(varargin{i})
		case {'scr','screen'}
			% =============================================================
			Allow = {
				'C'		'c'		'center'	'main'		'primary'
				'W'		'w'		'west'		'l'			'left'
				'E'		'e'		'east'		'r'			'right'
				'N'		'n'		'north'		't'			'top'
				'S'		's'		'south'		'b'			'bottom'
				'Ext'	'ext'	'external'	'secondary'	NaN
				};
			Trigger = any(strcmp(Allow,varargin{i+1}),2);
			if (~any(Trigger))
				warning('Input ''%s'' not recognized for ''%s''!',varargin{i+1},varargin{i});
			else
				Scr = Allow{Trigger,1};
			end
		case {'pos','position'}
			% =============================================================
			Allow = {
				'C'		'c'		'center'	NaN		NaN
				'W'		'w'		'west'		'l'		'left'
				'E'		'e'		'east'		'r'		'right'
				'N'		'n'		'north'		't'		'top'
				'S'		's'		'south'		'b'		'bottom'
				'NW'	'nw'	NaN			NaN		NaN
				'NE'	'ne'	NaN			NaN		NaN
				'SW'	'sw'	NaN			NaN		NaN
				'SE'	'se'	NaN			NaN		NaN
				};
			Trigger = any(strcmp(Allow,varargin{i+1}),2);
			if (~any(Trigger))
				warning('Input ''%s'' not recognized for ''%s''!',varargin{i+1},varargin{i});
			else
				Pos = Allow{Trigger,1};
			end
		case {'rows','r'}
			% =============================================================
			if (strcmp(varargin{i+1},'auto'))
				Rows = -1;
			else
				if ~((isnumeric(varargin{i+1})) && (mod(varargin{i+1},1) == 0))
					warning('Input to ''%s'' must be a round numeric value!',varargin{i});
				else
					Rows = varargin{i+1};
				end
			end
		case {'cols','columns'}
			% =============================================================
			if (strcmp(varargin{i+1},'auto'))
				Cols = -1;
			else
				if ~((isnumeric(varargin{i+1})) && (mod(varargin{i+1},1) == 0))
					warning('Input to ''%s'' must be a round numeric value!',varargin{i});
				else
					Cols = varargin{i+1};
				end
			end
		case {'ext','extra'}
			% =============================================================
			Allow = {
				'ignore'
				'restart'
				};
			Trigger = any(strcmp(Allow,varargin{i+1}),2);
			if (~any(Trigger))
				warning('Input ''%s'' not recognized for ''%s''!',varargin{i+1},varargin{i});
			else
				Extra = Allow{Trigger,1};
			end
		case {'menu','bar'}
			% =============================================================
			Allow = {
				'figure'
				'none'
				};
			Trigger = any(strcmp(Allow,varargin{i+1}),2);
			if (~any(Trigger))
				warning('Input ''%s'' not recognized for ''%s''!',varargin{i+1},varargin{i});
			else
				Menu = Allow{Trigger,1};
			end
		case {'transpose'}
			% =============================================================
			if (~islogical(varargin{i+1}))
				warning('Input to ''%s'' must be logical!',varargin{i});
			else
				Transpose = varargin{i+1};
			end
		case {'offset'}
			% =============================================================
			if ~((isnumeric(varargin{i+1})) && (mod(varargin{i+1},1) == 0))
				warning('Input to ''%s'' must be a round numeric value!',varargin{i});
			else
				Offset = varargin{i+1};
			end
		case {'not'}
			% =============================================================
			if ~((isnumeric(varargin{i+1})) && all(mod(varargin{i+1},1) == 0))
				warning('Input to ''%s'' must be round numeric values!',varargin{i});
			else
				Not = varargin{i+1};
			end
		case {'only'}
			% =============================================================
			if ~((isnumeric(varargin{i+1})) && all(mod(varargin{i+1},1) == 0))
				warning('Input to ''%s'' must be round numeric values!',varargin{i});
			else
				Only = varargin{i+1};
			end
		case {'simu','simulink'}
			% =============================================================
			Allow = {
				'include'	NaN
				'exclude'	'ignore'
				'only'		NaN
				};
			Trigger = any(strcmp(Allow,varargin{i+1}),2);
			if (~any(Trigger))
				warning('Input ''%s'' not recognized for ''%s''!',varargin{i+1},varargin{i});
			else
				Simulink = Allow{Trigger,1};
			end
		case {'tight'}
			% =============================================================
			if (~islogical(varargin{i+1}))
				warning('Input to ''%s'' must be logical!',varargin{i});
			else
				Tight = varargin{i+1};
			end
		otherwise
			% =============================================================
			fprintf('Input ''%s'' not recognized!',varargin{i});
	end
end

% =========================================================================
% ===== Constants =========================================================
% =========================================================================

% ===== Limits ============================================================
Limits = {...
	'C'		[0,1]		[0,1]	
	'W'		[0,0.5]		[0,1]
	'E'		[0.5,1]		[0,1]
	'N'		[0,1]		[0.5,1]
	'NW'	[0,0.5]		[0.5,1]
	'NE'	[0.5,1]		[0.5,1]
	'S'		[0,1]		[0,0.5]
	'SW'	[0,0.5]		[0,0.5]
	'SE'	[0.5,1]		[0,0.5]
	};

% ===== Taskbar height ====================================================
Taskbar_Height = 40;

% =========================================================================
% ===== Figure list =======================================================
% =========================================================================

% ===== All figures =======================================================
Fig_Object = findall(0,'type','figure'); 

% ===== Remove not visible figures ========================================
Visible = arrayfun(@(n) (strcmp(get(Fig_Object(n),'Visible'),'on')),1:numel(Fig_Object));
Fig_Object = Fig_Object(Visible);

% ===== Include simulink figures ==========================================
Fig_Number = {Fig_Object.Number};
Empty = arrayfun(@(n) (isempty(Fig_Number{n})),1:numel(Fig_Number));
Fig_Number(Empty) = {0};
Fig_Number = cell2mat(Fig_Number);
[~,Index] = sort(get(Fig_Object(Fig_Number == 0),'Name'));
Temp = find(Fig_Number == 0);
Fig_Number(Temp(Index)) = -(numel(Index):(-1):1);

% ===== Logical distribution array ========================================
Fig_Dist = true(1,numel(Fig_Object));

% ===== Simulink figures ==================================================
if strcmp(Simulink,'exclude')
	Fig_Dist = (Fig_Number > 0);
end
if strcmp(Simulink,'only')
	Fig_Dist = (Fig_Number < 0);
end

% ===== Not ===============================================================
Fig_Dist(ismember(Fig_Number,Not)) = false;

% ===== Only ==============================================================
if (~isempty(Only))
	Fig_Dist(~ismember(Fig_Number,Only)) = false;
end

% ===== Number of figures =================================================
nFig_Dist = sum(Fig_Dist);

% =========================================================================
% ===== Monitor ===========================================================
% =========================================================================

Monitors = get(0,'MonitorPositions');
Monitors_Label = cell(size(Monitors,1),1);
for i = 1:size(Monitors,1)
	if ((Monitors(i,1) == 1) && (Monitors(i,2) == 1))
		Monitors_Label{i} = 'C';
	elseif (Monitors(i,1) < 1)
		Monitors_Label{i} = 'W';
	elseif (Monitors(i,1) > 1)
		Monitors_Label{i} = 'E';
	elseif (Monitors(i,2) > 1)
		Monitors_Label{i} = 'N';
	elseif (Monitors(i,2) < 1)
		Monitors_Label{i} = 'S';
	end
end
Monitor = Monitors(strcmp(Monitors_Label,Scr),:);

if (isempty(Monitor))
	if (strcmp(Scr,'Ext'))
		if (size(Monitors,1) == 1)
			warning('No external monitor could be found!');
			Monitor = Monitors(1,:);
		else
			Monitor = Monitors(find(~strcmp(Monitors_Label,'C'),1,'first'),:);
		end
	else
		warning('Screen ''%s'' could not be found!',Scr);
		Monitor = Monitors(strcmp(Monitors_Label,'C'),:);
	end
end

% =========================================================================
% ===== Rows and columns ==================================================
% =========================================================================

Limit = Limits(strcmp(Limits(:,1),Pos),:);
if ((Rows == (-1)) && (Cols == (-1)))
	AR_Ideal = 1.2;
	AR_Mean = Inf;
	for i = 1:50
		% ===== Exception =================================================
		if ((nFig_Dist + Offset) == 4)
			Rows = 2;
			Cols = 2;
			break;
		end
		
		% ===== Temporary rows and columns ================================
		Rows = i;
		Cols = ceil((nFig_Dist + Offset) / Rows);
		
		% ===== Sizes =====================================================
		x = round(linspace(Limit{2}(1),Limit{2}(2),Cols + 1) * Monitor(3)) + 1;
		y = round(linspace(Limit{3}(1),Limit{3}(2),Rows + 1) * (Monitor(4) - Taskbar_Height)) + Taskbar_Height + 1;
		clear Size;
		[Size(:,:,1),Size(:,:,2)] = meshgrid(diff(x),diff(y));
		
		% ===== Output ====================================================
		AR_Mean_Prev = AR_Mean;
		AR_Mean = mean(mean(Size(:,:,1) ./ Size(:,:,2)));
		if (AR_Mean > AR_Ideal)
			AR_Error = abs([AR_Ideal - AR_Mean_Prev,AR_Ideal - AR_Mean]);
			if (AR_Error(1) < AR_Error(2))
				Rows = i - 1;
			end
			Cols = ceil((nFig_Dist + Offset) / Rows);
			clear Size;
			break;
		end
	end
elseif (Rows == (-1))
	Rows = ceil((nFig_Dist + Offset) / Cols);
elseif (Cols == (-1))
	Cols = ceil((nFig_Dist + Offset) / Rows);
end

% =========================================================================
% ===== Location matrix ===================================================
% =========================================================================

Sets = ceil(nFig_Dist / (Rows * Cols) + Offset);
Mat = nan(Rows,Cols,Sets);
if (Transpose)
	Mat = permute(Mat,[2,1,3]);
end
Mat((1:nFig_Dist) + Offset) = sort(Fig_Number(Fig_Dist));
if (Transpose)
	Mat = permute(Mat,[2,1,3]);
end
if (strcmp(Extra,'ignore'))
	Sets = 1;
end

% =========================================================================
% ===== Position and size =================================================
% =========================================================================

% ===== Position ==========================================================
x = round(linspace(Limit{2}(1),Limit{2}(2),Cols + 1) * Monitor(3)) + 1;
y = round(linspace(Limit{3}(1),Limit{3}(2),Rows + 1) * (Monitor(4) - Taskbar_Height)) + Taskbar_Height + 1;
[FPos(:,:,1),FPos(:,:,2)] = meshgrid(x(1:end-1),y(1:end-1));
FPos(:,:,2) = flipud(FPos(:,:,2));
FPos(:,:,1) = FPos(:,:,1) + Monitor(1) - 1;
FPos(:,:,2) = FPos(:,:,2) + Monitor(2) - 1;

% ===== Size ==============================================================
[Size(:,:,1),Size(:,:,2)] = meshgrid(diff(x),diff(y));

% =========================================================================
% ===== Distribute figures ================================================
% =========================================================================

for s = 1:Sets
	for i = 1:size(Mat,1)
		for j = 1:size(Mat,2)
			if (~isnan(Mat(i,j,s)))
				% ===== Position and figure ===============================
				Pos_Temp = [squeeze(FPos(i,j,:))',squeeze(Size(i,j,:))'];
				gcf_Temp = Fig_Object(Fig_Number == Mat(i,j,s));
				gca_Temp = get(gcf_Temp,'CurrentAxes');

				% ===== Focus simulink figure to bring to front ===========
				if (Mat(i,j,s) < 0)
					figure(gcf_Temp);
				end
				
				% ===== Set units =========================================
				set(gcf_Temp,'MenuBar',Menu);
				if (Mat(i,j,s) < 0)
					drawnow;
				end
				Units_Prev = get(gcf_Temp,'Units');
				
				% ===== Distribute figure =================================
				set(gcf_Temp,'Units','pixels','OuterPosition',Pos_Temp);
				set(gcf_Temp,'Units',Units_Prev);
				
				% ===== Apply tight =======================================
				if ((Tight) && (Mat(i,j,s) > 0))
					drawnow;
					set(gca_Temp,'Position',[get(gca_Temp,'TightInset') * eye(4,2),1 - get(gca_Temp,'TightInset') * [1,0,1,0;0,1,0,1]']);
				end
			end
		end
	end
end

Contact us