gusucode.com > vision工具箱matlab源码程序 > vision/+vision/+internal/+pc/initializePCSceneControl.m

    % Adds 3d scene control to figure for point cloud viewing.
function initializePCSceneControl(hFigure, currentAxes, vertAxis, vertAxisDir, ptCloudThreshold, varargin)

% Equal axis is required for cameratoolbar
if numel(varargin) > 0 && varargin{1}
    axis(currentAxes, 'equal');
end

vision.internal.pc.initializeVerticalAxis(currentAxes, vertAxis, vertAxisDir);
resetplotview(currentAxes,'SaveCurrentView');

% Initialize interation mode before setting callbacks.
initUIMode(hFigure);

% Register callbacks. Left click rotate, wheel zoom.
registerCallbacks(hFigure,vertAxis,vertAxisDir,ptCloudThreshold);

% Initialize user data
initUserData(hFigure,currentAxes,ptCloudThreshold);

% Switch to Camera pan/zoom mode
enableCameraPanZoomMode(currentAxes);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Register Callbacks
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function registerCallbacks(hFigure,vertAxis,vertAxisDir,ptCloudThreshold)
% Replace rotate3d's callback;
currentAxes = get(hFigure,'CurrentAxes');

% Enable rotate3d so we can get the registered mode.
rotate3d(currentAxes,'on');
hui = getuimode(hFigure,'Exploration.Rotate3d');

set(hui,'WindowButtonDownFcn',{@localBtnDown,hFigure,vertAxis,vertAxisDir});
set(hui,'WindowScrollWheelFcn',{@localScrollWheel,hFigure});
set(hui,'WindowButtonMotionFcn',@localPointerChange);
% Do not do anything extra than (un)click buttons/menu entries.
% This can preserve the context menu.
set(hui,'ModeStartFcn',{@localRotateStartMode,hFigure});
set(hui,'ModeStopFcn',{@localRotateStopMode,hFigure});

% Do not allow keyboard interaction
set(hui,'KeyReleaseFcn','');
set(hui,'KeyPressFcn','');

% Add context menu items
initContextMenu(hFigure, hui,vertAxis, vertAxisDir);

udata = getUData(hFigure);
if ~isfield(udata,'pcCallbackRegistered') || isempty(udata.pcCallbackRegistered)
    % If there is no userdata, register listeners
    addlistener(hFigure,'WindowMousePress',@(o,e)localDownsample(o,e,ptCloudThreshold));
    addlistener(hFigure,'WindowMouseRelease',@(o,e)resetWindowMotion(o,e));  
end

udata.pcCallbackRegistered = true;
setUData(hFigure,udata);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Rotate mode starts, click the button
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localRotateStartMode(hFigure)
btn = findall(hFigure,'tag','Exploration.Rotate');
btn.State = 'on';
rmenu = findall(hFigure,'tag','figMenuRotate3D');
rmenu.Checked = 'on';
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Rotate mode stop
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localRotateStopMode(hFigure)
% pop back the button, uncheck the menu entry
btn = findall(hFigure,'tag','Exploration.Rotate');
btn.State = 'off';

rmenu = findall(hFigure,'tag','figMenuRotate3D');
rmenu.Checked = 'off';
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Change the pointer when over an axes.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localPointerChange(obj,~)
% Change the icon to indicate the rotation
if strcmpi(obj.Pointer,'custom');
    %We already have custom icon
    return;
end
SetData = setptr('rotate');
set(obj, SetData{:});
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Initialize User Data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function initUserData(hFigure,currentAxes,ptCloudThreshold)
udata = getUData(currentAxes);
% Do not show rotation axis by default
if isfield(udata,'pcShowRAxis') && udata.pcShowRAxis
    % Remove Rotation Axis if it exists
    removeRAxis(currentAxes);
end
udata.pcShowRAxis = false;

% Do not downsample by default
udata.pcNeedsDownsample = false;
% Flush out caches
udata.pcCacheScatter = {};
udata.pcCachePlot3 = {};
udata.pcCacheScatterCdata = {};
udata.pcCachePlot3Cdata = {};

udata.pcshowMouseData = [];
setUData(currentAxes,udata);

