Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matlab - Optional handle argument first for plot like functions

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?

like image 856
bhillam Avatar asked Sep 07 '16 09:09

bhillam


2 Answers

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.

like image 145
Sam Roberts Avatar answered Nov 15 '22 21:11

Sam Roberts


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.

like image 29
Stewie Griffin Avatar answered Nov 15 '22 20:11

Stewie Griffin