function dummy = showmargin(Editor,Margins)
%SHOWMARGIN  Displays stability margins in Bode Editor.

%   Authors: P. Gahinet
%   Copyright 1986-2000 The MathWorks, Inc. 
%   $Revision: 1.16 $  $Date: 2000/08/02 16:06:24 $

% REVISIT
dummy = [];
NormalRefresh = strcmp(Editor.RefreshMode,'normal');

% Create HG objects for margin rendering if they don't already exist 
% or have been deleted
HG = Editor.HG;
if isempty(HG.GainMargin) | ~ishandle(HG.GainMargin.Dot)
    % REVISIT merge
    LocalCreateObjects(Editor,'GainMargin',HG.PlotAxes(1));
    LocalCreateObjects(Editor,'PhaseMargin',HG.PlotAxes(2));
end

% Get margins from model database in normal mode
% RE: Interpolated margins are passed to SHOWMARGIN in drag mode
if NormalRefresh
    Margins = Editor.LoopData.Margins;
end

% Render gain and phase margins
LocalUpdateGainMargin(Editor,Margins.Gm,Margins.Wcg,Margins.Stable);
LocalUpdatePhaseMargin(Editor,Margins.Pm,Margins.Wcp);

% Adjust visibility and extents (in normal mode only)
if NormalRefresh
    HG = Editor.HG;
    if strcmp(Editor.MagVisible,'on')
        LocalRefresh(HG.GainMargin,HG.PlotAxes(1),HG.BodePlot(1))
    end
    if strcmp(Editor.PhaseVisible,'on')
        LocalRefresh(HG.PhaseMargin,HG.PlotAxes(2),HG.BodePlot(2))
    end
end


%----------------- Local functions ---------------------------


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% LocalUpdateGainMargin %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function LocalUpdateGainMargin(Editor,Gm,Wcg,isStable)

HG = Editor.HG;
Handles = HG.GainMargin;
Frequency = get(HG.BodePlot(1),'Xdata');   % in current units

% Unit conversions
Wcg = unitconv(Wcg,'rad/sec',Editor.FrequencyUnits);
Gm = unitconv(Gm,'abs',Editor.MagnitudeUnits);

% Horizontal 0dB gain line 
if isfinite(Gm) & (Wcg>=Frequency(1) & Wcg<=Frequency(end))
   Yg = unitconv(1,'abs',Editor.MagnitudeUnits);
else
   Yg = NaN;  % trick to make it invisible
end

% Unit dependent entities
if strcmpi(Editor.MagnitudeUnits,'abs')
    MagUnitStr = '(abs)';
    Ydot = 1/Gm;   Yvl = Yg/Gm;
else
    MagUnitStr = 'dB';
    Ydot = -Gm;    Yvl = Yg-Gm;
end


% Margin data text
if isfinite(Gm),
    % Finite value
    Display = sprintf('G.M.: %0.3g %s\nFreq: %0.3g %s',...
        Gm,MagUnitStr,Wcg,Editor.FrequencyUnits);
else
    Display = sprintf('G.M.: Inf\nFreq: %0.3g',Wcg);
end
if ~isnan(isStable)
    if isStable,
        Display = sprintf('%s\n%s',Display,'Stable loop');
    else
        Display = sprintf('%s\n%s',Display,'Unstable loop');
    end
end

% Set margin-related attributes
set(Handles.Dot,'Xdata',Wcg,'Ydata',Ydot)               % dot marker
set(Handles.vLine,'Xdata',[Wcg Wcg],'Ydata',[Yvl Yg])   % vertical line
set(Handles.hLine,'Ydata',[Yg Yg])                      % horizontal line
set(Handles.Text,'String',Display)                      % text


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% LocalUpdatePhaseMargin %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function LocalUpdatePhaseMargin(Editor,Pm,Wcp)

HG = Editor.HG;
Handles = HG.PhaseMargin;
Frequency = get(HG.BodePlot(2),'Xdata');

% Unit conversions
Wcp = unitconv(Wcp,'rad/sec',Editor.FrequencyUnits);
PhaseConvert = unitconv(1,'deg',Editor.PhaseUnits);
Pm = Pm * PhaseConvert;

% Determine the phase line associated with the phase margin (-180 modulo 360)
if isfinite(Pm) & (Wcp>=Frequency(1) & Wcp<=Frequency(end))
   Phase = get(HG.BodePlot(2),'Ydata');
   u180 = 180 * PhaseConvert;
   Yp = u180 * round((interp1(Frequency(:),Phase(:),Wcp)-Pm)/u180);
else
   Yp = NaN;
end

% Margin data text
if isfinite(Pm),
   % Finite value
   Display = sprintf('P.M.: %0.3g %s\nFreq: %0.3g %s',...
       Pm,Editor.PhaseUnits,Wcp,Editor.FrequencyUnits);
else
   Display = sprintf('P.M.: Inf\nFreq: %0.3g',Wcp);
end

% Set margin-related attributes
set(Handles.Dot,'Xdata',Wcp,'Ydata',Yp+Pm)                % dot marker
set(Handles.vLine,'Xdata',[Wcp Wcp],'Ydata',[Yp Yp+Pm])   % vertical line
set(Handles.hLine,'Ydata',[Yp Yp])                        % horizontal line
set(Handles.Text,'String',Display)


%%%%%%%%%%%%%%%%%%%%
%%% LocalRefresh %%%
%%%%%%%%%%%%%%%%%%%%
function LocalRefresh(MarginObjects,Axes,Plot)
% Refreshes margin display when axis limits change

% Get axis limits 
Xlims = get(Axes,'Xlim');
Ylims = get(Axes,'Ylim');
Scales = get(Axes,{'Xscale','Yscale'});

