function out=mmpolar(varargin) %MMPOLAR Polar Plot with Settable Properties. % 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. % For example, set(H,'LineWidth',2) sets all linewidths to 2 points. % Note: 'LineWidth' is NOT a property that can be set with MMPOLAR. It must % be set as shown above by using the SET function on the line handles H. % % 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, YLABEL, TITLE, ZOOM, SUBPLOT % but does not work with AXIS, GRID (Use MMPOLAR properties to set these) % % 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, 2008-03-18 %-------------------------------------------------------------------------- % 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 local_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 local_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 local_error('Unknown Input Argument.') end else local_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 local_error('String Input Property Name Argument Expected.') end end local_updatePlot(HAxes,S) return elseif ischar(varargin{1}) % Unknown Input Unknown Input Unknown Input local_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 local_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) local_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) local_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 xylims=[-1 1]*1.08; % Finalize Axes View Finalize Axes View set(HAxes,'DataAspectRatio',[1 1 1],.... 'XLimMode','manual','YLimMode','manual',... 'XLim',xylims,'YLim',xylims,... 'Visible','Off','Tag','MMPOLAR_Axes') Hlabels=get(HAxes,{'Xlabel','YLabel', 'Title'}); set([Hlabels{:}],'Visible','on') % make labels 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) % 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 local_error('Unknown ''Axis'' Property Value.') end case 'BackgroundColor' % BackgroundColor [istrue,cs]=local_iscolorspec(S.BackgroundColor); if istrue set(D.HAPatch,'FaceColor',cs) P.BackgroundColor=cs; else local_error('Unknown ''BackgroundColor'' 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 local_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 local_error('Unknown ''BorderColor'' 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 local_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 local_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 local_error('Unknown ''FontWeight'' 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 local_error('Unknown ''Grid'' Property Value.') end case 'RGridColor' % RGridColor [istrue,cs]=local_iscolorspec(S.RGridColor); if istrue set(D.HRGrid,'Color',cs) set(D.HRTickLabel,'Color',cs) P.RGridColor=cs; else local_error('Unknown ''RGridColor'' Property Value.') end case 'RGridLineStyle' % RGridLineStyle if local_islinespec(S.RGridLineStyle) set(D.HRGrid,'LineStyle',S.RGridLineStyle) P.RGridLineStyle=S.RGridLineStyle; else local_error('Unknown ''RGridLineStyle'' Property Value.') end case 'RGridLineWidth' % RGridLineWidth if isnumeric(S.RGridLineWidth) && isscalar(S.RGridLineWidth) set(D.HRGrid,'LineWidth',S.RGridLineWidth) P.RGridLineWidth=S.RGridLineWidth; else local_error('Unknown ''RGridLineWidth'' Property Value.') end case 'RGridVisible' % RGridVisible [istrue,onoff]=local_isonoff(S.RGridVisible); if istrue set(D.HRGrid,'Visible',onoff) P.RGridVisible=onoff; else local_error('Unknown ''RGridVisible'' 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 local_error('Unknown ''RLimit'' 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) && (radP.TLimit(1)) P.RTickAngle=S.RTickAngle; D.RTickAngle=rad; elseif rad>P.TLimit(1) && rad0 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 local_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 local_error('Unknown ''RTickVisible'' Property Value.') end case 'Style' % Style if strncmpi(S.Style,'cartesian',3) % Cartesian style set(HAxes,'View',[0 90]) P.TDirection='ccw'; P.TZeroDirection='east'; P.Style='cartesian'; elseif strncmpi(S.Style,'compass',3) % Compass style set(HAxes,'View',[90 -90]) P.TDirection='cw'; P.TZeroDirection='north'; P.Style='compass'; else local_error('Unknown ''Style'' Property Value.') end case 'TDirection' % TDirection if ischar(S.TDirection) && strcmpi(S.TDirection,'cw') P.TDirection='cw'; if strcmp(P.TZeroDirection,'north') set(HAxes,'View',[90 -90]) elseif strcmp(P.TZeroDirection,'east') set(HAxes,'View',[0 -90]) elseif strcmp(P.TZeroDirection,'south') set(HAxes,'View',[270 -90]) elseif strcmp(P.TZeroDirection,'west') set(HAxes,'View',[180 -90]) end elseif ischar(S.TDirection) && strcmpi(S.TDirection,'ccw') P.TDirection='ccw'; if strcmp(P.TZeroDirection,'north') set(HAxes,'View',[270 90]) elseif strcmp(P.TZeroDirection,'east') set(HAxes,'View',[0 90]) elseif strcmp(P.TZeroDirection,'south') set(HAxes,'View',[90 90]) elseif strcmp(P.TZeroDirection,'west') set(HAxes,'View',[180 90]) end else local_error('Unknown ''TDirection'' Property Value.') end P.Style='unknown'; case 'TGridColor' % TGridColor [istrue,cs]=local_iscolorspec(S.TGridColor); if istrue set(D.HTGrid,'Color',cs) set(D.HTTickLabel,'Color',cs) P.TGridColor=cs; else local_error('Unknown ''TGridColor'' Property Value.') end case 'TGridLineStyle' % TGridLineStyle if local_islinespec(S.TGridLineStyle) set(D.HTGrid,'LineStyle',S.TGridLineStyle) P.TGridLineStyle=S.TGridLineStyle; else local_error('Unknown ''TGridLineStyle'' Property Value.') end case 'TGridLineWidth' % TGridLineWidth if isnumeric(S.TGridLineWidth) && isscalar(S.TGridLineWidth) set(D.HTGrid,'LineWidth',S.TGridLineWidth) P.TGridLineWidth=S.TGridLineWidth; else local_error('Unknown ''TGridLineWidth'' Property Value.') end case 'TGridVisible' % TGridVisible [istrue,onoff]=local_isonoff(S.TGridVisible); if istrue set(D.HTGrid,'Visible',onoff) P.TGridVisible=onoff; else local_error('Unknown ''TGridVisible'' 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 local_error('Unknown ''TickLength'' Property Value.') end case 'TLimit' % TLimit if isnumeric(S.TLimit) && numel(S.TLimit)==2 if abs(diff(S.TLimit))>1.9*pi % make full circle if close P.TLimit=[0 2*pi]; else P.TLimit=mod(S.TLimit,2*pi); % move limits to range 0 to 2pi end [P,D]=local_getTTickValue(P,D,S); [P,D]=local_placeAxesPatch(HAxes,P,D,S); [P,D]=local_placeRGrid(HAxes,P,D,S); [P,D]=local_placeTGrid(HAxes,P,D,S); [P,D]=local_placeTTickLabel(HAxes,P,D,S); [P,D]=local_placeRTickLabel(HAxes,P,D,S); for k=1:length(D.TData) % hide data outside TLimits tdata=D.TData{k}; if P.TLimit(1)>P.TLimit(2) tdata(tdataP.TLimit(2))=NaN; else tdata(tdataP.TLimit(2))=NaN; end xdata=D.RDataN{k}.*cos(tdata); ydata=D.RDataN{k}.*sin(tdata); set(D.HLines(k),'XData',xdata,'YData',ydata) end else local_error('Unknown ''TLimit'' Property Value.') end case 'TTickDelta' % TTickDelta if isnumeric(S.TTickDelta) && isscalar(S.TTickDelta) if strcmp(P.TTickScale,'degrees') P.TTickDelta=min(max(abs(S.TTickDelta),5),90); else P.TTickDelta=min(max(abs(S.TTickDelta),pi/36),pi/2); end [P,D]=local_getTTickValue(P,D,S); [P,D]=local_placeTGrid(HAxes,P,D,S); [P,D]=local_placeTTickLabel(HAxes,P,D,S); else local_error('Unknown ''TTickDelta'' Property Value.') end case 'TTickDirection' % TTickDirection if ischar(S.TTickDirection) &&... (strcmpi(S.TTickDirection,'out') || strcmpi(S.TTickDirection,'in')) P.TTickDirection=S.TTickDirection; 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 else local_error('Unknown ''TTickDirection'' Property Value.') end case 'TTickLabel' % TTickLabel if iscellstr(S.TTickLabel) NumS=length(S.TTickLabel); for k=1:D.TTickLabelN str=S.TTickLabel{rem(k-1,NumS)+1}; set(D.HTTickLabel(k),'String',str) end P.TTickLabel=S.TTickLabel; else local_error('Unknown ''TTickLabel'' Property Value.') end case 'TTickLabelVisible' % TTickLabelVisible [istrue,onoff]=local_isonoff(S.TTickLabelVisible); if istrue set(D.HTTickLabel,'Visible',onoff) P.TTickLabelVisible=onoff; else local_error('Unknown ''TTickLabelVisible'' Property Value.') end case 'TTickOffset' % TTickOffset if isnumeric(S.TTickOffset) && isscalar(S.TTickOffset) P.TTickOffset=S.TTickOffset; for k=1:D.TTickLabelN xdata=(1+P.TTickOffset)*cos(D.TTickValue(k)); ydata=(1+P.TTickOffset)*sin(D.TTickValue(k)); set(D.HTTickLabel(k),'Position',[xdata ydata]) end else local_error('Unknown ''TTickOffset'' Property Value.') end case 'TTickScale' % TTickScale if ischar(S.TTickScale) && strncmpi(S.TTickScale,'degrees',3)... && strcmp(P.TTickScale,'radians') P.TTickScale='degrees'; P.TTickDelta=P.TTickDelta*180/pi; P.RTickAngle=P.RTickAngle*180/pi; [P,D]=local_getTTickValue(P,D,S); [P,D]=local_placeTTickLabel(HAxes,P,D,S); elseif ischar(S.TTickScale) && strncmpi(S.TTickScale,'radians',3)... && strcmp(P.TTickScale,'degrees') P.TTickScale='radians'; P.TTickDelta=P.TTickDelta*pi/180; P.RTickAngle=P.RTickAngle*pi/180; [P,D]=local_getTTickValue(P,D,S); [P,D]=local_placeTTickLabel(HAxes,P,D,S); elseif ~ischar(S.TTickScale) local_error('Unknown ''TTickScale'' Property Value.') end case 'TTickSign' % TTickSign if ischar(S.TTickSign) if strcmp(S.TTickSign,'+') P.TTickSign='+'; else P.TTickSign='+-'; end [P,D]=local_getTTickValue(P,D,S); [P,D]=local_placeTTickLabel(HAxes,P,D,S); else local_error('Unknown ''TTickSign'' Property Value.') end case 'TTickValue' % TTickValue if isnumeric(S.TTickValue) && numel(S.TTickValue)>0 TTick=S.TTickValue(:)'; if strcmp(P.TTickScale,'degrees') TTick=TTick*pi/180; end if P.TLimit(1)>P.TLimit(2) idx=TTick<=P.TLimit(2) | TTick>=P.TLimit(1); % keepers else idx=TTick>=P.TLimit(1) & TTick<=P.TLimit(2); % keepers end S.TTickValue=S.TTickValue(idx); if length(S.TTickValue)>1 P.TTickValue=S.TTickValue(:)'; D.TTickValue=TTick(idx); D.TTickLabelN=length(P.TTickValue); [P,D]=local_placeTGrid(HAxes,P,D,S); [P,D]=local_placeTTickLabel(HAxes,P,D,S); end else local_error('Unknown ''TTickValue'' Property Value.') end case 'TTickVisible' % TTickVisible [istrue,onoff]=local_isonoff(S.TTickVisible); if istrue set(D.HTTick,'Visible',onoff) P.TTickVisible=onoff; else local_error('Unknown ''RTickVisible'' Property Value.') end case 'TZeroDirection' % TZeroDirection if ischar(S.TZeroDirection) && strncmpi(S.TZeroDirection,'north',1) P.TZeroDirection='north'; if strcmp(P.TDirection,'ccw') set(HAxes,'View',[270 90]) elseif strcmp(P.TDirection,'cw') set(HAxes,'View',[90 -90]) end elseif ischar(S.TZeroDirection) && strncmpi(S.TZeroDirection,'east',1) P.TZeroDirection='east'; if strcmp(P.TDirection,'ccw') set(HAxes,'View',[0 90]) elseif strcmp(P.TDirection,'cw') set(HAxes,'View',[0 -90]) end elseif ischar(S.TZeroDirection) && strncmpi(S.TZeroDirection,'south',1) P.TZeroDirection='south'; if strcmp(P.TDirection,'ccw') set(HAxes,'View',[90 90]) elseif strcmp(P.TDirection,'cw') set(HAxes,'View',[270 -90]) end elseif ischar(S.TZeroDirection) && strncmpi(S.TZeroDirection,'west',1) P.TZeroDirection='west'; if strcmp(P.TDirection,'ccw') set(HAxes,'View',[180 90]) elseif strcmp(P.TDirection,'cw') set(HAxes,'View',[180 -90]) end else local_error('Unknown ''TZeroDirection'' Property Value.') end P.Style='unknown'; end end setappdata(HAxes,'MMPOLAR_Properties',P) setappdata(HAxes,'MMPOLAR_Data',D) %-------------------------------------------------------------------------- %-------------------------------------------------------------------------- function [out,errmsg]=local_isfield(fnames,str) % local_isfield % compare str to fnames, if found, return complete fieldname % otherwise return error and empty string. % fnames is cell array, str is a string % outputs are strings % look for exact match first idx=find(strcmpi(fnames,str)); if isempty(idx) % no exact match, so look for more general match idx=find(strncmpi(str,fnames,max(length(str),2))); end if numel(idx)==1 % unique match found out=fnames{idx}; errmsg=''; else % trouble out=''; errmsg=sprintf('Unknown or Not Unique Property: %s',str); end %-------------------------------------------------------------------------- function [istrue,cs]=local_iscolorspec(arg) % local_iscolorspec % see if arg is a valid color specification including 'none' rgb={[1 0 0],[0 1 0],[0 0 1],[0 1 1],[1 0 1],[1 1 0],[0 0 0],[1 1 1],'none'}; cc='rgbcmykwn'; istrue=false; cs=[]; if ~isempty(arg) && ischar(arg) idx=find(arg(1)==cc); if ~isempty(idx) istrue=true; cs=rgb{idx}; end elseif ~isempty(arg) && isnumeric(arg) istrue=all(size(arg)==[1 3])... && all(arg>=0) && all(arg<=1); cs=arg; else istrue=false; end %-------------------------------------------------------------------------- function istrue=local_islinespec(arg) % local_islinespec % see if arg is a valid line style specification str={'-','-.','--',':'}; if isempty(arg) istrue=false; elseif ischar(arg) && length(arg)<3 istrue=any(strncmp(str,arg,length(arg))); else istrue=false; end %-------------------------------------------------------------------------- function [istrue,s]=local_isonoff(arg) % local_isonoff % see if arg is either on or off istrue=false; s=''; if ~isempty(arg) && ischar(arg) && strcmpi(arg,'on') istrue=true; s='on'; elseif ~isempty(arg) && ischar(arg) && strcmpi(arg,'off') istrue=true; s='off'; end %-------------------------------------------------------------------------- function local_error(arg) % Add message identifier to error message and post error if ~isempty(arg) && ischar(arg) error('MMPOLAR:error',arg) end %-------------------------------------------------------------------------- function [P,D]=local_placeAxesPatch(HAxes,P,D,S) %#ok local_placeAxesPatch % Draw Axes Border and Patch tinc=pi/250; if P.TLimit(1)>P.TLimit(2) theta=[P.TLimit(1):tinc:(P.TLimit(2)+2*pi-eps) P.TLimit(2)+2*pi]; else theta=[P.TLimit(1):tinc:P.TLimit(2)-eps P.TLimit(2)]; end costheta=cos(theta); sintheta=sin(theta); if abs(diff(P.TLimit))<2*(1-eps)*pi; % less than 4 quadrant box xdata=[0 costheta 0]; ydata=[0 sintheta 0]; else % four quadrant box xdata=costheta; ydata=sintheta; end % let the axes grow for less than 4 quadrant box set(HAxes,'Xlim',[min(xdata) max(xdata)],'Ylim',[min(ydata) max(ydata)]) if nargin==3 % new plot D.HAPatch=patch('XData',xdata,'YData',ydata,... 'Parent',HAxes,... 'LineStyle','-',... 'Linewidth',2*P.RGridLineWidth,... 'EdgeColor',P.BorderColor,... 'FaceColor',P.BackgroundColor,... 'HandleVisibility','off',... 'HitTest','off'); else % old plot, update data set(D.HAPatch,'XData',xdata,'YData',ydata) end %-------------------------------------------------------------------------- function [P,D]=local_placeRGrid(HAxes,P,D,S) %#ok local_placeRGrid tinc=pi/250; if P.TLimit(1)>P.TLimit(2) theta=[P.TLimit(1):tinc:(P.TLimit(2)+2*pi-eps) P.TLimit(2)+2*pi]; else theta=[P.TLimit(1):tinc:P.TLimit(2)-eps P.TLimit(2)]; end costheta=cos(theta); sintheta=sin(theta); xdata=[]; ydata=[]; % no outer grid if outer tick is at outer RLimit D.RGridN=length(P.RTickValue(P.RTickValue6 if rem(NumRTick,2)~=0 % odd number keep alternate ones P.RTickValue=P.RTickValue(1:2:end); else % even number, add one tick, then keep alternate ones if P.RTickValue(1)==0 % keep lowest tick if zero, add one at outside P.RTickValue=[P.RTickValue(1:2:end-1) ... 2*P.RTickValue(end)-P.RTickValue(end-1)]; P.RTickValue(P.RTickValue>P.RLimit(2))=[]; % no label past limit else % add one tick at inside P.RTickValue=[2*P.RTickValue(1)-P.RTickValue(2)... P.RTickValue(2:2:end)]; end P.RLimit(1)=P.RTickValue(1); % make first tick lower axis limit end end if NumRTick<3 m=sum(P.RTickValue)/2; P.RTickValue(3)=P.RTickValue(2); P.RTickValue(2)=m; end D.RLimitDiff=diff(P.RLimit); D.RMin=P.RLimit(1); if abs(P.RTickValue(1)-P.RLimit(1)) < abs(D.RLimitDiff)/100 P.RTickValue(1)=[]; % throw out inner tick if at inner axis limit end D.RTickRadius=(P.RTickValue-D.RMin)/D.RLimitDiff; D.RTickLabelN=length(P.RTickValue); %-------------------------------------------------------------------------- function [P,D]=local_placeTGrid(HAxes,P,D,S) %#ok local_placeTGrid xdata=[]; ydata=[]; costheta=cos(D.TTickValue); sintheta=sin(D.TTickValue); % no grid on first or last ticks are axis limits if less than 4 quadrants ki=1; ke=length(D.TTickValue); if abs(diff(P.TLimit))<2*(1-eps)*pi; % less than 4 quadrant box ki=1+(D.TTickValue(1)==P.TLimit(1)); ke=length(D.TTickValue)-(D.TTickValue(end)==P.TLimit(2)); end D.TGridN=ke-ki+1; for k=ki:ke xdata=[xdata 0 costheta(k) NaN]; %#ok ydata=[ydata 0 sintheta(k) NaN]; %#ok end if nargin<4 % new grid D.HTGrid=line(xdata,ydata,... 'Parent',HAxes,... 'LineStyle',P.TGridLineStyle,... 'LineWidth',P.TGridLineWidth,... 'Color',P.TGridColor,... 'HandleVisibility','off',... 'HitTest','off'); else set(D.HTGrid,'Xdata',xdata,'YData',ydata) end % Draw Theta Axis Tick Marks 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(:)); if nargin==3 % new plot D.HTTick=line(xdata,ydata,... 'Parent',HAxes,... 'LineStyle','-',... 'LineWidth',2*P.RGridLineWidth,... 'Color',P.BorderColor,... 'Clipping','off',... 'HandleVisibility','off',... 'HitTest','off'); else % old plot, update data set(D.HTTick,'XData',xdata,'YData',ydata) end %-------------------------------------------------------------------------- function [P,D]=local_placeTTickLabel(HAxes,P,D,S)%#ok local_placeTTickLabel % Draw Theta Ticks if nargin==4 % delete old labels and create new ones delete(D.HTTickLabel) end P.TTickLabel=cell(D.TTickLabelN,1); D.HTTickLabel=zeros(D.TTickLabelN,1); for k=1:D.TTickLabelN % label ticks at theta axis limits xdata=(1+P.TTickOffset)*cos(D.TTickValue(k)); ydata=(1+P.TTickOffset)*sin(D.TTickValue(k)); if strcmp(P.TTickScale,'radians') [n,d]=rat(P.TTickValue(k)/pi); % ticks as fractions if n==0 Tstr='0'; elseif n==1 && d==1 Tstr='\pi'; elseif n==-1 && d==1 Tstr='-\pi'; elseif n==1 Tstr=['\pi/' num2str(d)]; elseif n==-1 Tstr=['-\pi/' num2str(d)]; elseif d==1 Tstr=[num2str(n) '\pi']; else Tstr=[num2str(n) '\pi/' num2str(d)]; end P.TTickLabel{k}=Tstr; else % degrees P.TTickLabel{k}=[num2str(P.TTickValue(k)) '\circ']; if P.TTickValue(k)==-180 P.TTickLabel{k}=['\pm' P.TTickLabel{k}(2:end)]; end end D.HTTickLabel(k)=text(xdata,ydata,P.TTickLabel{k},... 'Parent',HAxes,... 'Color',P.TGridColor,... 'FontName',P.FontName,... 'FontSize',P.FontSize,... 'FontWeight',P.FontWeight',... 'HorizontalAlignment','center',... 'VerticalAlignment','middle',... 'Clipping','off',... 'HandleVisibility','off',... 'HitTest','off'); end %-------------------------------------------------------------------------- function [P,D]=local_getTTickValue(P,D,S) %#ok local_getTTickValue % Get Theta Ticks if strcmp(P.TTickScale,'degrees') % ticks are in degrees TTick=0:P.TTickDelta:360; % possible ticks if P.TLimit(1)>P.TLimit(2) idx=TTick<=P.TLimit(2)*180/pi | TTick>=P.TLimit(1)*180/pi; % keepers else idx=TTick>=P.TLimit(1)*180/pi & TTick<=P.TLimit(2)*180/pi; % keepers end P.TTickValue=TTick(idx); P.TTickValue=unique(rem(P.TTickValue,360)); % get unique ticks D.TTickValue=P.TTickValue*pi/180; % store ticks in radians if strcmp(P.TTickSign,'+-') tmp=P.TTickValue>=180; P.TTickValue(tmp)=P.TTickValue(tmp)-360; end else % ticks are in radians TTick=(0:P.TTickDelta*180/pi:360)*pi/180; % possible ticks if P.TLimit(1)>P.TLimit(2) idx=TTick<=P.TLimit(2) | TTick>=P.TLimit(1); % keepers else idx=TTick>=P.TLimit(1) & TTick<=P.TLimit(2); % keepers end P.TTickValue=TTick(idx); P.TTickValue=unique(rem(P.TTickValue,2*pi)); % get unique ticks D.TTickValue=P.TTickValue; % store ticks in radians for plotting if strcmp(P.TTickSign,'+-') tmp=P.TTickValue>=pi; P.TTickValue(tmp)=P.TTickValue(tmp)-2*pi; end end D.TTickLabelN=length(D.TTickValue); kmid=max(1,floor(length(P.TTickValue)/2)); P.RTickAngle=(D.TTickValue(kmid)+D.TTickValue(kmid+1))/2; D.RTickAngle=P.RTickAngle; if strcmp(P.TTickScale,'degrees') P.RTickAngle=P.RTickAngle*180/pi; end %-------------------------------------------------------------------------- function out=local_getDefaults % local_getDefaults out.Style='cartesian'; out.Axis='on'; out.Border='on'; out.Grid='on'; out.RLimit=[0 1]; out.TLimit=[0 2*pi]; out.RTickUnits=''; out.TTickScale='degrees'; out.TDirection='ccw'; out.TZeroDirection='east'; out.BackgroundColor=get(0,'defaultaxescolor'); out.BorderColor=[0 0 0]; out.FontName=get(0,'defaultaxesfontname'); out.FontSize=get(0,'defaultaxesfontsize'); out.FontWeight=get(0,'defaultaxesfontweight'); out.TickLength=0.02; out.RGridColor=get(0,'defaultaxesycolor'); out.RGridLineStyle=get(0,'defaultaxesgridlinestyle'); out.RGridLineWidth=get(0,'defaultaxeslinewidth'); out.RGridVisible='on'; out.RTickAngle=0; out.RTickOffset=0.04; out.RTickLabel='0|0.5|1.0'; out.RTickLabelHalign='center'; out.RTickLabelValign='middle'; out.RTickLabelVisible='on'; out.RTickValue=[0 .5 1]; out.RTickVisible='on'; out.TGridColor=get(0,'defaultaxesxcolor'); out.TGridVisible='on'; out.TGridLineStyle=get(0,'defaultaxesgridlinestyle'); out.TGridLineWidth=get(0,'defaultaxeslinewidth'); out.TTickOffset=0.08; out.TTickDelta=15; out.TTickDirection='in'; out.TTickLabel=''; out.TTickLabelVisible='on'; out.TTickSign='+-'; out.TTickValue=0:15:359; out.TTickVisible='on'; %-------------------------------------------------------------------------- % Description of Plot Data Stored Description of Plot Data Stored %-------------------------------------------------------------------------- % % VARIABLE TYPE DESCRIPTION % D.TData Cell Raw input theta data % D.RData Cell Raw input rho data % D.RDataN Cell Normalized rho data % D.LineColor Cell Line colors of plotted data % D.LineStyle Cell Line styles of plotted data % D.Marker Cell Markers of plotted data % D.RLimitDiff Double Rho axis limit difference % D.RMin Double Rho axis minimum % D.RScale Double Power of ten scaling rho axis ticks % D.RTickAngle Double Angle of rho tick labels in radians % D.RTickRadius Double Radial position of rho tick labels % D.RGridN Double Number of rho axis grid lines % D.RTickLabelN Double Number of rho axis tick labels % D.TGridN Double Number of theta axis grid lines % D.TTickLabelN Double Number of theta axis tick labels % D.TTickValue Double Theta tick values in RADIANS %-------------------------------------------------------------------------- % Description of Stored Handles Description of Stored Handles %-------------------------------------------------------------------------- % % HANDLE TYPE DESCRIPTION % D.HAPatch Patch Axis background and border % D.HTTick Line Theta tick marks % D.HRTick Line Rho tick marks % D.HRGrid Line Rho grid % D.HTTickLabel Text Theta Tick Labels % D.HRTIckLabel Text Rho Tick Labels % D.HLines Line Plotted Data % %--------------------------------------------------------------------------