8

Matlab includes many plotting functions which take an optional argument being the handle to the axis to plot to. There are many solutions online for adding optional arguments to user-defined functions (varargin, inputParser), however they usually require that the optional arguments come only after the mandatory arguments, whilst plotting functions in matlab typically are of the form

plot(optional, mandatory, optional)

That is, the optional arguments can come both before and after the mandatory arguments.

I would like to replicate this behaviour for a custom plot type so that it follows the same style as the built-in plot functions. The following use-cases are presented to demonstrate that checking the number of arguments alone is insufficient to accomplish the desired behaviour:

x = [1:10];
y = x.^2;
ax(1) = subplot(1, 2, 1);
ax(2) = subplot(1, 2, 2);

myplot(x, y);                 %Mandatory
myplot(x, y, 'r+');           %Mandatory, optional
myplot(ax(1), x, y);          %Optional, mandatory
myplot(ax(2), x, y, 'r+');    %Optional, mandatory, optional

My question is, what techniques can we use to emulate this behaviour?

Thierry Dalon
  • 779
  • 5
  • 21
bhillam
  • 193
  • 6

2 Answers2

8

I usually use a pattern like this, which is also used by many of the plotting functions that are part of MATLAB:

function varargout = myplot(obj, varargin)

    % Check the number of output arguments.
    nargoutchk(0,1);

    % Parse possible axes input.
    [ax, args, ~] = axescheck(varargin{:}); %#ok<ASGLU>

    % Get handle to either the requested or a new axis.
    if isempty(ax)
        hax = gca;
    else
        hax = ax;
    end

    % At this point, hax refers either to a specified axis, or
    % to a fresh one if none was specified. args refers to the
    % remainder of any arguments passed in varargin.

    % Parse the rest of args

    % Make the plot in hax

    % Output a handle to the axes if requested.
    if nargout == 1
        varargout{1} = hax;
    end  

end

axescheck is an undocumented function. You're always taking a small risk by doing that, but it's been present and unchanged in MATLAB since forever, and it's used by many very stable plotting functions within MATLAB, so you should be OK.

What it does is to check whether the first argument is a handle to an axis. If it is, then ax is that handle, and args is the rest of the input arguments. If not, then ax is empty and args contains all of the input arguments.

Hope that helps!


Edit: More information about axescheck as requested.

Firstly, you can see the location and source code for axescheck by typing which axescheck and edit axescheck. In this way you can see exactly what it does.

The syntax is [AX, ARGS, NARGS] = AXESCHECK(ARG1, ARG2, ...).

Firstly, it checks if ARG1 is a handle to an axis. If so, it's returned as AX, the remaining arguments (ARG2, ...) are returned in ARGS, and NARGS is the value of nargin minus 1.

Secondly, it checks if any of the input arguments is a parameter-value pair with parameter Parent. If so, then all parameter-value pairs with the parameter Parent are removed from the list. The specified axis is returned in AX, the remaining arguments are returned in ARGS, and NARGS is the value of nargin minus the number of removed arguments.

If no axis is specified in either of the above ways, then AX is empty, ARGS is just the input arguments, and NARGS is the value of nargin.

axescheck works with either old-style (Handle Graphics 1) double handles, and new-style (Handle Graphics 2) handles of the class matlab.graphics.axis.Axes.

It also checks whether the supplied handle is a handle to a deleted object, throwing an error if it is.

It's quite widely used within many built-in MATLAB plotting functions - see, for example, hist.m, polar.m, surfl.m, bar3.m, comet.m, pie.m and many others.

Sam Roberts
  • 23,951
  • 1
  • 40
  • 64
  • This answer seems promising but could be improved with additional information on the undocumented axescheck function - cursory googling didn't turn up any results. e.g. how does axescheck(varargin{:}) differ from isa(varargin{1}, 'matlab.graphics.axis.Axes')? – bhillam Sep 07 '16 at 15:02
  • I always feel bad having to choose just one answer - I wish I could upvote yours again. – bhillam Sep 13 '16 at 12:58
4

You can write a function that takes varargin as input. Then, you check the number of arguments. If it's less than 2 (or something else, depending on your function), cast an error or warning. Then, check the class of the input parameters.

If the class of your first input is 'matlab.graphics.axis.Axes', then your function should call: plot(ax,___). If it's a double, then it must be the format plot(X,Y,LineSpec).

Something along these lines should work

function [] = myplot(varargin)

if nargin < 2
   error('Minimum two input must be given'); % You probably want something other than an error. This was just an example.
elseif nargin == 2
    % Code for plotting
    plot(x, y)
elseif nargin == 3
    if strcmp(class(varargin{1}),'matlab.graphics.axis.Axes')
       ax1 = varargin{1};
       x = varargin{2};
       y = varargin{3};
       plot(ax1, x, y)
    elseif isa(varargin{2}, 'double') && isa(varargin{3}, 'double') && isa(varargin{3}, 'char')
       x = varargin{1};
       y = varargin{2};
       LineSpec = varargin{3};
     else ...

PS! You don't need to do x = varargin{1} etc., this was just to illustrate what each of the different cell elements represents if the if evaluates to true.

You can continue with "Name-Value Pair Arguments". Check if the class of the input argument is char, and that it can't represent something other than a parameter name. If it's a parameter name, then you know the next argument is a parameter value.

Stewie Griffin
  • 14,889
  • 11
  • 39
  • 70
  • I had a feeling this would be the way it's done but was hoping something more elegant was possible. – bhillam Sep 07 '16 at 15:04
  • @bhillam I do not see what would be inelegant with this? MATLAB does not allow function overload. This would have required you to write 2 functions anyway. – patrik Sep 08 '16 at 06:33
  • There will be some code duplication that can get messy very quickly if a large number of argument combinations are possible. Unfortunately it seems that this will be an issue regardless of the technique used. I've accepted this answer because it addresses how to include the optional argument before the mandatory arguments in the same vain as built-in plot functions, though some elements of Sam Roberts' answer are useful as well. – bhillam Sep 12 '16 at 15:25