function sfunxy2(block, varargin)
%SFUNXY2 S-function that acts as an X-(double Y) scope using MATLAB plotting functions.
%   This function is designed to be used in a Simulink S-function block. It
%   draws a line from the previous input point, which is stored using
%   discrete states, and the current point. It then stores the current
%   point for use in the next invocation.
%
%   The first input works as the X variable and the last two inputs are the
%   Y's variables.
%
%   See also SFUNXYS, LORENZS, SFUNXY.

%   Copyright 1992-2022 The MathWorks, Inc.

if nargin == 1
    setup(block)
else
    flag = varargin{3};
    switch flag
        case 'Start'
            LocalBlockStartFcn

        case 'Stop'
            LocalBlockStopFcn

        case 'NameChange'
            LocalBlockNameChangeFcn

        case { 'CopyBlock', 'LoadBlock' }
            LocalBlockLoadCopyFcn

        case 'DeleteBlock'
            LocalBlockDeleteFcn

        case 'DeleteFigure'
            LocalFigureDeleteFcn
    end
end
end

function setup(block)
block.NumDialogPrms = 2;
mdlInitializeSizes(block);
SetBlockCallbacks(gcbh);
block.RegBlockMethod('Update', @mdlUpdate);
end

function mdlInitializeSizes(block)
ax = block.DialogPrm(1).Data;
Ts = block.DialogPrm(2).Data;

if length(ax)~=4
    nnerr.throw('Control', 'Axes limits must be defined.')
end

block.NumInputPorts = 1;
block.InputPort(1).DatatypeID = 0;
block.InputPort(1).Dimensions = 3;
block.NumOutputPorts = 0;

if isempty(Ts)
    sampleTime = -1;
else
    sampleTime = Ts;
end
block.SampleTimes = [sampleTime 0];
end

function SetBlockCallbacks(block)
% The actual source of the block is the parent subsystem.
block=get_param(block,'Parent');

% If the block isn't linked, issue a warning, and then set the callbacks
% for the block so that it has the proper operation.
if strcmp(get_param(block,'LinkStatus'),'none')
    callbacks={
        'CopyFcn',       'sfunxy2([],[],[],''CopyBlock'')' ;
        'DeleteFcn',     'sfunxy2([],[],[],''DeleteBlock'')' ;
        'LoadFcn',       'sfunxy2([],[],[],''LoadBlock'')' ;
        'StartFcn',      'sfunxy2([],[],[],''Start'')' ;
        'StopFcn'        'sfunxy2([],[],[],''Stop'')'
        'NameChangeFcn', 'sfunxy2([],[],[],''NameChange'')' ;
        };

    for i=1:length(callbacks)
        if ~strcmp(get_param(block,callbacks{i,1}),callbacks{i,2})
            set_param(block,callbacks{i,1},callbacks{i,2})
        end
    end
end
end

function mdlUpdate(block)
ax = block.DialogPrm(1).Data;
u = block.InputPort(1).Data;

% Locate the figure window associated with this block.  If it's not a valid
% handle (it may have been closed by the user), then return.
FigHandle=GetSfunXYFigure(gcbh);
if ~ishandle(FigHandle)
    return
end

% Get UserData of the figure.
ud = get(FigHandle,'UserData');
if isempty(ud.XData)
    x_data = [u(1) u(1)];
    y_data = [u(2) u(2)];
    y2_data = [u(3) u(3)];
else
    x_data = [ud.XData(end) u(1)];
    y_data = [ud.YData(end) u(2)];
    y2_data = [ud.Y2Data(end) u(3)];
end

ud.axislimits(2)=ax(1);
ud.axislimits(2)=max([ud.axislimits(2) ax(2)]);
aux=0;
if (u(1)> ud.axislimits(2))
    aux=ceil(u(1)/ud.axislimits(2));
    ud.axislimits(2)=ud.axislimits(2)*aux;
end
ud.axislimits(3)=min([ud.axislimits(3) ax(3)]);
aux2=min([y_data y2_data]);
if aux2<ud.axislimits(3)
    if aux2<0
        ud.axislimits(3)=-ceil(-aux2);
    else
        ud.axislimits(3)=fix(aux2);
    end
    aux=1;
end
ud.axislimits(4)=max([ud.axislimits(4) ax(4)]);
aux2=max([y_data y2_data]);
if aux2>ud.axislimits(4)
    ud.axislimits(4)=ceil(aux2);
    aux=1;
end

% Plot the input lines.
set(ud.XYAxes, ...
    'Visible','on',...
    'Xlim', ud.axislimits(1:2),...
    'Ylim', ud.axislimits(3:4));
set(ud.XYLine(1),...
    'Xdata',x_data,...
    'Ydata',y_data,...
    'LineStyle','-');
set(ud.XYLine(2),...
    'Xdata',x_data,...
    'Ydata',y2_data,...
    'LineStyle','-');
set(ud.XYTitle,'String','X Y Plot');
set(FigHandle,'Color',get(FigHandle,'Color'));

