function RLRespObj = rlplot(RespObj,Systems,LocusData,Void,PlotStyle)
%RLPLOT   Create a Root Locus response object for the Control System Toolbox.

%   Authors: Karen Gondoly, 2-2-98
%   Revised: Adam DiVergilio, 11-99
%   Copyright 1986-2000 The MathWorks, Inc. 
%   $Revision: 1.16 $  $Date: 2000/08/02 16:05:42 $

ni = nargin;

%---Properties
RLRespObj = struct(...
   'FrequencyUnits','',...
   'Data',[],...
   'GridType',[],...
   'GridLabelType','damping',...
   'GridLines',[]);

%---Return cases (ni==0,1)
switch ni
case 0
   RespObj = response;
   RLRespObj = class(RLRespObj,'rlplot',RespObj);
   return
case 1
   if ~isa(RespObj,'response')
      error('Invalid Response Object passed to RLPLOT')
   elseif isa(RespObj,'rlplot')
      RLRespObj = RespObj;
   else
      RLRespObj = class(RLRespObj,'rlplot',RespObj);
   end
   return
end

%---Initialize RLRespObj
RLFlag = isa(RespObj,'rlplot');
if RLFlag
   RLRespObj = RespObj;
   RespObj = RLRespObj.response;
else
   Preferences = get(RespObj,'Preferences');
   RLRespObj.FrequencyUnits = Preferences.FrequencyUnits;
   set(get(RespObj,'PlotAxes'),'XScale','linear','YScale','linear');
end

%---Access needed parent property values
PV = get(RespObj,{'PlotAxes','UIContextMenu','SystemNames'});
[LTIdisplayAxes,ContextMenu,SystemNames] = deal(PV{:});
if RLFlag
   % Adding to existing RLPLOT object
   RH0 = get(RespObj,'ResponseHandles');
else
   % New RLPLOT object
   RH0 = {};
end

%---Draw the Real/Imag axes lines if they are not already there
%   Do it here to make them lowest in the HG stack
if isempty(findall(LTIdisplayAxes,'Tag','PZxaxisLine'))
   line([-1 1],[0 0],'LineStyle',':','Color',get(LTIdisplayAxes,'YColor'),...
      'HitTest','off','HandleVis','off',...
      'Parent',LTIdisplayAxes,'Tag','PZxaxisLine');
   line([0 0],[-1 1],'LineStyle',':','Color',get(LTIdisplayAxes,'XColor'),...
      'HitTest','off','HandleVis','off',...
      'Parent',LTIdisplayAxes,'Tag','PZyaxisLine');
end

%---Get dimension info and names of new systems
[numout,numin] = size(LTIdisplayAxes);
NumOldSys = length(RH0);                      % number of existing systems
NewSystemNames = SystemNames(NumOldSys+1:end);
NumNewSys = length(NewSystemNames);           % number of added systems 

%---Initialize Line Userdata
ResponseHandles = cell(NumNewSys,1);

for ctR=1:length(ResponseHandles)  % each system
   SizeArray = size(LocusData(ctR).Gains);
   NumArray = prod(SizeArray);

   %---Get plot style and legend
   % RE: SingleLocus=1 -> use different color for each locus branch
   %     (this flag is set by RLOCUS for single model with no color specifier)
   Style = struct2cell(PlotStyle(ctR));
   [CC,LL,MM,LegendStr] = deal(Style{:});
   SingleLocus = strcmp(CC,'special');
   if SingleLocus
      CC = [0 0 1];
      [garb,tail] = strtok(LegendStr,',');
      LegendStr = sprintf('multicolor%s',tail);
   end
   if NumArray>1
      ArrayDimsStr = sprintf('%dx',SizeArray);
      LegendStr = sprintf('%s array: %s',ArrayDimsStr(1:end-1),LegendStr);
   end

   %---Add legend to system name for new systems
   set(ContextMenu.Systems.Names(NumOldSys+ctR),...
      'Label',sprintf('%s (%s)',NewSystemNames{ctR},LegendStr),...
      'ForegroundColor',CC)

   %---Determine if system is discrete
   RH = cell(SizeArray);
   RH2 = cell(SizeArray);

   for ctArray=1:NumArray
      %---Response line UserData
      LineUd = struct('System',NewSystemNames{ctR},'Input',1,...
         'Output',1,'Array',rguihelper('ind2sub',SizeArray,ctArray));

      % Plot locus
      Roots = LocusData(ctR).Roots{ctArray}.';
      RH{ctArray} = line(real(Roots),imag(Roots),...
         'DeleteFcn','delresp(gcbo)',...
         'Parent',LTIdisplayAxes(1,1),'Tag','LTIresponseLines','Visible','off',...
         'UserData',LineUd,...
         'ButtonDownFcn',@localButtonDown);
      if SingleLocus
         set(RH{ctArray},'LineStyle',LL,'Marker',MM)
      else
         set(RH{ctArray},'Color',CC,'LineStyle',LL,'Marker',MM)
      end

      % Plot open-loop poles (if any)
      Poles = LocusData(ctR).OpenLoopPoles{ctArray};
      if ~isempty(Poles)
         RH{ctArray}(end+1) = line(real(Poles),imag(Poles),...
            'Color',CC,'LineStyle','none','Marker','x',...
            'DeleteFcn','delresp(gcbo)',...
            'Parent',LTIdisplayAxes,'Tag','LTIresponseLines','Visible','off',...
            'HitTest','off');
      end

      % Plot open-loop zeros (if any)
      Zeros = LocusData(ctR).OpenLoopZeros{ctArray};
      if ~isempty(Zeros)
         RH{ctArray}(end+1) = line(real(Zeros),imag(Zeros),...
            'Color',CC,'LineStyle','none','Marker','o',...
            'DeleteFcn','delresp(gcbo)',...
            'Parent',LTIdisplayAxes,'Tag','LTIresponseLines','Visible','off',...
            'HitTest','off');
      end
   end
   ResponseHandles{ctR} = {RH};