% X extent and visibility of horizontal line
Wc = get(MarginObjects.Dot,'Xdata');
if isfinite(Wc) & Wc>=Xlims(1) & Wc<=Xlims(2)
    set(MarginObjects.hLine,'Xdata',Xlims,'Visible','on') 
else
    set(MarginObjects.hLine,'Visible','off') 
end

% Position text (based on Y coordinate of left-most visible point)
Xdata = get(Plot,'Xdata');
Ydata = get(Plot,'Ydata');
isLinearY = strcmp(Scales{2},'linear');
idx = find(Xdata>=Xlims(1));
if (isLinearY & Ydata(idx(1))>=(Ylims(1)+Ylims(2))/2) | ...
        (~isLinearY & Ydata(idx(1))>=sqrt(Ylims(1)*Ylims(2)))
    % Place it at the bottom left corner
    set(MarginObjects.Text,'Position',[.02 .03 0],'VerticalAlignment','bottom')
else
    set(MarginObjects.Text,'Position',[.02 .97 0],'VerticalAlignment','top')
end

% Resize patch
LocalResizePatch(MarginObjects,Xlims,Ylims,Scales{:});


%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% LocalCreateObjects %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%
function LocalCreateObjects(Editor,MarginType,Axes)

% Prefs
Style = Editor.LineStyle;
MarginColor = Style.Color.Margin;  
Visible = get(Axes,'Visible');

% Create objects
MarginObjects = ...
    struct('Dot',[],'hLine',[],'vLine',[],'Text',[],'Patch',[]);
MarginObjects.Dot = line(1,0,'Parent',Axes,...
   'HitTest','off','Visible',Visible,...
   'MarkerEdgeColor',MarginColor ,...
   'MarkerFaceColor',MarginColor , ...
   'Marker','o','MarkerSize',6,...
   'HelpTopicKey','gainphasemargin');
MarginObjects.hLine = line([.1 10],[NaN NaN],'parent',Axes, ...
   'HitTest','off','Visible',Visible,...
   'Color',MarginColor,'LineStyle','-.');
MarginObjects.vLine = line([1 1],[NaN NaN],'parent',Axes, ...
   'HitTest','off','Visible',Visible,...
   'Tag','MarginVline',...
   'Color',MarginColor,'LineStyle','-');
AxesColor = get(Axes,'Color');
if strcmp(AxesColor,'none'),
    AxesColor = get(get(Axes,'Parent'),'Color');
end
MarginObjects.Patch = patch([0 0 0],[0 0 0],AxesColor,...
   'parent',Axes,'Visible',Visible, ...
   'EdgeColor',AxesColor,...
   'HitTest','off',...
   'HelpTopicKey','gainphasemargin');
MarginObjects.Text = text(1,0,'','parent',Axes, ...
   'HitTest','off',...
   'Interpreter','none',...
   'Units','normalized',...
   'Visible',Visible,...
   'Color',(AxesColor==0),...
   'HelpTopicKey','gainphasemargin');
Editor.HG = setfield(Editor.HG,MarginType,MarginObjects);

% Add listeners to axis Position and Limits
hAxes = handle(Axes);
set(MarginObjects.Patch,'UserData',...
    handle.listener(hAxes,findprop(hAxes,'Position'),...
    'PropertyPostSet',{@LocalResizeCB Editor MarginType}))


%%%%%%%%%%%%%%%%%%%%%
%%% LocalResizeCB %%%
%%%%%%%%%%%%%%%%%%%%%
function LocalResizeCB(hSrc,event,Editor,MarginType)
% Sets patch extent (for text background)
HG = Editor.HG;  % REVISIT: merge in next
if strcmpi(MarginType(1),'g')
    Visible = Editor.MagVisible;    
    Axes = HG.PlotAxes(1);
else
    Visible = Editor.PhaseVisible;  
    Axes = HG.PlotAxes(2);
end

if strcmp(Visible,'on')
    % Adjust patch size
    LimScale = get(Axes,{'Xlim','Ylim','Xscale','Yscale'});
    LocalResizePatch(getfield(HG,MarginType),LimScale{:});
end


%%%%%%%%%%%%%%%%%%%%%%%%
%%% LocalResizePatch %%%
%%%%%%%%%%%%%%%%%%%%%%%%
function LocalResizePatch(MarginObjects,Xlim,Ylim,Xscale,Yscale)
% Sets patch extent (for text background)
Extent = get(MarginObjects.Text,'Extent');
[X1,Y1] = LocalNorm2Data(Extent(1)/2,Extent(2),Xlim,Ylim,Xscale,Yscale);
[X2,Y2] = LocalNorm2Data(Extent(1)+Extent(3),Extent(2)+Extent(4),...
    Xlim,Ylim,Xscale,Yscale);
set(MarginObjects.Patch,'Xdata',[X1 X1 X2 X2],'Ydata',[Y1 Y2 Y2 Y1])


%%%%%%%%%%%%%%%%%%%%%%
%%% LocalNorm2Data %%%
%%%%%%%%%%%%%%%%%%%%%%
function [X,Y] = LocalNorm2Data(Xnorm,Ynorm,Xlims,Ylims,Xscale,Yscale)
% Converts normalized coordinates into data coordinates
if strcmp(Xscale,'log')
    X = Xlims(1)^(1-Xnorm) * Xlims(2)^Xnorm;
else
    X = (1-Xnorm) * Xlims(1) + Xnorm * Xlims(2);
end
if strcmp(Yscale,'log')
    Y = Ylims(1)^(1-Ynorm) * Ylims(2)^Ynorm;
else
    Y = (1-Ynorm) * Ylims(1) + Ynorm * Ylims(2);
end

