function out = hhsim_zoom(varargin) %ZOOM Zoom in and out on a 2-D plot. % ZOOM with no arguments toggles the zoom state. % ZOOM(FACTOR) zooms the current axis by FACTOR. % Note that this does not affect the zoom state. % ZOOM ON turns zoom on for the current figure. % ZOOM XON or ZOOM YON turns zoom on for the x or y axis only. % ZOOM OFF turns zoom off in the current figure. % % ZOOM RESET resets the zoom out point to the current zoom. % ZOOM OUT returns the plot to its current zoom out point. % If ZOOM RESET has not been called this is the original % non-zoomed plot. Otherwise it is the zoom out point % set by ZOOM RESET. % % When zoom is on, click the left mouse button to zoom in on the % point under the mouse. Each time you click, the axes limits will be % changed by a factor of 2 (in or out). You can also click and drag % to zoom into an area. It is not possible to zoom out beyond the plots' % current zoom out point. If ZOOM RESET has not been called the zoom % out point is the original non-zoomed plot. If ZOOM RESET has been % called the zoom out point is the zoom point that existed when it % was called. Double clicking zooms out to the current zoom out point - % the point at which zoom was first turned on for this figure % (or to the point to which the zoom out point was set by ZOOM RESET). % Note that turning zoom on, then off does not reset the zoom out point. % This may be done explicitly with ZOOM RESET. % % ZOOM(FIG,OPTION) applies the zoom command to the figure specified % by FIG. OPTION can be any of the above arguments. % % H = ZOOM(FIG) returns the figure's zoom mode object for customization. % The following properties can be modified using set/get: % % ButtonDownFilter % The application can inhibit the zoom operation under circumstances % the programmer defines, depending on what the callback returns. % The input function handle should reference a function with two % implicit arguments (similar to handle callbacks): % % function [res] = myfunction(obj,event_obj) % % OBJ handle to the object that has been clicked on. % % EVENT_OBJ handle to event object (empty in this release). % % RES a logical flag to determine whether the zoom % operation should take place or the % 'ButtonDownFcn' property of the object should % take precedence. % % ActionPreCallback % Set this callback to listen to when a zoom operation will start. % The input function handle should reference a function with two % implicit arguments (similar to handle callbacks): % % function myfunction(obj,event_obj) % % OBJ handle to the figure that has been clicked on. % % EVENT_OBJ handle to event object. % % The event object has the following read only % property: % Axes The handle of the axes that is being zoomed. % % ActionPostCallback % Set this callback to listen to when a zoom operation has finished. % The input function handle should reference a function with two % implicit arguments (similar to handle callbacks): % % function myfunction(obj,event_obj) % % OBJ handle to the figure that has been clicked on. % % EVENT_OBJ handle to event object. The object has the same % properties as the EVENT_OBJ of the % 'ModePreCallback' callback. % % Enable 'on'|{'off'} % Specifies whether this figure mode is currently % enabled on the figure. % % FigureHandle % The associated figure handle. This property supports GET only. % % Motion 'horizontal'|'vertical'|{'both'} % The type of zooming for the figure. % % Direction {'in'}|'out' % The direction of the zoom operation. % % RightClickAction 'InverseZoom'|{'PostContextMenu'} % The behavior of a right-click action. A value of 'InverseZoom' % will cause a right-click to zoom out. A value of 'PostContextMenu' % will display a context menu. This setting will persist between % MATLAB sessions. % % UIContextMenu % Specifies a custom context menu to be displayed during a % right-click action. This property is ignored if the % 'RightClickAction' property has been set to 'InverseZoom'. % % FLAGS = isAllowAxesZoom(H,AXES) % Calling the function ISALLOWAXESZOOM on the zoom object, H, with a % vector of axes handles, AXES, as input will return a logical array % of the same dimension as the axes handle vector which indicate % whether a zoom operation is permitted on the axes objects. % % setAllowAxesZoom(H,AXES,FLAG) % Calling the function SETALLOWAXESZOOM on the zoom object, H, with % a vector of axes handles, AXES, and a logical scalar, FLAG, will % either allow or disallow a zoom operation on the axes objects. % % INFO = getAxesZoomMotion(H,AXES) % Calling the function GETAXESZOOMMOTION on the zoom object, H, with % a vector of axes handles, AXES, as input will return a character % cell array of the same dimension as the axes handle vector which % indicates the type of zoom operation for each axes. Possible values % for the type of operation are 'horizontal', 'vertical' or 'both'. % % setAxesZoomMotion(H,AXES,STYLE) % Calling the function SETAXESZOOMMOTION on the zoom object, H, with a % vector of axes handles, AXES, and a character array, STYLE, will % set the style of zooming on each axes. % % EXAMPLE 1: % % plot(1:10); % zoom on % % zoom in on the plot % % EXAMPLE 2: % % plot(1:10); % h = zoom; % set(h,'Motion','horizontal','Enable','on'); % % zoom in on the plot in the horizontal direction. % % EXAMPLE 3: % % ax1 = subplot(2,2,1); % plot(1:10); % h = zoom; % ax2 = subplot(2,2,2); % plot(rand(3)); % setAllowAxesZoom(h,ax2,false); % ax3 = subplot(2,2,3); % plot(peaks); % setAxesZoomMotion(h,ax3,'horizontal'); % ax4 = subplot(2,2,4); % contour(peaks); % setAxesZoomMotion(h,ax4,'vertical'); % % zoom in on the plots. % % EXAMPLE 4: (place in an m-file) % % function demo % % Allow a line to have its own 'ButtonDownFcn' callback. % hLine = plot(rand(1,10)); % set(hLine,'ButtonDownFcn','disp(''This executes'')'); % set(hLine,'Tag','DoNotIgnore'); % h = zoom; % set(h,'ButtonDownFilter',@mycallback); % set(h,'Enable','on'); % % mouse click on the line % % function [flag] = mycallback(obj,event_obj) % % If the tag of the object is 'DoNotIgnore', then return true. % objTag = get(obj,'Tag'); % if strcmpi(objTag,'DoNotIgnore') % flag = true; % else % flag = false; % end % % Example 5: (place in an m-file) % % function demo % % Listen to zoom events % plot(1:10); % h = zoom; % set(h,'ActionPreCallback',@myprecallback); % set(h,'ActionPostCallback',@mypostcallback); % set(h,'Enable','on'); % % function myprecallback(obj,evd) % disp('A zoom is about to occur.'); % % function mypostcallback(obj,evd) % newLim = get(evd.Axes,'XLim'); % msgbox(sprintf('The new X-Limits are [%.2f %.2f].',newLim)); % % Use LINKAXES to link zooming across multiple axes. % % See also PAN, ROTATE3D, LINKAXES. % % Copyright 1993-2006 The MathWorks, Inc. % Internal use undocumented syntax (this may be removed in a future % release) % Additional syntax not already dead in zoom m-help % % ZOOM(FIG,'UIContextMenu',...) % Specify UICONTEXTMENU for use in zoom mode % ZOOM(FIG,'Constraint',...) % Specify constrain option: % 'none' - No constraint (default) % 'horizontal' - Horizontal zoom only for 2-D plots % 'vertical' - Vertical zoom only for 2-D plots % ZOOM(FIG,'Direction',...) % Specify zoom direction 'in' or 'out' % OUT = ZOOM(FIG,'IsOn') % Returns true if zoom is on, otherwise returns false. % OUT = ZOOM(FIG,'Constraint') % Returns 'none','horizontal', or 'vertical' % OUT = ZOOM(FIG,'Direction') % Returns 'in' or 'out' % Undocumented syntax that will never get documented % (but we have to keep it around for legacy reasons) % OUT = ZOOM(FIG,'getmode') 'in'|'out'|'off' % % Note: zoom uses the figure buttondown and buttonmotion functions % % ZOOM XON zooms x-axis only % ZOOM YON zooms y-axis only % ZOOM v6 off Switches to new zoom implementation % ZOOM v6 on Switches to old zoom implementation % ZOOM FILL scales a plot such that it is as big as possible % within the axis position rectangle for any azimuth and elevation. % Undocumented switch to v6 zoom implementation. This will be removed. if nargin==2 && ... ischar(varargin{1}) && ... strcmp(varargin{1},'v6') && ... ischar(varargin{2}) if strcmp(varargin{2},'on') localSetV6Zoom(true); else localSetV6Zoom(false); end return; end % Bypass to v6 zoom if localIsV6Zoom if nargout==0 v6_zoom(varargin{:}); else out = v6_zoom(varargin{:}); end return; end % Parse input arguments [target,action,action_data] = localParseArgs(nargout,varargin{:}); % Return early if target is not an axes or figure if isempty(target) || ... (~isa(target,'hg.axes') && ~isa(target,'hg.figure')) return; end hFigure = ancestor(target,'hg.figure'); if isa(target,'hg.axes') set(hFigure,'CurrentAxes',target); end % Return early if setting zoom off and there's no app data % this avoids making any objects or setting app data when % it doesn't need to. For example, hgload calls zoom(fig,'off') appdata = getappdata(hFigure,'ZoomOnState'); if strcmp(action,'off') && isempty(appdata); return; end % Get the mode object hMode = locGetMode(hFigure); % Update zoom target in case it changed hMode.ModeStateData.target = target; % Get current axes if ~ishandle(hFigure) return; end hCurrentAxes = get(hFigure,'CurrentAxes'); if ~isempty(hCurrentAxes) b = hggetbehavior(hCurrentAxes,'Zoom','-peek'); if ~isempty(b) && ishandle(b) && ~get(b,'Enable') hCurrentAxes = []; elseif isappdata(hCurrentAxes,'NonDataObject') hCurrentAxes = []; end end % Parse various zoom options change_ui = []; switch lower(action) case 'on' hMode.ModeStateData.Constraint = 'none'; change_ui = 'on'; case 'xon' hMode.ModeStateData.Constraint = 'horizontal'; change_ui = 'on'; case 'yon' hMode.ModeStateData.Constraint = 'vertical'; change_ui = 'on'; case 'getmode' if localIsZoomOn(hMode) out = hMode.ModeStateData.Direction; else out = 'off'; end case 'constraint' out = hMode.ModeStateData.Constraint; case 'direction' out = hMode.ModeStateData.Direction; case 'ison' out = localIsZoomOn(hMode); case 'getstyle' %TBD: Remove out = hMode.ModeStateData.Constraint; case 'getdirection' %TBD: Remove out = hMode.ModeStateData.Direction; case 'toggle' if localIsZoomOn(hMode) change_ui = 'off'; else change_ui = 'on'; end % Undocumented legacy API, used by 'ident', see g194435 % It would be nice to get rid to dependencies on this API, but % many old toolboxes seem to be calling this API. case 'down' localStartDrag(hMode,'dorightclick',true); hLine = hMode.ModeStateData.LineHandles; if any(ishandle(hLine)) % Mimic rbbox, don't return until line handles are % removed waitfor(hLine(1)); end case 'off' change_ui = 'off'; case 'inmode' localChangeDirection(hMode,'in'); change_ui = 'on'; case 'outmode' localChangeDirection(hMode,'out'); change_ui = 'on'; case 'scale' if ~isempty(hCurrentAxes) % Register current axes view for reset view support resetplotview(hCurrentAxes,'InitializeCurrentView'); hMode.fireActionPreCallback(localConstructEvd(hCurrentAxes)); localApplyZoomFactor(hMode,hCurrentAxes,action_data,false); end case 'fill' if ~isempty(hCurrentAxes) localResetPlot(hCurrentAxes,hMode); end case 'reset' resetplotview(hCurrentAxes,'SaveCurrentView'); setappdata(hCurrentAxes,'zoom_zoomOrigAxesLimits',localComputeLimits(hCurrentAxes)); case 'out' if ~isempty(hCurrentAxes) localResetPlot(hCurrentAxes,hMode); end case 'noaction' out = locGetObj(hFigure); case 'setzoomproperties' % undocumented localSetZoomProperties(hMode,action_data{:}); otherwise error('MATLAB:zoom:unrecognizedinput','Unknown action string.'); end % Update the user interface if ~isempty(change_ui) localSetZoomState(hFigure,change_ui); end %-----------------------------------------------% function hZoom = locGetObj(hFig) % Return the zoom accessor object, if it exists. hMode = locGetMode(hFig); if ~isfield(hMode.ModeStateData,'accessor') ||... ~ishandle(hMode.ModeStateData.accessor) hZoom = graphics.zoom(hMode); else hZoom = hMode.ModeStateData.accessor; end %-----------------------------------------------% function localSetZoomProperties(hMode,varargin) % Set the properties of the mode as specified by user input. % The input is in the form of param/value pairs. for i=1:2:nargin-1 switch varargin{i} case 'UIContextMenu' if ~isempty(varargin{i+1}) && ~strcmpi(get(varargin{i+1},'Type'),'uicontextmenu') error('MATLAB:Zoom:InvalidContextMenu',... 'Handle must be a uicontextmenu.'); end hMode.ModeStateData.CustomContextMenu = varargin{i+1}; case 'Direction' localChangeDirection(hMode,varargin{i+1}); case 'Constraint' if ~strcmpi(varargin{i+1},'vertical') && ... ~strcmpi(varargin{i+1},'horizontal') && ... ~strcmpi(varargin{i+1},'none') error('MATLAB:Zoom:InvalidConstraint',... 'Constraint must either be ''vertical'',''horizontal'' or ''none''.'); end hMode.ModeStateData.Constraint = lower(varargin{i+1}); otherwise error('MATLAB:Zoom:InvalidProperty',... 'Invalid property ''%s'' for zoom mode.',varargin{i}); end end %-----------------------------------------------% function localResetPlot(hAxes,hMode) % For now, only consider one axes hAxes = hAxes(1); if ~all(ishandle(hAxes)) return; end % reset 2-D axes if is2D(hAxes) origLim = axis(hAxes); resetplotview(hAxes,'ApplyStoredView'); newLim = axis(hAxes); localCreate2DUndo(hAxes,origLim(1:4),newLim(1:4)); % reset 3-D axes else origVa = camva(hAxes); origTarget = camtarget(hAxes); resetplotview(hAxes,'ApplyStoredView'); newVa = camva(hAxes); newTarget = camtarget(hAxes); localCreate3DUndo(hAxes,origVa,newVa,origTarget,newTarget); end hMode.fireActionPostCallback(localConstructEvd(hAxes)); %-----------------------------------------------% function localSetZoomState(hFig,state) if strcmp(state,'on') activateuimode(hFig,'Exploration.Zoom'); % zoom off elseif strcmp(state,'off') if isactiveuimode(hFig,'Exploration.Zoom') activateuimode(hFig,''); end end %-----------------------------------------------% function [bool] = localIsZoomOn(hMode) fig = hMode.FigureHandle; bool = true; if isempty(hMode.ParentMode) if ~isactiveuimode(fig,'Exploration.Zoom'); bool = false; end end %-----------------------------------------------% function [target,action,action_data] = localParseArgs(outputs,varargin) target = []; %#ok action = []; action_data = []; errstr = {'Zoom:InvalidSyntax','Invalid Syntax'}; % zoom if nargin==1 target = gcf; if outputs==0 action = 'toggle'; else action = 'noaction'; end elseif nargin==2 arg1 = varargin{1}; if outputs==1 if ishandle(arg1) target = arg1; action = 'noaction'; end else % zoom(SCALE) if all(size(arg1)==[1,1]) && isnumeric(arg1) target = gcf; action = 'scale'; action_data = arg1; % zoom(OPTION) elseif ischar(arg1) target = gcf; action = arg1; % zoom(FIG) % zoom(HZOOM) %elseif ishandle(arg1) elseif any(ishandle(arg1)) if isa(handle(arg1),'hg.figure') target = arg1; action = 'toggle'; end else error(errstr{:}); end end elseif nargin==3 % zoom('newzoom',0) if ischar(varargin{1}) target = gcf; action = varargin{1}; action_data = varargin{2}; % zoom(FIG,SCALE) % zoom(FIG,OPTION) elseif any(ishandle(varargin{1})) target = varargin{1}; arg2 = varargin{2}; if ischar(arg2) action = arg2; elseif isnumeric(arg2) action = 'scale'; action_data = arg2; end end % zoom(FIG,); elseif nargin>=4 target = varargin{1}; arg2 = varargin{2}; %if ~isempty(target) && ishandle(target) && ischar(arg2) if any(ishandle(target)) && ischar(arg2) action = 'setzoomproperties'; action_data = {varargin{2:end}}; end end target = handle(target); %-----------------------------------------------% function localSetV6Zoom(bool) setappdata(0,'V6Zoom',bool); %-----------------------------------------------% function [bool] = localIsV6Zoom bool = getappdata(0,'V6Zoom'); %-----------------------------------------------% function localChangeDirection(hMode,newValue) %Modify the User interface if the direction is changed while the mode is %running. if localIsZoomOn(hMode) if strcmp(newValue,'in') localUISetZoomIn(hMode.FigureHandle); elseif strcmp(newValue,'out') localUISetZoomOut(hMode.FigureHandle); else error('MATLAB:Zoom:InvalidDirection',... 'Direction must either be ''in'' or ''out'''); end end hMode.ModeStateData.Direction = newValue; %-----------------------------------------------% function [hMode] = locGetMode(hFig) hMode = getuimode(hFig,'Exploration.Zoom'); if isempty(hMode) hMode = uimode(hFig,'Exploration.Zoom'); set(hMode,'WindowButtonDownFcn',{@localWindowButtonDownFcn,hMode}); set(hMode,'WindowButtonUpFcn',[]); set(hMode,'WindowButtonMotionFcn',{@localMotionFcn,hMode}); set(hMode,'KeyPressFcn',{@localKeyPressFcn,hMode}); set(hMode,'ModeStartFcn',{@localStartZoom,hMode}); set(hMode,'ModeStopFcn',{@localStopZoom,hMode}); set(hMode,'WindowScrollWheelFcn',{@localButtonWheelFcn,hMode}); % Insert the default properties in the ModeStateData structure: % Property for storing the figure handle hMode.ModeStateData.Constraint = 'none'; hMode.ModeStateData.Direction = 'in'; hMode.ModeStateData.Target = []; hMode.ModeStateData.MaxViewAngle = 75; % Property for holding the RBBOX lines. hMode.ModeStateData.LineHandles = []; % Property holding the axes handles of the last zoomed-in axes hMode.ModeStateData.CurrentAxes = []; hMode.ModeStateData.MousePoint = []; hMode.ModeStateData.CameraViewAngle = []; hMode.ModeStateData.CustomContextMenu = []; hMode.ModeStateData.DoRightClick = getpref('MATLABZoom','RightClick','off'); end %---------------------------------------------------------------------% function localStartZoom(hMode) hFigure = hMode.FigureHandle; %Refresh context menu hui = get(hMode,'UIContextMenu'); if ishandle(hMode.ModeStateData.CustomContextMenu) set(hMode,'UIContextMenu',hMode.ModeStateData.CustomContextMenu); elseif ishandle(hui) delete(hui); set(hMode,'UIContextMenu',''); end set(hMode,'WindowButtonUpFcn',[]) set(hFigure,'PointerShapeHotSpot',[5 5]); % Turn on Zoom UI (i.e. toolbar buttons, menus) % This must be called AFTER uiclear to avoid uiclear state munging zoom_direction = hMode.ModeStateData.Direction; switch zoom_direction case 'in' localUISetZoomIn(hFigure); case 'out' localUISetZoomOut(hFigure); end % Define appdata to avoid breaking code in % scribefiglisten, hgsave, and figtoolset setappdata(hFigure,'ZoomOnState','on'); %---------------------------------------------------------------------% function localStopZoom(hMode) hFigure = hMode.FigureHandle; %Edge case, we turn off the zoom while in drag-mode: hLines = hMode.ModeStateData.LineHandles; if any(ishandle(hLines)) delete(hLines); end % Turn off Zoom UI (i.e. toolbar buttons, menus) localUISetZoomOff(hFigure); % Remove uicontextmenu hui = get(hMode,'UIContextMenu'); if (~isempty(hui) && ishandle(hui)) && ... (isempty(hMode.ModeStateData.CustomContextMenu) || ~ishandle(hMode.ModeStateData.CustomContextMenu)) delete(hui); end % Remove appdata to avoid breaking code in % scribefiglisten, hgsave, and figtoolset if isappdata(hFigure,'ZoomOnState'); rmappdata(hFigure,'ZoomOnState'); end %-----------------------------------------------% function localButtonWheelFcn(hObj, evd, hMode) %#ok hFigure = hMode.FigureHandle; hAxes = locFindAxes(hFigure); if (isempty(hAxes)) || ~localInBounds(hAxes) return; end dir = evd.VerticalScrollCount; if dir < 0 zoom_factor = 2; else zoom_factor = 0.5; end resetplotview(hAxes,'InitializeCurrentView'); hMode.fireActionPreCallback(localConstructEvd(hAxes)); localApplyZoomFactor(hMode,hAxes,zoom_factor,false) %-----------------------------------------------% function localWindowButtonDownFcn(hFigure,evd,hMode) %#ok fig_sel_type = get(hFigure,'SelectionType'); fig_mod = get(hFigure,'CurrentModifier'); zoom_update; hAxes = locFindAxes(hFigure); if (isempty(hAxes)) || ~localInBounds(hAxes) if strcmp(fig_sel_type,'alt') hMode.ShowContextMenu = false; end return; end switch (lower(fig_sel_type)) case 'alt' % right click % display context menu if strcmpi(hMode.ModeStateData.DoRightClick,'off') localGetContextMenu(hMode); else hMode.ShowContextMenu = false; hMode.fireActionPreCallback(localConstructEvd(hAxes)); localStartDrag(hMode,'dorightclick',true); end otherwise % left click, center click, double click % Zoom out if user clicked on 'alt' or shift % ToDo: Remove "alt" in a future release if ~isempty(fig_mod) && ... (strcmp(fig_mod,'alt') || strcmp(fig_mod,'shift')) resetplotview(locFindAxes(hMode.FigureHandle),'InitializeCurrentView'); limits = getappdata(hAxes,'zoom_zoomOrigAxesLimits'); if isempty(limits) limits = localComputeLimits(hAxes); setappdata(hAxes,'zoom_zoomOrigAxesLimits',limits); end switch hMode.ModeStateData.Direction case 'in' hMode.fireActionPreCallback(localConstructEvd(hAxes)); localApplyZoomFactor(hMode,locFindAxes(hMode.FigureHandle),.5,false); case 'out' hMode.fireActionPreCallback(localConstructEvd(hAxes)); localApplyZoomFactor(hMode,locFindAxes(hMode.FigureHandle),2,true); end else hMode.fireActionPreCallback(localConstructEvd(hAxes)); localStartDrag(hMode); end end %---------------------------------------------------% function limits = localComputeLimits(hAxes) xlim = local_get_xlim(hAxes); xmin = xlim(1); xmax = xlim(2); ylim = local_get_ylim(hAxes); ymin = ylim(1); ymax = ylim(2); limits = [xmin xmax ymin ymax]; %---------------------------------------------------% function xlim = local_get_xlim(ax) %GET_XLIM Return equivalent linear scale xlim xlim = get(ax,'xlim'); if strcmp(get(ax,'XScale'),'log'), xlim = log10(xlim); end %---------------------------------------------------% function ylim = local_get_ylim(ax) %GET_YLIM Return equivalent linear scale ylim ylim = get(ax,'ylim'); if strcmp(get(ax,'YScale'),'log'), ylim = log10(ylim); end %---------------------------------------------------% function localStartDrag(hMode,param,dorightclick) % Mouse Button Down Function % By default, don't support right click zoom out if nargin==1 dorightclick = false; end hFig = get(hMode,'FigureHandle'); hAxesVector = locFindAxes(hFig); if ~isempty(hAxesVector), hMode.ModeStateData.CurrentAxes = hAxesVector; sel_type = get(hFig,'SelectionType'); switch sel_type case 'normal' % left click if strcmpi(hMode.ModeStateData.Direction,'out') && is2D(hAxesVector(1)) localApplyZoomFactor(hMode,hAxesVector,.5,false); else localZoom(hMode,hAxesVector); end case 'open' % double click % Reset top plot localResetPlot(hAxesVector,hMode); case 'alt' % right click % zoom out if dorightclick if strcmpi(hMode.ModeStateData.Direction,'in') && is2D(hAxesVector(1)) localApplyZoomFactor(hMode,hAxesVector,.5,false); else localZoom(hMode,hAxesVector); end end case 'extend' % center click % Do nothing end end %---------------------------------------------------% function localZoom(hMode,hAxesVector) resetplotview(hAxesVector,'InitializeCurrentView'); for i=1:length(hAxesVector) hAxes = hAxesVector(i); limits = getappdata(hAxes,'zoom_zoomOrigAxesLimits'); if isempty(limits) limits = localComputeLimits(hAxes); setappdata(hAxes,'zoom_zoomOrigAxesLimits',limits); end end % Call appropriate zoom method based on whether plot is % 2-D or 3-D plot if is2D(hAxesVector(1)) localButtonDownFcn2D(hMode,hAxesVector); else localButtonDownFcn3D(hMode,hAxesVector); end %-----------------------------------------------% function localButtonDownFcn2D(hMode,hAxesVector) % Button down function for 2-D zooming localZoom2DIn(hMode,hAxesVector); %---------------------------------------------------% function localZoom2DIn(hMode,hAxesVector) zoom_update; hFig = get(hMode,'FigureHandle'); % Get current point in pixels figPoint = get(hFig,'CurrentPoint'); new_pt = hgconvertunits(hFig,[0 0 figPoint],... get(hFig,'Units'),'pixels',hFig); hMode.ModeStateData.MousePoint = new_pt(3:4); % First element in the currentAxes is the axes on the % top (on which the lines are drawn). hAxes = hAxesVector(1); % Get the current point on this axes cp = get(hAxes, 'CurrentPoint'); cp = cp(1,1:2); cpMulAxes = []; % Get the current point on the other axes too (multiple axes case). % This will be passed as an argument to the mousebtnupfcn of the zoom % object where the new axes limits are set. for k = 2:length(hAxesVector), cpAxes = get(hAxesVector(k), 'CurrentPoint'); cpMulAxes(k-1,:) = cpAxes(1,1:2); end x = ones(2,4) * cp(1); y = ones(2,4) * cp(2); % Display rrbox lines in top axes hRbboxAxes = hAxesVector(1); % Get zoom lines hLines = hMode.ModeStateData.LineHandles; if all(isempty(hLines)) || all(~ishandle(hLines)) hLines = localCreateZoomLines(hFig,hRbboxAxes,x,y); hMode.ModeStateData.LineHandles = hLines; else set(hLines,'xdata',x(:,1),'ydata',y(:,1)); end %For "zoom('down')" syntax to work: if ~localIsZoomOn(hMode) obj = hFig; else obj = hMode; end % Set the window button motion and up fcn's. origMotionFcn = get(obj,'WindowButtonMotionFcn'); % Only perform this step if the zoom-state is changing (i.e, we are not % stuck in drag-mode. if ~iscell(origMotionFcn) || ~isequal(origMotionFcn{1}, @local2DButtonMotionFcn) set(obj, 'WindowButtonMotionFcn',... {@local2DButtonMotionFcn,hMode,obj}); set(obj, 'WindowButtonUpFcn', ... {@local2DButtonUpFcn, ... hMode, ... cpMulAxes, ... origMotionFcn,obj}); end %---------------------------------------------------% function [hLines] = localCreateZoomLines(hFig,hAxes,x,y) % Create the line for the RbBox. lineprops.Parent = hAxes; lineprops.Visible = 'on'; lineprops.EraseMode = 'xor'; lineprops.LineWidth = 1; lineprops.Color = [0.65 0.65 0.65]; lineprops.Tag = '_TMWZoomLines'; lineprops.HandleVisibility = 'off'; if get(0,'ScreenDepth') == 8, lineprops.LineWidth = 1; end hLines = line(x,y,lineprops); % Mark this object as non-data so that it is ignored % by various tools such as basic fitting. hBehavior = hgbehaviorfactory('DataDescriptor'); set(hBehavior,'Enable',false); hgaddbehavior(hLines,hBehavior); %---------------------------------------------------% function local2DButtonMotionFcn(hcbo,evd,hMode,obj) % Mouse Button Motion Function % This zoom code originates from signal and simulink toolbox disp('test') zoom_update; hLines = hMode.ModeStateData.LineHandles; currentAxes = hMode.ModeStateData.CurrentAxes; fig = hMode.FigureHandle; if isa(obj,'uitools.uimode') curr_units = hgconvertunits(fig,[0 0 evd.CurrentPoint],... 'pixels',get(fig,'Units'),fig); curr_units = curr_units(3:4); set(evd.Source,'CurrentPoint',curr_units); end % The first axes in currentAxes is the one on the top. On this axes the % lines are to be drawn. cAx = currentAxes(1); cp = get(cAx, 'CurrentPoint'); cp = cp(1,1:2); xcp = cp(1); ycp = cp(2); % The first point of line 1 is always the zoom origin. XDat = get(hLines(1), 'XData'); YDat = get(hLines(1), 'YData'); origen = [XDat(1), YDat(1)]; % Consider log plots % TBD isxlog = false; isylog = false; if strcmpi(get(cAx,'xscale'),'log') isxlog = true; end if strcmpi(get(cAx,'yscale'),'log') isylog = true; end % Draw rbbox depending on mode. switch(localChooseConstraint(hMode.ModeStateData.Constraint,cAx)) case 'none', % Both x and y zoom. % RBBOX - lines: % % 2 % o------------- % | | % 1 | | 4 % | | % -------------- % 3 % % Set data for line 1. YDat = get(hLines(1), 'YDat'); YDat(2) = ycp; set(hLines(1),'YDat',YDat); % Set data for line 1. XDat = get(hLines(2),'XDat'); XDat(2) = xcp; set(hLines(2),'XDat',XDat); % Set data for line 3. XDat = get(hLines(3),'XDat'); YDat = [ycp ycp]; XDat(2) = xcp; set(hLines(3),'XDat',XDat,'YDat',YDat); % Set data for line 4. YDat = get(hLines(4), 'YDat'); XDat = [xcp xcp]; YDat(2) = ycp; set(hLines(4),'XDat',XDat,'YDat',YDat); case 'horizontal', % x only zoom. % RBBOX - lines (only 1-3 used): % % | 1 | % 2 o------------| 3 % | | % % Set the end bracket lengths (actually the halfLength). YLim = get(get(fig,'CurrentAxes'), 'YLim'); % Set data for line 1. XDat = get(hLines(1),'XDat'); XDat(2) = xcp; set(hLines(1),'XDat',XDat); if isylog YLim = log10(YLim); origen = log10(origen); end endHalfLength = (YLim(2) - YLim(1)) / 30; YDat = [origen(2) - endHalfLength, origen(2) + endHalfLength]; if isylog YDat = 10.^YDat; end % Set data for line 2. set(hLines(2), 'YDat', YDat); % Set data for line 3. XDat = [xcp xcp]; set(hLines(3), 'XDat', XDat, 'YDat', YDat); case 'vertical', % y only zoom. % RBBOX - lines (only 1-3 used): % 2 % --o-- % | % 1 | % | % ----- % 3 % % Set the end bracket lengths (actually the halfLength). XLim = get(get(fig,'CurrentAxes'), 'XLim'); if isxlog XLim = log10(XLim); origen = log10(origen); end endHalfLength = (XLim(2) - XLim(1)) / 30; % Set data for line 1. YDat = get(hLines(1),'YDat'); YDat(2) = ycp; set(hLines(1),'YDat',YDat); % Set data for line 2. XDat = [origen(1) - endHalfLength, origen(1) + endHalfLength]; if isxlog XDat = 10.^XDat; end set(hLines(2), 'XDat', XDat); % Set data for line 3. YDat = [ycp ycp]; set(hLines(3), 'XDat', XDat, 'YDat', YDat); end %---------------------------------------------------% function local2DButtonUpFcn(hcbo, eventData,hMode,cpMulAxes,origMotionFcn,obj) % Mouse button up function % This code originates from simulink zoom. zoom_update; hFig = get(hMode,'FigureHandle'); % This constant specifies the number of pixels the mouse % must move in order to do a rbbox zoom. POINT_MODE_MAX_PIXELS = 5; % pixels % Get necessary handles hLines = hMode.ModeStateData.LineHandles; currentAxes = hMode.ModeStateData.CurrentAxes; % Get net mouse movement in pixels orig_units = get(hFig,'Units'); set(hFig,'Units','Pixels'); fp = get(hFig,'CurrentPoint'); set(hFig,'Units',orig_units); orig_fp = hMode.ModeStateData.MousePoint; dpixel = abs(orig_fp-fp); % The first point of line 1 is always the zoom origin. XDat = get(hLines(1), 'XData'); YDat = get(hLines(1), 'YData'); origin = [XDat(1), YDat(1)]; % Loop through all the currentAxes and zoom-in each of them for k = 1:length(currentAxes), cAx = currentAxes(k); % cpMulAxes is the zoom origin for multiple axes if any. if k > 1 origin = cpMulAxes(k-1,:); end % Get the current limits. currentXLim = get(cAx, 'XLim'); newXLim = currentXLim; currentYLim = get(cAx, 'YLim'); newYLim = currentYLim; % Perform zoom operation based on zoom mode. switch(localChooseConstraint(hMode.ModeStateData.Constraint,cAx)) case 'none', % % Both x and y zoom. % RBBOX - lines: % % 2 % o------------- % | | % 1 | | 4 % | | % -------------- % 3 % % Determine the end point of zoom operation. % Get current point. cp = get(cAx, 'CurrentPoint'); cp = cp(1,1:2); xcp = cp(1); ycp = cp(2); % Uncomment to clip rbbox zoom to current axes limits %if xcp > currentXLim(2), % xcp = currentXLim(2); %end %if xcp < currentXLim(1), % xcp = currentXLim(1); %end %if ycp > currentYLim(2), % ycp = currentYLim(2); %end %if ycp < currentYLim(1), % ycp = currentYLim(1); %end endPt = [xcp ycp]; % Determine the mode: POINT or RBBOX. if (dpixel(1) <= POINT_MODE_MAX_PIXELS) ... && (dpixel(2) <= POINT_MODE_MAX_PIXELS) localApplyZoomFactor(hMode,cAx,2,true); else newXLim(k,:) = localGetNewXLim(cAx,k,xcp,origin,endPt); newYLim(k,:) = localGetNewYLim(cAx,k,ycp,origin,endPt); localDoZoom2D(cAx,currentXLim,currentYLim,newXLim,newYLim); % Fire mode post callback function: hMode.fireActionPostCallback(localConstructEvd(cAx)); end case 'horizontal', % x only zoom. % RBBOX - lines (only 1-3 used): % % | 1 | % 2 o------------| 3 % | | % % % Determine the end point of zoom operation. cp = get(cAx, 'CurrentPoint'); cp = cp(1,1:2); xcp = cp(1); % Uncomment to clip rbbox zoom to current axes limits % if xcp > currentXLim(2), % xcp = currentXLim(2); % end % if xcp < currentXLim(1), % xcp = currentXLim(1); % end endPt = [xcp origin(2)]; % Determine mode: POINT or RBBOX. if dpixel(1) <= POINT_MODE_MAX_PIXELS localApplyZoomFactor(hMode,cAx,2,true); else newXLim(k,:) = localGetNewXLim(cAx,k,xcp,origin,endPt); newYLim(k,:) = currentYLim; localDoZoom2D(cAx,currentXLim,currentYLim,newXLim,newYLim); % Fire mode post callback function: hMode.fireActionPostCallback(localConstructEvd(cAx)); end case 'vertical', % y only zoom. % RBBOX - lines (only 1-3 used): % 2 % --o-- % | % 1 | % | % ----- % 3 % % Determine the end point of zoom operation. % End pt is the 2nd point of line 1. cp = get(cAx, 'CurrentPoint'); cp = cp(1,1:2); ycp = cp(2); % Uncomment to clip rbbox zoom to current axes limits % if ycp > currentYLim(2), % ycp = currentYLim(2); % end % if ycp < currentYLim(1), % ycp = currentYLim(1); % end endPt = [origin(1) ycp]; % Determine mode: POINT or RBBOX. if dpixel(2) <= POINT_MODE_MAX_PIXELS localApplyZoomFactor(hMode,cAx,2,true); else newYLim(k,:) = localGetNewYLim(cAx,k,ycp,origin,endPt); newXLim(k,:) = currentXLim; localDoZoom2D(cAx,currentXLim,currentYLim,newXLim,newYLim); % Fire mode post callback function: hMode.fireActionPostCallback(localConstructEvd(cAx)); end end % switch end % for % Delete the RBBOX lines. if ishandle(hLines) delete(hLines); end % Call drawnow to flush axes update since the next line, create2Dundo, % will take a long time when called for the first time (class loading). drawnow expose if length(currentAxes)==1 % This runs slow the first time due to UDD class loading localCreate2DUndo(cAx,[currentXLim,currentYLim],[newXLim(k,:),newYLim(k,:)]); end set(obj,'WindowButtonMotionFcn',origMotionFcn); set(obj,'WindowButtonUpFcn',''); hMode.ModeStateData.MousePoint = []; %----------------------------------------------------% function [newXLim] = localGetNewXLim(hAxes,k,xcp,origin,endPt) % ToDo: clean up input arguments % Calculate the new X-Limits. x_lim = [origin(1) endPt(1)]; if x_lim(1) > x_lim(2), x_lim = x_lim([2 1]); end % Set new Xlimits. % NOTE: Check that the limits aren't equal. This happens % at very small limits. In this case, we do nothing. % if abs(x_lim(1) - x_lim(2)) > 1e-10*(abs(x_lim(1)) + abs(x_lim(2))) newXLim(k,:) = x_lim; else newXLim = xlim(hAxes); end %----------------------------------------------------% function [newYLim] = localGetNewYLim(hAxes,k,ycp,origin,endPt) % ToDo: clean up input arguments % Calculate the new Y-Limits. y_lim = [origin(2) endPt(2)]; if y_lim(1) > y_lim(2), y_lim = y_lim([2 1]); end % Set new Ylimits. % NOTE: Check that the limits aren't equal. This happens % at very small limits. In this case, we do nothing. % if abs(y_lim(1) - y_lim(2)) > 1e-10*(abs(y_lim(1)) + abs(y_lim(2))) newYLim(k,:) = y_lim; else newYLim = ylim(hAxes); end %---------------------------------------------------% function localButtonDownFcn3D(hMode,hAxesVector) % Button down function for 3-D zooming zoom_update; % For now, only consider the first axes hAxes = hAxesVector(1); % Set the original camera view angle % We'll need this for generating the undo/redo % command object. hMode.ModeStateData.CameraViewAngle = camva(hAxes); % Force axis to be 'vis3d' to avoid wacky resizing axis(hAxes,'vis3d'); % Set the window button motion and up fcn's. origMotionFcn = get(hMode,'WindowButtonMotionFcn'); set(hMode, 'WindowButtonMotionFcn',{@local3DButtonMotionFcn,hMode,hAxes}); set(hMode, 'WindowButtonUpFcn', {@local3DButtonUpFcn,hMode,hAxes,origMotionFcn}); %---------------------------------------------------% function local3DButtonMotionFcn(hcbo,eventData,hMode,hAxes) hFig = get(hMode,'FigureHandle'); zoom_update; % Get current point in pixels new_pt = eventData.CurrentPoint; starting_pt = hMode.ModeStateData.MousePoint; if isempty(starting_pt) hMode.ModeStateData.MousePoint = new_pt; else % Determine change in pixel position xy = new_pt - starting_pt; % Heuristic for pixel change to camera zoom factor q = max(-.9, min(.9, sum(xy)/70))+1; % hueristic avoids small view angles which will crash on solaris MIN_VIEW_ANGLE = .001; MAX_VIEW_ANGLE = hMode.ModeStateData.MaxViewAngle; vaOld = camva(hAxes); camzoom(hAxes,q); va = camva(hAxes); %If the act of zooming puts us at an extreme, back the zoom out if ~((q>1 || vaMIN_VIEW_ANGLE)) set(hAxes,'CameraViewAngle',vaOld); end % Flush render update and restore double buffer state drawnow expose; hMode.ModeStateData.MousePoint = new_pt; end %---------------------------------------------------% function local3DButtonUpFcn(hobj, eventData, hMode, hAxes, origMotionFcn) hFig = get(hMode,'FigureHandle'); % If the mouse position never moved then just zoom in % on the mouse click if isempty(hMode.ModeStateData.MousePoint) localOneShot3DZoom(hMode,hAxes); else % Add zoom operation to undo/redo stack origVa = hMode.ModeStateData.CameraViewAngle; newVa = camva(hAxes); localCreate3DUndo(hAxes,origVa,newVa); end set(hMode,'WindowButtonUpFcn',''); set(hMode,'WindowButtonMotionFcn',origMotionFcn); % Clear out Mouse Position hMode.ModeStateData.MousePoint = []; % Clear out original camera view angle hMode.ModeStateData.CameraViewAngle = []; % Fire mode post callback function: hMode.fireActionPostCallback(localConstructEvd(hAxes)); %---------------------------------------------------% function localOneShot3DZoom(hMode,hAxes) % If button motion function never ran then do a % simple 3D zoom based on current mouse position if strcmpi(hMode.ModeStateData.Direction,'in') zoomLeftFactor = 1.5; zoomRightFactor = (2/3); else zoomLeftFactor = (2/3); zoomRightFactor = 1.5; end % Determine new zoom factor based on mouse click hFig = get(hMode,'FigureHandle'); switch get(hFig,'selectiontype') case 'normal' % Left click fac = zoomLeftFactor; case 'open' % Double click % do nothing otherwise % Right click fac = zoomRightFactor; end % Actual zoom operation newct = mean(get(hAxes,'CurrentPoint'),1); origct = camtarget(hAxes); %Check to see if the new camera target is within the bounds of the axes. If %it is not, zoom based on the current target. targetInBounds = true; XLims = get(hAxes,'XLim'); if newct(1) < min(XLims) || newct(1) > max(XLims) targetInBounds = false; end YLims = get(hAxes,'YLim'); if newct(2) < min(YLims) || newct(2) > max(YLims) targetInBounds = false; end ZLims = get(hAxes,'ZLim'); if newct(3) < min(ZLims) || newct(3) > max(ZLims) targetInBounds = false; end if targetInBounds origpos = campos(hAxes); origtar = camtarget(hAxes); ctDiff = newct - get(hAxes,'CameraTarget'); set(hAxes,'CameraTarget',newct); set(hAxes,'CameraPosition',get(hAxes,'CameraPosition') + ctDiff); end origva = camva(hAxes); camzoom(hAxes,fac); newva = camva(hAxes); MIN_VIEW_ANGLE = .001; MAX_VIEW_ANGLE = hMode.ModeStateData.MaxViewAngle; %If the act of zooming puts us at an extreme, back the zoom out if ~((fac>1 || newvaMIN_VIEW_ANGLE)) set(hAxes,'CameraViewAngle',origva); if targetInBounds set(hAxes,'CameraTarget',origtar,'CameraPosition',origpos); end end % Add operation to undo/redo stack localCreate3DUndo(hAxes,origva,newva,origct,newct); %-----------------------------------------------% function localMotionFcn(obj,evd,hMode) %#ok hFigure = evd.Source; % Get current point in figure units curr_units = hgconvertunits(hFigure,[0 0 evd.CurrentPoint],... 'pixels',get(hFigure,'Units'),hFigure); curr_units = curr_units(3:4); set(hFigure,'CurrentPoint',curr_units); hAx = locFindAxes(hFigure); if ~isempty(hAx) && localInBounds(hAx) if strcmp(hMode.ModeStateData.Direction,'in') setptr(hFigure,'glassplus'); else setptr(hFigure,'glassminus'); end else setptr(hFigure,'arrow'); end %-----------------------------------------------% function localKeyPressFcn(hFigure,evd,hMode) %#ok consumekey = false; zoom_update; % Exit early if invalid conditions if ~isstruct(evd) || ~isfield(evd,'Key') || ... isempty(evd.Key) || ~isfield(evd,'Character') return; end % Parse key press zoomfactor = []; switch evd.Key case 'uparrow' zoomfactor = 2; case 'downarrow' zoomfactor = .5; case 'alt' consumekey = true; case 'z' if strcmpi(evd.Modifier,'control') consumekey = true; % Undo command uiundo(hMode.FigureHandle,'execUndo'); end case 'y' if strcmpi(evd.Modifier,'control') consumekey = true; % Redo command uiundo(hMode.FigureHandle,'execRedo'); end end hFigure = get(hMode,'FigureHandle'); if ishandle(hFigure) hAxes = get(hFigure,'CurrentAxes'); if ishandle(hAxes) b = hggetbehavior(hAxes,'Zoom','-peek'); if ~isempty(b) && ishandle(b) && ~get(b,'Enable') hAxes = []; elseif isappdata(hAxes,'NonDataObject') hAxes = []; end if ~isempty(zoomfactor) & ishandle(hAxes) consumekey = true; resetplotview(hAxes,'InitializeCurrentView'); hMode.fireActionPreCallback(localConstructEvd(hAxes)); localApplyZoomFactor(hMode,hAxes,zoomfactor,false); end end end if ~consumekey graph2dhelper('forwardToCommandWindow',hFigure,evd); end %-------------------------------------------% function localApplyZoomFactor(hMode,hAxes,zoom_factor,useCurrentPoint) % hAxes is a scalar axes handle % zoom_factor is a scalar double if ~isempty(hAxes) && ishandle(hAxes) if is2D(hAxes) localZoomFactor2D(hMode,hAxes,zoom_factor,useCurrentPoint) else if zoom_factor <= 0 return; end localZoomFactor3D(hMode,hAxes,zoom_factor) end end % Fire mode post callback function: hMode.fireActionPostCallback(localConstructEvd(hAxes)); %-------------------------------------------% function p = local_get_currentpoint(ax) %GET_CURRENTPOINT Return equivalent linear scale current point p = get(ax,'currentpoint'); p = p(1,1:2); if strcmp(get(ax,'XScale'),'log'), p(1) = log10(p(1)); end if strcmp(get(ax,'YScale'),'log'), p(2) = log10(p(2)); end %-------------------------------------------% function localZoomFactor2D(hMode,hAxes,zoom_factor,useCurrentPoint) % Most of the code here is from the original zoom function if zoom_factor > 0 m = 0; else m = -1; end % Get bounding limits for zooming out. viewinfo = resetplotview(hAxes,'GetStoredViewStruct'); limits = getappdata(hAxes,'zoom_zoomOrigAxesLimits'); if isempty(limits) limits = localComputeLimits(hAxes); setappdata(hAxes,'zoom_zoomOrigAxesLimits',limits); end if ~isempty(viewinfo) maxbounds = [viewinfo.XLim, viewinfo.YLim]; else % Use axes data bounds maxbounds = objbounds(hAxes); % Use current bounds, g161225, 163055 if isempty(maxbounds) maxbounds = axis(hAxes); end end boundXLim = maxbounds(1:2); boundYLim = maxbounds(3:4); isXLog = strcmpi(get(hAxes,'XScale'),'log'); isYLog = strcmpi(get(hAxes,'YScale'),'log'); % Current axis limits currLim = axis(hAxes); currLim = currLim(1:4); currXLim = currLim(1:2); if isXLog currXLim = log10(currXLim); end currYLim = currLim(3:4); if isYLog currYLim = log10(currYLim); end newXLim = currXLim; newYLim = currYLim; zoomConstraint = localChooseConstraint(hMode.ModeStateData.Constraint,hAxes); dx = diff(currXLim); dy = diff(currYLim); if useCurrentPoint pts = local_get_currentpoint(hAxes); center_x = pts(1); center_y = pts(2); else center_x = currXLim(1) + dx/2; center_y = currYLim(1) + dy/2; end % Calculate new x-limits if ~any(isinf(currXLim)) && (strcmpi(zoomConstraint,'horizontal') || strcmp(zoomConstraint,'none')) xmin = limits(1); xmax = limits(2); newdx = dx * (1/zoom_factor.^(m+1)); newdx = min(newdx,xmax-xmin); % Limit zoom. center_x = max(center_x,xmin + newdx/2); center_x = min(center_x,xmax - newdx/2); newXLim = [max(xmin,center_x-newdx/2) min(xmax,center_x+newdx/2)]; % Check for log axes and return to linear values. if isXLog newXLim = 10.^newXLim(1:2); end end % Calculate new y-limits if ~any(isinf(currYLim)) && (strcmpi(zoomConstraint,'vertical') || strcmp(zoomConstraint,'none')) ymin = limits(3); ymax = limits(4); newdy = dy * (1/zoom_factor.^(m+1)); newdy = min(newdy,ymax-ymin); % Limit zoom. center_y = max(center_y,ymin + newdy/2); center_y = min(center_y,ymax - newdy/2); newYLim = [max(ymin,center_y-newdy/2) min(ymax,center_y+newdy/2)]; % Check for log axes and return to linear values. if isYLog newYLim = 10.^newYLim(1:2); end end %Check for strangeness in the limits: if newXLim(1) >= newXLim(2) newXLim = currXLim; end if newYLim(1) >= newYLim(2) newYLim = currYLim; end localDoZoom2D(hAxes,currXLim,currYLim,newXLim,newYLim); % Register with undo/redo localCreate2DUndo(hAxes,currLim,[newXLim,newYLim]); zoom_update; %-------------------------------------------% function localDoZoom2D(hAxes,currXLim,currYLim,newXLim,newYLim) % Check for axis equal and update axes limits as necessary if strcmp(get(hAxes,'dataaspectratiomode'),'manual') olddx = diff(get(hAxes,'XLim')); olddy = diff(get(hAxes,'YLim')); ratio = olddx / olddy; dx = newXLim(2)-newXLim(1); dy = newYLim(2)-newYLim(1); % Convert the dx and dy into pixel units hFig = ancestor(hAxes,'Figure'); % Since we are always dealing with 2-D axes, convert the data units % into normalized units by normalizing against the current limits of % the data. ndx = dx / olddx; ndy = dy / olddy; pixPosRect = hgconvertunits(hFig,get(hAxes,'Position'),get(hAxes,'Units'),'Pixels',hFig); pdx = pixPosRect(1) + pixPosRect(3)*ndx; pdy = pixPosRect(2) + pixPosRect(4)*ndy; % Use the larger axis of the zoom as a base, unless the other axes % hasn't moved. if (pdy > pdx && ~isequal(newYLim,get(hAxes,'YLim'))) || ... isequal(newXLim,get(hAxes,'XLim')) diffSize = ratio * dy; diffDiff = dx - diffSize; diffHalf = diffDiff / 2; newXLim(1) = newXLim(1) + diffHalf; newXLim(2) = newXLim(2) - diffHalf; else diffSize = dx / ratio; diffDiff = dy - diffSize; diffHalf = diffDiff / 2; newYLim(1) = newYLim(1) + diffHalf; newYLim(2) = newYLim(2) - diffHalf; end %If the camera view angle is set to be manual and axis equal has %been set, bad things may happen: set(hAxes,'CameraViewAngleMode','auto'); end h = findobj(hAxes,'Type','Image'); if ~isempty(h) lims = objbounds(hAxes); x = lims(1:2); y = lims(3:4); %If we are within the bounds of the image to begin with. This is to %prevent odd behavior if we panned outside the bounds of the image if x(1) <= currXLim(1) && x(2) >= currXLim(2) &&... y(1) <= currYLim(1) && y(2) >= currYLim(2) dx = newXLim(2) - newXLim(1); if newXLim(1) < x(1) newXLim(1) = x(1); newXLim(2) = newXLim(1) + dx; end if newXLim(2) > x(2) newXLim(2) = x(2); newXLim(1) = newXLim(2) - dx; end dy = newYLim(2) - newYLim(1); if newYLim(1) < y(1) newYLim(1) = y(1); newYLim(2) = newYLim(1) + dy; end if newYLim(2) > y(2) newYLim(2) = y(2); newYLim(1) = newYLim(2) - dy; end % Edge case: If the pan ends up outside the image bounds, clip to % the image. if newXLim(1) < x(1) newXLim(1) = x(1); end if newXLim(2) > x(2) newXLim(2) = x(2); end if newYLim(1) < y(1) newYLim(1) = y(1); end if newYLim(2) > y(2) newYLim(2) = y(2); end end end % Actual zoom operation newLim = [newXLim,newYLim]; axis(hAxes,newLim); %-------------------------------------------% function localZoomFactor3D(hMode,hAxes,zoom_factor) % Actual zoom operation origVa = camva(hAxes); va_rad = origVa*pi/360; %newVa = camva(hAxes)/zoom_factor; newVa = atan(tan(va_rad)*(1/zoom_factor))*360/pi; % Limit view angle % hueristic avoids small view angles which will crash on solaris MIN_VIEW_ANGLE = .001; MAX_VIEW_ANGLE = hMode.ModeStateData.MaxViewAngle; if (newVaMIN_VIEW_ANGLE) camva(hAxes,newVa); end % Can't use camzoom since that breaks reverse compatibility %camzoom(hAxes,zoom_factor); % sets view angle %newVa = camva(hAxes); % Register with undo/redo localCreate3DUndo(hAxes,origVa,newVa); %-----------------------------------------------% function evd = localConstructEvd(hAxes) % Construct event data for post callback evd.Axes = hAxes; %-----------------------------------------------% function localCreate2DUndo(hAxes,origLim,newLim) hFigure = ancestor(hAxes,'figure'); % Don't add operations that don't change limits if(origLim==newLim) return; end % Determine undo stack name (this will appear in menu) if all([origLim(1),origLim(3)] < [newLim(1),newLim(3)]) && ... all([origLim(2),origLim(4)] > [newLim(2),newLim(4)]) name = 'Zoom In'; else name = 'Zoom Out'; end % We need to be robust against axes deletions. To this end, operate in % terms of the object's proxy value. proxyVal = getProxyValueFromHandle(hAxes); % Create command structure cmd.Name = name; cmd.Function = @localDo2DUndo; cmd.Varargin = {hFigure,proxyVal,origLim,newLim}; cmd.InverseFunction = @localDo2DUndo; cmd.InverseVarargin = {hFigure,proxyVal,newLim,origLim}; % Register with undo/redo uiundo(hFigure,'function',cmd); %---------------------------------------------------% function localDo2DUndo(hFig,proxyVal,origLim,newLim) hAxes = getHandleFromProxyValue(hFig,proxyVal); if ishandle(hAxes) localDoZoom2D(hAxes,origLim(1:2),origLim(3:4),newLim(1:2),newLim(3:4)); end %---------------------------------------------------% function localCreate3DUndo(hAxes,origVa,newVa,origTarget,newTarget) if nargin==3 localViewAngleUndo(hAxes,origVa,newVa); elseif nargin==5 localTargetViewAngleUndo(hAxes,origVa,newVa,origTarget,newTarget); end %---------------------------------------------------% function localTargetViewAngleUndo(hAxes,... origVa,newVa,... origCamTarget,newCamTarget) % We need to be robust against axes deletions. To this end, operate in % terms of the object's proxy value. hFigure = ancestor(hAxes,'figure'); proxyVal = plotedit({'getProxyValueFromHandle',hAxes}); % Create command structure cmd.Name = '3-D Zoom'; cmd.Function = @localUpdateCameraTargetViewAngle; cmd.Varargin = {hFigure,proxyVal,newCamTarget,newVa}; cmd.InverseFunction = @localUpdateCameraTargetViewAngle; cmd.InverseVarargin = {hFigure,proxyVal,origCamTarget,origVa}; uiundo(hFigure,'function',cmd); %---------------------------------------------------% function localUpdateCameraTargetViewAngle(hFig,proxyVal,t,va) hAxes = getHandleFromProxyValue(hFig,proxyVal); if ishandle(hAxes) camtarget(hAxes,t); camva(hAxes,va); end %---------------------------------------------------% function localViewAngleUndo(hAxes,origVa,newVa) hFigure = ancestor(hAxes,'figure'); % Don't add operations that don't change limits if (origVa==newVa) return end % We need to be robust against axes deletions. To this end, operate in % terms of the object's proxy value. proxyVal = getProxyValueFromHandle(hAxes); if origVa > newVa name = 'Zoom In'; else name = 'Zoom Out'; end % Create command structure cmd.Name = '3-D Zoom'; cmd.Function = @localDoViewAngleUndo; cmd.Varargin = {hFigure,proxyVal,newVa}; cmd.InverseFunction = @localDoViewAngleUndo; cmd.InverseVarargin = {hFigure,proxyVal,origVa}; % Register with undo/redo uiundo(hFigure,'function',cmd); %-----------------------------------------------% function localDoViewAngleUndo(hFig,proxyVal,origVa) hAxes = getHandleFromProxyValue(hFig,proxyVal); if ishandle(hAxes) camva(hAxes,origVa); end %-----------------------------------------------% function [hui] = localUICreateDefaultContextMenu(hMode) % Create default context menu hFig = get(hMode,'FigureHandle'); props_context.Parent = hFig; props_context.Tag = 'ZoomContextMenu'; props_context.Callback = {@localUIContextMenuCallback,hMode}; props_context.ButtonDown = {@localUIContextMenuCallback,hMode}; hui = uicontextmenu(props_context); % Generic attributes for all zoom context menus props.Callback = {@localUIContextMenuCallback,hMode}; props.Parent = hui; props.Label = 'Zoom Out Shift-Click'; props.Tag = 'ZoomInOut'; props.Separator = 'off'; uzoomout = uimenu(props); %#ok % Full View context menu props.Label = 'Reset to Original View'; props.Tag = 'ResetView'; props.Separator = 'off'; ufullview = uimenu(props); %#ok % Zoom Constraint context menu props.Callback = ''; props.Label = 'Zoom Options'; props.Tag = 'Constraint'; props.Separator = 'on'; uConstraint = uimenu(props); props.Parent = uConstraint; props.Callback = {@localUIContextMenuCallback,hMode}; props.Label = 'Unconstrained Zoom'; props.Tag = 'ZoomUnconstrained'; props.Separator = 'off'; uimenu(props); props.Label = 'Horizontal Zoom (2-D Plots Only)'; props.Tag = 'ZoomHorizontal'; uimenu(props); props.Label = 'Vertical Zoom (2-D Plots Only)'; props.Tag = 'ZoomVertical'; uimenu(props); localUIContextMenuUpdate(hMode,hMode.ModeStateData.Constraint); %-----------------------------------------------% function localGetContextMenu(hMode) % Create context menu hui = get(hMode,'UIContextMenu'); if isempty(hui) || ~ishandle(hui) hui = localUICreateDefaultContextMenu(hMode); set(hMode,'UIContextMenu',hui); end if isempty(hMode.ModeStateData.CustomContextMenu) || ~ishandle(hMode.ModeStateData.CustomContextMenu) localUIUpdateContextMenuLabel(hMode); end drawnow expose; %-------------------------------------------------% function localUIContextMenuCallback(obj,evd,hMode) %#ok tag = get(obj,'tag'); switch(tag) case 'ZoomInOut' hAxes = locFindAxes(hMode.FigureHandle); resetplotview(hAxes,'InitializeCurrentView'); limits = getappdata(hAxes,'zoom_zoomOrigAxesLimits'); if isempty(limits) limits = localComputeLimits(hAxes); setappdata(hAxes,'zoom_zoomOrigAxesLimits',limits); end switch hMode.ModeStateData.Direction case 'in' hMode.fireActionPreCallback(localConstructEvd(hAxes)); localApplyZoomFactor(hMode,locFindAxes(hMode.FigureHandle),0.5,false); case 'out' hMode.fireActionPreCallback(localConstructEvd(hAxes)); localApplyZoomFactor(hMode,locFindAxes(hMode.FigureHandle),2,false); end case 'ResetView' hAxes = locFindAxes(hMode.FigureHandle); hMode.fireActionPreCallback(localConstructEvd(hAxes)); resetplotview(hAxes,'ApplyStoredView'); hMode.fireActionPostCallback(localConstructEvd(hAxes)); case 'ZoomContextMenu' localUIContextMenuUpdate(hMode,hMode.ModeStateData.Constraint); case 'ZoomUnconstrained' localUIContextMenuUpdate(hMode,'none'); case 'ZoomHorizontal' localUIContextMenuUpdate(hMode,'horizontal'); case 'ZoomVertical' localUIContextMenuUpdate(hMode,'vertical'); end %-------------------------------------------------% function localUIContextMenuUpdate(hMode,zoom_Constraint) hFigure = get(hMode,'FigureHandle'); ux = findall(hFigure,'Tag','ZoomHorizontal','Type','UIMenu'); uy = findall(hFigure,'Tag','ZoomVertical','Type','UIMenu'); uxy = findall(hFigure,'Tag','ZoomUnconstrained','Type','UIMenu'); switch(zoom_Constraint) case 'none' hMode.ModeStateData.Constraint = 'none'; set(ux,'checked','off'); set(uy,'checked','off'); set(uxy,'checked','on'); case 'horizontal' hMode.ModeStateData.Constraint = 'horizontal'; set(ux,'checked','on'); set(uy,'checked','off'); set(uxy,'checked','off'); case 'vertical' hMode.ModeStateData.Constraint = 'vertical'; set(ux,'checked','off'); set(uy,'checked','on'); set(uxy,'checked','off'); end %-----------------------------------------------% function localUISetZoomIn(fig) set(uigettoolbar(fig,'Exploration.ZoomIn'),'State','on'); set(uigettoolbar(fig,'Exploration.ZoomOut'),'State','off'); %-----------------------------------------------% function localUISetZoomOut(fig) h = findall(fig,'type','uitoolbar'); set(uigettool(h,'Exploration.ZoomIn'),'State','off'); set(uigettool(h,'Exploration.ZoomOut'),'State','on'); %-----------------------------------------------% function localUISetZoomOff(fig) h = findall(fig,'type','uitoolbar'); set(uigettool(h,'Exploration.ZoomIn'),'State','off'); set(uigettool(h,'Exploration.ZoomOut'),'State','off'); % Remove the following lines after UITOOLBARFACTORY API is on by default set(findall(fig,'Tag','figToolZoomIn'),'State','off'); set(findall(fig,'Tag','figToolZoomOut'),'State','off'); %-----------------------------------------------% function localUIUpdateContextMenuLabel(hMode) h = findobj(get(hMode,'UIContextMenu'),'Tag','ZoomInOut'); zoom_direction = hMode.ModeStateData.Direction; if strcmp(zoom_direction,'in') set(h,'Label','Zoom Out Shift-Click'); else set(h,'Label','Zoom In Shift-Click'); end %-----------------------------------------------% function targetInBounds = localInBounds(hAxes) %Check if the user clicked within the bounds of the axes. If not, do %nothing. targetInBounds = true; tol = 3e-16; cp = get(hAxes,'CurrentPoint'); XLims = get(hAxes,'XLim'); if ((cp(1,1) - min(XLims)) < -tol || (cp(1,1) - max(XLims)) > tol) && ... ((cp(2,1) - min(XLims)) < -tol || (cp(2,1) - max(XLims)) > tol) targetInBounds = false; end YLims = get(hAxes,'YLim'); if ((cp(1,2) - min(YLims)) < -tol || (cp(1,2) - max(YLims)) > tol) && ... ((cp(2,2) - min(YLims)) < -tol || (cp(2,2) - max(YLims)) > tol) targetInBounds = false; end ZLims = get(hAxes,'ZLim'); if ((cp(1,3) - min(ZLims)) < -tol || (cp(1,3) - max(ZLims)) > tol) && ... ((cp(2,3) - min(ZLims)) < -tol || (cp(2,3) - max(ZLims)) > tol) targetInBounds = false; end %-----------------------------------------------% function cons = localChooseConstraint(modeConstraint,hAx) % Check to see if the axes has a constraint localBehavior = hggetbehavior(hAx,'Zoom','-peek'); if isempty(localBehavior) cons = modeConstraint; else % Reconcile the mode constraint with the axes constraint if ~strcmpi(localBehavior.Style,'both') if strcmpi(localBehavior.Style,'horizontal') if ~strcmpi(modeConstraint,'vertical') cons = 'horizontal'; else cons = 'both'; end else if ~strcmpi(modeConstraint,'horizontal') cons = 'vertical'; else cons = 'both'; end end else cons = modeConstraint; end end %-----------------------------------------------% function [ax] = locFindAxes(fig) % Return the axes that the mouse is currently over % Return empty if no axes found (i.e. axes has hidden handle) if ~ishandle(fig) return; end % Return all axes under the current mouse point allHit = hittest(fig,'axes'); allAxes = findobj(allHit,'flat','Type','Axes','HandleVisibility','on'); ax = []; for i=1:length(allAxes), candidate_ax=allAxes(i); b = hggetbehavior(candidate_ax,'Zoom','-peek'); if ~isempty(b) && ishandle(b) && ~get(b,'Enable') % ignore this axes % 'NonDataObject' is a legacy flag defined in % datachildren m-file. elseif ~isappdata(candidate_ax,'NonDataObject') ax = candidate_ax; break; end end