end

%---Set the BackgroundAxes labels
BackAxLabels = {...
   'Title','Root Locus',...
   'Ylabel','Imag Axis',...
   'Xlabel','Real Axis'};

if RLFlag  % Append Data
   RespObj = pvset(RespObj,'UIContextMenu',ContextMenu,...
      'ResponseHandles',[RH0;ResponseHandles],...
      BackAxLabels{:});
   RLRespObj.Data = [RLRespObj.Data ; LocusData];
   RLRespObj.response = RespObj;
else
   %---Hide the Plot Option Menus
   set(ContextMenu.PlotOptions.Main,'visible','off')
   set(ContextMenu.GridMenu,'Separator','on')
   RespObj = pvset(RespObj,'ResponseHandles',ResponseHandles,...
      'UIContextMenu',ContextMenu,BackAxLabels{:});
   RLRespObj.Data = LocusData;
   RLRespObj = class(RLRespObj,'rlplot',RespObj);
end

SampleTimes = [RLRespObj.Data.Ts];
if all(~SampleTimes)
   %---All continuous
   RLRespObj.GridType = 'sgrid';
elseif all(SampleTimes)
   %---All discrete
   RLRespObj.GridType = 'zgrid';
else
   %---Mixed
   RLRespObj.GridType = 'square';
end

%---Update HG pointer to Response Object
builtin('set',ContextMenu.Main,'userdata',RLRespObj)


%%%%%%%%%%%%%%%%%%%
% localButtonDown %
%%%%%%%%%%%%%%%%%%%
function localButtonDown(eventSrc,eventData)

%---Get line/axes/figure handles
 lobj = eventSrc;
 aobj = get(lobj,'Parent');
 fobj = get(aobj,'Parent');

%---Left-click on line only (normal)
 if ~strcmpi(get(fobj,'SelectionType'),'normal'), return; end

%---Get Response Data  
 ContextMenu = get(aobj,'UIContextMenu'); 
 RespObj = get(ContextMenu,'UserData');
 AllRespProps = get(RespObj,'Undocumented');
 ResponseType = AllRespProps.ResponseType;
 ResponseHandles = AllRespProps.ResponseHandles;
 SystemNames = AllRespProps.SystemNames;
 udLine = get(lobj,'UserData');
 SystemIndex = find(strcmp(udLine.System,SystemNames));
 SystemIndex = SystemIndex(1);
 Handles = ResponseHandles{SystemIndex}{udLine.Output,udLine.Input};
 IOSize = size(AllRespProps.SelectedChannels);
 ArraySize = size(Handles);

%---Make appropriate system name string
 sysstr = rguihelper('labelsystem',udLine.System,...
    udLine.Input,udLine.Output,udLine.Array,IOSize,ArraySize,ResponseType);

 Data = get(RespObj,'Data');
 ArrayInd = num2cell(udLine.Array);  % array indices
 StringData = struct(...
    'SysStr',        sysstr,...
    'z',             Data(SystemIndex).OpenLoopZeros{ArrayInd{:}},...
    'p',             Data(SystemIndex).OpenLoopPoles{ArrayInd{:}},...
    'k',             Data(SystemIndex).OpenLoopGain(ArrayInd{:}),...
    'Ts',            abs(Data(SystemIndex).Ts),...
    'FrequencyUnits',AllRespProps.FrequencyUnits,...
    'X',             1,...
    'Y',             1,...
    'Index',         1);

linescan(lobj,'xy',1,{@localBuildString,StringData});


%%%%%%%%%%%%%%%%%%%%
% localBuildString %
%%%%%%%%%%%%%%%%%%%%
function STRING = localBuildString(DATA)

%---Pole location
 r = DATA.X + i*DATA.Y;

%---Gain value
 den = DATA.k*prod(DATA.z-r);
 if den==0
    g = Inf;
 else
    g = abs(prod(DATA.p-r)/den);
 end

%---Damping/Frequency values
 [wn,z] = damp(r,DATA.Ts);

%---Percentage Peak Overshoot
 ppo = exp(-pi*tan(atan2(z,sqrt(1-z*z)))); % equiv to exp(-z*pi/sqrt(1-z*z))
 ppo = round(1e5*ppo)/1e3; % round off small values

%---Convert Frequency value to current units
 wn = unitconv(wn,'rad/sec',DATA.FrequencyUnits);

%---Build display string
 gstr = sprintf('Gain: %0.3g',g);
 dstr = sprintf('Damping: %0.3g',z);
 ostr = sprintf('Overshoot (%%): %0.3g',ppo);
 fstr = sprintf('Frequency (%s): %0.3g',DATA.FrequencyUnits,wn);
 if DATA.Y>0
    pstr = sprintf('Pole: %0.3g + %0.3gi',DATA.X,DATA.Y);
 elseif DATA.Y<0;
    pstr = sprintf('Pole: %0.3g - %0.3gi',DATA.X,abs(DATA.Y));
 else
    pstr = sprintf('Pole: %0.3g',DATA.X);
 end
 STRING = sprintf(' %s \n %s \n %s \n %s \n %s \n %s ',DATA.SysStr,gstr,pstr,dstr,ostr,fstr);
