www.gusucode.com > mbcguitools 工具箱 matlab 源码程序 > mbcguitools/mv_rotate3d.m
function mv_rotate3d(arg,arg2,varargin) %MV_ROTATE3D Interactively rotate the view of a 3-D plot. % % MV_ROTATE3D ON turns on mouse-based 3-D rotation. % MV_ROTATE3D OFF turns if off. % % MV_ROTATE3D(AX,...) works on the axes AX. % % See also ZOOM. % Copyright 2000-2013 The MathWorks, Inc. and Ford Global Technologies, Inc. % rotate3d on enables text feedback % rotate3d ON disables text feedback. % Revised by Rick Paxson 10-25-96 % Clay M. Thompson 5-3-94 if nargin==1 if isgraphics(arg) setState(arg,'toggle') else switch(lower(arg)) % how much performance hit here case 'on' setState(gcf,arg,gca); case 'off' setState(gcf,arg,gca); otherwise error(message('mbc:mv_rotate3d:InvalidArgument')); end end elseif nargin==2 if ~isgraphics(arg), error(message('mbc:mv_rotate3d:InvalidArgument1')); end if isgraphics(arg, 'axes') ax= arg; hFig= ancestor(ax,'figure'); end switch(lower(arg2)) % how much performance hit here case 'on' setState(hFig,arg2,ax) case 'off' setState(hFig,arg2,ax); otherwise error(message('mbc:mv_rotate3d:InvalidArgument')); end end end %-------------------------------- % Set activation state. Options on, ON, off function setState(fig,state,ax) rotaObj = findobj(allchild(fig),'Tag','xreg.rotaObj'); if strcmpi(state,'on') if isempty(rotaObj) rotaObj = makeRotaObj(fig,ax); else % add axes to rotaobj list rdata=get(rotaObj,'UserData'); rdata = removeBadAxes(rdata); rdata.targetAxis=unique([rdata.targetAxis ax]); set(rotaObj,'UserData',rdata); end % Handle toggle of text feedback. ON means no feedback on means feedback. rdata = get(rotaObj,'UserData'); if(strcmp(state,'on')) rdata.textState = 1; else rdata.textState = 0; end set(rotaObj,'UserData',rdata); elseif strcmpi(state,'off') % remove axis handle from list of rotation axes if ~isempty(rotaObj) rdata = get(rotaObj,'UserData'); rdata = removeBadAxes(rdata); rdata.targetAxis(rdata.targetAxis==ax)=[]; if isempty(rdata.targetAxis) destroyRotaObj(rotaObj); else set(rotaObj,'UserData',rdata); end end end end %--------------------------- % Button down callback function rotaButtonDownFcn(figH,evt,rotaObj) ax=get(figH,'CurrentAxes'); if isempty(ax) || ~isgraphics(ax) || strcmp(get(ax,'Visible'),'off') return end rdata = get(rotaObj,'UserData'); if ~ismember(ax,rdata.targetAxis) return end % Activate axis that is clicked in funits = get(figH,'Units'); set(figH,'Units','pixels'); rdata.currentAxis=ax; cp = get(figH,'CurrentPoint'); pos = getpixelposition(ax, true); % aunits = get(ax,'units'); % set(ax,'units','pixels'); % pos = get(ax,'position'); % set(ax,'units',aunits); axes_found = 0; if cp(1) >= pos(1) && cp(1) <= pos(1)+pos(3) && ... cp(2) >= pos(2) && cp(2) <= pos(2)+pos(4) axes_found = 1; set(figH,'CurrentAxes',ax); end % if set(figH,'Units',funits) if axes_found==0, return, end % store the state on the zlabel: that way if the user % plots over this axis, this state will be cleared and % we get to start over. viewData = getappdata(get(ax,'ZLabel'),'ROTATEAxesView'); if isempty(viewData) setappdata(get(ax,'ZLabel'),'ROTATEAxesView', get(ax, 'View')); end selection_type = get(figH,'SelectionType'); if strcmp(selection_type,'open') new_azel = getappdata(get(ax,'ZLabel'),'ROTATEAxesView'); if(rdata.textState) set(rdata.textBoxText,'String',... sprintf('Az: %4.0f El: %4.0f',new_azel)); end set(rotaObj, 'View', new_azel); rotaButtonUpFcn(rdata.buttonupmanager, [], rotaObj); return end rdata.oldFigureUnits = get(figH,'Units'); set(figH,'Units','pixels'); rdata.oldPt = get(figH,'CurrentPoint'); rdata.prevPt = rdata.oldPt; rdata.oldAzEl = get(ax,'View'); % Map azel from -180 to 180. rdata.oldAzEl = rem(rem(rdata.oldAzEl+360,360)+180,360)-180; if abs(rdata.oldAzEl(2))>90 % Switch az to other side. rdata.oldAzEl(1) = rem(rem(rdata.oldAzEl(1)+180,360)+180,360)-180; % Update el rdata.oldAzEl(2) = sign(rdata.oldAzEl(2))*(180-abs(rdata.oldAzEl(2))); end set(rotaObj,'UserData',rdata); setOutlineObjToFitAxes(rotaObj); copyAxisProps(ax, rotaObj); rdata = get(rotaObj,'UserData'); if(rdata.oldAzEl(2) < 0) rdata.CrossPos = 1; set(rdata.outlineObj,'ZData',rdata.scaledData(4,:)); else rdata.CrossPos = 0; set(rdata.outlineObj,'ZData',rdata.scaledData(3,:)); end set(rotaObj,'UserData',rdata); if(rdata.textState) fig_color = get(figH,'Color'); c = sum([.3 .6 .1].*fig_color); set(rdata.textBoxText,'BackgroundColor',fig_color); if(c > .5) set(rdata.textBoxText,'ForegroundColor',[0 0 0]); else set(rdata.textBoxText,'ForegroundColor',[1 1 1]); end set(rdata.textBoxText,'Visible','on'); end if strcmpi(rdata.rotatestyle,'-view') set(rdata.outlineObj,'Visible','on'); end rdata.buttonupmanager.getNextEvent({@rotaButtonUpFcn, rotaObj}); % attach to motion manager rdata.manager.EnableTree=false; rdata.manager.MouseMoveFcn={@rotaMotionFcn, rotaObj}; end %------------------------------- % Button up callback function rotaButtonUpFcn(bm,evt,rotaObj) figH = bm.Figure; if isempty(rotaObj) return else rdata = get(rotaObj,'UserData'); rdata = removeBadAxes(rdata); set([rdata.outlineObj rdata.textBoxText],'Visible','off'); rdata.oldAzEl = get(rotaObj,'View'); if strcmpi(rdata.rotatestyle,'-view') set(rdata.currentAxis,'View',rdata.oldAzEl); end set(figH,'Units',rdata.oldFigureUnits); set(rotaObj,'Parent', figH, 'UserData',rdata); rdata.manager.EnableTree=true; rdata.manager.MouseMoveFcn=''; end end %----------------------------- % Mouse motion callback function rotaMotionFcn(figH,evt,rotaObj) rdata = get(rotaObj,'UserData'); if strcmp(rdata.rotatestyle,'-orbit') localRotateOrbitMotionFcn(rotaObj); else localRotateViewMotionFcn(rotaObj); end end %----------------------------- function localRotateViewMotionFcn(rotaObj) % Change the axes view as the user moves the mouse rdata = get(rotaObj,'UserData'); figH = rdata.figure; new_pt = get(figH,'CurrentPoint'); old_pt = rdata.oldPt; dx = new_pt(1) - old_pt(1); dy = new_pt(2) - old_pt(2); new_azel = mappingFunction(rdata, dx, dy); set(rotaObj,'View',new_azel); if(new_azel(2) < 0 && rdata.crossPos == 0) set(rdata.outlineObj,'ZData',rdata.scaledData(4,:)); rdata.crossPos = 1; set(rotaObj,'UserData',rdata); end if(new_azel(2) > 0 && rdata.crossPos == 1) set(rdata.outlineObj,'ZData',rdata.scaledData(3,:)); rdata.crossPos = 0; set(rotaObj,'UserData',rdata); end if(rdata.textState) set(rdata.textBoxText,'String',sprintf('Az: %4.0f El: %4.0f',new_azel)); end end %---------------------------- function localRotateOrbitMotionFcn(rotateObj) % Orbit the camera as the user moves the mouse % Get necessary handles rdata = get(rotateObj,'UserData'); hFig = rdata.figure; hAxes = rdata.currentAxis; origFigureUnits = get(hFig,'Units'); set(hFig,'Units','pixels'); % Determine change in pixel position pt = rdata.prevPt; new_pt = get(hFig, 'CurrentPoint'); dx = new_pt(1) - pt(1); dy = new_pt(2) - pt(2); % Consider when the axes is upside down % Assume 'z' based coordinate system for now % (See cameratoolbar.m code for an example of supporting % multiple coordinate systems) up = camup(hAxes); if (up(3) < 0); dx = -dx; end % Orbit the camera, assume z based coordinate system camorbit(hAxes,-dx,-dy,'data','z') % Update the azimuth/elevation display if(rdata.textState) axView = get(hAxes,'View'); set(rdata.textBoxText,'String',sprintf('Az: %4.0f El: %4.0f',axView)); end % Calling drawnow will result in hang, see g201318 drawnow expose; set(hFig,'Units',origFigureUnits); % Update recent point for next time rdata.prevPt = new_pt; % Save data set(rotateObj,'UserData',rdata); end %---------------------------- % Map a dx dy to an azimuth and elevation function azel = mappingFunction(rdata, dx, dy) delta_az = round(rdata.GAIN*(-dx)); delta_el = round(rdata.GAIN*(-dy)); azel(1) = rdata.oldAzEl(1) + delta_az; azel(2) = min(max(rdata.oldAzEl(2) + 2*delta_el,-90),90); if abs(azel(2))>90 % Switch az to other side. azel(1) = rem(rem(azel(1)+180,360)+180,360)-180; % Map new az from -180 to 180. % Update el azel(2) = sign(azel(2))*(180-abs(azel(2))); end end %----------------------------- % Scale data to fit target axes limits function setOutlineObjToFitAxes(rotaObj) rdata = get(rotaObj,'UserData'); ax = rdata.currentAxis; x_extent = get(ax,'XLim'); y_extent = get(ax,'YLim'); z_extent = get(ax,'ZLim'); X = rdata.outlineData; X(1,:) = X(1,:)*diff(x_extent) + x_extent(1); X(2,:) = X(2,:)*diff(y_extent) + y_extent(1); X(3,:) = X(3,:)*diff(z_extent) + z_extent(1); X(4,:) = X(4,:)*diff(z_extent) + z_extent(1); set(rdata.outlineObj,'XData',X(1,:),'YData',X(2,:),'ZData',X(3,:)); rdata.scaledData = X; set(rotaObj,'UserData',rdata); end %------------------------------- % Copy properties from one axes to another. function copyAxisProps(original, dest) props = { 'DataAspectRatio' 'DataAspectRatioMode' 'CameraViewAngle' 'CameraViewAngleMode' 'XLim' 'YLim' 'ZLim' 'PlotBoxAspectRatio' 'PlotBoxAspectRatioMode' 'Units' 'Position' 'View' 'Projection' 'Parent' }; values = get(original,props); set(dest,props,values); end %------------------------------------------- % Constructor for the Rotate object. function rotaObj = makeRotaObj(fig,ax) rdata.targetAxis = ax; % Axis that is being rotated (target axis) rdata.GAIN = 0.4; % Motion gain rdata.oldPt = []; % Point where the button down happened rdata.oldAzEl = []; curax = get(fig,'CurrentAxes'); rotaObj = axes('Parent',fig,'Visible','off','HandleVisibility','off'); % Data points for the outline box. rdata.outlineData = [0 0 1 0;0 1 1 0;1 1 1 0;1 1 0 1;0 0 0 1;0 0 1 0; ... 1 0 1 0;1 0 0 1;0 0 0 1;0 1 0 1;1 1 0 1;1 0 0 1;0 1 0 1;0 1 1 0; ... NaN NaN NaN NaN;1 1 1 0;1 0 1 0]'; rdata.outlineObj = line(rdata.outlineData(1,:),rdata.outlineData(2,:),rdata.outlineData(3,:), ... 'Parent',rotaObj,'Visible','off','HandleVisibility','off', ... 'Clipping','off'); % Make text box. rdata.textBoxText = uicontrol('Parent',fig,'Units','Pixels','Position',[2 2 130 20],'Visible','off', ... 'Style','text','HandleVisibility','off'); rdata.textState = []; rdata.oldFigureUnits = ''; rdata.crossPos = 0; % where do we put the X at zmin or zmax? 0 means zmin 1 means zmax rdata.scaledData = rdata.outlineData; rdata.figure=fig; % property to link with mouse motion manager rdata.manager=MotionManager(fig); % property to link with buttonup manager rdata.buttonupmanager = xregGui.ButtonUpManager(fig); % Specify camera orbiting or box rotation style (-orbit or -view) rdata.rotatestyle = '-view'; rdata.prevPt = []; set(fig,'WindowButtonDownFcn',{@rotaButtonDownFcn, rotaObj}); set(fig,'ButtonDownFcn',''); % Make a listener that looks out for anyone clearing our % WindowButtonDownFcn. Doing this breaks rotation for everyone. rdata.BDListener = mbcgui.hgclassesutil.proplistener(fig, ... 'WindowButtonDownFcn', 'PostSet', @i_reclaimbdfcn); set(rotaObj,'Tag','xreg.rotaObj','UserData',rdata); if ~isempty(curax) set(fig,'CurrentAxes',curax) end function i_reclaimbdfcn(src, evt) new_fcn = get(fig, 'WindowButtonDownFcn'); if isempty(new_fcn) warning(message('mbc:mv_rotate3d:InvalidState')); set(fig,'WindowButtonDownFcn',{@rotaButtonDownFcn, rotaObj}); else warning(message('mbc:mv_rotate3d:InvalidState1')); end end end %---------------------------------- % Deactivate rotate object function destroyRotaObj(rotaObj) rdata = get(rotaObj,'UserData'); delete(rdata.BDListener); fig=ancestor(rotaObj,'figure'); delete(rdata.textBoxText); delete(rotaObj); set(fig,'WindowButtonDownFcn',''); end %---------------------------------- % Remove axes handles that no longer exist from the target list function rdata = removeBadAxes(rdata) isH = isgraphics(rdata.targetAxis); rdata.targetAxis = rdata.targetAxis(isH); end