% Determine if current axes needs downsample
localSetDownsample(hFigure,currentAxes,ptCloudThreshold);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Initialize Interaction UI mode
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function initUIMode(hFigure)
% Reset zoom and pan, enable rotation mode
zoom(hFigure,'off');
pan(hFigure,'off');
rotate3d(hFigure,'on');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Initialize right click context menu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function initContextMenu(hFigure, huimode,vertAxis, vertAxisDir)

if isempty(findall(hFigure,'tag','contextPCDownsample'))
    props_context.Parent = hFigure;
    props_context.Tag = 'PCRotateContextMenu';
    huimode.UIContextMenu = uicontextmenu(props_context);
    hui = huimode.UIContextMenu;
    
    % Generic attributes for all rotate context menus
    % Full View context menu
    props = [];
    props.Label = getString(message('MATLAB:uistring:pan:ResetToOriginalView'));
    props.Tag = 'ResetView';
    props.Separator = 'off';
    props.Callback = {@localResetView,hFigure};
    ufullview = uimenu(hui,props); %#ok
    
    % Down sample flag
    props = [];
    props.Label = getString(message('vision:pointcloud:localDownSample'));
    props.Tag = 'contextPCDownsample';
    props.Visible = 'off';
    props.Separator = 'off';
    props.Callback = {@toggleDsampleBtn,hFigure};
    udsample = uimenu(hui,props); %#ok
    
    % Show Rotation Axis
    props = [];
    props.Label = getString(message('vision:pointcloud:localRotationAxis'));
    props.Tag = 'contextPCShowRotationAxis';
    props.Separator = 'off';
    props.Callback = {@toggleRAxisBtn,hFigure,vertAxis,vertAxisDir};
    uraxis = uimenu(hui,props); %#ok
else
    % Reset to default state
    ufullview = findall(hFigure,'tag','ResetView');
    ufullview.Checked = 'off';
    udsample = findall(hFigure,'tag','contextPCDownsample');
    udsample.Checked = 'off';
    uraxis = findall(hFigure,'tag','contextPCShowRotationAxis');
    uraxis.Checked = 'off';
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Register button callbacks
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localBtnDown(src,~,hFigure,vertAxis,vertAxisDir)
switch src.SelectionType
    case 'normal'
        src.WindowButtonMotionFcn = {@localRotate,hFigure,vertAxis,vertAxisDir};
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Rotation callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localRotate(src,~,hFigure,vertAxis,vertDir)

currentAxes = hFigure.CurrentAxes;
udata = getUData(currentAxes);
if ~isfield(udata,'pcshowMouseData')
    return;
end

if isempty(udata.pcshowMouseData)
    udata.pcshowMouseData = hFigure.CurrentPoint;
    setUData(currentAxes,udata);
end
% Previous mouse position
hData = udata.pcshowMouseData;

% Grab current mouse point in pixels
pt = hgconvertunits(src,[0 0 src.CurrentPoint],...
    src.Units,'pixels',src.Parent);
pt = pt(3:4);

% Change in mouse position
deltaPix  = -(pt-hData);

% Update mouse position
udata.pcshowMouseData = pt;
setUData(currentAxes,udata);

% Rotation center set to axes center
Xc = (currentAxes.XLim(2)+currentAxes.XLim(1))/2;
Yc = (currentAxes.YLim(2)+currentAxes.YLim(1))/2;
Zc = (currentAxes.ZLim(2)+currentAxes.ZLim(1))/2;
rotCenter = [Xc,Yc,Zc];
showRAxis = udata.pcShowRAxis;
vision.internal.pc.rotateAxes(currentAxes,deltaPix(1),deltaPix(2),rotCenter,vertAxis,vertDir,showRAxis);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Scroll wheel zoom callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localScrollWheel(~,evt,hFigure)
currentAxes = get(hFigure,'CurrentAxes');
zoomlevel = 0.9;
if evt.VerticalScrollCount < 0
    camzoom(currentAxes,1/zoomlevel);
elseif evt.VerticalScrollCount > 0
    camzoom(currentAxes,zoomlevel);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Unset mouse motion callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function resetWindowMotion(src,~)
