function PZRespObj = pzplot(RespObj,Systems,P,Z,PlotStyle);
%PZPLOT Create a Pole-Zero Object for the Control System Toolbox

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

ni = nargin;

%---Properties
PZRespObj = struct(...
   'FrequencyUnits','',...
   'SampleTimes',[],...
   'Poles',[],...
   'Zeros',[],...
   'GridType',[],...
   'GridLabelType','damping',...
   'GridLines',[]);

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

%---Initialize PZRespObj
PZFlag = isa(RespObj,'pzplot');
if PZFlag
   PZRespObj = RespObj;
   RespObj = PZRespObj.response;
else
   Preferences = get(RespObj,'Preferences');
   PZRespObj.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 PZFlag
   % Adding to existing STEPPLOT object
   RH0 = get(RespObj,'ResponseHandles');
else
   % New PZPLOT object
   RH0 = {};
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

%---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

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

for ctR=1:length(ResponseHandles)  % each system
   % Get plot data
   Parray = P{ctR};
   Zarray = Z{ctR};
   SizeArray = size(Parray);
   NumArray = prod(SizeArray);

   % Get the style and legend
   % RE: Remove line style and marker from legend (irrelevant)
   Style = struct2cell(PlotStyle(ctR));
   [CC,LL,MM,LegendStr] = deal(Style{:});
   LegendStr = strtok(LegendStr,',');
   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)

   %---Create additional response lines
   RH = cell(SizeArray);

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

      Poles = Parray{ctArray};    nP = length(Poles);
      Zeros = Zarray{ctArray};    nZ = length(Zeros);
      RH{ctArray} = zeros(length(Poles)+length(Zeros),1);

      for ctP=1:nP
         x = real(Poles(ctP));
         y = imag(Poles(ctP));
         if y>0
            LineUd.String = sprintf('Pole: %0.3g + %0.3gi',x,y);
         elseif y<0
            LineUd.String = sprintf('Pole: %0.3g - %0.3gi',x,abs(y));
         else
            LineUd.String = sprintf('Pole: %0.3g',x);
         end
         RH{ctArray}(ctP,1)=line(x,y,...
            'Color',CC,'LineStyle','none','Marker','x',...
            'DeleteFcn','delresp(gcbo)',...
            'Parent',LTIdisplayAxes(1,1),'Tag','LTIresponseLines','Visible','off',...
            'UserData',LineUd,...
            'ButtonDownFcn',@localButtonDown);
      end

      for ctZ=1:nZ
         x = real(Zeros(ctZ));
         y = imag(Zeros(ctZ));
         if y>0
            LineUd.String = sprintf('Zero: %0.3g + %0.3gi',x,y);
         elseif y<0
            LineUd.String = sprintf('Zero: %0.3g - %0.3gi',x,abs(y));
         else
            LineUd.String = sprintf('Zero: %0.3g',x);
         end
         RH{ctArray}(nP+ctZ,1)=line(x,y,...
            'Color',CC,'LineStyle','none','Marker','o',...
            'DeleteFcn','delresp(gcbo)',...
            'Parent',LTIdisplayAxes(1,1),'Tag','LTIresponseLines','Visible','off',...
            'UserData',LineUd,...
            'ButtonDownFcn',@localButtonDown);
      end
   end
   ResponseHandles{ctR} = {RH};
end

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

%---Determine sample times
Ts = zeros(size(Systems));
for ctS=1:length(Systems)
   Ts(ctS)=Systems{ctS}.Ts;
end

if PZFlag  % Append Data
   RespObj = pvset(RespObj,'UIContextMenu',ContextMenu,...
      'ResponseHandles',[RH0;ResponseHandles],...
      BackAxLabels{:});
   PZRespObj.SampleTimes = [PZRespObj.SampleTimes ; Ts(:)];
   PZRespObj.Poles = [PZRespObj.Poles ; P];
   PZRespObj.Zeros = [PZRespObj.Zeros ; Z];
   PZRespObj.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{:});
   PZRespObj.SampleTimes = Ts(:);
   PZRespObj.Poles = P;
   PZRespObj.Zeros = Z;
   PZRespObj = class(PZRespObj,'pzplot',RespObj);
end

if all(~PZRespObj.SampleTimes)
   %---All continuous
   PZRespObj.GridType = 'sgrid';
elseif all(PZRespObj.SampleTimes)
   %---All discrete
   PZRespObj.GridType = 'zgrid';
else
   %---Mixed
   PZRespObj.GridType = 'square';
end

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


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

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

%---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);

 SampleTimes = AllRespProps.SampleTimes;

 StringData = struct(...
   'SysStr',        sysstr,...
   'PoleStr',       udLine.String,...
   'Ts',            abs(SampleTimes(SystemIndex)),...
   'FrequencyUnits',AllRespProps.FrequencyUnits,...
   'X',             1,...
   'Y',             1,...
   'Index',         1);

%---If we have an output argument, then we are in hovermode (always show marker)
 if nargout
    varargout{1} = linescan(lobj,'x',0,{@localBuildString,StringData});

%---Otherwise, only show marker if we are left-clicking
 elseif strcmpi(get(fobj,'SelectionType'),'normal')
    linescan(lobj,'x',0,{@localBuildString,StringData});
 end


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

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

%---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
 dstr = sprintf('Damping: %0.3g',z);
 ostr = sprintf('Overshoot (%%): %0.3g',ppo);
 fstr = sprintf('Frequency (%s): %0.3g',DATA.FrequencyUnits,wn);
 STRING = sprintf(' %s \n %s \n %s \n %s \n %s ',...
    DATA.SysStr,DATA.PoleStr,dstr,ostr,fstr);
