gusucode.com > 信号处理工具箱 - signal源码程序 > signal\signal\siggui\panfcn.m
function varargout = panfcn(varargin) %PANFCN Pan Function for Axes and Signal GUI % Usage: call this function either as the buttondownfcn of the lines in % an axes that you want to pan, or as the windowbuttondownfcn when you % click inside an axes. % Inputs are in param/value pairs % 'Ax' axes to pan, default: gca % 'Bounds' structure with .xlim and .ylim - panning will not % ever leave these bounds. default: no bounds % 'DirectFlag' 1 ==> drag in main axes, 0 ==> drag in panner. default = 1 % 'borderAxes' axes whose perimeter is one pixel outside of 'Axes' - % if not defined, the 'Axes' will get erased if the erasemode % of the lines is background. % 'PannerPatch' handle to pannerpatch - if [], don't update. % Defaults to []. % 'DynamicDrag' 1 ==> update lines on the fly (default), % 0 ==> update panner patch only % 'Data' structure array with fields % .h vector of handles to lines % .data matrix of data for the lines (in columns) % .xdata if this is present, it must be the same size as .data, and % the entire line will be transformed into the current limits; % if not present, a linearly % spaced xdata will be assumed, and during the dynamic pan, the % xdata and ydata of the lines will be updated in such a way % as to only display what is between the current xlimits % defaults to all the lines in the axes % 'Transform' function string - applied with feval before setting ydata % defaults to '' % 'Immediate' 1==>switch to border Axes immediately (default) % 0==> " " " " on mouse motion only % 'Invisible' vector of handles to hide and restore on exit % 'UserHand' handle to object to use for userdata - defaults to Ax % 'EraseMode' used for lines during panning, should be 'xor' or % 'background', defaults to 'background' % 'Pointer' used during drag - defaults to 'closedhand' % 'InterimPointer' used prior to drag in case Immediate == 0; defaults % to nothing (doesn't change). This is a string which is passed % to setptr. % OUTPUTS: % returns 1 if limits changed, 0 else % Copyright (c) 1988-98 by The MathWorks, Inc. % $Revision: 1.1 $ % define default values: ax = []; bounds = []; directflag = 1; borderaxes = []; pannerpatch = []; panneraxes = []; % parent of pannerpatch dynamicdrag = 1; data = []; transform = ''; immediate = 1; invisible = []; userhand = []; erasemode = 'background'; pointer = 'closedhand'; interimpointer = ''; % parse parameters - could use some error checking for i=1:2:length(varargin) param = lower(varargin{i}); value = varargin{i+1}; eval([param ' = value;']) end % finish default value setting if isempty(ax) ax = gca; end fig = get(ax,'parent'); ptr = getptr(fig); if ~isempty(interimpointer) setptr(fig,interimpointer) end if isempty(userhand) userhand = ax; end if ~isempty(pannerpatch) panneraxes = get(pannerpatch,'parent'); end % initialize "data" structure array from axes line objects here. % This costly computation will be skipped if you pass in the "data" % structure array yourself. % This array has fields: % .data - matrix % .columns - index vector; indicates which columns of .data are % actually displayed as lines % .h - line handles - one per column of .data(:, data.columns) % .xdata - either the same size as .data or empty if .Fs >0 % .Fs - scalar; -1 indicates xdata matrix contains x information, % if >0, xdata is created evenly spaced % .t0 - scalar; used with .Fs to compute xdata if isempty(data) h = findobj(ax,'type','line'); h = setdiff(h,invisible); if isempty(h) setptr(fig,ptr) return end for i=1:length(h) data(i).h = h(i); data(i).data = get(h(i),'ydata'); data(i).data = data(i).data(:); % make into column data(i).columns = 1; data(i).xdata = get(h(i),'xdata'); data(i).xdata = data(i).xdata(:); % make into column xdf=diff(data(i).xdata); % see if this line is evenly spaced if length(xdf)>1 & all(xdf==xdf(1)) & xdf(1)~=0 data(i).t0 = data(i).xdata(1); data(i).Fs = 1./xdf(1); else data(i).t0 = 0; data(i).Fs = -1; end end end save_userhand_tag = get(userhand,'tag'); set(userhand,'tag','userhand') % get current point of mouse click if directflag p = get(ax,'currentpoint'); else p = get(panneraxes,'currentpoint'); end p = p(1,1:2); np = p; if immediate [oldptr,save_visible] = start_motion(fig,ax,borderaxes,... data,erasemode,invisible,pointer); end xlim = get(ax,'xlim'); ylim = get(ax,'ylim'); % To get fast panning without axes redrawing, we never change % the xlim and ylim of ax during the dragging. Instead, we % set the xdata and ydata of the lines during dragging and % set the xlim and ylim when we are done. actual_xlim = xlim; actual_ylim = ylim; logscale = [strcmp(get(ax,'xscale'),'log') strcmp(get(ax,'yscale'),'log')]; save_callBacks = ... installCallbacks(userhand,fig,... {'windowbuttonmotionfcn', 'windowbuttonupfcn'},{'motion', 'up'}); done = 0; moved = 0; while ~done event = waitForNextEvent(userhand); switch event case 'motion' if ~moved, if ~immediate [oldptr,save_visible] = start_motion(fig,ax,borderaxes,... data,erasemode,invisible,pointer); end moved = 1; end p = np; [xlim,ylim,oxlim,oylim,np] = ... draglims(directflag,ax,panneraxes,xlim,ylim,bounds,p,logscale); if ~isequal([xlim ylim],[oxlim oylim]) if ~isempty(pannerpatch) setpdata(pannerpatch,xlim,ylim) end if dynamicdrag doDynamicDrag(xlim,ylim,actual_xlim,actual_ylim,.... data,transform) end end case 'up' done = 1; end end if immediate | moved p = np; [xlim,ylim,oxlim,oylim,np] = ... draglims(directflag,ax,panneraxes,xlim,ylim,bounds,p,logscale); stop_motion(ax,borderaxes,data,invisible,save_visible) if dynamicdrag & moved % need to restore x and y data for i=1:length(data) if data(i).Fs > 0 xdata = data(i).t0 + (0:size(data(i).data,1)-1)/data(i).Fs; else xdata = data(i).xdata; end for j=1:length(data(i).columns) y = data(i).data(:,data(i).columns(j)); if ~isempty(transform) y = feval(transform,y); end set(data(i).h(j),'xdata',xdata,'ydata',y) end end end set(ax,'xlim',xlim,'ylim',ylim) set(fig,oldptr{:}) end set(fig,{'windowbuttonmotionfcn' 'windowbuttonupfcn'},save_callBacks) set(userhand,'tag',save_userhand_tag) if nargout>0 varargout{1} = moved; end set(fig,ptr{:}) return %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [oldptr,save_visible] = start_motion(fig,ax,borderaxes,... data,erasemode,invisible,pointer) oldptr = getptr(fig); setptr(fig,pointer) switchToBorderAxes(ax,borderaxes) for i=1:length(data) set(data(i).h,'erasemode',erasemode) end save_visible = get(invisible,{'visible'}); set(invisible,'visible','off') function stop_motion(ax,borderaxes,data,invisible,save_visible) for i=1:length(data) set(data(i).h,'erasemode','normal') end set(invisible,{'visible'},save_visible) switchToMainAxes(ax,borderaxes) function [xlim,ylim,oxlim,oylim,np] = draglims(directflag,ax,... panneraxes,xlim,ylim,bounds,p,logscale); % This function takes the current point information and the old limits, % and calculates the new xlims and ylims. if directflag np = get(ax,'currentpoint'); % new point else np = get(panneraxes,'currentpoint'); % new point end np = np(1,1:2); if any(isnan(np)), np = p; end oxlim = xlim; oylim = ylim; if directflag % drag line in main axes if logscale(1) xlim = 10.^ (log10(oxlim) - log10(np(1)/p(1))); else xlim = oxlim - (np(1)-p(1)); end if logscale(2) ylim = 10.^ (log10(oylim) - log10(np(2)/p(2))); else ylim = oylim - (np(2)-p(2)); end else % drag panner patch if logscale(1) xlim = 10.^ (log10(oxlim) + log10(np(1)/p(1))); else xlim = oxlim + (np(1)-p(1)); end if logscale(2) ylim = 10.^ (log10(oylim) + log10(np(2)/p(2))); else ylim = oylim + (np(2)-p(2)); end end if ~isempty(bounds) xlim1 = inbounds(xlim,bounds.xlim,logscale(1)); ylim1 = inbounds(ylim,bounds.ylim,logscale(2)); if ~isequal(xlim,xlim1) if directflag if logscale(1) np(1) = 10.^( log10(np(1)) - log10(xlim1(1)/xlim(1)) ); else np(1) = np(1)-(xlim1(1)-xlim(1)); end else if logscale(1) np(1) = 10.^( log10(np(1)) + log10(xlim1(1)/xlim(1)) ); else np(1) = np(1)+(xlim1(1)-xlim(1)); end end xlim = xlim1; end if ~isequal(ylim,ylim1) if directflag if logscale(2) np(2) = 10.^( log10(np(2)) - log10(ylim1(1)/ylim(1)) ); else np(2) = np(2)-(ylim1(1)-ylim(1)); end else if logscale(1) np(2) = 10.^( log10(np(2)) + log10(ylim1(1)/ylim(1)) ); else np(2) = np(2)+(ylim1(1)-ylim(1)); end end ylim = ylim1; end end function saveCallbacks = installCallbacks(h,fig,callbackList,valueList) % installCallbacks % inputs: % h - handle of object which will be changed by callbacks % fig - handle of figure % callbackList - list of figure callbacks in cell array % elements are e.g., 'windowbuttonmotionfcn' % valueList - same length as callbackList - cell array containing % values (string or numeric) for h's userdata % outputs: % saveCallbacks - cellarray of what the callbacks were before saveCallbacks = cell(1,length(callbackList)); for i=1:length(callbackList) if isstr(valueList{i}) vstr = ['''' valueList{i} '''']; else vstr = num2str(valueList{i}); end if 0, % if problems with fig not being gcf, set this to 1 figstr = ['hex2num(''' sprintf('%bx',h) ''')']; else figstr = 'gcf'; end str = ['set(findall(' figstr ',' ... '''tag'',''' get(h,'tag') '''),''userdata'',' ... vstr ')']; saveCallbacks{i} = get(fig,callbackList{i}); set(fig,callbackList{i},str) end % function event = waitForNextEvent(h) % waitForNextEvent set(h,'userdata',0) waitfor(h,'userdata') event = get(h,'userdata'); function switchToBorderAxes(ax,borderaxes) % This function hides the main axes and shows the border axes. % IF borderaxes is NOT EMPTY: % It hides the main axes by setting its x and ycolor to the % color of the axes, instead of turning the mainaxes invisible - % this is because if we just set visible to off, any lines % in background erasemode would erase in the FIGURE's background % color, not the axes' (and that would be bad). % ELSE % Just turns off the axes ticks. if ~isempty(borderaxes) ax_color = get(ax,'color'); if isstr(ax_color) % use figure's color in case axes's color is 'none' ax_color = get(get(ax,'parent'),'color'); end set(ax,'xcolor',ax_color,'ycolor',ax_color) set(get(ax,'xlabel'),'color',get(borderaxes,'xcolor')) set(get(ax,'ylabel'),'color',get(borderaxes,'ycolor')) set(borderaxes,'visible','on') end set(ax,'xtick',[],'ytick',[]) function switchToMainAxes(ax,borderaxes) % This function hides the border axes and shows the main axes. % It undoes what switchToBorderAxes does. % Assumes xcolor and ycolor of ax should be the same as those of borderaxes if ~isempty(borderaxes) set(ax,'xcolor',get(borderaxes,'xcolor'),... 'ycolor',get(borderaxes,'ycolor')) set(borderaxes,'visible','off') end set(ax,'xtickmode','auto','ytickmode','auto') function doDynamicDrag(xlim,ylim,actual_xlim,actual_ylim,data,... transform) % needed by setDataWithTransform: xl = [xlim(:)' actual_xlim]; yl = [ylim(:)' actual_ylim]; hh=[]; xx={}; yy={}; for i=1:length(data) if data(i).Fs>0 % translate interval to integer indices xlim1 = xlim*data(i).Fs + (1-data(i).Fs*data(i).t0); xlim1 = [floor(xlim1(1)) ceil(xlim1(2))]; ind = max(1,xlim1(1)):min(xlim1(2),size(data(i).data,1)); if ~isempty(ind) if ind(1) == 0, ind(1) = []; end x = (ind-1)/data(i).Fs + data(i).t0; for j = 1:length(data(i).h) y = data(i).data(ind,j); if ~isempty(transform) y = feval(transform,y); end hh = [hh; data(i).h(j)]; xx = {xx{:} x}; yy = {yy{:} y}; end else hh = [hh; data(i).h(:)]; for j = 1:length(data(i).h) xx = {xx{:} []}; yy = {yy{:} []}; end end else for j = 1:length(data(i).h) x = data(i).xdata(:,j); y = data(i).data(:,j); if ~isempty(transform) y = feval(transform,y); end hh = [hh; data(i).h(j)]; xx = {xx{:} x}; yy = {yy{:} y}; end end end % wait until end to set data, so all lines are erased, and THEN % redrawn. This prevents background erasures from erasing other % previously drawn lines. setDataWithTransform(hh,xl,yl,xx',yy') function setDataWithTransform(h,xlim,ylim,xd,yd) %setDataWithTransform Set xdata and ydata of lines transforming from one % set of limits to another. % Inputs: % h - vector of line handles % xlim, ylim - limits of mainaxes % if xlim and ylim are 4 elements long, uses the 3rd and 4th % elements as the interval in which to map the data. % xd - vector or cell array. Each element of the cell % will get mapped from xlim to [0 1] or xlim(3:4) % yd - vector or cell array. Each element of the cell % will get mapped from ylim to [0 1] or ylim(3:4) if length(xlim)<=2 xslope = 1/diff(xlim); xintercept = -xlim(1)*xslope; else xslope = (xlim(4)-xlim(3))/(xlim(2)-xlim(1)); xintercept = xlim(3)-xlim(1)*xslope; end if length(ylim)<=2 yslope = 1/diff(ylim); yintercept = -ylim(1)*yslope; else yslope = (ylim(4)-ylim(3))/(ylim(2)-ylim(1)); yintercept = ylim(3)-ylim(1)*yslope; end if ~iscell(xd) xd = {xd}; end if ~iscell(yd) yd = {yd}; end for j=1:length(h) xd{j} = xslope*xd{j}+xintercept; yd{j} = yslope*yd{j}+yintercept; end set(h,{'xdata'},xd,{'ydata'},yd) function setpdata(panpatch,xlim,ylim) %setpdata - set x and ydata of patch object to rectangle specified by % xlim and ylim input set(panpatch,'xdata',[xlim(1) xlim(2) xlim(2) xlim(1) xlim(1)], ... 'ydata',[ylim(1) ylim(1) ylim(2) ylim(2) ylim(1)]) % thumb patch