Search code examples
matlabfunctiongraphicssimulinkhandle

Graphic object handle array in embedded matlab function in Simulink in R2014a or newer


For debugging, I have some plots of vectors in an embedded matlab function in Simulink. Up to Matlab R2013b, everything works just fine with the following minimum example code:

function fcn
%#minimum example for plot within for-loop of embedded matlab function

coder.extrinsic('delete');
coder.extrinsic('quiver');
coder.extrinsic('gobjects');
numSteps=4;

persistent hFig;
persistent hVector;

if isempty(hFig)
    hFig = figure;
    hold on;
    hVector=zeros(numSteps,1);
    hVector=gobjects(numSteps,1);
    for i=1:numSteps
        hVector(i) = quiver(0,0,0,0);
    end
end
set(0, 'CurrentFigure', hFig);
delete(hVector);
for i=1:numSteps
    startInc=[1 1;1 1].*i;
    endInc=[2 3;2 -3].*i;
    hVector(i) = quiver(startInc(1,:),startInc(2,:),endInc(1,:),endInc(2,:));
end

For the handle array hVector, an initialization is necessary due to its use in the for-loop. However, for an Initialization of a graphics handle object, the function gobjects is needed and in turn the initialization as double with zeros(numSteps,1) becomes necessary, since matlab cannot correctly determine the data type of an output of an extrinsic function. As I said, this code snippet works just fine if copied to an embedded matlab function block in simulink without anything else in the model.

My problem: Mathworks changed a lot of the plotting functions in R2014a, one of the changes being the datatype of the graphics handles which are no of type quiver for my quiver plot. Thus, the initialization with zeros(numSteps,1) initializes the wrong data type for the handle array. However leaving it out still does not work, because of the problem mentioned above. Neither does a init loop or anything similar compile without errors.

I'd greatly appreciate any help on that issue.


Solution

  • You can try to remove the gobject initialisation and use double() to wrap your call to any matlab graphic object. Ex:

    hVector(i) = double( quiver(startInc(1,:),startInc(2,:),endInc(1,:),endInc(2,:)) ) ;
    

    I suggest reading Loren's article about the compatibility issues which can arise when switching to HG2 versions of Matlab.

    A quick quote from it which apply more to your issue:

    Graphics Functions Return Objects, not Numeric Handles

    Prior to R2014b, you could store a set of handles to graphics objects in an array and then add some numeric data to that array. In R2014b, that will cause an error.
    [...]
    If you find yourself really stuck, it is possible to cast object handles to numeric handles using the double function. You can then cast the number back to an object handle using the handle function. We don't recommend this as a long term solution. Be aware that we may choose to remove this feature in a future version of MATLAB. If we do, we'll let you know in advance.


    Now if you really have to use this solution, note that both functions works on single elements but also on arrays. So

    hVector_HG  = handle( hVector_Num ) ; %// works if "hVector_Num" is an array of numeric handles
    %// and ...
    hVector_Num = double( hVector_HG ) ;  %// works if "hVector_HG" is an array of HG2 graphic handles
    

    That may simplify the round trips between a format or another if they often become necessary.


    Edit:

    I put this at the bottom of the post for the moment because the beginning is already accepted answer but please try the following and let me know if it works. It may solve your problem in a better (more future-proof) way.

    Another way to initialise an array of handle of a given graphic object is to create one (empty is good enough) and replicate it. For example:

    hqNaN   = quiver(NaN,NaN) ;                 %// create an empty quiver
    hVector = repmat( hqNaN , numSteps , 1 ) ;  %// replicate it in an array
    

    will give you an array hVector containing numSteps HG2 graphic object handles. At this point they all point to the very same object but it is perfectly legal to overwrite each element with a handle of the same type. So a later :

    hVector(i) = quiver( ... , etc ... ) ; %// overwrite handle "i" with a new quiver plot handle
    

    will (should) work without problem.

    A few things to take care with this approach:

    • where will the empty quiver be created ?
      you may already have a "current" figure and you don't want it to be messed up. If not a new empty plot will be created. So to make sure the empty quiver do not cause problem (just a flicker on the screen), you could wrap it this way:

      figure ; hqNaN = quiver(NaN,NaN) ; close(gcf) ;

    or you can also park it in a figure (it will be invisible anyway) in case you need to re-use a handle of this type for other array initialisation. Just don't forget that once you close the figure it is on, or you delete the graphic object, the hqNaN variable is still there but it is not the same type of handle any more (so not useful to replicate if you want this type exactly).

    • What if you do not overwrite all you initial handle array ?
      Remember all the initial handles of the array point to the same graphic object. So in case your array contains 12 elements but let's say by mistake you only overwrite 10 of them, then 2 elements will be handles pointing to the same object (which you may already have deleted)). This means that calling:
      delete(hVector)
      will throw you the annoying:
      Error using handle.handle/delete. Invalid or deleted object.
      gna gna gna ... luckily you can easily prepare for that by programming defensively and using instead:
      delete( hVector(ishandle(hVector)) )
      Matlab will do the check automatically and only delete the right handles.