classdef MDDAxis
% Helper class for handling axis in MDD class
properties
name = '' % (optional) 1x1 - string naming the axis in question (e.g. time, voltage, cell number, etc)
values = [] % (optional) 1xN - can be numerical matrix or cell array of strings describing axis in question
axismeta = struct % (optional) 1xN structure array of additional information
end
methods
function obj = MDDAxis(varargin)
% MDDAxis - constructor
% #todo perhaps change this to be consistent with MDD.
% Usage:
% obj = MDDAxis()
% obj = MDDAxis(axis_name)
% obj = MDDAxis(axis_name, axis_vals)
% obj = MDDAxis(axis_name, axis_vals, axismeta)
if nargin > 0 && ~isempty(varargin{1})
obj.name = varargin{1};
end
if nargin > 1 && ~isempty(varargin{2})
obj.values = varargin{2};
end
if nargin > 2 && ~isempty(varargin{3})
obj.axismeta = varargin{3};
end
end
function out = printAxisInfo(obj,show_class)
if nargin < 2
show_class = 1;
end
max_values_to_display = 10;
if isempty(obj.values); out = 'Empty axis'; return; end
% Add type
values_class = obj.getclass_values;
if show_class
temp = [obj.name, ' (' values_class ') -> '];
else
temp = [obj.name, ' -> '];
end
Nvals = length(obj.values);
Nvals = min(Nvals,max_values_to_display); % Limit to displaying 10 values
for i = 1:Nvals-1
temp = [temp,obj.getvalue_char(i),', '];
end
if length(obj.values) > max_values_to_display
temp = [temp,obj.getvalue_char(Nvals),', ...'];
else
temp = [temp,obj.getvalue_char(Nvals)];
end
if nargout > 0
out = temp;
else
fprintf([temp, '\n'])
end
end
function out = getvalues_cellstr(obj)
% Looks at entry obj.value(i) and returns its output as a
% cell array of strings
out = cell(1,length(obj.values));
for i = 1:length(obj.values)
out{i} = num2str(obj.getvalue_noncell(i));
end
end
%% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
% % % % % % % % % % % OVERLOADED OPERATORS % % % % % % % % % % %
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
function varargout = subsref(obj, S)
% Notes:
% Default settings for cell outputs:
% varargout = builtin('subsref', obj, S);
%
% Default settings for single non cell outputs:
% varargout = {builtin('subsref', obj, S)};
%
% Default settings for multiple outputs:
% [varargout{1:nargout}] = builtin('subsref', obj, S);
%
% Only 2 arguments to subsref.
%
% S is a struct array with length equal to number of consecutive
% operations to perform. Order follows the order written/performed,
% so that the first entry is the closest to the left on the call.
%
% varargout needs to be cell array. Non cell output should be
% enclosed in cell. Multiple outputs should go into cells.
% Importantly, if length(S) > 1, meaning multiple consecutive
% subsref operations, varargout should equal length of S. Thus,
% one needs to make space for recursive outputs when calling
% builtin again, which takes the form of a cell{:} multiple outputs.
switch S(1).type
case '()'
allnames = {obj.name};
if iscellstr(S(1).subs)
selection_out = MDDAxis.regex_lookup(allnames, S(1).subs{1});
S(1).subs{1} = selection_out;
varargout = {builtin('subsref', obj, S)};
else
% Default
varargout = {builtin('subsref', obj, S)};
end
case '{}'
% Default
varargout = builtin('subsref', obj, S);
case '.'
% Default
[varargout{1:nargout}] = builtin('subsref', obj, S);
otherwise
error('Unknown indexing method. Should never reach this');
end
end
end % public methods
methods (Access = protected)
%% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
% % % % % % % % PROTECTED FUNCTIONS % % % % % % % % % % %
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
function out = getclass_values(obj)
out = obj.calcAxClasses(obj.values,'values');
end
function out = getclass_name(obj)
out = obj.calcAxClasses(obj.name,'name');
end
function out = calcAxClasses(obj,input,field)
switch field
case 'values'
% Returns class type of obj.values.
if isnumeric(input)
out = 'numeric';
elseif iscell(input)
if iscellstr(input)
out = 'cellstr';
elseif iscellnum(input)
out = 'cellnum';
end
else
out = 'unknown';
end
case 'name'
% Returns class type of obj.name.
if ischar(input)
out = 'char';
else
out = 'unknown';
end
otherwise
error('Unrecognized input foramt');
end
end
function out = getvalue_noncell(obj,i)
% Looks at entry obj.value(i) and returns its output as a
% numeric, regardless of whether it is actually cell array.
if ~exist('i','var')
warning('Need to specify an index (eg ''obj.getvalue_noncell(#)'')')
out = [];
return
end
if length(i) > 1; error('i must be singleton'); end
if iscell(obj.values)
out = obj.values{i};
else
out = obj.values(i);
end
end
function out = getvalue_char(obj,i)
% Looks at entry obj.value(i) and returns its output as a
% char array, regardless of what data type it actually is (char array,
% cell, numeric, etc).
if ~exist('i','var')
warning('Need to specify an index (eg ''obj.getvalue_char(#)'')')
out = [];
return
end
if length(i) > 1; error('i must be singleton'); end
out = obj.getvalue_noncell(i);
if isnumeric(out)
out = num2str(out);
end
end
end % protected methods
methods (Static)
function [selection_out, startIndex] = regex_lookup(vals, selection)
% uses regexp when selection is of the form '/selection/' with
% enclosing forward slashes. else uses strfind for substring
% matching.
if ~iscellstr(vals); error('Axis values must be strings when using regular expressions');
end
if ~ischar(selection); error('Selection must be string when using regexp');
end
if strcmp([selection(1) selection(end)], '//') % use re
selection = selection(2:end-1);% remove slashes
startIndex = regexp(vals,selection);
else % use strfind
startIndex = strfind(vals,selection);
end
selection_out = logical(~cellfun(@isempty,startIndex));
selection_out = find(selection_out);
if isempty(selection_out)
error('Supplied regex did not match the name of any axis or value');
end
end
end % static methods
end