if localSupportedMode(src)    
    currentAxes = get(src,'CurrentAxes');
    src.WindowButtonMotionFcn = '';
    udata = getUData(currentAxes);
    sobj = localFindScatterData(currentAxes);
    pobj = localFindPlot3Data(currentAxes);
    if isempty(sobj) && isempty(pobj)
        return
    end
    % Restore axis limit mode 
    currentAxes.XLimMode = udata.XLimMode;
    currentAxes.YLimMode = udata.YLimMode;
    currentAxes.ZLimMode = udata.ZLimMode;

    udata.pcshowMouseData = [];
    needsDownsample = udata.pcNeedsDownsample;
    setUData(currentAxes, udata);
    if needsDownsample
        % Restore all Scatter3 Object
        pcCacheScatter = udata.pcCacheScatter;
        for i = 1:numel(pcCacheScatter)
            cdata = udata.pcCacheScatterCdata{i};
            sobj(i).XData = pcCacheScatter{i}.Location(:,1)';
            sobj(i).YData = pcCacheScatter{i}.Location(:,2)';
            sobj(i).ZData = pcCacheScatter{i}.Location(:,3)';
            sobj(i).CData = cdata;
        end
        % Restore all Plot3 Object
        pcCachePlot3 = udata.pcCachePlot3;
        for i = 1:numel(pcCachePlot3)
            cdata = udata.pcCachePlot3Cdata{i};
            pobj(i).XData = pcCachePlot3{i}.Location(:,1)';
            pobj(i).YData = pcCachePlot3{i}.Location(:,2)';
            pobj(i).ZData = pcCachePlot3{i}.Location(:,3)';
            pobj(i).Color = cdata;
        end
    end
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Find all point cloud object in an axes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function sobj = localFindScatterData(ax)
% Finds all scatter3 data object in ax.
sobj = findobj(ax,'type','scatter');
end
function pobj = localFindPlot3Data(ax)
% Finds all plot3 data object in ax.
pobj = findobj(ax,'type','line','Marker','.');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Cache current Point Cloud data for downsampling
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function cachePCdata(currentAxes)
udata = getUData(currentAxes);
sobj = localFindScatterData(currentAxes);
pobj = localFindPlot3Data(currentAxes);

udata.pcCacheScatter = {};
udata.pcCachePlot3 = {};
udata.pcCacheScatterCdata = {};
udata.pcCachePlot3Cdata = {};