% Update the X/Y stored data points.
ud.XData(end+1) = u(1);
ud.YData(end+1) = u(2);
ud.Y2Data(end+1) = u(3);

if aux~=0
    set(ud.XYLine(1),...
        'Xdata',ud.XData,...
        'Ydata',ud.YData,...
        'LineStyle','-');
    set(ud.XYLine(2),...
        'Xdata',ud.XData,...
        'Ydata',ud.Y2Data,...
        'LineStyle','-');
end

set(FigHandle,'UserData',ud);
drawnow
end

function LocalBlockStartFcn
% Get the figure associated with this block, create a figure if it doesn't
% exist.
FigHandle = GetSfunXYFigure(gcbh);
if ~ishandle(FigHandle)
    FigHandle = CreateSfunXYFigure;
else
    set(FigHandle,'Toolbar','none');
    set(FigHandle,'Menubar','none');
end

ud = get(FigHandle,'UserData');
set(ud.XYLine,'XData',[],'YData',[],'XData',[],'YData',[]);
set(ud.XYLine,'XData',0,'YData',0,'XData',0,'YData',0);
ud.XData = [];
ud.YData = [];
ud.Y2Data = [];
ud.axislimits = [0 0 Inf -Inf];
set(FigHandle,'UserData',ud);
end

function LocalBlockStopFcn
FigHandle=GetSfunXYFigure(gcbh);
if ishandle(FigHandle)
    % Get UserData of the figure.
    ud = get(FigHandle,'UserData');
    set(ud.XYLine(1),...
        'Xdata',ud.XData,...
        'Ydata',ud.YData,...
        'LineStyle','-');
    set(ud.XYLine(2),...
        'Xdata',ud.XData,...
        'Ydata',ud.Y2Data,...
        'LineStyle','-');
    set(FigHandle,'Toolbar','figure');
    set(FigHandle,'Menubar','figure');

end
end

function FigHandle=GetSfunXYFigure(block)
if strcmp(get_param(block,'BlockType'),'M-S-Function')
    block=get_param(block,'Parent');
end

FigHandle=get_param(block,'UserData');
if isempty(FigHandle)
    FigHandle=-1;
end
end

function LocalBlockLoadCopyFcn
SetSfunXYFigure(gcbh,-1);

    function SetSfunXYFigure(block,FigHandle)
        if strcmp(get_param(bdroot,'BlockDiagramType'),'model')
            if strcmp(get_param(block,'BlockType'),'M-S-Function')
                block=get_param(block,'Parent');
            end

            set_param(block,'UserData',FigHandle);
        end
    end
end

function FigHandle=CreateSfunXYFigure
% If the figure doesn't exist, create one.
FigHandle = figure('Units',          'pixel',...
    'Position',       [100 100 400 300],...
    'Name',           get_param(gcbh,'Name'),...
    'Tag',            'SIMULINK_XYGRAPH_FIGURE',...
    'NumberTitle',    'off',...
    'IntegerHandle',  'off',...
    'Toolbar',        'none',...
    'Menubar',        'none',...
    'DeleteFcn',      'sfunxy2([],[],[],''DeleteFigure'')');

% store the block's handle in the figure's UserData.
ud.Block=gcbh;

% create various objects in the figure.
ud.XYAxes     = axes;
ud.XYLine     = plot(0,0,0,0);
ud.XYXlabel   = xlabel('X Axis');
ud.XYYlabel   = ylabel('Y Axis');
ud.XYTitle    = get(ud.XYAxes,'Title');
ud.XData      = [];
ud.YData      = [];
ud.Y2Data     = [];
ud.axislimits = [0 0 0 0];
set(ud.XYAxes,'Visible','off');

% Associate the figure with the block, and set the figure's UserData.
SetSfunXYFigure(gcbh,FigHandle);
set(FigHandle,'HandleVisibility','callback','UserData',ud);
end

function LocalBlockNameChangeFcn

% get the figure associated with this block, if it's valid, change
% the name of the figure.
FigHandle = GetSfunXYFigure(gcbh);
if ishandle(FigHandle)
    set(FigHandle,'Name',get_param(gcbh,'Name'));
end
end

function LocalBlockDeleteFcn
% Get the figure handle associated with the block, if it exists, delete
% the figure.
FigHandle=GetSfunXYFigure(gcbh);
if ishandle(FigHandle)
    delete(FigHandle);
    SetSfunXYFigure(gcbh,-1);
end
end

function LocalFigureDeleteFcn
% Get the block associated with this figure and set it's figure to -1.
ud=get(gcbf,'UserData');
SetSfunXYFigure(ud.Block,-1)
end

function SetSfunXYFigure(block,FigHandle)
if strcmp(get_param(bdroot,'BlockDiagramType'),'model')
    if strcmp(get_param(block,'BlockType'),'M-S-Function')
        block=get_param(block,'Parent');
    end

    set_param(block,'UserData',FigHandle);
end
end