-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprintstruct.m
424 lines (410 loc) · 19.1 KB
/
printstruct.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
function varargout = printstruct(S, varargin)
% PRINTSTRUCT Recursively print hierarchical outline of structure contents
%
% This is a minor adaptation of the File Exchange contribution "Structure
% outline" written by B. Roossien <[email protected]> and available here:
% http://mathworks.com/matlabcentral/fileexchange/13500-structure-outline
% __________________________________________________________________________
% USAGE: pS = printstruct(S, varargin)
%
% IN: S = structure variable to print
% OUT*: pS = cell array containing printed structure
%
% *If defined, result will NOT display in command window
% __________________________________________________________________________
% OPTIONAL VARARGIN* [entered as 'name', value pairs]:
% *Run printstruct w/no arguments to see default values
%
% NLEVELS: N levels to print. If negative, all levels printed.
% NINDENT: number of tab indents for each line of printed struct
% STRUCTNAME: top level name (if empty, variable name will be used)
% PRINTCONTENTS: flag to print field values/contents as well
% SORTFIELDS: flag to sort fields within each dtype (by level)
% MAXARRAYLENGTH: for fields containing arrays, sets max number of values to
% print. Values of a 2D (m,n) array are printed if the number of
% elements (mxn) is less than or equal to maxarraylength. This is ignored
% if printcontents is set to 0.
% _______________________________
% EXAMPLES
%
% pS = printstruct(S, 'maxarray', 100);
% printstruct(S, 'nlevels', 2, 'printcontents', 0, 'nindent', 3)
%
% ---------------------- Copyright (C) 2015 Bob Spunt ----------------------
% Created: 2015-08-13
% Email: [email protected]
% __________________________________________________________________________
def = { ...
'nlevels', -1, ...
'printcontents', 1, ...
'nindent', 0, ...
'structname', '', ...
'sortfields', 1, ...
'maxarraylength', 75 ...
};
vals = setargs(def, varargin);
if nargin==0, mfile_showhelp; fprintf('\t| - VARARGIN DEFAULTS - |\n'); disp(vals); return; end
if iscell(S)
if all(cellfun(@isstruct, S))
fprintf(' - detected cell array of structs. printing iteratively...\n\n');
end
else
S = {S};
end
if nlevels==0, nlevels = -1; end;
if isempty(structname), structname = inputname(1); end
pS = [];
for i = 1:length(S)
cS = S{i};
if sortfields, cS = orderfields(cS); end
if length(cS)==1
str = recFieldPrint(cS, nindent, nlevels, printcontents, maxarraylength, structname, sortfields);
str = [cellstr(structname); str];
else
str = [];
for i = 1:length(cS)
tmpstr = recFieldPrint(cS(i), nindent, nlevels, printcontents, maxarraylength, structname, sortfields);
str = [str; cellstr(sprintf('%s(%d)', structname, i)); tmpstr];
if i<length(cS), str = [str; {' '}]; end
end
end
if nargout==0
for i = 1:length(str), disp(cell2mat(str(i, 1))); end
else
varargout{1} = [pS; str];
end
end
end
% ==========================================================================
% SUBFUNCTIONS ------------------------------------------------------------=
% ==========================================================================
function mfile_showhelp(varargin)
% MFILE_SHOWHELP
ST = dbstack('-completenames');
if isempty(ST), fprintf('\nYou must call this within a function\n\n'); return; end
eval(sprintf('help %s', ST(2).file));
end
function argstruct = setargs(defaults, optargs)
% SETARGS Name/value parsing and assignment of varargin with default values
%
% This is a utility for setting the value of optional arguments to a
% function. The first argument is required and should be a cell array of
% "name, default value" pairs for all optional arguments. The second
% argument is optional and should be a cell array of "name, custom value"
% pairs for at least one of the optional arguments.
%
% USAGE: argstruct = setargs(defaults, args)
% __________________________________________________________________________
% OUTPUT
%
% argstruct: structure containing the final argument values
% __________________________________________________________________________
% INPUTS
%
% defaults:
% cell array of "name, default value" pairs for all optional arguments
%
% optargs [optional]
% cell array of "name, custom value" pairs for at least one of the
% optional arguments. this will typically be the "varargin" array.
% __________________________________________________________________________
% USAGE EXAMPLE (WITHIN FUNCTION)
%
% defaults = {'arg1', 0, 'arg2', 'words', 'arg3', rand};
% argstruct = setargs(defaults, varargin)
%
if nargin < 1, mfile_showhelp; return; end
if nargin < 2, optargs = []; end
defaults = reshape(defaults, 2, length(defaults)/2)';
if ~isempty(optargs)
if mod(length(optargs), 2)
error('Optional inputs must be entered as Name, Value pairs, e.g., myfunction(''name'', value)');
end
arg = reshape(optargs, 2, length(optargs)/2)';
for i = 1:size(arg,1)
idx = strncmpi(defaults(:,1), arg{i,1}, length(arg{i,1}));
if sum(idx) > 1
error(['Input "%s" matches multiple valid inputs:' repmat(' %s', 1, sum(idx))], arg{i,1}, defaults{idx, 1});
elseif ~any(idx)
error('Input "%s" does not match a valid input.', arg{i,1});
else
defaults{idx,2} = arg{i,2};
end
end
end
for i = 1:size(defaults,1), assignin('caller', defaults{i,1}, defaults{i,2}); end
if nargout>0, argstruct = cell2struct(defaults(:,2), defaults(:,1)); end
end
% ==========================================================================
% ADAPTED FROM FROM STRUCDISP - fileexchange/13831-structure-display -------
% ==========================================================================
function listStr = recFieldPrint(Structure, indent, depth, printValues, maxArrayLength, structname, sortfields)
% RECFIELDPRINT Recursive printing of a structure variable
% This function and its dependencies were taken from:
% STRUCDISP.m by
% Contact author: B. Roossien <[email protected]>
% (c) ECN 2007-2008
% Version 1.3.0
%
if sortfields, Structure = orderfields(Structure); end
listStr = {};
if length(Structure) > 1
if (printValues == 0)
varStr = createArraySize(Structure, structname);
listStr = [{' '}; {[structname, varStr]}];
body = recFieldPrint(Structure(1), indent, depth, ...
printValues, maxArrayLength, '', sortfields);
listStr = [listStr; body];
else
for iStruc = 1 : length(Structure)
listStr = [listStr; {' '}; {sprintf('%s(%d)', structname, iStruc)}];
body = recFieldPrint(Structure(iStruc), indent, depth, ...
printValues, maxArrayLength, '', sortfields);
listStr = [listStr; body];
end
end
return
end
fields = fieldnames(Structure);
isStruct = structfun(@isstruct, Structure);
strucFields = fields(isStruct == 1);
strIndent = getIndentation(indent + 1);
listStr = [listStr; {strIndent}];
strIndent = getIndentation(indent);
for iField = 1 : length(strucFields)
fieldName = cell2mat(strucFields(iField));
Field = Structure.(fieldName);
% Empty structure
if isempty(Field)
strSize = createArraySize(Field, 'Structure');
line = sprintf('%s |--- %s :%s', ...
strIndent, fieldName, strSize);
listStr = [listStr; {line}];
% Scalar structure
elseif isscalar(Field)
line = sprintf('%s |--- %s', strIndent, fieldName);
% Recall this function if the tree depth is not reached yet
if (depth < 0) || (indent + 1 < depth)
lines = recFieldPrint(Field, indent + 1, depth, ...
printValues, maxArrayLength, '', sortfields);
listStr = [listStr; {line}; lines];
else
listStr = [listStr; {line}];
end
% Short vector structure of which the values should be printed
elseif (isvector(Field)) && ...
(printValues > 0) && ...
(length(Field) < maxArrayLength) && ...
((depth < 0) || (indent + 1 < depth))
% Use a for-loop to print all structures in the array
for iFieldElement = 1 : length(Field)
line = sprintf('%s |--- %s(%g)', ...
strIndent, fieldName, iFieldElement);
lines = recFieldPrint(Field(iFieldElement), indent + 1, ...
depth, printValues, maxArrayLength, '', sortfields);
listStr = [listStr; {line}; lines];
if iFieldElement ~= length(Field)
listStr = [listStr; {[strIndent ' | ']}];
end
end
% Structure is a matrix or long vector
% No values have to be printed or depth limit is reached
else
varStr = createArraySize(Field, 'Structure');
line = sprintf('%s |--- %s :%s', ...
strIndent, fieldName, varStr);
lines = recFieldPrint(Field(1), indent + 1, depth, ...
0, maxArrayLength, '', sortfields);
listStr = [listStr; {line}; lines];
end
% Some extra blank lines to increase readability
listStr = [listStr; {[strIndent ' | ']}];
end % End iField for-loop
%% Field Filler
% To properly align the field names, a filler is required. To know how long
% the filler must be, the length of the longest fieldname must be found.
% Because 'fields' is a cell array, the function 'cellfun' can be used to
% extract the lengths of all fields.
maxFieldLength = max(cellfun(@length, fields));
%% Print non-structure fields without values
% Print non-structure fields without the values. This can be done very
% quick.
if printValues == 0
noStrucFields = fields(isStruct == 0);
for iField = 1 : length(noStrucFields)
Field = cell2mat(noStrucFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
listStr = [listStr; {[strIndent ' |' filler ' ' Field]}];
end
return
end
%% Select non-structure fields (to print with values)
% Select fields that are not a structure and group them by data type. The
% following groups are distinguished:
% - characters and strings
% - numeric arrays
% - logical
% - empty arrays
% - matrices
% - numeric scalars
% - cell arrays
% - other data types
% Character or string (array of characters)
isChar = structfun(@ischar, Structure);
charFields = fields(isChar == 1);
% Numeric fields
isNumeric = structfun(@isnumeric, Structure);
% Numeric scalars
isScalar = structfun(@isscalar, Structure);
isScalar = isScalar .* isNumeric;
scalarFields = fields(isScalar == 1);
% Numeric vectors (arrays)
isVector = structfun(@isvector, Structure);
isVector = isVector .* isNumeric .* not(isScalar);
vectorFields = fields(isVector == 1);
% Logical fields
isLogical = structfun(@islogical, Structure);
logicalFields = fields(isLogical == 1);
% Empty arrays
isEmpty = structfun(@isempty, Structure);
emptyFields = fields(isEmpty == 1);
% Numeric matrix with dimension size 2 or higher
isMatrix = structfun(@(x) ndims(x) >= 2, Structure);
isMatrix = isMatrix .* isNumeric .* not(isVector) .* not(isScalar) .* not(isEmpty);
matrixFields = fields(isMatrix == 1);
% Cell array
isCell = structfun(@iscell, Structure);
cellFields = fields(isCell == 1);
% Datatypes that are not checked for
isOther = not(isChar + isNumeric + isCell + isStruct + isLogical + isEmpty);
otherFields = fields(isOther == 1);
%% Print non-structure fields
% Print all the selected non structure fields
% - Strings are printed to a certain amount of characters
% - Vectors are printed as long as they are shorter than maxArrayLength
% - Matrices are printed if they have less elements than maxArrayLength
% - The values of cells are not printed
% Start with printing strings and characters. To avoid the display screen
% becoming a mess, the part of the string that is printed is limited to 31
% characters. In the future this might become an optional parameter in this
% function, but for now, it is placed in the code itself.
% if the string is longer than 31 characters, only the first 31 characters
% are printed, plus three dots to denote that the string is longer than
% printed.
maxStrLength = 31;
for iField = 1 : length(charFields)
Field = cell2mat(charFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
if (size(Structure.(Field), 1) > 1) && (size(Structure.(Field), 2) > 1)
varStr = createArraySize(Structure.(Field), 'char');
elseif length(Field) > maxStrLength
Val = Structure.(Field);
varStr = sprintf(' ''%s...''', Val(1:end));
else
varStr = sprintf(' ''%s''', Structure.(Field));
end
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}];
end
% Print empty fields
for iField = 1 : length(emptyFields)
Field = cell2mat(emptyFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' : [ ]' ]}];
end
% Print logicals. If it is a scalar, print true/false, else print vector
% information
for iField = 1 : length(logicalFields)
Field = cell2mat(logicalFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
if isscalar(Structure.(Field))
logicalValue = {'False', 'True'};
varStr = sprintf(' %s', logicalValue{Structure.(Field) + 1});
else
varStr = createArraySize(Structure.(Field), 'Logic array');
end
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}];
end
% Print numeric scalar field. The %g format is used, so that integers,
% floats and exponential numbers are printed in their own format.
for iField = 1 : length(scalarFields)
Field = cell2mat(scalarFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
varStr = sprintf(' %g', Structure.(Field));
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}];
end
% Print numeric array. If the length of the array is smaller then
% maxArrayLength, then the values are printed. Else, print the length of
% the array.
for iField = 1 : length(vectorFields)
Field = cell2mat(vectorFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
if length(Structure.(Field)) > maxArrayLength
varStr = createArraySize(Structure.(Field), 'Array');
else
varStr = sprintf('%g ', Structure.(Field));
varStr = ['[' varStr(1:length(varStr) - 1) ']'];
end
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' : ' varStr]}];
end
% Print numeric matrices. If the matrix is two-dimensional and has more
% than maxArrayLength elements, only its size is printed.
% If the matrix is 'small', the elements are printed in a matrix structure.
% The top and the bottom of the matrix is indicated by a horizontal line of
% dashes. The elements are also lined out by using a fixed format
% (%#10.2e). Because the name of the matrix is only printed on the first
% line, the space is occupied by this name must be filled up on the other
% lines. This is done by defining a 'filler2'.
% This method was developed by S. Wegerich.
for iField = 1 : length(matrixFields)
Field = cell2mat(matrixFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
if numel(Structure.(Field)) > maxArrayLength
varStr = createArraySize(Structure.(Field), 'Array');
varCell = {[strIndent ' |' filler ' ' Field ' :' varStr]};
else
matrixSize = size(Structure.(Field));
filler2 = char(ones(1, maxFieldLength + 6) * 32);
dashes = char(ones(1, 12 * matrixSize(2) + 1) * 45);
varCell = {[strIndent ' |' filler2 dashes]};
% first line with field name
varStr = sprintf('%#10.2e |', Structure.(Field)(1, :));
varCell = [varCell; {[strIndent ' |' filler ' ' ...
Field ' : |' varStr]}];
% second and higher number rows
for j = 2 : matrixSize(1)
varStr = sprintf('%#10.2e |', Structure.(Field)(j, :));
varCell = [varCell; {[strIndent ' |' filler2 '|' varStr]}];
end
varCell = [varCell; {[strIndent ' |' filler2 dashes]}];
end
listStr = [listStr; varCell];
end
% Print cell array information, i.e. the size of the cell array. The
% content of the cell array is not printed.
for iField = 1 : length(cellFields)
Field = cell2mat(cellFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
varStr = createArraySize(Structure.(Field), 'Cell');
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}];
end
% Print unknown datatypes. These include objects and user-defined classes
for iField = 1 : length(otherFields)
Field = cell2mat(otherFields(iField));
filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45);
varStr = createArraySize(Structure.(Field), 'Unknown');
listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}];
end
end
function str = getIndentation(indent)
x = ' | ';
str = '';
for i = 1 : indent
str = cat(2, str, x);
end
end
function varStr = createArraySize(varName, type)
varSize = size(varName);
arraySizeStr = sprintf('%gx', varSize);
arraySizeStr(length(arraySizeStr)) = [];
varStr = [' [' arraySizeStr ' ' type ']'];
end