diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1289f6e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# MATLAB binaries
+*.mlx -crlf -diff -merge
+*.mat -crlf -diff -merge
+*.fig -crlf -diff -merge
+*.p -crlf -diff -merge
+*.slx -crlf -diff -merge
+*.mdl -crlf -diff -merge
+
+# other binaries
+*.png -crlf -diff -merge
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 9364c54..be8cd74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,7 +16,4 @@
slprj/
# MATLAB App Files
-*.mlapp
-
-# MATLAB figure files
-*.fig
\ No newline at end of file
+*.mlapp
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
index e1c7c48..830d335 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,157 +1,190 @@
-[](http://www.mathworks.com/matlabcentral/fileexchange/57241-hg2-legend-tools) 
+[](http://www.mathworks.com/matlabcentral/fileexchange/57241-hg2-legend-tools) -brightgreen.svg) -orange.svg)
-# LEGTOOLS
-`legtools` is a MATLAB class definition providing the user with a set of methods to modify existing Legend objects.
+# `legtools`
+`legtools` is a MATLAB class of methods to modify existing Legend objects.
-This is an HG2 specific implementation and requires MATLAB R2014b or newer.
+`legtools` requires MATLAB R2014b or newer.
## Methods
-* [`append`](#append) - Add one or more entries to the end of the legend
-* [`permute`](#permute) - Rearrange the legend entries
-* [`remove`](#remove) - Remove one or more legend entries
-* [`adddummy`](#adddummy) - Add legend entries for one or more unsupported graphics objects
+Name | Description
+----------|--------------
+[`append`](#append) | Append entries to legend
+[`permute`](#permute) | Rearrange legend entries
+[`remove`](#remove) | Remove entries from legend
+[`adddummy`](#adddummy) | Add dummy entries to legend
-### *legtools*.**append**(*legendhandle*, *newStrings*)
-#### Description
-Append string(s), `newStrings`, to the specified `Legend` object, `legendhandle`. `newStrings` can be a 1D character array or a 1D cell array of strings. Character arrays are treated as a single string. If multiple `Legend` objects are specified, only the first will be modified.
-
-The legend will only be updated with the new strings if the number of strings in the existing legend plus the number of strings in `newStrings` is the same as the number of plots on the associated `Axes` object (e.g. if you have 2 lineseries and 2 legend entries already no changes will be made).
+### `legtools.`*`append`*`(lh, newStrings)`
+#### Syntax
+`legtools.append(lh, newStrings)` appends strings specified
+by `newStrings` to the Legend object specified by `lh`.
+`newStrings` can be a 1D character array or a 1D cell array
+of strings. Character arrays are treated as a single
+string. From MATLAB R2016b onwards the string data type is
+also supported. If multiple `Legend` objects are specified
+in `lh`, only the first will be modified.
+
+The total number of entries, i.e. the number of current
+entries in the legend plus the number of entries in
+`newStrings`, can exceed the number of graphics objects in
+the axes. However, any extra entries to append will not be
+added to the legend. For example, if you have plotted two
+lines and the current legend contains one entry, appending
+three new entries will only append the first of them.
#### Examples
-##### Adding one legend entry
- % Sample data
- x = 1:10;
- y1 = x;
- y2 = x + 1;
-
- % Plot a thing!
- figure
- plot(x, y1, 'ro');
- lh = legend('Circle', 'Location', 'NorthWest');
-
- % Add a thing!
- hold on
- plot(x, y2, 'bs');
- legtools.append(lh, 'Square')
-
-
+##### Append one legend entry
+```matlab
+% Plot a sine!
+figure
+fplot(@sin)
+lh = legend('sine');
+
+% Append to axes and legend!
+hold on
+fplot(@cos)
+legtools.append(lh, 'cosine')
+```
+
#### Adding two legend entries
-
- % Sample data
- x = 1:10;
- y1 = x;
- y2 = x + 1;
- y3 = x + 2;
-
- % Plot a thing!
- figure
- plot(x, y1, 'ro');
- lh = legend('Circle', 'Location', 'NorthWest');
-
- % Add two things!
- hold on
- plot(x, y2, 'bs', x, y3, 'g+');
- legtools.append(lh, {'Square', 'Plus'})
-
-
+```matlab
+% Plot a sine!
+figure
+fplot(@sin)
+lh = legend('sine');
+
+% Add two things!
+hold on
+fplot(@cos)
+fplot(@tan)
+legtools.append(lh, {'cosine', 'tangent'})
+```
+
-### *legtools*.**permute**(*legendhandle*, *newOrder*)
-#### Description
-Rearrange the entries of the specified `Legend` object, `legendhandle`, so they are in the order specified by the vector `newOrder`. `newOrder` must be the same length as the number of legend entries in `legendhandle`. All elements of order must be unique, real, positive, integer values.
+### `legtools.`*`permute`*`(legendhandle, newOrder)`
+#### Syntax
+`legtools.permute(lh, order)` rarranges the entries of the
+Legend object specified by `lh` in the order specified by
+`order`. `order` must be a vector with the same number of
+elements as the number of entries in the specified legend.
+All elements in order must be unique, real and positive
+integers.
#### Example
- % Sample data
- x = 1:10;
- y1 = x;
- y2 = x + 1;
- y3 = x + 2;
-
- % Plot a thing!
- figure
- plot(x, y1, 'ro', x, y2, 'bs', x, y3, 'g+');
- lh = legend({'One', 'Two', 'Three'}, 'Location', 'NorthWest');
-
- legtools.permute(lh, [3, 1, 2]);
-
-
+```matlab
+% Plot a thing!
+figure
+fplot(@sin)
+hold on
+fplot(@cos)
+fplot(@tan)
+legend sine cosine tangent
+lh = legend;
+
+% Rearrange legend entries!
+legtools.permute(lh, [3, 1, 2])
+```
+
-### *legtools*.**remove**(*legendhandle*, *removeidx*)
-#### Description
-Remove the legend entries of the `Legend` object, `legendhandle`, at the locations specified by `removeidx`. All elements of `removeidx` must be real, positive, integer values.
+### `legtools.`*`remove`*`(legendhandle, removeidx)`
+#### Syntax
+`legtools.remove(lhm, remidx)` removes the legend entries from
+the legend specified in `lh` at the locations specified by
+`remidx`. All elements of `remidx` must be real and positive
+integers.
-If `removeidx` specifies all the legend entries the `Legend` object, `legendhandle`, is deleted.
-
-If a legend entry to be removed is one generated by `legtools.adddummy`, its corresponding Chart Line Object will also be deleted.
+If `remidx` specifies all the legend entries, the legend
+object is deleted.
#### Example
- % Sample data
- x = 1:10;
- y1 = x;
- y2 = x + 1;
- y3 = x + 2;
-
- % Plot a thing!
- figure
- plot(x, y1, 'ro', x, y2, 'bs', x, y3, 'g+');
- lh = legend({'One', 'Two', 'Three'}, 'Location', 'NorthWest');
-
- legtools.remove(lh, [3, 1]);
-
-
+```matlab
+% Plot a thing!
+figure
+fplot(@sin)
+hold on
+fplot(@cos)
+fplot(@tan)
+legend sine cosine tangent
+lh = legend;
+
+% Remove entries one and three!
+legtools.remove(lh, [3, 1])
+```
+
-### *legtools*.**addummy**(*legendhandle*, *newStrings*, *plotParams*)
-#### Description
-`adddummy` appends strings, `newStrings`, to the Legend Object, `lh`, for graphics objects that are not supported by `legend`.
-
-For a single dummy legend entry, `plotParams` is defined as a cell array of strings that follow MATLAB's `plot` syntax. Entries can be either a `LineSpec` or a series of Name/Value pairs. For multiple dummy legend entries, `plotParams` is defined as a cell array of cells where each top-level cell corresponds to a string in `newStrings`.
-
-`adddummy` adds a Chart Line Object to the parent axes of `lh` consisting of a single `NaN` value. Nothing is rendered in the axes but it provides a valid object for `legend` to include. `legtools.remove` will remove this Chart Line Object if its legend entry is removed.
-
+### `legtools.`*`adddummy`*`(legendhandle, newStrings, plotParams)`
+#### Syntax
+`legtools.adddummy(lh, newStrings)` appends strings, specified
+by `newStrings`, to the Legend object, specified by `lh`, for
+graphics objects that are not supported by legend. The
+default line specification for a plot is used for the dummy
+entries in the legend, i.e. a line.
+
+`legtools.adddummy(lh, newStrings, plotParams)` additionally
+uses plot parameters specified in `plotParams` for the
+creation of the dummy legend entries.
+
+The `plotParams` input argument can have multiple formats.
+All formats are based on the LineSpec and Name-Value pair
+arguments syntax of the built-in [`plot`](https://mathworks.com/help/matlab/ref/plot.html) function. `plotParams`
+can be in the following formats (with example parameters):
+- absent (like in the first syntax)
+- empty, e.g. `''`, `[]` or `{}`
+- one set of plot parameters for all dummy entries, e.g.:
+ - `legtools.adddummy(lh, newStrings, ':', 'Color' ,'red')`. This is the regular `plot` syntax.
+ - `legtools.adddummy(lh, newStrings, {'Color','red'})`. This is one set of plot parameters in a cell.
+- two or more sets of plot parameters, e.g.:
+ - `legtools.adddummy(lh, newStrings, {'k'}, {'--b'})`. These are two sets of plot parameters, each in a cell.
+ - `legtools.adddummy(lh, newStrings, {{'r'}, {':m'}})`. These are two sets of plot parameters, each in a cell in a cell.
+
+For more than two dummies, the previous syntaxes can be
+extended with additional sets of plot parameters.
+
+`legtools.adddummy` adds an invisible point to the parent
+axes of the legend. More specifically, it adds a `Line`
+object to the parent axes of `lh` consisting of a single `NaN`
+value so nothing is visibly changed in the axes while
+providing a valid object to include in the legend.
+
+`legtools.remove` deletes dummy `Line` objects when their
+corresponding legend entries are removed.
#### Examples
-##### Add legend entry for single annotation
- % Sample data
- x = 1:10;
- y1 = x;
-
- % Plot a thing!
- figure
- plot(x, y1);
- lh = legend('My Data', 'Location', 'NorthWest');
-
- % Add a box!
- dim = [0.4 0.4 0.2 0.2];
- annotation('rectangle', dim, 'Color', 'red')
-
- % Add a legend entry for the box!
- legtools.adddummy(lh, 'A Red Rectangle', {'Color', 'red'})
-
-
-
-##### Add legend entries for multiple annotations
-
- % Sample data
- x = 1:10;
- y = x;
-
- % Plot a thing!
- plot(x, y);
- lh = legend('My Data', 'Location', 'NorthWest');
-
- % Add a box and a circle!
- dim1 = [0.5 0.6 0.2 0.2];
- annotation('rectangle', dim1, 'Color', 'red')
- dim2 = [0.3 0.4 0.2 0.2];
- annotation('ellipse', dim2, 'Color', 'green')
-
- % Add legend entries!
- newStrings = {'A Red Rectangle', 'A Green Ellipse'};
- plotParams = {{'Color', 'Red'}, {'Color', 'green'}};
- legtools.adddummy(lh, newStrings, plotParams)
-
-
+##### Add dummy legend entry for single annotation
+```matlab
+% Plot a thing!
+figure
+fplot(@sin)
+lh = legend('sin');
+
+% Add a box!
+dim = [0.4 0.4 0.2 0.2];
+annotation('rectangle', dim, 'Color', 'red')
+
+% Add a legend entry for the box!
+legtools.adddummy(lh, 'rectangle', 'Color', 'red')
+```
+
+
+##### Add dummy legend entries for multiple annotations
+```matlab
+% Plot a thing!
+fplot(@sin)
+lh = legend('sine');
+
+% Add a box and a circle!
+dim1 = [0.5 0.6 0.2 0.2];
+annotation('rectangle', dim1, 'Color', 'red')
+dim2 = [0.3 0.4 0.2 0.2];
+annotation('ellipse', dim2, 'Color', 'green')
+
+% Add legend entries!
+newStrings = {'rectangle', 'ellipse'};
+plotParams = {{'Color', 'red'}, {'g'}};
+legtools.adddummy(lh, newStrings, plotParams)
+```
+
diff --git a/legtools.m b/legtools.m
index 7a09afd..e335b9a 100644
--- a/legtools.m
+++ b/legtools.m
@@ -1,253 +1,420 @@
classdef legtools
- % LEGTOOLS is a MATLAB class definition providing the user with a set of
- % methods to modify existing Legend objects.
+ %LEGTOOLS A class of methods to modify existing Legend objects.
%
- % This is an HG2 specific implementation and requires MATLAB R2014b or
- % newer.
+ % LEGTOOLS requires MATLAB R2014b or newer.
%
- % legtools methods:
- % append - Add one or more entries to the end of the legend
- % permute - Rearrange the legend entries
- % remove - Remove one or more legend entries
- % adddummy - Add one or more entries to the legend for unsupported graphics objects
+ % LEGTOOLS methods:
+ % append - Append entries to legend
+ % permute - Rearrange legend entries
+ % remove - Remove entries from legend
+ % adddummy - Add dummy entries to legend
%
- % See also legend
+ % See also legend
methods
function obj = legtools
% Dummy constructor so we don't return an empty class instance
clear obj
- end
- end
+ end % of constructor
+ end % of methods
methods (Static)
function append(lh, newStrings)
- % APPEND appends strings, newStrings, to the specified Legend
- % object, lh. newStrings can be a 1D character array or a 1D
- % cell array of strings. Character arrays are treated as a
- % single string. If multiple Legend objects are specified, only
- % the first will be modified.
+ %LEGTOOLS.APPEND Append entries to legend
%
- % The legend will only be updated with the new strings if the
- % number of strings in the existing legend plus the number of
- % strings in newStrings is the same as the number of plots on
- % the associated axes object (e.g. if you have 2 lineseries and
- % 2 legend entries already no changes will be made).
- legtools.verchk()
- lh = legtools.handlecheck('append', lh);
+ % LEGTOOLS.APPEND(lh,newStrings) appends strings specified
+ % by newStrings to the Legend object specified by lh.
+ % newStrings can be a 1D character array or a 1D cell array
+ % of strings. Character arrays are treated as a single
+ % string. From MATLAB R2016b onwards the string data type is
+ % also supported. If multiple Legend objects are specified
+ % in lh, only the first will be modified.
+ %
+ % The total number of entries, i.e. the number of current
+ % entries in the legend plus the number of entries in
+ % newStrings, can exceed the number of graphics objects in
+ % the axes. However, any extra entries to append will not be
+ % added to the legend. For example, if you have plotted two
+ % lines and the current legend contains one entry, appending
+ % three new entries will only append the first of them.
- % Make sure newString exists & isn't empty
- if ~exist('newStrings', 'var') || isempty(newStrings)
- error('legtools:append:EmptyStringInput', ...
- 'No strings provided' ...
- );
- end
+ % Check number of input arguments
+ narginchk(2,2)
+
+ % Check MATLAB version
+ legtools.verchk
+
+ % Check legend handle
+ lh = legtools.handlecheck('append', lh);
+ % Check new strings
newStrings = legtools.strcheck('append', newStrings);
% To make sure we target the right axes, pull the legend's
% PlotChildren and get their parent axes object
- parentaxes = lh.PlotChildren(1).Parent;
+ ax = lh.PlotChildren(1).Parent;
- % Get line object handles
- plothandles = flipud(parentaxes.Children); % Flip so order matches
+ % Get graphics object handles
+ axchildren = flip(ax.Children); % Flip so order matches
+ legchildren = lh.PlotChildren;
- % Update legend with line object handles & new string array
- newlegendstr = [lh.String newStrings]; % Need to generate this before adding new plot objects
- lh.PlotChildren = plothandles;
- lh.String = newlegendstr;
- end
-
+ % Sort the children of the future legend object in an order
+ % depending on current legend PlotChildren property, because
+ % this may not be in the same order as axchildren, e.g. after
+ % permuting the legend entries
+ [~,~,icurrent] = intersect(legchildren,axchildren,'stable');
+ [~,idiff] = setdiff(axchildren,legchildren,'stable');
+ ifuture = [icurrent;idiff];
+ axchildren = axchildren(ifuture);
+
+ % Strings desired order for future legend
+ newstr = [lh.String, newStrings];
+
+ % Update legend with graphics object handles & new string array
+ lh.PlotChildren = axchildren;
+ lh.String = newstr;
+ end % of append method
function permute(lh, order)
- % PERMUTE rearranges the entries of the specified Legend
- % object, lh, so they are then the order specified by the
- % vector order. order must be the same length as the number of
- % legend entries in lh. All elements of order must be unique,
- % real, positive, integer values.
- legtools.verchk()
+ %LEGTOOLS.PERMUTE Rearrange legend entries
+ %
+ % LEGTOOLS.PERMUTE(lh,order) rarranges the entries of the
+ % Legend object specified by lh in the order specified by
+ % order. order must be a vector with the same number of
+ % elements as the number of entries in the specified legend.
+ % All elements in order must be unique, real and positive
+ % integers.
+
+ % Check number of input arguments
+ narginchk(2,2)
- % TODO: Add check for presence of order
+ % Check MATLAB version
+ legtools.verchk
+ % Check legend handle
lh = legtools.handlecheck('permute', lh);
% Catch length & uniqueness issues with order, let MATLAB deal
- % with the rest.
- if numel(order) ~= numel(lh.String)
- error('legtools:permute:TooManyIndices', ...
- 'Number of values in order must match the number of legend strings' ...
- );
- end
+ % with the rest
+ assert( ...
+ numel(order) == numel(lh.String), ...
+ 'legtools:permute:TooManyIndices', ...
+ ['Number of values in order must match number ' ...
+ 'of legend strings.'] ...
+ )
- if numel(unique(order)) < numel(lh.String)
- error('legtools:permute:NotEnoughUniqueIndices', ...
- 'order must contain enough unique indices to index all legend strings' ...
- );
- end
+ assert( ...
+ numel(unique(order)) == numel(lh.String), ...
+ 'legtools:permute:NotEnoughUniqueIndices', ...
+ ['Input argument order must contain enough unique ' ...
+ 'indices to index all legend strings.'] ...
+ )
% Permute the legend data source(s) and string(s)
% MATLAB has a listener on the PlotChildren so when their order
% is modified the string order is changed with it
lh.PlotChildren = lh.PlotChildren(order);
- end
-
+ end % of permute method
function remove(lh, remidx)
- % REMOVE removes the legend entries of the legend object, lh,
- % at the locations specified by remidx. All elements of remidx
- % must be real, positive, integer values.
+ %LEGTOOLS.REMOVE Remove entries from legend
%
- % If remidx specifies all the legend entries, the legend
- % object is deleted
- legtools.verchk()
+ % LEGTOOLS.REMOVE(lhm,remidx) removes the legend entries from
+ % the legend specified in lh at the locations specified by
+ % remidx. All elements of remidx must be real and positive
+ % integers.
+ %
+ % If remidx specifies all the legend entries, the legend
+ % object is deleted.
+
+ % Check number of input arguments
+ narginchk(2,2)
+
+ % Check MATLAB version
+ legtools.verchk
+
+ % Check legend handle
lh = legtools.handlecheck('remove', lh);
% Catch length issues, let MATLAB deal with the rest
- if numel(unique(remidx)) > numel(lh.String)
- error('legtools:remove:TooManyIndices', ...
- 'Number of unique values in remidx exceeds number of legend entries' ...
- );
- end
+ assert( ...
+ numel(unique(remidx)) <= numel(lh.String), ...
+ 'legtools:remove:TooManyIndices', ...
+ ['Number of unique values in remidx exceeds ' ...
+ 'number of legend entries.'] ...
+ )
+
+ assert( ...
+ max(remidx) <= numel(lh.String), ...
+ 'legtools:remove:BadSubscript', ...
+ 'Index in remidx exceeds number of legend entries.' ...
+ )
+ % Remove specified legend entries
if numel(unique(remidx)) == numel(lh.String)
- delete(lh);
- warning('legtools:remove:LegendDeleted', ...
- 'All legend entries specified for removal, deleting Legend Object' ...
- );
+ delete(lh)
else
- % Check legend entries to be removed for dummy lineseries
+ % Check legend entries to be removed for dummy graphics
% objects and delete them
- count = 1;
- for ii = remidx
- % Our dummy lineseries contain a single NaN YData entry
- if length(lh.PlotChildren(ii).YData) == 1 && isnan(lh.PlotChildren(ii).YData)
+ lc = lh.PlotChildren;
+ obj2delete = gobjects(numel(remidx));
+ for ii = numel(remidx):-1:1
+ ir = remidx(ii);
+ % Our dummy lineseries have the UserData property set
+ % to 'legtools.dummy'
+ if strcmp(lc(ir).UserData,'legtools.dummy')
% Deleting the graphics object here also deletes it
% from the legend, which screws up the one-liner
% plot children removal. Instead store the objects
% to be deleted and delete them after the legend is
% properly modified
- objtodelete(count) = lh.PlotChildren(ii);
- count = count + 1;
+ obj2delete(ii) = lc(ir);
end
end
lh.PlotChildren(remidx) = [];
- delete(objtodelete);
+ delete(obj2delete);
end
- end
+ end % of remove method
- function adddummy(lh, newStrings, plotParams)
- % ADDDUMMY appends strings, newStrings, to the Legend Object,
- % lh, for graphics objects that are not supported by legend.
+ function adddummy(lh, newStrings, varargin)
+ %LEGTOOLS.ADDDUMMY Add dummy entries to legend
%
- % For a single dummy legend entry, plotParams is defined as a
- % cell array of strings that follow MATLAB's PLOT syntax.
- % Entries can be either a LineSpec or a series of Name/Value
- % pairs. For multiple dummy legend entries, plotParams is
- % defined as a cell array of cells where each top-level cell
- % corresponds to a string in newStrings.
+ % LEGTOOLS.ADDDUMMY(lh,newStrings) appends strings, specified
+ % by newStrings, to the Legend object, specified by lh, for
+ % graphics objects that are not supported by legend. The
+ % default line specification for a plot is used for the dummy
+ % entries in the legend, i.e. a line.
%
- % ADDDUMMY adds a Chart Line Object to the parent axes of lh
- % consisting of a single NaN value so nothing is rendered in
- % the axes but it provides a valid object for legend to include
+ % LEGTOOLS.ADDDUMMY(lh,newStrings,plotParams) additionally
+ % uses plot parameters specified in plotParams for the
+ % creation of the dummy legend entries.
%
- % LEGTOOLS.REMOVE will remove this Chart Line Object if its
- % legend entry is removed.
-
- legtools.verchk()
- lh = legtools.handlecheck('addummy', lh);
-
- % Make sure newStrings exists & isn't empty
- if ~exist('newStrings', 'var') || isempty(newStrings)
- error('legtools:adddummy:EmptyStringInput', ...
- 'No string provided' ...
- );
- end
+ % The plotParams input argument can have multiple formats.
+ % All formats are based on the LineSpec and Name-Value pair
+ % arguments syntax of the built-in plot function. plotParams
+ % can be in the following formats (with example parameters):
+ % - absent (like in the first syntax)
+ % - empty, e.g. '', [] or {}
+ % - one set of plot parameters for all dummy entries, e.g.:
+ % - LEGTOOLS.ADDDUMMY(lh, newStrings, ':', 'Color' ,'red')
+ % This is the regular plot syntax.
+ % - LEGTOOLS.ADDDUMMY(lh, newStrings, {'Color','red'})
+ % This is one set of plot parameters in a cell.
+ % - two or more sets of plot parameters, e.g.:
+ % - LEGTOOLS.ADDDUMMY(lh, newStrings, {'k'}, {'--b'})
+ % These are two sets of plot parameters, each in a cell.
+ % - LEGTOOLS.ADDDUMMY(lh, newStrings, {{'r'}, {':m'}})
+ % These are two sets of plot parameters, each in a cell
+ % in a cell.
+ % For more than two dummies, the previous syntaxes can be
+ % extended with additional sets of plot parameters.
+ %
+ % LEGTOOLS.ADDDUMMY adds an invisible point to the parent
+ % axes of the legend. More specifically, it adds a Line
+ % object to the parent axes of lh consisting of a single NaN
+ % value so nothing is visibly changed in the axes while
+ % providing a valid object to include in the legend.
+ %
+ % LEGTOOLS.REMOVE deletes dummy Line objects when their
+ % corresponding legend entries are removed.
- newStrings = legtools.strcheck('adddummy', newStrings);
+ % Check number of input arguments
+ narginchk(2,inf)
- % See if we have a character input for the single addition case
- % and put it into a cell
- if ischar(plotParams)
- plotParams = {{plotParams}};
- end
+ % Check MATLAB version
+ legtools.verchk
+
+ % Check legend handle
+ lh = legtools.handlecheck('adddummy', lh);
- % TODO: More plotParams error checking
+ % Check new strings
+ newStrings = legtools.strcheck('adddummy', newStrings);
+ nnew = numel(newStrings);
+
+ % Check and set plot parameters
+ plotParams = legtools.checkPlotParams(varargin,nnew);
parentaxes = lh.PlotChildren(1).Parent;
- hold(parentaxes, 'on');
- for ii = 1:length(newStrings)
- plot(parentaxes, NaN, plotParams{ii}{:}); % Leave input validation up to plot
+
+ % Hold parent axes
+ if ishold(parentaxes)
+ washold = true;
+ else
+ washold = false;
+ hold(parentaxes, 'on');
end
- hold(parentaxes, 'off');
- legtools.append(lh, newStrings); % Add legend entries
- end
-
- end
+ for ii = 1:nnew
+ plot(parentaxes, NaN, ...
+ plotParams{ii}{:}, ... % Leave validation up to plot
+ 'UserData', 'legtools.dummy')
+ end
+
+ % Restore previous hold state
+ if ~washold, hold(parentaxes, 'off'); end
+
+ % Append dummy entries to legend
+ legtools.append(lh, newStrings);
+ end % of adddummy method
+ end % of Static methods
methods (Static, Access = private)
- function verchk()
+ function verchk
% Throw error if we're not using R2014b or newer
if verLessThan('matlab','8.4')
error('legtools:UnsupportedMATLABver', ...
- 'MATLAB releases prior to R2014b are not supported' ...
- );
+ 'MATLAB releases prior to R2014b are not supported.')
end
- end
+ end % of verchk method
- function [lh] = handlecheck(src, lh)
+ function lh = handlecheck(src, lh)
% Make sure lh exists and is a legend object
- if ~isa(lh, 'matlab.graphics.illustration.Legend')
- msgID = sprintf('legtools:%s:InvalidLegendHandle', src);
- error(msgID, 'Invalid legend handle provided');
- end
+ assert( ...
+ ~isempty(lh) && isgraphics(lh,'legend') && isvalid(lh), ...
+ sprintf('legtools:%s:InvalidLegendHandle', src), ...
+ 'Invalid legend handle provided.' ...
+ )
- % Pick first legend handle if more than one is passed
+ % Keep first legend handle if more than one is passed
if numel(lh) > 1
- msgID = sprintf('legtools:%s:TooManyLegends', src);
- warning(msgID, ...
- '%u Legend objects specified, modifying the first one only', ...
- numel(lh) ...
- );
+ warning( ...
+ sprintf('legtools:%s:TooManyLegends', src), ...
+ ['%u Legend objects specified, ' ...
+ 'modifying the first one only.'], ...
+ numel(lh) ...
+ )
lh = lh(1);
end
- end
+ end % of handlecheck method
- function [newString] = strcheck(src, newString)
+ function newString = strcheck(src, newString)
+ % Make sure newString exists & isn't empty
+ assert( ...
+ ~isempty(newString), ...
+ 'legtools:append:EmptyStringInput', ...
+ 'No strings provided.' ...
+ )
+
% Validate the input strings
if ischar(newString)
% Input string is a character array, assume it's a single
% string and dump into a cell
- newString = {newString};
+ newString = cellstr(newString);
end
- % Check to see if we now have a cell array
- if ~iscell(newString)
- msgID = sprintf('legtools:%s:InvalidLegendString', src);
- error(msgID, ...
- 'Invalid Data Type Passed: %s\n\nData must be of type(s): %s, %s', ...
- class(newString), class({}), class('') ...
- );
+ % Message identifier for cellstr assertion below
+ msgID = sprintf('legtools:%s:InvalidLegendString', src);
+
+ % Check MATLAB version for support for string class
+ if verLessThan('matlab','9.1')
+ msgArgs = { ...
+ ['Invalid data type passed: '...
+ '%s\nData must be any of the following types: ' ...
+ '%s, %s'], ...
+ class(newString), class(cell.empty), class(char) ...
+ };
+ else
+ % MATLAB R2016b and newer support the string data type
+ % Force conversion to cell array of strings
+ newString = cellstr(newString);
+ msgArgs = { ...
+ ['Invalid data type passed: %s\n'...
+ 'Data must be any of the following types: ' ...
+ '%s, %s, %s'], ...
+ class(newString), ...
+ class(string), class(cell.empty), class(char) ...
+ };
end
+ % Check if we now have a cell array of strings
+ assert(iscellstr(newString), msgID, msgArgs{:})
+
% Check shape of newStrings and make sure it's 1D
- if size(newString, 1) > 1
- newString = reshape(newString', 1, []);
- end
+ newString = newString(:)';
+ end % of strcheck method
+
+ function plotParams = checkPlotParams(plotParams,nnew)
+ % Check plot parameter set format in plotParams, can be:
+ % - empty ({}, [], '', etc.)
+ % - one set
+ % - in plot syntax
+ % - in a cell
+ % - two or more sets
+ % - in one cell
+ % - in two or more cells
- % Check to make sure we're only passing strings
- for ii = 1:length(newString)
- % Check for characters, let MATLAB handle errors for data
- % types not compatible with num2str
- if ~ischar(newString{ii})
- msgID = sprintf('legtools:%s:ConvertingInvalidLegendString', src);
- warning(msgID, ...
- 'Input legend ''string'' is of type %s, converting to %s', ...
- class(newString{ii}), class('') ...
- );
- newString{ii} = num2str(newString{ii});
+ if nnew>1
+ if isempty(plotParams)
+ % plotParams is an empty set of plot parameters for all new
+ % dummy entries
+ plotParams = repmat({{}},1,nnew);
+ else
+ % plotParams is not empty, check for one or more sets
+ if numel(plotParams)==1
+ % check if cell or string
+ if iscellstr(plotParams)
+ % plotParams is one set in string format,
+ % repeat nnew times in cell format
+ plotParams = repmat({plotParams},1,nnew);
+ elseif iscellstr(plotParams{1}(1))
+ % plotParams is one set in cell format, repeat
+ % nnew times as is
+ plotParams = repmat(plotParams,1,nnew);
+ else
+ % plotParams contains one or more sets in cell
+ % format
+ if numel(plotParams{1})==1
+ % plotParams contains one set, repeat nnew
+ % times
+ plotParams = repmat(plotParams{1},1,nnew);
+ else
+ % plotParams contains multiple sets in
+ % cells, uncell them
+ plotParams = plotParams{:};
+ assert( ...
+ numel(plotParams) == nnew, ...
+ 'legtools:adddummy:TooManyPlotParamSets', ...
+ 'Too many plot parameter sets specified.' ...
+ )
+ end
+ end
+ else
+ % plotParams may be one set of plot parameters in a
+ % cell array, or more than one set, each in a cell
+ if iscellstr(plotParams(1))
+ % plotParams is one set, repeat nnew times
+ plotParams = repmat({plotParams},1,nnew);
+ else
+ % plotParams contains more than one set in
+ % cells, so do nothing, but assert the number
+ % is correct
+ assert( ...
+ numel(plotParams) == nnew, ...
+ 'legtools:adddummy:TooManyPlotParamSets', ...
+ 'Too many plot parameter sets specified.' ...
+ )
+ end
+ end
+ end
+ else
+ % In the single addition case, the input may be a cell of
+ % strings
+ if iscellstr(plotParams)
+ % Single addition case, plotParams contains a character
+ % array
+ plotParams = {plotParams};
+ end
+
+ % Make sure plotParams is in cell format if it is empty
+ if isempty(plotParams{1})
+ plotParams = {{}};
+ end
+
+ % Make sure plotParams is a cell of cells
+ if ~iscell(plotParams{1})
+ plotParams = {plotParams};
end
end
- end
- end
+ end % of checkPlotParams method
+ end % of Static, Access = private methods
end
\ No newline at end of file