v_xyzticksi

PURPOSE ^

V_XYZTIXKSI labels an axis of a plot using SI multipliers S=(AX,AH)

SYNOPSIS ^

function s=v_xyzticksi(ax,ah)

DESCRIPTION ^

V_XYZTIXKSI labels an axis of a plot using SI multipliers S=(AX,AH)

 This routine is not intended to be called directly. See XTICKSI and YTICKSI.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function s=v_xyzticksi(ax,ah)
0002 %V_XYZTIXKSI labels an axis of a plot using SI multipliers S=(AX,AH)
0003 %
0004 % This routine is not intended to be called directly. See XTICKSI and YTICKSI.
0005 
0006 % Revision History
0007 %
0008 % 2018-09-21       Initial version
0009 % 2022-02-28       Fixed bug that could result in negative zero values as tick labels
0010 
0011 %       Copyright (C) Mike Brookes 2009-2022
0012 %      Version: $Id: v_xyzticksi.m 10865 2018-09-21 17:22:45Z dmb $
0013 %
0014 %   VOICEBOX is a MATLAB toolbox for speech processing.
0015 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
0016 %
0017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0018 %   This program is free software; you can redistribute it and/or modify
0019 %   it under the terms of the GNU General Public License as published by
0020 %   the Free Software Foundation; either version 2 of the License, or
0021 %   (at your option) any later version.
0022 %
0023 %   This program is distributed in the hope that it will be useful,
0024 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
0025 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0026 %   GNU General Public License for more details.
0027 %
0028 %   You can obtain a copy of the GNU General Public License from
0029 %   http://www.gnu.org/copyleft/gpl.html or by writing to
0030 %   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
0031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0032 
0033 % Bugs:
0034 %   (1) ipan=3 or 4 is not really debugged yet:
0035 %   (2) axis lengths incorrect for 3D graphs
0036 %   (3) should take account of axis shortening due to long labels at the ends
0037 %   (4) should calculate axis orentation from CameraPosition, CameraTarget and CameraUpVector
0038 %   (5) should allow a scale factor or, better, a mapping function converting from plot to axis coordinates
0039 if nargin<2
0040     ah=gca;
0041     if nargin<1
0042         ax=1;
0043     end
0044 end
0045 axfield={'XLim' 'YLim' 'ZLim'; 'XTick' 'YTick' 'ZTick'; 'XMinorTick' 'YMinorTick' 'ZMinorTick'; 'XTickLabel' 'YTickLabel' 'ZTickLabel'; 'XScale' 'YScale' 'ZScale'};
0046 tryglobal=nargout>0;
0047 digith=1;    % height of a digit in font units
0048 digitw=0.5;    % width of a digit in font units
0049 prefix={'y','z','a','f','p','n','μ','m','','k','M','G','T','P','E','Z','Y'};
0050 marg=[2 0.5 0.25 0.25];     % gap between labels in font units
0051 ntreq=[3 2 2 1];        % minimum number of labelled ticks required as a function of IPAN
0052 % grid template: each pair is [#steps final-value]. Start=10, end=100
0053 lgridtem={1; [1 20 1 50 1]; [1 20 4]; 9; [2 20 8]; [5 20 2 30 7]; [10 20 5 30 4 50 5]};
0054 ngrid=length(lgridtem);
0055 lgrid=cell(ngrid,1);
0056 agrid=zeros(ngrid,1);       % min and max ratio in decades
0057 % build the actual grid layouts
0058 for i=1:ngrid
0059     igridtem=[lgridtem{i} 100];
0060     igrid=zeros(1,sum(igridtem(1:2:end)));
0061     ntem=length(igridtem)/2;
0062     k=0;
0063     tick0=10;
0064     for j=1:ntem
0065         nstep=igridtem(2*j-1);
0066         igrid(k+1:k+nstep)=tick0+(0:nstep-1)*(igridtem(2*j)-tick0)/igridtem(2*j-1);
0067         k=k+nstep;
0068         tick0=igridtem(2*j);
0069     end
0070     agrid(i)=sum(log10([igrid(2:end) 100]./igrid).^2); % average log interval
0071     lgrid{i}=igrid';                 % grid positions
0072 end
0073 minsubsp=1;        % minimum subtick spacing (in font units)
0074 delcheck=[log10(2) log10(5) 2];     % check linear spacing
0075 delval=[1 2 5];
0076 dosubtick=0;         % default is not to allow subticks
0077 
0078 ngrid=length(lgrid);
0079 loggrid=cell(ngrid,1);
0080 for i=1:ngrid
0081     loggrid{i}=log10(lgrid{i})-1;
0082 end
0083 
0084 getgca=get(ah);
0085 set(ah,'Units','points','FontUnits','points');
0086 getgcac=get(ah);
0087 set(ah,'Units',getgca.Units,'FontUnits',getgca.FontUnits); % return to original values
0088 if ax==1
0089     widthc=getgcac.Position(3)/getgcac.FontSize;     % x axis length in font units
0090     axdir=[1 0];        % unit vector in axis direction
0091 else
0092     widthc=2*getgcac.Position(4)/getgcac.FontSize;     % y axis length in font units
0093     axdir=[0 1];        % unit vector in axis direction
0094 end
0095 axdir=max(abs(axdir),1e-10);        % avoid infinity problems
0096 a=getgca.(axfield{1,ax})(1);
0097 b=getgca.(axfield{1,ax})(2);
0098 
0099 ntick=0;
0100 tickint=[];                 % integer label
0101 tickdp=[];                  % tick decimal point position
0102 ticksi=[];                  % tick SI multiplier (always a multiple of 3)
0103 subtick=[];                 % sub tick positions
0104 if strcmp(getgca.(axfield{5,ax}),'log')   % log axis
0105     width10=widthc/log10(b/a); % fount units per decade
0106     ai3=3*ceil(log10(a)/3);             % lowest SI multiplier
0107     bi3=3*floor(log10(b)/3);            % highest SI multiplier
0108     if ai3>=-24 && bi3<=24              % do nothing if outside the SI multiplier range
0109         % first sort out if we can use a global SI multiplier
0110         if tryglobal && a>=10^(bi3-1)
0111             gi=bi3;
0112             s=prefix{9+gi/3};
0113             globalsi=1;                % global multiplier
0114         else
0115             gi=0;
0116             globalsi=0;                % disable global multiplier
0117             s='';
0118         end
0119         g=10^gi;
0120         ag=a/g;
0121         bg=b/g;
0122         al=log10(ag);
0123         bl=log10(bg);
0124         ai=ceil(al);
0125         bi=floor(bl);
0126         ai3=3*ceil(ai/3);
0127         bi3=3*floor(bi/3);
0128         for ipan=1:4                    % panic level: 1=spacious, 2=cramped, 3=any two labels, 4=any label
0129             % first try labelling only exact SI multiples
0130             margin=marg(ipan);
0131             incsi=3*ceil(min((2*digitw+margin)/axdir(1),(digith+margin)/axdir(2))/(3*width10));   % SI increment
0132             switch ipan
0133                 case {1,2}
0134                     ticksi=incsi*ceil(ai/incsi):incsi:incsi*floor(bi/incsi);
0135                 case {3,4}
0136                     ticksi=ai3:incsi:bi3;
0137             end
0138             ntick=length(ticksi);
0139             tickint=ones(1,ntick);
0140             tickdp=zeros(1,ntick);
0141             if width10>0.25
0142                 ticki=ai:bi;
0143                 subtick=10.^(ticki(ticki~=3*fix(ticki/3)));      % put subticks at all powers of 10;
0144             end
0145             if incsi==3         % no point in trying anything else if incsi>3
0146                 ci=floor(al);   % start of first decade that includes the start of the axis
0147                 cibi=ci:bi;     % ennumerate the decades that cover the entire axis
0148                 ndec=bi-ci+1;   % number of decades
0149                 if globalsi
0150                     siq0=zeros(1,ndec); % no SI multipliers within the labels if using a global multiplier
0151                 else
0152                     siq0=3*floor((cibi)/3); % determine the SI multiplier for each of the decades
0153                 end
0154                 siw0=siq0~=0;                % width of SI multiplier
0155                 dpq0=max(siq0-cibi+1,1);    % number of decimal places
0156                 for jgrid=1:ngrid
0157                     igrid=jgrid-(ipan<=2)*(2*jgrid-ngrid-1);
0158                     lgridi=lgrid{igrid};
0159                     ngridi=length(lgridi);
0160                     intq=reshape(repmat(lgridi,1,ndec).*repmat(10.^(cibi+dpq0-siq0-1),ngridi,1),1,[]);
0161                     dpq=reshape(repmat(dpq0,ngridi,1),1,[]);
0162                     msk=dpq>0 & rem(intq,10)==0;
0163                     intq(msk)=intq(msk)/10;
0164                     dpq(msk)=dpq(msk)-1;
0165                     widq=1+floor(log10(intq));
0166                     widq=digitw*(widq+(dpq>0).*max(1,dpq+2-widq)+reshape(repmat(siw0,ngridi,1),1,[]));
0167                     logvq=reshape(repmat(loggrid{igrid},1,ndec)+repmat(ci:ndec+ci-1,ngridi,1),1,[]);
0168                     % mask out any ticks outside the axis range
0169                     msk=logvq>=al & logvq<=bl;
0170                     widq=widq(msk);
0171                     logvq=logvq(msk);
0172                     % debug1=[intq(msk); -1 min((widq(1:end-1)+widq(2:end)+2*margin)/axdir(1),2*(digith+margin)/axdir(2))<=2*width10*(logvq(2:end)-logvq(1:end-1))];
0173                     if numel(widq)>=ntreq(ipan) && all(min((widq(1:end-1)+widq(2:end)+2*margin)/axdir(1),2*(digith+margin)/axdir(2))<=2*width10*(logvq(2:end)-logvq(1:end-1)))
0174                         % success: we have an acceptable pattern
0175                         ntick=numel(widq);       % number of ticks
0176                         tickint=intq(msk);      % integer label value (i.e. neglecting decimal point)
0177                         tickdp=dpq(msk);        % number of decimal places
0178                         siq=reshape(repmat(siq0,ngridi,1),1,[]);    % SI mltiplier power
0179                         ticksi=siq(msk);        % SI multiplier power masked
0180                         subtick=[];             % recalculate any subticks
0181                         dosubtick=igrid>1;
0182                         break;                  % do not try any more grid patterns
0183                     end
0184                 end % alternative grid patterns
0185                 % finally just try a linear increment
0186                 if ntick<5
0187                     ldeltamin=log10(bg- bg*10^(-min((digitw+margin)/axdir(1),(digith+margin)/axdir(2))/width10));  % smallest conceivable increment
0188                     ildelta=floor(ldeltamin);
0189                     ix=find(ldeltamin-ildelta<=delcheck,1);
0190                     jx=ildelta*3+ix;
0191                     while 1
0192                         deltax=floor(jx/3);
0193                         deltav=delval(jx-3*deltax+1);
0194                         delta=deltav*10^deltax;
0195                         multq=ceil(ag/delta):floor(bg/delta);   % multiples of delta to display
0196                         ntickq=numel(multq);
0197                         if ntickq<=ntick || ntickq<ntreq(ipan)   % give up now
0198                             break;
0199                         end
0200                         intq=deltav*multq;
0201                         lintq=floor(log10(intq));
0202                         siq=3*floor((lintq+deltax)/3);      % SI multiplier
0203                         dpq=siq-deltax;
0204                         msk=dpq<0;
0205                         intq(msk)=intq(msk).*10.^(-dpq(msk));
0206                         dpq(msk)=0;
0207                         msk=rem(intq,10)==0 & dpq>0;
0208                         while any(msk)      % remove unwanted trailing zeros
0209                             dpq(msk)=dpq(msk)-1;
0210                             intq(msk)=intq(msk)/10;
0211                             msk=rem(intq,10)==0 & dpq>0;
0212                         end
0213                         widq=1+floor(log10(intq));
0214                         widq=digitw*(widq+(dpq>0).*max(1,dpq+2-widq)+(siq~=0));
0215                         logvq=log10(multq)+log10(deltav)+deltax;
0216                         % debug2=[intq; widq; -1 (widq(1:end-1)+widq(2:end)+2*margin)<=2*width10*(logvq(2:end)-logvq(1:end-1))];
0217                         if all(min((widq(1:end-1)+widq(2:end)+2*margin)/axdir(1),2*(digith+margin)/axdir(2))<=2*width10*(logvq(2:end)-logvq(1:end-1)))
0218                             ntick=ntickq;
0219                             tickint=intq;
0220                             tickdp=dpq;
0221                             ticksi=siq;
0222                             dosubtick=1;
0223                             break
0224                         end
0225                         jx=jx+1;                            % try next coarser spacing
0226                     end
0227                 end
0228             end % try grid patterns since at most one exact SI multiple
0229             if ntick>=ntreq(ipan)
0230                 break% quit if we have enough labels
0231             end
0232         end% try next panic level
0233     end    % check if within SI range
0234     if ntick
0235         tickint(tickint==0)=0; % get rid of negative zeros
0236         tickexp=gi+ticksi-tickdp;
0237         tickpos=tickint .* 10.^tickexp;
0238         ratthresh=10^(minsubsp/width10);   % min subtick ratio
0239         if dosubtick       % check for subticks
0240             subtick=[];
0241             if ntick>1      % at least two labelled ticks
0242                 stepexp=min(tickexp(1:end-1),tickexp(2:end))-1;
0243                 stepint=round((tickpos(2:end)-tickpos(1:end-1)).*10.^(-stepexp));  % always a multiple of 10
0244                 stepleft=tickint(1:end-1).*10.^(tickexp(1:end-1)-stepexp); % leftmost label in units of 10^stepexp
0245                 subbase=10.^ceil(log10(stepint)-1); % base sub-tick interval in units of 10^stepexp
0246                 substep=[-1 -3 5]*((1+[1; 2; 5]*(subbase./stepleft))>ratthresh); % actual step is 1,2 or 5 times subbase
0247                 substep(stepint~=10*substep)=max(2-substep(stepint~=10*substep),0); % but only >1 if stepint==10
0248                 substep=substep.*subbase; % subtick step in units of 10^stepexp
0249                 for i=1:ntick-1
0250                     ss=substep(i);
0251                     sl=stepleft(i);
0252                     if ss
0253                         subtick=[subtick (sl+(ss:ss:stepint(i)-ss))*10^stepexp(i)];
0254                         if i==1 && sl/(sl-ss)>ratthresh
0255                             subtick=[subtick (sl-(ss:ss:floor((tickpos(1)-a)/(ss*10^stepexp(i)))*ss))*10^stepexp(i)];
0256                         elseif i==ntick-1 && (1+ss/(sl+stepint(1)))>ratthresh
0257                             subtick=[subtick (sl+stepint(i)+(ss:ss:floor((b-tickpos(end))/(ss*10^stepexp(i)))*ss))*10^stepexp(i)];
0258                         end
0259                     end
0260                 end
0261             end
0262         end % if subtick
0263         [tps,ix]=sort([tickpos subtick]);
0264         nticks=length(tps);
0265         ticklab=cell(nticks,1);
0266         for j=1:nticks
0267             i=ix(j);
0268             if i>ntick
0269                 ticklab{j}='';
0270             else
0271                 ticklab{j}=sprintf(sprintf('%%.%df%%s',tickdp(i)),tickint(i)*10^(-tickdp(i)),prefix{ticksi(i)/3+9});
0272             end
0273         end
0274         if width10<2.5
0275             set(ah,axfield{3,ax},'off');
0276         end
0277         set(ah,axfield{2,ax},tps);
0278         set(ah,axfield{4,ax},ticklab);
0279     end
0280 
0281 else                    % linear axis
0282     for ipan=1:4                    % panic level: 1=spacious, 2=cramped, 3=any two labels, 4=any label
0283         margin=marg(ipan);
0284         % select a global multiplier
0285         if tryglobal
0286             gi=3*floor(log10(max(abs(a),abs(b)))/3);
0287             s=prefix{9+gi/3};
0288 
0289         else
0290             gi=0;
0291             s='';
0292         end
0293         g=10^gi;
0294         ag=a/g;
0295         bg=b/g;
0296         width1=widthc/(bg-ag);                  % width of 1 plot unit in font units
0297         ldeltamin=log10(min((digitw+margin)/axdir(1),(digith+margin)/axdir(2))/width1);        % log of smallest conceivable increment
0298         ildelta=floor(ldeltamin);
0299         ix=find(ldeltamin-ildelta<=delcheck,1);
0300         jx=ildelta*3+ix;
0301         while 1 % loop trying increasingly coarse labelling
0302             deltax=floor(jx/3);         % exponent of label increment
0303             deltav=delval(jx-3*deltax+1); % mantissa of label increment
0304             delta=deltav*10^deltax;         % actual label inrement
0305             multq=ceil(ag/delta):floor(bg/delta);   % multiples of delta to display
0306             ntickq=numel(multq);
0307             if ntickq<ntreq(ipan)   % give up now if too few labels
0308                 break;
0309             end
0310             intq=deltav*multq;
0311             lintq=floor(log10(abs(intq)+(intq==0)));
0312             siq=3*floor((lintq+deltax)/3)*~tryglobal;      % SI multiplier, but only if no global multiplier
0313             dpq=siq-deltax;
0314             msk=dpq<0;
0315             intq(msk)=intq(msk).*10.^(-dpq(msk));
0316             dpq(msk)=0;
0317             msk=rem(intq,10)==0 & dpq>0;
0318             while any(msk)      % remove unwanted trailing zeros
0319                 dpq(msk)=dpq(msk)-1;
0320                 intq(msk)=intq(msk)/10;
0321                 msk=rem(intq,10)==0 & dpq>0;
0322             end
0323             widq=1+floor(log10(abs(intq)+(intq==0)));
0324             widq=digitw*(widq+(dpq>0).*max(1,dpq+2-widq)+(siq~=0).*(intq~=0)+(intq<0)); % calculate width of each label
0325             % debug2=[intq; widq; digith+margin<=axdir(2)*width1*delta (widq(1:end-1)+widq(2:end)+2*margin)<=2*width1*delta];
0326             if all((widq(1:end-1)+widq(2:end)+2*margin)<=2*axdir(1)*width1*delta) || (digith+margin<=axdir(2)*width1*delta);
0327                 ntick=ntickq;
0328                 tickint=intq;
0329                 tickdp=dpq;
0330                 ticksi=siq;
0331                 if deltav>1 && width1*delta>0.5*deltav          % put explicit subticks if delta = 2 or 5
0332                     mults=ceil(ag*deltav/delta):floor(bg*deltav/delta);
0333                     subtick=(mults(deltav*fix(mults/deltav)~=mults))*delta/deltav;
0334                 else
0335                     subtick=[];
0336                 end
0337                 break                       % do not try any more coarser spacings
0338             end
0339             jx=jx+1;                            % try next coarser spacing
0340         end
0341         if ntick>=ntreq(ipan)
0342             break% quit if we have enough labels
0343         end
0344     end
0345     if ntick
0346         tickint(tickint==0)=0; % get rid of negative zeros
0347         tickexp=gi+ticksi-tickdp;
0348         tickpos=tickint .* 10.^tickexp;
0349         [tps,ix]=sort([tickpos subtick*10^gi]);
0350         nticks=length(tps);
0351         ticklab=cell(nticks,1);
0352         for j=1:nticks
0353             i=ix(j);
0354             if i>ntick
0355                 ticklab{j}='';
0356             else
0357                 if tickint(i)==0
0358                     ticklab{j}=sprintf(sprintf('%%.%df%%s',tickdp(i)),0,prefix{(ticksi(i)/3)*(tickint(i)~=0)+9});
0359                 else
0360                     ticklab{j}=sprintf(sprintf('%%.%df%%s',tickdp(i)),tickint(i)*10^(-tickdp(i)),prefix{(ticksi(i)/3)*(tickint(i)~=0)+9});
0361                 end
0362             end
0363         end
0364         set(ah,axfield{2,ax},tps);
0365         set(ah,axfield{4,ax},ticklab);
0366         set(ah,axfield{3,ax},'on');
0367     end
0368 end

Generated by m2html © 2003