for i = 1:numel(sobj)
    ptcloud = pointCloud([sobj(i).XData',sobj(i).YData',sobj(i).ZData']);
    udata.pcCacheScatter{i} = ptcloud;
    udata.pcCacheScatterCdata{i} = sobj(i).CData;
end

for i = 1:numel(pobj)
    ptcloud = pointCloud([pobj(i).XData',pobj(i).YData',pobj(i).ZData']);
    udata.pcCachePlot3{i} = ptcloud;
    udata.pcCachePlot3Cdata{i} = pobj(i).Color;
end

setUData(currentAxes,udata);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Determine if current axes needs downsample
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localSetDownsample(hFigure,currentAxes,ptCloudThreshold)
% Find all scatter3 and plot3 children
sobj = localFindScatterData(currentAxes);
pobj = localFindPlot3Data(currentAxes);

% Adaptive Downsample:
needsDownsample = false;
numDataScatter  = 0;
numDataPlot3= 0;
if ~isempty(sobj)
    numDataScatter = numel([sobj.XData]);
end
if ~isempty(pobj)
    numDataPlot3 = numel([pobj.XData]);
end

numData = numDataScatter + numDataPlot3;

if numData > ptCloudThreshold(1) && numData < ptCloudThreshold(2)
    needsDownsample = true;
    cachePCdata(currentAxes);
    btn = findobj(hFigure,'tag','contextPCDownsample');
    if ~strncmpi(btn,'on',2);
        toggleDsampleBtn(btn,false,hFigure);
    end
end
udata = getUData(currentAxes);
udata.pcNeedsDownsample = needsDownsample;
setUData(currentAxes,udata);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Decide if we are in rotate/pan/zoom mode
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function supported = localSupportedMode(hFigure)
hManager = uigetmodemanager(hFigure);

supported = strcmpi(hFigure.SelectionType,'normal') && ~isempty(hManager.CurrentMode) ...
    && ismember(hManager.CurrentMode.Name,...
    {'Exploration.Rotate3d','Exploration.Pan','Exploration.Zoom'});
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Perform downsampling in current Axes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localDownsample(hFigure,~,ptCloudThreshold)

if localSupportedMode(hFigure)
    
    currentAxes = get(hFigure,'CurrentAxes');    
    
    sobj = localFindScatterData(currentAxes);
    pobj = localFindPlot3Data(currentAxes);
    
    if isempty(sobj) && isempty(pobj)
        % no  point cloud data available
        return;
    end
    
    % Determine if current axes needs downsample
    localSetDownsample(hFigure,currentAxes,ptCloudThreshold);

    % Perform downsample on all scatter3 and plot3 objects
    udata = getUData(currentAxes);
    
    % Set the axis limit to manual to prevent auto-snapping
    udata.XLimMode = currentAxes.XLimMode;
    udata.YLimMode = currentAxes.YLimMode;
    udata.ZLimMode = currentAxes.ZLimMode;
    setUData(currentAxes,udata);
    udata.XLimMode = 'manual';
    udata.YLimMode = 'manual';
    udata.ZLimMode = 'manual';
    
    needsDownsample = udata.pcNeedsDownsample;
    
    % Handle datacursormode
    dcm_obj = datacursormode(hFigure);
    cInfo = getCursorInfo(dcm_obj);
    % We do not downsample when there are datatips. The datatip relies on
    % the linear index of its underlying data, this will change when we
    % downsample the points. 
    
    hasDatatip = ~isempty(cInfo);
    
    % only needs to check isempty(pccache)
    if ~isempty(needsDownsample) &&  needsDownsample ...
        && ~hasDatatip 
        % Cache current axes
        cachePCdata(currentAxes);
        udata = getUData(currentAxes);

        % Downsample all scatter3 objects
        ptclouds = udata.pcCacheScatter;
        for i = 1:numel(ptclouds)
            ptcloud =  ptclouds{i};
            K = min(round(ptcloud.Count*0.5),ptCloudThreshold(1));
            indices = vision.internal.samplingWithoutReplacement(ptcloud.Count, K);
            if max(indices(:)) > numel(sobj(i).XData)
                % Renderer is not ready yet.
                continue;
            end
            sobj(i).XData = sobj(i).XData(indices);
            sobj(i).YData = sobj(i).YData(indices);
            sobj(i).ZData = sobj(i).ZData(indices);
            if numel(sobj(i).CData) > 3
                sobj(i).CData = sobj(i).CData(indices,:);
            end            
        end
        
        % Downsample all plot3 objects
        ptclouds = udata.pcCachePlot3;
        for i = 1:numel(ptclouds)
            ptcloud =  ptclouds{i};
            K = min(round(ptcloud.Count*0.5),ptCloudThreshold(1));
            indices = vision.internal.samplingWithoutReplacement(ptcloud.Count, K);
            if max(indices(:)) > numel(pobj(i).XData)
                % Renderer is not ready yet.
                continue;
            end

            pobj(i).XData = pobj(i).XData(indices);
            pobj(i).YData = pobj(i).YData(indices);
            pobj(i).ZData = pobj(i).ZData(indices);
            if numel(pobj(i).Color) > 3
                pobj(i).Color = pobj(i).Color(indices,:);
            end
        end
        
    end
    
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback for toggling downsample option
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localToggleDownsample(currentAxes,state)
needsDownsample = strncmpi(state,'on',2);
if ~needsDownsample
    % Flush out old cache
    udata = getUData(currentAxes);
    udata.pcCacheScatter = {};
    udata.pcCachePlot3 = {};
    udata.pcCacheScatterCdata = {};
    udata.pcCachePlot3Cdata = {};
    setUData(currentAxes,udata);
else
    cachePCdata(currentAxes)
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Toggles Downsample option in context menu and figure menu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function toggleDsampleBtn(src,~,hFigure)
currentAxes = get(hFigure,'CurrentAxes');

% Clicked from context menu, toggle downsample on current axes
% Toggle flag
udata = getUData(currentAxes);
udata.pcNeedsDownsample = ~udata.pcNeedsDownsample;
setUData(currentAxes,udata);

% Set context menu
state = convertButtonState(udata.pcNeedsDownsample);
contextBtn = src;
contextBtn.Checked = state;

% Toggle Downsample on Current Axes
localToggleDownsample(currentAxes,state);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Reset to default view callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localResetView(~,~,hFigure)
currentAxes = get(hFigure,'CurrentAxes');
resetplotview(currentAxes,'ApplyStoredView');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Remove rotation axis from axes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function removeRAxis(currentAxes)
hline1 = findobj(currentAxes,'tag','pcViewerRAxis1');
hline2 = findobj(currentAxes,'tag','pcViewerRAxis2');
hline3 = findobj(currentAxes,'tag','pcViewerRAxis3');
if ~isempty(hline1)
    delete(hline1);
end
if ~isempty(hline2)
    delete(hline2);
end
if ~isempty(hline3)
    delete(hline3);
end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Show/Hide rotation axis in current Axes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localToggleRAxis (currentAxes, vertAxis, vertAxisDir,state)
showRAxis = strncmpi(state,'on',2);
if ~showRAxis
    removeRAxis(currentAxes);
else
    Xc = mean(currentAxes.XLim);
    Yc = mean(currentAxes.YLim);
    Zc = mean(currentAxes.ZLim);
    origPoint = [Xc,Yc,Zc];
    
    currentPose = get(currentAxes,'cameraposition');
    up = get(currentAxes,'cameraupvector');
    
    vaxis = origPoint - currentPose;
    coordsysval = lower(vertAxis) - 'x' + 1;
    
    raxis = [0 0 0];
    if strcmpi(vertAxisDir,'down')
        raxis(coordsysval) = -1;
    else
        raxis(coordsysval) = 1;
    end
    XSpan = currentAxes.XLim(2)-currentAxes.XLim(1);
    YSpan = currentAxes.YLim(2)-currentAxes.YLim(1);
    ZSpan = currentAxes.ZLim(2)-currentAxes.ZLim(1);
    
    minSpan = min([XSpan,YSpan,ZSpan]);
    
    vlength = minSpan*0.2;
    raxis1 = raxis*vlength;
    raxis2 = cross(vaxis,raxis);
    raxis2 = raxis2/norm(raxis2)*vlength;
    upsidedown = (up(coordsysval) < 0);
    if upsidedown
        raxis2 = -raxis2;
    end
    raxis3 = crossSimple(raxis1,raxis2);
    raxis3 = raxis3/norm(raxis3)*vlength;
    
    hold(currentAxes,'on');
    line([origPoint(1),origPoint(1)+raxis1(1)],...
        [origPoint(2),origPoint(2)+raxis1(2)],...
        [origPoint(3),origPoint(3)+raxis1(3)],...
        'tag','pcViewerRAxis1','Parent',currentAxes);
    line([origPoint(1),origPoint(1)+raxis2(1)],...
        [origPoint(2),origPoint(2)+raxis2(2)],...
        [origPoint(3),origPoint(3)+raxis2(3)],...
        'color','red','tag','pcViewerRAxis2','Parent',currentAxes);
    line([origPoint(1),origPoint(1)+raxis3(1)],...
        [origPoint(2),origPoint(2)+raxis3(2)],...
        [origPoint(3),origPoint(3)+raxis3(3)],...
        'color','green','tag','pcViewerRAxis3','Parent',currentAxes);
    hold(currentAxes,'off');
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Convert t/f to 'on'/'off'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function state = convertButtonState(tf)
if tf
    state = 'on';
else
    state = 'off';
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Toggles Show Rotation Axis option in context menu and figure menu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function toggleRAxisBtn(~,~,hFigure,vertAxis,vertAxisDir)
currentAxes = get(hFigure,'CurrentAxes');
% Clicked from context menu, show/hide RAxis on current axes

% Toggle flag
udata = getUData(currentAxes);
udata.pcShowRAxis = ~udata.pcShowRAxis;
setUData(currentAxes,udata);

% Set context menu
state = convertButtonState(udata.pcShowRAxis);

% Draw/Delete Rotation Axis
localToggleRAxis(currentAxes,vertAxis,vertAxisDir,state);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get/Set User Data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function hData = getUData(hgHandle)
hData = getappdata(hgHandle,'PCUserData');
end
function setUData(hgHandle, data)
setappdata(hgHandle,'PCUserData',data);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% simple cross product
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function c=crossSimple(a,b)
c(1) = b(3)*a(2) - b(2)*a(3);
c(2) = b(1)*a(3) - b(3)*a(1);
c(3) = b(2)*a(1) - b(1)*a(2);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Limit Pan/Zoom mode is not suitable for Point Cloud application
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function enableCameraPanZoomMode(currentAxes)
z = zoom(currentAxes);
z.setAxes3DPanAndZoomStyle(currentAxes,'camera');
end