function out=mmpolar(varargin) %MMPOLAR Polar Plot with Settable Properties. (MM) % MMPOLAR(Theta,Rho) creates a polar coordinate plot using the angle Theta % in RADIANS and radius in Rho. Rho can contain negative values. % MMPOLAR(Theta,Rho,S) creates the plot using the line spec given by S. See % the function PLOT for information about S. % MMPOLAR(Theta1,Rho1,S1,Theta2,Rho2,S2,...) plots all the defined curves. % % MMPOLAR(Theta1,Rho1,S1,...,'PName',PValue,...) plots all defined curves, % and sets plot property names to the corresponding property values. % MMPOLAR(Theta1,Rho1,S1,...,P) plots all the defined curves, and uses the % structure P having fieldnames equal to plot property names to set % corresponding property values contained in the associated fields. % H=MMPOLAR(Theta,Rho,...) returns handles to lines or lineseries objects. % % MMPOLAR('PName',PValue,...) sets the property names to the corresponding % property values. See below for property name/value pairs. Just as with % the function SET 'PName' is case insensitive and need only be unique. % MMPOLAR with no input argument returns a structure with fieldnames equal % to property names each containing the associated property values. % MMPOLAR(P) sets property values using the structure P as described above. % MMPOLAR('PName') returns the property value associated with 'PName'. % MMPOLAR({'PName1','PName2',...}) returns multiple property values in a % cell array. % MMPOLAR(Hax,...) uses the axes having handle Hax. % % Examples: MMPOLAR(Theta,Rho,S,'Style','compass') creates a polar plot with % theta=0 pointing North and theta increasing in the clockwise direction. % % MMPOLAR(Theta,Rho,S) creates a cartesian polar plot where theta=0 is along % the x-axis and theta increases in the counterclockwise direction. % % MMPOLAR works with HOLD, XLABEL, and ZOOM, but not with AXIS or GRID % % See also POLAR, PLOT, HOLD % % PROPERTY VALUE {Default} DESCRIPTION % Style {cartesian} | compass shortcut to two common polar % styles. Cartesian: theta=0 points east and increases % going north. Compass: theta=0 points north and % increases going east. See TDirection and TZeroDirection. % Axis {on} | off shortcut for grids, ticks, border, % backgroundcolor, visibility % Border {on} | off shortcut for axis border, tick mark visibility. % Grid {on} | off shortcut for visibility of rho and theta grids % RLimit [Rmin Rmax] rho axis limits, may be negative values % TLimit [Tmin Tmax] theta axis limits in RADIANS % RTickUnits {''} string added to last rho tick label to denote units % TTickScale {degrees} | radians theta axis tick label scaling % TDirection cw | {ccw} direction of increasing theta % TZeroDirection North | {East} | South | West theta=0 axis direction % % BackgroundColor {w} colorspec for axis background color % BorderColor {k} colorspec for axis border and tick mark colors % FontName string font name for tick labels % FontSize scalar font size for tick labels % FontWeight {normal} | bold font weight for tick labels % TickLength {.02} normalized length of rho and theta axis tick marks % % RGridColor {k} colorspec for rho axis grid color % RGridLineStyle - | -- | {:} | -. rho axis grid line style % RGridLineWidth {0.5} rho axis grid line width in points % RGridVisible {on} | off rho axis grid visibility % RTickAngle [scalar] angular position of rho axis tick labels in % TTickScale units % RTickOffset {.04} Normalized radial offset for rho tick labels % RTickLabel string cell array containing rho axis tick labels % RTickLabelVisible {on} | off visibility of rho axis tick labels % RTickLabelHalign {center} | left | right horizontal % alignment of rho axis tick labels % RTickLabelValign {middle} | top | cap | baseline | bottom vertical % alignment of rho axis tick labels % RTickValue [vector] vector containing rho axis tick positions % RTickVisible {on} | off rho axis tick visibility % % TGridColor colorspec for theta axis grid color % TGridLineStyle - | -- | {:} | -. theta axis grid line style % TGridLineWidth {0.5} theta axis grid line width in points % TGridVisible {on} | off theta axis grid visibility % TTickDelta theta axis tick spacing in TTickScale units % {15 degrees or pi/12 radians} % TTickDirection {in} | out direction of theta tick marks % TTickOffset {.08} normalized radial offset of theta tick labels % TTickLabel string cell array containing theta axis tick labels % TTickLabelVisible {on} | off visiblity of theta axis tick labels % TTickSign {+-} | + sign of theta tick labels % TTickValue [vector] vector of theta ticks in TTickScale units % TTickVisible {on} | off theta axis tick visibility % D.C. Hanselman, University of Maine, Orono, ME 04469 % MasteringMatlab@yahoo.com % Mastering MATLAB 7 % 2005-04-25, 2006-01-18, 2006-04-06, 2006-05-17, 2006-05-18 % 2006-10-03, 2007-03-04 %-------------------------------------------------------------------------- % Parse Inputs Parse Inputs %-------------------------------------------------------------------------- % Find MMPOLAR axes if it exists nargi=nargin; % find MMPOLAR axes if it is supplied or if it is the current axes if nargi>0 && isscalar(varargin{1}) && ishandle(varargin{1}) HAxes=varargin{1}; % see if first argument is an MMPOLAR axes if strcmp(get(HAxes,'Tag'),'MMPOLAR_Axes') HFig=ancestor(HAxes,'figure'); HoldIsON=strcmp(get(HAxes,'nextplot'),'add')... && strcmp(get(HFig,'nextplot'),'add'); P=getappdata(HAxes,'MMPOLAR_Properties'); Pfn=fieldnames(P); varargin(1)=[]; % strip initial axes handle off varargin nargi=nargi-1; % varargin now contains rest of input arguments else error('First Argument is Not a Valid MMPOLAR Axes Handle.') end else % see if MMPOLAR axes is current axes HFig=get(0,'CurrentFigure'); if isempty(HFig) HAxes=[]; Pfn=fieldnames(local_getDefaults); HoldIsON=false; else HAxes=get(HFig,'CurrentAxes'); if isempty(HAxes) Pfn=fieldnames(local_getDefaults); HoldIsON=false; else if strcmp(get(HAxes,'Tag'),'MMPOLAR_Axes') HoldIsON=strcmp(get(HAxes,'nextplot'),'add')... && strcmp(get(HFig,'nextplot'),'add'); P=getappdata(HAxes,'MMPOLAR_Properties'); Pfn=fieldnames(P); else % no MMPOLAR axes exists HAxes=[]; Pfn=fieldnames(local_getDefaults); HoldIsON=false; set(HAxes,'NextPlot','replace') % hold off end end end end %-------------------------------------------------------------------------- % Consider input arguments Consider input arguments %-------------------------------------------------------------------------- if nargi==0 % MMPOLAR() MMPOLAR() MMPOLAR() MMPOLAR() MMPOLAR() MMPOLAR() if ~isempty(HAxes) out=P; % return property structure if it exists return else error('No MMPOLAR Axes exists or is not Current Axes.') end end if nargi==1 % Consider SET and GET Requests Consider SET and GET Requests if ~isempty(HAxes) arg=varargin{1}; if ischar(arg) % MMPOLAR('Pname') MMPOLAR('Pname') MMPOLAR('Pname') [fn,errmsg]=local_isfield(Pfn,arg); error(errmsg) out=P.(fn); return elseif iscellstr(arg) % MMPOLAR({'PName1','PName2',...}) nc=length(arg); out=cell(1,nc); for k=1:nc [fn,errmsg]=local_isfield(Pfn,arg{k}); error(errmsg) out{k}=P.(fn); end return elseif isstruct(arg) % MMPOLAR(S) MMPOLAR(S) MMPOLAR(S) MMPOLAR(S) Sfn=fieldnames(arg); for k=1:length(Sfn) [fn,errmsg]=local_isfield(Pfn,Sfn{k}); error(errmsg) S.(fn)=arg.(Sfn{k}); end local_updatePlot(HAxes,S); return else error('Unknown Input Argument.') end else error('No MMPOLAR exists or is not Current Axes.') end end % MMPOLAR('PName1',PValue1,'PName2',PValue2,'PName3',PValue3,...) if rem(nargi,2)==0 && ischar(varargin{1}) && ~isempty(HAxes) for k=1:2:nargi-1 PName=varargin{k}; if ischar(PName) [fn,errmsg]=local_isfield(Pfn,PName); error(errmsg) S.(fn)=varargin{k+1}; else error('String Input Property Name Argument Expected.') end end local_updatePlot(HAxes,S) return elseif ischar(varargin{1}) % Unknown Input Unknown Input Unknown Input error('Unknown Input Arguments or NO MMPOLAR Axes Exists.') elseif isnumeric(varargin{1})%MMPOLAR(Theta,Rho,...) MMPOLAR(Theta,Rho,...) % find out if there are appended 'PName',PValue pairs or a structure P last=[]; k=3; % 'Pname' or P can't appear before 3rd argument while k<=nargi vark=varargin{k}; k=k+1; if ischar(vark) fn=local_isfield(Pfn,vark); if ~isempty(fn) if isempty(last) last=k-1; end S.(fn)=varargin{k}; k=k+1; % skip known PValue end elseif isstruct(vark) % found appended structure if isempty(last) last=k-1; end Sfn=fieldnames(vark); for ki=1:length(Sfn) [fn,errmsg]=local_isfield(Pfn,Sfn{ki}); error(errmsg) S.(fn)=vark.(Sfn{ki}); end end end if ~isempty(last) varargin(last:end)=[]; % strip properties and values from input end else error('Unknown Input Arguments.') end %-------------------------------------------------------------------------- % Now have valid data for plotting Now have valid data for plotting %-------------------------------------------------------------------------- if HoldIsON % a current held plot exists D=getappdata(HAxes,'MMPOLAR_Data'); % get stored data P=getappdata(HAxes,'MMPOLAR_Properties'); tmpaxes=axes('Position',get(HAxes,'Position')); try % the plot function should work with new data Hlines=plot(tmpaxes,varargin{:}); catch delete(tmpaxes) error('Input Arguments Not Understood.') end D.TData=[D.TData; get(Hlines,{'XData'})]; % add to held data D.RData=[D.RData; get(Hlines,{'YData'})]; D.LineColor=[D.LineColor; get(Hlines,{'Color'})]; D.LineStyle=[D.LineStyle; get(Hlines,{'LineStyle'})]; D.Marker=[D.Marker; get(Hlines,{'Marker'})]; D.NumLines=length(D.TData); delete(Hlines) % got the data, lines are no longer needed delete(D.HLines) % delete original lines as well set(tmpaxes,'NextPlot','add') % hold on for k=1:D.NumLines % plot ALL data to find new RTicks and RLimits plot(tmpaxes,D.TData{k},D.RData{k}) end P.RLimit=get(tmpaxes,'YLim'); % Rho axis limits P.RTickValue=get(tmpaxes,'YTick'); % Default Rho axis ticks delete(tmpaxes) % Temporary axes no longer needed [P,D]=local_getRTickValue(HAxes,P,D); % get rho tick values D.RDataN=cell(D.NumLines,1); for k=1:D.NumLines % normalize rho data for plotting D.TData(k)={mod(D.TData{k},2*pi)}; % map theta into [0 2*pi] D.RDataN(k)={(D.RData{k}-D.RMin)/D.RLimitDiff}; end P.TLimit=[0 2*pi]; % plot full circle [P,D]=local_getTTickValue(P,D); % get theta tick values [P,D]=local_placeAxesPatch(HAxes,P,D,1);% draw axes patch, border, ticks [P,D]=local_placeRGrid(HAxes,P,D,1); % Draw Rho Grid [P,D]=local_placeTGrid(HAxes,P,D,1); % Draw Theta Grid [P,D]=local_placeTTickLabel(HAxes,P,D,1); % Add Theta Tick Labels [P,D]=local_placeRTickLabel(HAxes,P,D,1); % Add Rho Tick Lablels else % Hold is OFF try % the plot function should work now HAxes=newplot; % create axes D.HLines=plot(HAxes,varargin{:}); catch delete(gcf) error('Input Arguments Not Understood.') end HFig=ancestor(HAxes,'figure'); D.NumLines=length(D.HLines); % get all data for storage D.TData=get(D.HLines,{'XData'}); D.RData=get(D.HLines,{'YData'}); D.LineColor=get(D.HLines,{'Color'}); D.LineStyle=get(D.HLines,{'LineStyle'}); D.Marker=get(D.HLines,{'Marker'}); P=local_getDefaults; % get default properties, update as needed P.RLimit=get(HAxes,'YLim'); % Rho axis limits P.RTickValue=get(HAxes,'YTick'); % Default Rho axis ticks [P,D]=local_getRTickValue(HAxes,P,D); % get rho tick values D.RDataN=cell(D.NumLines,1); for k=1:D.NumLines % Condition plotted data % wrap angles into first revolution D.TData{k}=mod(D.TData{k},2*pi); % normalize rho data for plotting D.RDataN(k)={(D.RData{k}-D.RMin)/D.RLimitDiff}; end P.TLimit=[0 2*pi]; % plot full circle [P,D]=local_getTTickValue(P,D); % get theta tick values delete(D.HLines) % clear cartesian lines, then create polar axes [P,D]=local_placeAxesPatch(HAxes,P,D); % draw axes patch, border, ticks [P,D]=local_placeRGrid(HAxes,P,D); % Draw Rho Grid [P,D]=local_placeTGrid(HAxes,P,D); % Draw Theta Grid [P,D]=local_placeTTickLabel(HAxes,P,D); % Add Theta Tick Labels [P,D]=local_placeRTickLabel(HAxes,P,D); % Add Rho Tick Lablels end % Finalize Axes View Finalize Axes View set(HAxes,'DataAspectRatio',[1 1 1],.... 'XLimMode','manual','YLimMode','manual',... 'Visible','Off','Tag','MMPOLAR_Axes') Hlabels=get(HAxes,{'Xlabel','YLabel','Title'}); set([Hlabels{:}],'Visible','on') % make labels and title visible % Plot the Data Plot the Data D.HLines=zeros(D.NumLines,1); % storage for lineseries handles set([HFig,HAxes],'NextPlot','add') % hold on for k=1:D.NumLines % plot the normalized data tdata=D.TData{k}; rdata=D.RDataN{k}; xdata=rdata.*cos(tdata); ydata=rdata.*sin(tdata); D.HLines(k)=plot(HAxes,xdata,ydata,... 'Color',D.LineColor{k},... 'LineStyle',D.LineStyle{k},... 'Marker',D.Marker{k}); end if HoldIsON set([HFig,HAxes],'NextPlot','add') % hold on else set([HFig,HAxes],'NextPlot','replace') % hold off end % Store Data Store Data setappdata(HAxes,'MMPOLAR_Properties',P) setappdata(HAxes,'MMPOLAR_Data',D) if nargout % output handles if requested out=D.HLines; end % Update Plot with 'PName' PValue pairs if they exist if exist('S','var')==1 local_updatePlot(HAxes,S) end %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- % Local Functions Local Functions %-------------------------------------------------------------------------- function local_updatePlot(HAxes,S) % update MMPOLAR plot properties % S contains known properties P=getappdata(HAxes,'MMPOLAR_Properties'); D=getappdata(HAxes,'MMPOLAR_Data'); Sfn=fieldnames(S); for kk=1:length(Sfn) switch Sfn{kk} case 'Axis' % Axis [istrue,onoff]=local_isonoff(S.Axis); if istrue set(D.HAPatch,'Visible',onoff) set(D.HRGrid,'Visible',onoff) set(D.HTGrid,'Visible',onoff) set(D.HRTick,'Visible',onoff) set(D.HTTick,'Visible',onoff) set(D.HRTickLabel,'Visible',onoff) set(D.HTTickLabel,'Visible',onoff) P.RGridVisible=onoff; P.TGridVisible=onoff; P.RTickLabelVisible=onoff; P.TTickLabelVisible=onoff; else error('Unknown ''Axis'' Property Value.') end case 'Border' % Border [istrue,onoff]=local_isonoff(S.Border); if istrue && strcmp(onoff,'on') set(D.HAPatch,'EdgeColor',P.BorderColor) set(D.HTTick,'Visible','on') set(D.HRTick,'Visible','on') P.RTickVisible='on'; P.TTickVisibel='on'; elseif istrue && strcmp(onoff,'off') set(D.HAPatch,'EdgeColor','none') set(D.HTTick,'Visible','off') set(D.HRTick,'Visible','off') P.RTickVisible='off'; P.TTickVisibel='off'; else error('Unknown ''Border'' Property Value.') end case 'BorderColor' % BorderColor [istrue,cs]=local_iscolorspec(S.BorderColor); if istrue P.BorderColor=cs; set(D.HAPatch,'EdgeColor',cs) set(D.HRTick,'Color',cs) set(D.HTTick,'Color',cs) else error('Unknown ''BorderColor'' Property Value.') end case 'Grid' % Grid [istrue,onoff]=local_isonoff(S.Grid); if istrue set(D.HRGrid,'Visible',onoff) set(D.HTGrid,'Visible',onoff) P.Grid=onoff; P.RGridVisible=onoff; P.TGridVisible=onoff; else error('Unknown ''Grid'' Property Value.') end case 'TickLength' % TickLength if isnumeric(S.TickLength) && isscalar(S.TickLength) P.TickLength=max(min(abs(S.TickLength),0.1),.001); tdir=2*strcmp(P.TTickDirection,'in')-1; tdata=[D.TTickValue;D.TTickValue;NaN(1,D.TTickLabelN)]; rdata=[ones(1,D.TTickLabelN) (1-tdir*P.TickLength)+zeros(1,D.TTickLabelN) NaN(1,D.TTickLabelN)]; xdata=rdata(:).*cos(tdata(:)); ydata=rdata(:).*sin(tdata(:)); set(D.HTTick,'XData',xdata,'YData',ydata) % theta ticks phi=asin(P.TickLength./(2*D.RTickRadius)); tdata=[D.RTickAngle-phi; D.RTickAngle+zeros(size(D.RTickRadius)) D.RTickAngle+phi; NaN(size(D.RTickRadius))]; rdata=[D.RTickRadius; D.RTickRadius D.RTickRadius; NaN(size(D.RTickRadius))]; xdata=rdata(:).*cos(tdata(:)); ydata=rdata(:).*sin(tdata(:)); set(D.HRTick,'XData',xdata,'YData',ydata) % rho ticks else error('Unknown ''TickLength'' Property Value.') end case 'BackgroundColor' % BackgroundColor [istrue,cs]=local_iscolorspec(S.BackgroundColor); if istrue set(D.HAPatch,'FaceColor',cs) P.BackgroundColor=cs; else error('Unknown ''BackgroundColor'' Property Value.') end case 'FontName' % FontName if ischar(S.FontName) && any(strcmpi(listfonts,S.FontName)) set([D.HRTickLabel; D.HTTickLabel],'FontName',S.FontName) P.FontName=S.FontName; else error('Unknown ''FontName'' Property Value.') end case 'FontSize' % FontSize if isnumeric(S.FontSize) && isscalar(S.FontSize) set([D.HRTickLabel; D.HTTickLabel],'FontSize',S.FontSize) P.FontSize=S.FontSize; else error('Unknown ''FontSize'' Property Value.') end case 'FontWeight' % FontWeight if ischar(S.FontWeight) && ... (strncmpi(S.FontWeight,'normal',3)... ||strncmpi(S.FontWeight,'bold',3)) set([D.HRTickLabel; D.HTTickLabel],'FontWeight',S.FontWeight) P.FontWeight=S.FontWeight; else error('Unknown ''FontWeight'' Property Value.') end case 'RGridColor' % RGridColor [istrue,cs]=local_iscolorspec(S.RGridColor); if istrue set(D.HRGrid,'Color',cs) P.RGridColor=cs; else error('Unknown ''RGridColor'' Property Value.') end case 'RGridVisible' % RGridVisible [istrue,onoff]=local_isonoff(S.RGridVisible); if istrue set(D.HRGrid,'Visible',onoff) P.RGridVisible=onoff; else error('Unknown ''RGridVisible'' Property Value.') end case 'RGridLineStyle' % RGridLineStyle if local_islinespec(S.RGridLineStyle) set(D.HRGrid,'LineStyle',S.RGridLineStyle) P.RGridLineStyle=S.RGridLineStyle; else error('Unknown ''RGridLineStyle'' Property Value.') end case 'RLimit' % RLimit if isnumeric(S.RLimit) && numel(S.RLimit)==2 S.RLimit=[min(S.RLimit) max(S.RLimit)]; S.RLimit(isinf(S.RLimit))=P.RLimit(isinf(S.RLimit)); [P,D]=local_getRTickValue(HAxes,P,D,S); [P,D]=local_placeRGrid(HAxes,P,D,S); [P,D]=local_placeRTickLabel(HAxes,P,D,S); % rescale rho data to new limits for k=1:length(D.RData) D.RDataN(k)={(D.RData{k}-D.RMin)/D.RLimitDiff}; D.RDataN{k}(D.RDataN{k}>1)=NaN; % hide data outside limits D.RDataN{k}(D.RDataN{k}<0)=NaN; theta=D.TData{k}; xdata=D.RDataN{k}.*cos(theta); ydata=D.RDataN{k}.*sin(theta); set(D.HLines(k),'XData',xdata,'YData',ydata) end else error('Unknown ''RLimit'' Property Value.') end case 'RGridLineWidth' % RGridLineWidth if isnumeric(S.RGridLineWidth) && isscalar(S.RGridLineWidth) set(D.HRGrid,'LineWidth',S.RGridLineWidth) P.RGridLineWidth=S.RGridLineWidth; else error('Unknown ''RGridLineWidth'' Property Value.') end case 'RTickOffset' % RTickOffset if isnumeric(S.RTickOffset) && isscalar(S.RTickOffset) P.RTickOffset=S.RTickOffset; for k=1:D.RTickLabelN xdata=(P.RTickOffset+D.RTickRadius(k))*cos(D.RTickAngle); ydata=(P.RTickOffset+D.RTickRadius(k))*sin(D.RTickAngle); set(D.HRTickLabel(k),'Position',[xdata ydata]) end else error('Unknown ''RTickOffset'' Property Value.') end case 'RTickValue' % RTickValue if isnumeric(S.RTickValue) && numel(S.RTickValue)>0 S.RTickValue=S.RTickValue(S.RTickValue>=P.RLimit(1)... & S.RTickValue<=P.RLimit(2)); if length(S.RTickValue)>1 P.RTickValue=S.RTickValue; D.RTickLabelN=length(P.RTickValue); D.RTickRadius=(P.RTickValue-D.RMin)/D.RLimitDiff; [P,D]=local_placeRGrid(HAxes,P,D,S); [P,D]=local_placeRTickLabel(HAxes,P,D,S); end else error('Unknown ''RTickValue'' Property Value.') end case 'RTickVisible' % RTickVisible [istrue,onoff]=local_isonoff(S.RTickVisible); if istrue set(D.HRTick,'Visible',onoff) P.RTickVisible=onoff; else error('Unknown ''RTickVisible'' Property Value.') end case 'RTickLabel' % RTickLabel if iscellstr(S.RTickLabel) NumS=length(S.RTickLabel); for k=1:D.RTickLabelN str=S.RTickLabel{rem(k-1,NumS)+1}; set(D.HRTickLabel(k),'String',str) end P.RTickLabel=S.RTickLabel; else error('Unknown ''RTickLabel'' Property Value.') end case 'RTickLabelHalign' % RTickLabelHalign fnames={'left' 'center' 'right'}; out=local_isfield(fnames,S.RTickLabelHalign); if ~isempty(out) P.RTickLabelHalign=out; set(D.HRTickLabel,'HorizontalAlignment',out) else error('Unknown ''RTickLabelHalign'' Property Value.') end case 'RTickLabelValign' % RTickLabelValign fnames={'top' 'cap' 'middle' 'baseline' 'bottom'}; out=local_isfield(fnames,S.RTickLabelValign); if ~isempty(out) P.RTickLabelValign=out; set(D.HRTickLabel,'VerticalAlignment',out) else error('Unknown ''RTickLabelValign'' Property Value.') end case 'RTickLabelVisible' % RTickLabelVisible [istrue,onoff]=local_isonoff(S.RTickLabelVisible); if istrue set(D.HRTickLabel,'Visible',onoff) P.RTickLabelVisible=onoff; else error('Unknown ''RTickLabelVisible'' Property Value.') end case 'RTickAngle' % RTickAngle if isnumeric(S.RTickAngle) && isscalar(S.RTickAngle) rad=S.RTickAngle; if strcmp(P.TTickScale,'degrees') rad=S.RTickAngle*pi/180; end if P.TLimit(1)>P.TLimit(2) && (rad