如何拖动末端使机器人运动仿真-使用Peter机器人工具箱

1、前记

这里这是一个简单的分享,一种在机器人模型末端添加一个marker并进行拖动的仿真。类似于机器人系统工具箱中的interactiveRigidBodyTree函数功能,不过简单了功能许多。这里只是提供一个支持机器人工具箱robotics toolbox的机器人仿真模型被拖动交互的方式。这方面当然还可以拓展....

(1)机器人系统工具箱的演示如MATLAB 2020b版本发布,下载试用版并上手使用记录。中所记录的一样。这里贴上个图---

GIF5

(2)这里的方法所用到的函数如下:【蓝色选中的代码为demo演示代码,其他为运行所需调用的代码】

function dragzoom(varargin)
%DRAGZOOM Drag and zoom tool
%
% Description:
%   DRAGZOOM allows you to interactively manage the axes in figure.
%   This simple function for usable draging and zooming of axes, using the 
%   mouse and the keyboard shortcuts.
%
%   - Supported 2D-Plots, 3D-plots, semilogx-plots, semilogy-plots, loglog-plots and Images;
%   - Supported subplots (mixed axes).
%
%
% Using:
%   dragzoom()
%   dragzoom(hFig)
%   dragzoom(hAx)
%   dragzoom(hAxes)
%   dragzoom(hFig, status)
%   dragzoom(status)
%
%
% Input:
%   hObj -- Figure or axes handle or array of axes handles
%   status -- 'on' or 'off'
%
%
% Control Enable Status of DRAGZOOM:
%   dragzoom(hFig, 'on') 	: Enable DRAGZOOM for figure "hFig"
%   dragzoom(hFig, 'off') 	: Disable DRAGZOOM for figure "hFig"
%   dragzoom('on')          : Enable DRAGZOOM for figure "GCF"
%   dragzoom('off')         : Disable DRAGZOOM for figure "GCF"
%
%
% Interactive mode:
%   Available the following actions:
% 
%   Mouse actions in 2D mode:
%   Normal mode:
%       single-click and holding LB : Activation Drag mode
%       single-click and holding RB : Activation Rubber Band for region zooming
%       single-click MB             : Activation 'Extend' Zoom mode
%       scroll wheel MB             : Activation Zoom mode
%       double-click LB, RB, MB     : Reset to Original View
% 
%   Magnifier mode:
%       single-click LB             : Not Used
%       single-click RB             : Not Used
%       single-click MB             : Reset Magnifier to Original View
%       scroll MB                   : Change Magnifier Zoom
%       double-click LB             : Increase Magnifier Size
%       double-click RB             : Decrease Magnifier Size
% 
%   Hotkeys in 2D mode:
%       '+'                         : Zoom plus
%       '-'                         : Zoom minus
%       '0'                         : Set default axes (reset to original view)
%       'uparrow'                   : Up or down (inrerse) drag
%       'downarrow'                 : Down or up (inverse) drag
%       'leftarrow'                 : Left or right (inverse) drag
%       'rightarrow'                : Right or left (inverse) drag
%       'c'                         : On/Off Pointer Symbol 'fullcrosshair'
%       'g'                         : On/Off Axes Grid
%       'x'                         : If pressed and holding, zoom and drag works only for X axis
%       'y'                         : If pressed and holding, zoom and drag works only for Y axis
%       'm'                         : If pressed and holding, Magnifier mode on
%       'l'                         : On/Off Synchronize XY manage of 2-D axes 
%       'control+l'                 : On Synchronize X manage of 2-D axes 
%       'alt+l'                     : On Synchronize Y manage of 2-D axes 
%       's'                         : On/Off Smooth Plot (Experimental)
% 
%   Mouse actions in 3D mode:
%       single-click and holding LB : Activation Drag mode
%       single-click and holding MB : Activation 'Extend' Zoom mode
%       single-click and holding RB : Activation Rotate mode
%       scroll wheel MB             : Activation Zoom mode
%       double-click LB, RB, MB     : Reset to Original View
% 
%   Hotkeys in 3D mode:
%       '+'                         : Zoom plus
%       '-'                         : Zoom minus  
%       '0'                         : Set default axes (reset to original view)
%       'uparrow'                   : Rotate up-down
%       'downarrow'                 : Rotate down-up
%       'leftarrow'                 : Rotate left-right
%       'rightarrow'                : Rotate right-left
%       'ctrl'+'uparrow'            : Up or down (inrerse) drag
%       'ctrl'+'downarrow'          : Down or up (inverse) drag
%       'ctrl'+'leftarrow'          : Left or right (inverse) drag
%       'ctrl'+'rightarrow'         : Right or left (inverse) drag
%       '1'                         : Go to X-Y view
%       '2'                         : Go to X-Z view
%       '3'                         : Go to Y-Z view
%       'v'                         : On/Off Visible Axes
%       'f'                         : On/Off Fixed Aspect Ratio
%       'g'                         : On/Off Axes Grid
% 
%
% Example:
%   x = -pi:0.1:pi;
%   y = sin(x);
%   figure; plot(x, y);
%   dragzoom
%
% Example:
%   I = imread('cameraman.tif');
%   figure; imshow(I, []);
%   dragzoom
%
% Example:
%   figure;
%   x = -pi*2:0.1:pi*2;
%   y1 = sin(x);
%   y2 = cos(x);
%   subplot(2,1,1); plot(x,y1, '.-r')
%   title('Income')
%   subplot(2,1,2); plot(x, y2, 'o-b')
%   title('Outgo')
%   dragzoom;
%
% Example:
%   figure;
%   x = -pi*2:0.1:pi*2;
%   y1 = sin(x);
%   y2 = cos(x);
%   hax1 = subplot(2,1,1); plot(hax1, x, y1, '.-r')
%   hax2 = subplot(2,1,2); plot(hax2, x, y2, 'o-b')
%   dragzoom(hax1); % manage only axes 1
%
% Example:
%   figure;
%   x = -pi*2:0.1:pi*2;
%   y1 = sin(x);
%   y2 = cos(x);
%   hax1 = subplot(2,1,1); plot(hax1, x, y1, '.-r')
%   hax2 = subplot(2,1,2); plot(hax2, x, y2, 'o-b')
%   dragzoom([hax1, hax2]); % manage axes 1 and axes 2
%
% Example:
%   figure;
%   k = 5;
%   n = 2^k-1;
%   [x,y,z] = sphere(n);
%   surf(x,y,z);
%   dragzoom;
%
% Example:
%   x = 0:100;
%   y = log10(x);
%   figure;
%   semilogx(x, y, '*-b')
%   dragzoom;
%
%
% See Also PAN, ZOOM, PANZOOM, ROTATE3D
%

% -------------------------------------------------------------------------
%   Version   : 0.9.7
%   Author    : Evgeny Pr aka iroln <esp.home@gmail.com>
%   Created   : 10.07.10
%   Updated   : 05.06.11
%
%   Copyright : Evgeny Pr (c) 2010-2011
% -------------------------------------------------------------------------

global g_key zoomPct


% First timer
persistent p_memory
if isempty(p_memory)
    p_memory.first_flag = true; 
end 


%TODO: Show Pixel Info (Pixel Value(s)) for Images
%TODO: "Sticking to Data" PointerCross

error(nargchk(0, 2, nargin));

% handles
hFig = [];
hAx = [];
hAxes = [];

% variables
mOrigFigName = [];
mOrigCallbacks = [];
mAxesInfo = [];
mDragStartX = [];
mDragStartY = [];
mDragKeysX = [];
mDragKeysY = [];
mDragShiftStep = [];
mDragSaveShiftStep = [];
mDragShiftStepInc = [];
mDragSave3DShiftStep = [];
mDrag3DShiftStep = [];
mZoomScroll = [];
mZoomMinPow = [];
mZoomMaxPow = [];
mZoomNum = [];
mZoomExtendNum = [];
mZoomKeysNum = [];
mZoom3DExtendNum = [];
mZoom3DKeysNum = [];
mZoom3DIndex = [];
mDefaultZoomGrid = [];
mDefaultZoomSteps = [];
mZoomGrid = [];
mZoomSteps = [];
mZoomIndexX = [];
mZoomIndexY = [];
mZoom3DStartX = [];
mZoom3DStartY = [];
mZoom3DBindX = [];
mZoom3DBindY = [];
mDefaultXLim = [];
mDefaultYLim = [];
mDefaultAxPos = [];
mRotStartAZ = [];
mRotStartEL = [];
mRotStartX = [];
mRotStartY = [];
mRot3DKeysInc = [];
mPointerCross = [];
mRubberBand = [];
mRbEdgeColor = [];
mRbFaceColor = [];
mRbFaceAlpha = [];
mMagnifier = [];
mMgSize = [];
mMgMinSize = [];
mMgMaxSize = [];
mMgZoom = [];
mMgMinZoom = [];
mMgMaxZoom = [];
mMgLinesWidth = [];
mMgShadow = [];
mMgSizeStep = [];
mMgZoomStep = [];
mMgDirection = [];
mLinkOpt = 'xy';

% flags
fIsEnabled = false;
fIsSelectedCurrentAxes = true;
fIsDragAllowed = false;
fIsZoomExtendAllowed = false;
fIsZoomExtend3DAllowed = false;
fIsRotate3DAllowed = false;
fIsRubberBandOn = false;
fIsPointerCross = false;
fIsAxesGrid = false;
fIsSmoothing = false;
fIsEnableDragX = true;
fIsEnableDragY = true;
fIsEnableZoomX = true;
fIsEnableZoomY = true;
fIsAxes2D = false;
fIsImage = false;
fIsMagnifierOn = false;
fIsLinkAxesOn = false;
fIsEnableControl = false;
fIsMouseOnLegend = false;


% Initialize and Setup
Initialize(varargin{:})
%--------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%==========================================================================
    function Setup()
        %Setup setup options
        
        % Drag Options
        mDragKeysX = 'normal';      % 'normal', 'reverse'
        mDragKeysY = 'normal';      % 'normal', 'reverse'
        mDragShiftStep = 3;         % step dragging on keys
        mDragShiftStepInc = 1;      % increase speed dragging on keys
        mDrag3DShiftStep = 10;      % step dragging 3D on keys
        
        % Zoom Options
        mZoomScroll = 'normal';     % 'normal', 'reverse'
        mZoomMinPow = 0;            % min zoom percent 10 ^ mZoomMinPow
        mZoomMaxPow = 5;            % max zoom perzent 10 ^ mZoomMaxPow
        mZoomNum = 51;              % count steps of log zoom grid
        mZoomExtendNum = 301;       % count steps of log grid zoom extend for 2D
        mZoomKeysNum = 181;         % count steps of log grid zoom for keys for 2D
        mZoom3DExtendNum = 201;     % count steps of log grid zoom extend for 3D
        mZoom3DKeysNum = 181;       % count steps of log grid zoom for keys for 3D
        
        % Rubber Band Options
        mRbEdgeColor = 'k';         % rubber band edge color
        mRbFaceColor = 'none';      % rubber band face color
        mRbFaceAlpha = 1;           % rubber band face alpha (transparency)
        
        % Magnifier Options
        mMgSize = 100;              % default size of magnifier (pixels)
        mMgMinSize = 50;            % min size of magnifier
        mMgMaxSize = 200;           % max size of magnifier
        mMgZoom = 2;                % default zoom on magnifier
        mMgMinZoom = 1;             % min zoom on magnifier
        mMgMaxZoom = 100;           % max zoom on magnifier
        mMgLinesWidth = 1;          % lines width on magnifier
        mMgShadow = 0.95;           % shadow area without magnifier
        mMgSizeStep = 15;           % step change in the magnifier size
        mMgZoomStep = 1.2;          % step change in the magnifier zoom
        
        % Rotate Options
        mRot3DKeysInc = 3;          % rotate increase for keys
    end
%--------------------------------------------------------------------------

%==========================================================================
    function Initialize(varargin)
        %Initialize initialize tool
        
        % Parse Input Arguments
        isWithStatus = ParseInputs(varargin{:});
        
        if isempty(hAxes), return; end
        
        hAx = hAxes(1);
        set(hFig, 'CurrentAxes', hAx);
        
        % setup tool
        Setup();
        
        % initialize tool
        UserData = get(hFig, 'UserData');
        
        if (isfield(UserData, 'axesinfo') && isWithStatus)
            mAxesInfo = UserData.axesinfo;
            mOrigCallbacks = UserData.origcallbacks;
            mOrigFigName = UserData.origfigname;
        else
            % first call dragzoom or call without enable status
            if ~isfield(UserData, 'origfigname')
                mOrigFigName = get(hFig, 'Name');
            end
            
            if isfield(UserData, 'origcallbacks')
                mOrigCallbacks = UserData.origcallbacks;
                SetOriginalCallbacks();
            end
            
            % save original callbacks
            SaveOriginalCallbacks();
            
            % get info about all axes and create axes info struct
            mAxesInfo = GetAxesInfo();
            
            
            % save initialize view for all axes
            for i = 1:numel(mAxesInfo)
                resetplotview(mAxesInfo(i).handle, 'InitializeCurrentView');
            end
            
            UserData.axesinfo = mAxesInfo;
            UserData.origcallbacks = mOrigCallbacks;
            UserData.origfigname = mOrigFigName;
            
            if ~isfield(UserData, 'tools')
                UserData.tools.pointercross = mPointerCross;
                UserData.tools.islinkaxeson = fIsLinkAxesOn;
            end
            
            set(hFig, 'UserData', UserData);    
        end
        
        DeleteOldTools();
        
        axi = GetCurrentAxesIndex();
        SetCurrentAxes(axi);
        SetDefaultZoomGrid();
        SetFigureName();
        
        mDragSaveShiftStep = mDragShiftStep;
        mDragSave3DShiftStep = mDrag3DShiftStep;
        
        % In case the figure will be saved
        set(hFig, 'CreateFcn', {@CreateFigureCallback});
        
        if fIsEnabled
            SetCallbacks();
        else
            SetOriginalCallbacks();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SaveOriginalCallbacks()
        %SaveOriginalCallbacks
        
        mOrigCallbacks.window_button_down_fcn   = get(hFig, 'WindowButtonDownFcn');
        mOrigCallbacks.window_button_up_fcn     = get(hFig, 'WindowButtonUpFcn');
        mOrigCallbacks.window_button_motion_fcn = get(hFig, 'WindowButtonMotionFcn');
        mOrigCallbacks.window_scroll_whell_fcn  = get(hFig, 'WindowScrollWheelFcn');
        mOrigCallbacks.window_key_press_fcn     = get(hFig, 'WindowKeyPressFcn');
        mOrigCallbacks.window_key_release_fcn   = get(hFig, 'WindowKeyReleaseFcn');
        mOrigCallbacks.create_fcn               = get(hFig, 'CreateFcn');
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetOriginalCallbacks()
        %SetOriginalCallbacks
        
        set(hFig, ...
            'WindowButtonDownFcn',      mOrigCallbacks.window_button_down_fcn, ...
        	'WindowButtonUpFcn',        mOrigCallbacks.window_button_up_fcn, ...
        	'WindowButtonMotionFcn',    mOrigCallbacks.window_button_motion_fcn, ...
        	'WindowScrollWheelFcn',     mOrigCallbacks.window_scroll_whell_fcn, ...
        	'WindowKeyPressFcn',        mOrigCallbacks.window_key_press_fcn, ...
        	'WindowKeyReleaseFcn',      mOrigCallbacks.window_key_release_fcn, ...
        	'CreateFcn',                mOrigCallbacks.create_fcn);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetCallbacks()
        %SetCallbacks
        
        if fIsAxes2D
            % 2D mode
            SetFigureCallbacks2D();
        else
            % 3D mode
            SetFigureCallbacks3D();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetFigureCallbacks2D()
        %SetFigureCallbacks2D set callback-functions for processing figure events in mode 2D
        
        set(hFig, ...
            'WindowButtonDownFcn',      {@WindowButtonDownCallback2D}, ...
            'WindowButtonUpFcn',        {@WindowButtonUpCallback2D}, ...
            'WindowButtonMotionFcn',    {@WindowButtonMotionCallback2D}, ...
            'WindowScrollWheelFcn',     {@WindowScrollWheelFcn2D}, ...
            'WindowKeyPressFcn',        {@WindowKeyPressCallback2D}, ...
            'WindowKeyReleaseFcn',      {@WindowKeyReleaseCallback2D});
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetFigureCallbacks3D()
        %SetFigureCallbacks3D set callback-functions for processing figure events in mode 3D
        
        set(hFig, ...
            'WindowButtonDownFcn',      {@WindowButtonDownCallback3D}, ...
            'WindowButtonUpFcn',        {@WindowButtonUpCallback3D}, ...
            'WindowButtonMotionFcn',    {@WindowButtonMotionCallback3D}, ...
            'WindowScrollWheelFcn',     {@WindowScrollWheelFcn3D}, ...
            'WindowKeyPressFcn',        {@WindowKeyPressCallback3D}, ...
            'WindowKeyReleaseFcn',      {@WindowKeyReleaseCallback3D});
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowButtonDownCallback2D(src, evnt)    %#ok
        %WindowButtonDownCallback2D
        
        if fIsMouseOnLegend, return; end
        
        clickType = get(src, 'SelectionType');
        
        switch clickType
            case 'normal'
                DragMouseBegin();
                mMgDirection = 'plus';
            case 'open'
                if fIsMagnifierOn
                    MagnifierSizeChange(mMgDirection);
                else
                    ResetAxesToOrigView();
                end
            case 'alt'
                RubberBandBegin();
                mMgDirection = 'minus';
            case 'extend'
                if fIsMagnifierOn
                    MagnifierReset();
                else
                    ZoomMouseExtendBegin();
                end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowButtonUpCallback2D(src, evnt)      %#ok
        %WindowButtonUpCallback2D
        
        DragMouseEnd();
        ZoomMouseExtendEnd();
        RubberBandEnd();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowButtonMotionCallback2D(src, evnt)  %#ok
        %WindowButtonMotionCallback2D
                
        if ~(fIsMagnifierOn || fIsDragAllowed || fIsRubberBandOn)
            % set current axes under cursor
            SelectAxesUnderCursor();
        end
        
        if fIsEnableControl
            DragMouse();
            RubberBandUpdate();
            MagnifierUpdate();
        end
        
        ZoomMouseExtend();
        PointerCrossUpdate();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowScrollWheelFcn2D(src, evnt)        %#ok
        %WindowScrollWheelFcn2D
        
        if fIsMouseOnLegend, return; end
        
        % Update Zoom Info
        % because it can be changed function 'zoom'
        UpdateCurrentZoomAxes();
        
        switch mZoomScroll
            case 'normal'
                directions = {'minus', 'plus'};
            case 'reverse'
                directions = {'plus', 'minus'};
        end
        
        verScrollCount = evnt.VerticalScrollCount;
        
        if (verScrollCount > 0)
            direction = directions{1};
        elseif (verScrollCount < 0)
            direction = directions{2};
        else
            return;
        end
        
        % if fIsEnableControl
        ZoomMouse(direction);
        PointerCrossUpdate();
        % end
        MagnifierZoomChange(direction);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowKeyPressCallback2D(src, evnt)      %#ok
        %WindowKeyPressCallback2D
        
        modifier = evnt.Modifier;
        
        switch evnt.Key
            case '0'
                ResetAxesToOrigView();
            case {'equal', 'add'}
                ZoomKeys('plus');
            case {'hyphen', 'subtract'}
                ZoomKeys('minus');
            case 'leftarrow'
                DragKeys('left');
            case 'rightarrow'
                DragKeys('right');
            case 'uparrow'
                DragKeys('up');
            case 'downarrow'
                DragKeys('down');
            case 'c'
                SetPointerCrossKeys();
            case 'g'
                SetAxesGridKeys();
            case 'x'
                DragEnable('y', 'off');
                ZoomEnable('y', 'off');
            case 'y'
                DragEnable('x', 'off');
                ZoomEnable('x', 'off');
            case 'm'
                if fIsEnableControl
                    MagnifierOn();
                end
            case 'l'
                SetLinkAxesKeys(modifier);
            case 's'
                % smooth plot
                % SetSmoothKeys();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowKeyReleaseCallback2D(src, evnt)    %#ok
        %WindowKeyReleaseCallback2D
        
        switch evnt.Key
            case {'leftarrow', 'rightarrow', 'uparrow', 'downarrow'}
                mDragShiftStep = mDragSaveShiftStep;
            case 'x'
                DragEnable('y', 'on');
                ZoomEnable('y', 'on');
            case 'y'
                DragEnable('x', 'on');
                ZoomEnable('x', 'on');
            case 'm'
                MagnifierOff();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowButtonDownCallback3D(src, evnt)    %#ok
        %WindowButtonDownCallback3D
        
        if fIsMouseOnLegend, return; end
        
        clickType = get(src, 'SelectionType');
        
        switch clickType
            case 'normal'
                DragMouseBegin3D();
            case 'open'
                ResetAxesToOrigView3D();
            case 'alt'
                RotateMouseBegin3D();
            case 'extend'
                ZoomMouseExtendBegin3D();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowButtonUpCallback3D(src, evnt)    %#ok
        %WindowButtonUpCallback3D
                
        DragMouseEnd3D();
        ZoomMouseExtendEnd3D();
        RotateMouseEnd3D();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowButtonMotionCallback3D(src, evnt)    %#ok
        %WindowButtonMotionCallback3D
                
        if ~(fIsDragAllowed || fIsZoomExtend3DAllowed || fIsRotate3DAllowed)
            SelectAxesUnderCursor();
        end
        
        if fIsEnableControl
            DragMouse3D();
            RotateMouse3D();
        end
        ZoomMouseExtend3D();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowScrollWheelFcn3D(src, evnt)    %#ok
        %WindowScrollWheelFcn3D
        
        if fIsMouseOnLegend, return; end
        
        UpdateCurrentZoomAxes3D();
        
        switch mZoomScroll
            case 'normal'
                directions = {'minus', 'plus'};
            case 'reverse'
                directions = {'plus', 'minus'};
        end
        
        verScrollCount = evnt.VerticalScrollCount;
        
        if (verScrollCount > 0)
            direction = directions{1};
        elseif (verScrollCount < 0)
            direction = directions{2};
        else
            return;
        end
        
        
        % if fIsEnableControl
        ZoomMouse3D(direction);
        % end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowKeyPressCallback3D(src, evnt)    %#ok
        %WindowKeyPressCallback3D
        
        isModifier = ~isempty(intersect(evnt.Modifier, {'control', 'command'}));
        
        g_key = evnt.Key;
        
        switch evnt.Key
            case {'equal', 'add'}
                ZoomKeys3D('plus');
            case {'hyphen', 'subtract'}
                ZoomKeys3D('minus');
            case 'leftarrow'
                if isModifier
                    % DragKeys3D('left');
                else
                    % RotateKeys3D('az-');
                end
            case 'rightarrow'
                if isModifier
                    % DragKeys3D('right');
                else
                    % RotateKeys3D('az+');
                end
            case 'uparrow'
                if isModifier
                    % DragKeys3D('up');
                else
                    % RotateKeys3D('el-');
                end
            case 'downarrow'
                if isModifier
                    % DragKeys3D('down');
                else
                    % RotateKeys3D('el+');
                end
            case '0'
                % ResetAxesToOrigView3D();
            case '1'
                % RotateKeys3D('xy');
            case '2'
                % RotateKeys3D('xz');
            case '3'
                % RotateKeys3D('yz');
            case 'v'
                % VisibleAxesKeys3D();
            case 'f'
                % SwitchAspectRatioKeys3D()
            case 'g'
                % SetAxesGridKeys();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function WindowKeyReleaseCallback3D(src, evnt)    %#ok
        %WindowKeyReleaseCallback3D
        
        switch evnt.Key
            case {'equal', 'add', 'hyphen', 'subtract'}
                SetDefaultZoomGrid3D();
            case {'leftarrow', 'rightarrow', 'uparrow', 'downarrow'}
                mDrag3DShiftStep = mDragSave3DShiftStep;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function CreateFigureCallback(src, evnt)    %#ok
        %CreateFigureCallback
        
        hFig = src;
        
        UserData = get(hFig, 'UserData');
        mAxesInfo = UserData.axesinfo;
        mOrigCallbacks = UserData.origcallbacks;
        mOrigFigName = UserData.origfigname;
        hAxes = arrayfun(@(x) x.handle, mAxesInfo);
        
        axi = GetCurrentAxesIndex();
        SetCurrentAxes(axi)
        DeleteOldTools();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetFigureName()
        %SetFigureName Set Name of Figure
        
        enableStatus = 'off';
        if fIsEnabled
            enableStatus = 'on';
        end
        
        syncMode = 'Normal';
        if fIsLinkAxesOn
            syncMode = sprintf('Synchronized %s', upper(mLinkOpt));
        end
        
        sep = '';
        if ~isempty(mOrigFigName)
            sep = ':';
        end
        
%         newName = sprintf('[DRAGZOOM : "%s" (%s)]%s %s', ...
%             enableStatus, syncMode, sep, mOrigFigName);
%         set(hFig, 'Name', newName)
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragMouseBegin()
        %DragMouseBegin begin draging
        
        if (~fIsDragAllowed && ~fIsMagnifierOn)
            [cx, cy] = GetCursorCoordOnWindow();
            
            mDragStartX = cx;
            mDragStartY = cy;
            
            fIsDragAllowed = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragMouseEnd()
        %DragMouseEnd end draging

        if fIsDragAllowed
            fIsDragAllowed = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragMouse()
        %DragMouse

        if fIsDragAllowed
            [cx, cy] = GetCursorCoordOnWindow();
            
            pdx = mDragStartX - cx;
            pdy = mDragStartY - cy;
            
            mDragStartX = cx;
            mDragStartY = cy;
            
            DragAxes(pdx, pdy);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragKeys(direction)
        %DragKeys
        
        dx = mDragShiftStep;
        dy = mDragShiftStep;
        
        % Increment of speed when you hold the button
        mDragShiftStep = mDragShiftStep + mDragShiftStepInc;
        
        directionsX = {'right', 'left'};
        directionsY = {'down', 'up'};
        
        switch mDragKeysX
            case 'normal'
            case 'reverse'
                directionsX = fliplr(directionsX);
        end
        switch mDragKeysY
            case 'normal'
            case 'reverse'
                directionsY = fliplr(directionsY);
        end
        
        switch direction
            case directionsX{1}
                DragAxes(-dx, 0);
            case directionsX{2}
                DragAxes(dx, 0);
            case directionsY{1}
                DragAxes(0, dy);
            case directionsY{2}
                DragAxes(0, -dy);
        end
        
        PointerCrossUpdate();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragAxes(pdx, pdy)
        %DragAxes
        
        [xLim, yLim] = GetAxesLimits();
        
        pos = GetObjPos(hAx, 'Pixels');
        pbar = get(hAx, 'PlotBoxAspectRatio');
        
        %NOTE: MATLAB Bug?
        % Fixed problem with AspectRatio and Position of Axes
        % MATLAB Function PAN is not correct works with rectangular images!
        % Here it is correctly.
        
        imAspectRatioX = pbar(2) / pbar(1);
        if (imAspectRatioX ~= 1)
            posAspectRatioX = pos(3) / pos(4);
            arFactorX = imAspectRatioX * posAspectRatioX;
            if (arFactorX < 1)
                arFactorX = 1;
            end
        else
            arFactorX = 1;
        end
        
        imAspectRatioY = pbar(1) / pbar(2);
        if (imAspectRatioY ~= 1)
            posAspectRatioY = pos(4) / pos(3);
            arFactorY = imAspectRatioY * posAspectRatioY;
            if (arFactorY < 1)
                arFactorY = 1;
            end
        else
            arFactorY = 1;
        end
        
        if fIsEnableDragX
            % For log plots, transform to linear scale
            if strcmp(get(hAx, 'xscale'), 'log')
                xLim = log10(xLim);
                xLim = FixInfLogLimits('x', xLim);
                isXLog = true;
            else
                isXLog = false;
            end
            
            dx = pdx * range(xLim) / (pos(3) / arFactorX);
            xLim = xLim + dx;
            
            % For log plots, untransform limits
            if isXLog
                xLim = 10.^(xLim);
            end
        end
        if fIsEnableDragY
            if strcmp(get(hAx, 'yscale'), 'log')
                yLim = log10(yLim);
                yLim = FixInfLogLimits('y', yLim);
                isYLog = true;
            else
                isYLog = false;
            end
            
            dy = pdy * range(yLim) / (pos(4) / arFactorY);
            
            if fIsImage
                yLim = yLim - dy; 
            else
                yLim = yLim + dy; 
            end
            
            if isYLog
                yLim = 10.^(yLim);
            end
        end
        
        SetAxesLimits(xLim, yLim);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragMouseBegin3D()
        %DragMouseBegin3D
        
        if ~fIsDragAllowed
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            mDragStartX = wcx;
            mDragStartY = wcy;
            
            fIsDragAllowed = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragMouseEnd3D()
        %DragMouseEnd3D
        
        if fIsDragAllowed
            fIsDragAllowed = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragMouse3D()
        %DragMouse3D
        
        if fIsDragAllowed
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            dx = mDragStartX - wcx;
            dy = mDragStartY - wcy;
            
            DragAxes3D(dx, dy);
            
            mDragStartX = wcx;
            mDragStartY = wcy;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragKeys3D(direction)
        %DragKeys3D
        
        dx = mDrag3DShiftStep;
        dy = mDrag3DShiftStep;
        
        mDrag3DShiftStep = mDrag3DShiftStep + mDragShiftStepInc;
        
        directionsX = {'right', 'left'};
        directionsY = {'down', 'up'};
        
        switch mDragKeysX
            case 'normal'
            case 'reverse'
                directionsX = fliplr(directionsX);
        end
        switch mDragKeysY
            case 'normal'
            case 'reverse'
                directionsY = fliplr(directionsY);
        end
        
        switch direction
            case directionsX{1}
                DragAxes3D(-dx, 0);
            case directionsX{2}
                DragAxes3D(dx, 0);
            case directionsY{1}
                DragAxes3D(0, dy);
            case directionsY{2}
                DragAxes3D(0, -dy);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragAxes3D(dx, dy)
        %DragAxes3D
        
        axPos = GetObjPos(hAx, 'pixels');
        
        axPos(1) = axPos(1) - dx;
        axPos(2) = axPos(2) - dy;
        
        SetObjPos(hAx, axPos, 'pixels');
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DragEnable(ax, action)
        %DragEnable
        
        switch lower(action)
            case 'on'
                tf = true;
            case 'off'
                tf = false;
        end
                
        switch lower(ax)
            case 'x'
                fIsEnableDragX = tf;
            case 'y'
                fIsEnableDragY = tf;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomEnable(ax, action)
        %ZoomEnable
        
        switch lower(action)
            case 'on'
                tf = true;
            case 'off'
                tf = false;
        end
                
        switch lower(ax)
            case 'x'
                fIsEnableZoomX = tf;
            case 'y'
                fIsEnableZoomY = tf;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouse(direction)
        %ZoomMouse zooming axes with mouse
        
        if (IsZoomMouseAllowed && ~fIsMagnifierOn)
            [acx, acy] = GetCursorCoordOnAxes();
            ZoomAxes(direction, acx, acy)
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouseExtendBegin()
        %ZoomMouseExtendBegin
        
        if ~fIsZoomExtendAllowed
            UpdateCurrentZoomAxes();
            
            % set new zoom grid for extend zoom
            [mZoomGrid, mZoomSteps] = ZoomLogGrid(mZoomMinPow, mZoomMaxPow, mZoomExtendNum);
            UpdateCurrentZoomAxes();
            
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            [acx, acy] = GetCursorCoordOnAxes();
            
            mZoom3DStartX = wcx;
            mZoom3DStartY = wcy;
            
            mZoom3DBindX = acx;
            mZoom3DBindY = acy;
            
            fIsZoomExtendAllowed = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouseExtendEnd()
        %ZoomMouseExtendEnd
        
        if fIsZoomExtendAllowed
            % set default zoom grid
            SetDefaultZoomGrid();
            fIsZoomExtendAllowed = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouseExtend()
        %ZoomMouseExtend
        
        if fIsZoomExtendAllowed
            directions = {'minus', 'plus'};
            
            switch mZoomScroll
                case 'normal'
                case 'reverse'
                    directions = fliplr(directions);
            end
        
            % Heuristic for pixel change to camera zoom factor 
            % (taken from function ZOOM)
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            xy(1) = wcx - mZoom3DStartX;
            xy(2) = wcy - mZoom3DStartY;
            q = max(-0.9, min(0.9, sum(xy)/70)) + 1;
   
            if (q < 1)
                direction = directions{1};
            elseif (q > 1)
                direction = directions{2};
            else
                return;
            end
            
            ZoomAxes(direction, mZoom3DBindX, mZoom3DBindY)
            
            mZoom3DStartX = wcx;
            mZoom3DStartY = wcy;            
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomKeys(direction)
        %ZoomKeys zooming axes with keyboard
        
        UpdateCurrentZoomAxes();
        
        [mZoomGrid, mZoomSteps] = ZoomLogGrid(mZoomMinPow, mZoomMaxPow, mZoomKeysNum);
        UpdateCurrentZoomAxes();
        
        [acx, acy] = GetCursorCoordOnAxes();
        
        ZoomAxes(direction, acx, acy)
        PointerCrossUpdate();
        SetDefaultZoomGrid();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomAxes(direction, cx, cy)
        %ZoomAxes Zoom axes in 2D and image modes
        
        [xLim, yLim] = GetAxesLimits();
        
        if fIsImage
            mZoomIndexX = ChangeZoomIndex(direction, mZoomIndexX);
            mZoomIndexY = mZoomIndexX;
            zoomPct = GetZoomPercent(mZoomIndexX);
            
            xLim = RecalcZoomAxesLimits('x', xLim, mDefaultXLim, cx, zoomPct);
            yLim = RecalcZoomAxesLimits('y', yLim, mDefaultYLim, cy, zoomPct);            
        else
            if fIsEnableZoomX
                mZoomIndexX = ChangeZoomIndex(direction, mZoomIndexX);
                zoomPct = GetZoomPercent(mZoomIndexX);
                
                xLim = RecalcZoomAxesLimits('x', xLim, mDefaultXLim, cx, zoomPct);
            end
            if fIsEnableZoomY
                mZoomIndexY = ChangeZoomIndex(direction, mZoomIndexY);
                zoomPct = GetZoomPercent(mZoomIndexY);
                
                yLim = RecalcZoomAxesLimits('y', yLim, mDefaultYLim, cy, zoomPct);
            end
        end
        
        SetAxesLimits(xLim, yLim);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function zoomPct = GetZoomPercent(zoomIndex, zoomGrid)
        %GetZoomPercent get zoom percent

        if (nargin < 2)
            zoomGrid = mZoomGrid;
        end
        
        zoomPct = zoomGrid(zoomIndex);       
    end
%--------------------------------------------------------------------------

%==========================================================================
    function zoomIndex = ChangeZoomIndex(direction, zoomIndex, zoomSteps)
        %ChangeZoomIndex
        
        if (nargin < 3)
            zoomSteps = mZoomSteps;
        end
        
        switch direction
            case 'plus'
                if (zoomIndex < zoomSteps)
                    zoomIndex = zoomIndex + 1;
                end
            case 'minus'
                if (zoomIndex > 1)
                    zoomIndex = zoomIndex - 1;
                end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function axLim = RecalcZoomAxesLimits(ax, axLim, axLimDflt, zcCrd, zoomPct)
        %RecalcZoomAxesLimits recalc axes limits
        
        if strcmp(get(hAx, [ax, 'scale']), 'log')
            axLim = log10(axLim);
            axLim = FixInfLogLimits(ax, axLim);
            axLimDflt = log10(axLimDflt);
            zcCrd = log10(zcCrd);
            isLog = true;
        else
            isLog = false;
        end
                
        if (zcCrd < axLim(1)), zcCrd = axLim(1); end
        if (zcCrd > axLim(2)), zcCrd = axLim(2); end
        
        rf = range(axLim);
        ra = range([axLim(1), zcCrd]);
        rb = range([zcCrd, axLim(2)]);
        
        cfa = ra / rf; 
        cfb = rb / rf;
        
        newRange = range(axLimDflt) * 100 / zoomPct;
        dRange = newRange - rf;
        
        axLim(1) = axLim(1) - dRange * cfa;
        axLim(2) = axLim(2) + dRange * cfb;
        
        if isLog
            axLim = 10.^axLim;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function UpdateCurrentZoomAxes()
        %UpdateCurrentZoomAxes
        
        [xLim, yLim] = GetAxesLimits();
        [curentZoomX, curentZoomY] = GetCurrentZoomAxesPercent(xLim, yLim);
        
        if (curentZoomX ~= GetZoomPercent(mZoomIndexX))
            [nu, mZoomIndexX] = min(abs(mZoomGrid - curentZoomX));  %#ok ([~, ...])
        end
        if (curentZoomY ~= GetZoomPercent(mZoomIndexY))
            [nu, mZoomIndexY] = min(abs(mZoomGrid - curentZoomY));  %#ok ([~, ...])
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function [curentZoomX, curentZoomY] = GetCurrentZoomAxesPercent(xLim, yLim)
        %GetCurrentZoomAxesPercent
        
        if strcmp(get(hAx, 'xscale'), 'log')
            xLim = log10(xLim);
            defaultXLim = log10(mDefaultXLim);
        else
            defaultXLim = mDefaultXLim;
        end
        if strcmp(get(hAx, 'yscale'), 'log')
            yLim = log10(yLim);
            defaultYLim = log10(mDefaultYLim);
        else
            defaultYLim = mDefaultYLim;
        end
        
        curentZoomX = range(defaultXLim) * 100 / range(xLim);
        curentZoomY = range(defaultYLim) * 100 / range(yLim);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouse3D(direction)
        %ZoomMouse3D
        
        if IsZoomMouseAllowed
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            ZoomAxes3D(direction, wcx, wcy)
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouseExtendBegin3D()
        %ZoomMouseExtendBegin3D
        
        if ~fIsZoomExtend3DAllowed
            UpdateCurrentZoomAxes3D();
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            % set new zoom grid for extend zoom
            SetNewZoomGrid3D(mZoomMinPow, mZoomMaxPow, mZoom3DExtendNum);
            
            mZoom3DStartX = wcx;
            mZoom3DStartY = wcy;
            
            mZoom3DBindX = wcx;
            mZoom3DBindY = wcy;
            
            fIsZoomExtend3DAllowed = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouseExtendEnd3D()
        %ZoomMouseExtendEnd3D
        
        if fIsZoomExtend3DAllowed
            SetDefaultZoomGrid3D();
            
            fIsZoomExtend3DAllowed = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomMouseExtend3D()
        %ZoomMouseExtended3D
        
        if fIsZoomExtend3DAllowed
            directions = {'minus', 'plus'};
            
            switch mZoomScroll
                case 'normal'
                case 'reverse'
                    directions = fliplr(directions);
            end
        
            % Heuristic for pixel change to camera zoom factor 
            % (taken from function ZOOM)
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            xy(1) = wcx - mZoom3DStartX;
            xy(2) = wcy - mZoom3DStartY;
            q = max(-0.9, min(0.9, sum(xy)/70)) + 1;
   
            if (q < 1)
                direction = directions{1};
            elseif (q > 1)
                direction = directions{2};
            else
                return;
            end
            
            ZoomAxes3D(direction, mZoom3DBindX, mZoom3DBindY)
            
            mZoom3DStartX = wcx;
            mZoom3DStartY = wcy;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomAxes3D(direction, cx, cy)
        %ZoomAxes3D
        
        mZoom3DIndex = ChangeZoomIndex(direction, mZoom3DIndex, mZoomSteps);
        zoomPct = GetZoomPercent(mZoom3DIndex, mZoomGrid);
        
        axPos = GetObjPos(hAx, 'pixels');
        
        [axPos(1), axPos(3)] = RecalcZoomAxesPos3D(axPos(1), axPos(3), ...
            mDefaultAxPos(3), cx, zoomPct);
        
        [axPos(2), axPos(4)] = RecalcZoomAxesPos3D(axPos(2), axPos(4), ...
            mDefaultAxPos(4), cy, zoomPct);
        
        SetObjPos(hAx, axPos, 'pixels');
    end
%--------------------------------------------------------------------------

%==========================================================================
    function [npc, nsz] = RecalcZoomAxesPos3D(pc, sz, dfltSz, zcCrd, zoomPct)
        %RecalcZoomAxesPos3D
        
        dd = dfltSz * zoomPct / 100 - sz;
        rng = range([pc zcCrd]);
        cf = rng / sz; 
        nsz = sz + dd;
        npc = pc - dd * cf;
    end
%--------------------------------------------------------------------------
        
%==========================================================================
    function UpdateCurrentZoomAxes3D()
        %UpdateCurrentZoomAxes3D
        
        curentZoom = GetCurrentZoomAxesPercent3D();
        [nu, mZoom3DIndex] = min(abs(mZoomGrid - curentZoom));  %#ok ([~, ...])
    end
%--------------------------------------------------------------------------

%==========================================================================
    function currentZoom = GetCurrentZoomAxesPercent3D()
        %GetCurrentZoomAxesPercent3D

        axPos = GetObjPos(hAx, 'pixels');
        currentZoom = axPos(3) / mDefaultAxPos(3) * 100;
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ZoomKeys3D(direction)
        %ZoomKeys3D
        
        UpdateCurrentZoomAxes3D();
        SetNewZoomGrid3D(mZoomMinPow, mZoomMaxPow, mZoom3DKeysNum);
        
        [wcx, wcy] = GetCursorCoordOnWindow('pixels');
        ZoomAxes3D(direction, wcx, wcy)
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetNewZoomGrid3D(minPow, maxPow, num)
        %SetNewZoomGrid3D set new zoom grid for 3D mode
        
        [mZoomGrid, mZoomSteps] = ZoomLogGrid(minPow, maxPow, num);
        UpdateCurrentZoomAxes3D();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetDefaultZoomGrid()
        %SetDefaultZoomGrid set default zoom grid
        
        [mDefaultZoomGrid, mDefaultZoomSteps] = ...
            ZoomLogGrid(mZoomMinPow, mZoomMaxPow, mZoomNum);
        
        mZoomGrid = mDefaultZoomGrid;
        mZoomSteps = mDefaultZoomSteps;
        
        mZoomIndexX = find(mZoomGrid == 100);
        mZoomIndexY = mZoomIndexX;
        mZoom3DIndex = mZoomIndexX;
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetDefaultZoomGrid3D()
        %SetDefaultZoomGrid for 3D mode
        
        mZoomGrid = mDefaultZoomGrid;
        mZoomSteps = mDefaultZoomSteps;
        UpdateCurrentZoomAxes3D();
    end
%--------------------------------------------------------------------------

%==========================================================================
    function VisibleAxesKeys3D()
        %VisibleAxesKeys3D
        
        axi = GetCurrentAxesIndex();
        
        if mAxesInfo(axi).isvisible
            set(hAx, 'Visible', 'off');
            mAxesInfo(axi).isvisible = false;
        else
            set(hAx, 'Visible', 'on');
            mAxesInfo(axi).isvisible = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SwitchAspectRatioKeys3D()
        %SwitchAspectRatioKeys3D
        
        axi = GetCurrentAxesIndex();
        
        if mAxesInfo(axi).isvis3d
            axis(hAx, 'normal');
            mAxesInfo(axi).isvis3d = false;
        else
            axis(hAx, 'vis3d');
            mAxesInfo(axi).isvis3d = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RotateMouseBegin3D()
        %RotateMouseBegin3D
        
        if ~fIsRotate3DAllowed
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            [az, el] = view(hAx);
            
            mRotStartAZ = az;
            mRotStartEL = el;
            mRotStartX = wcx;
            mRotStartY = wcy;
                        
            fIsRotate3DAllowed = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RotateMouseEnd3D()
        %RotateMouseEnd3D
        
        if fIsRotate3DAllowed
            fIsRotate3DAllowed = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RotateMouse3D()
        %RotateMouse3D
        
        if fIsRotate3DAllowed
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            dAZ = mRotStartX - wcx;
            dEL = mRotStartY - wcy;
            
            az = mRotStartAZ + dAZ;
            el = mRotStartEL + dEL;
            
            SetView3D(az, el);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RotateKeys3D(mode)
        %RotateKeys3D
        
        [az, el] = view(hAx);
                
        switch lower(mode)
            case 'xy'
                az = 0;
                el = 90;                
            case 'xz'
                az = 0;
                el = 0;
            case 'yz'
                az = 90;
                el = 0;
            case 'az+'
                az = az + mRot3DKeysInc;
            case 'az-'
                az = az - mRot3DKeysInc;
            case 'el+'
                el = el + mRot3DKeysInc;
            case 'el-'
                el = el - mRot3DKeysInc;
        end
        
        SetView3D(az, el)
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetView3D(az, el)
        %SetView3D
        
        if (el > 90), el = 90; end
        if (el < -90), el = -90; end
        
        view(hAx, [az, el]);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function PointerCrossOn()
        %PointerCrossOn
        
        if ~fIsPointerCross
            SetPointer('fullcrosshair');
            
            % text objects
            h = [
                text('Parent', hAx)     % left
                text('Parent', hAx)     % right
                text('Parent', hAx)     % bottom
                text('Parent', hAx)     % top
                ];
            
            % create pointer cross struct
            mPointerCross = struct(...
                'htext',    h, ...
                'left',     1, ...
                'right',    2, ...
                'bottom',   3, ...
                'top',      4);
            
            PointerCrossSetup();
            fIsPointerCross = true;
            PointerCrossUpdate();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function PointerCrossOff()
        %PointerCrossOff
        
        if fIsPointerCross
            delete(mPointerCross.htext);
            SetPointer('arrow');
            fIsPointerCross = false;
            mPointerCross = [];
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function PointerCrossSetup()
        %PointerCrossSetup
        
        left = mPointerCross.left;
        right = mPointerCross.right;
        bottom = mPointerCross.bottom;
        top = mPointerCross.top;
        
        vabt = {'top', 'bottom'};
        if fIsImage
            vabt = fliplr(vabt);
        end
        
        set(mPointerCross.htext(left), 'VerticalAlignment', 'bottom');
        set(mPointerCross.htext(right), 'VerticalAlignment', 'bottom');
        set(mPointerCross.htext(bottom), 'VerticalAlignment', vabt{1});
        set(mPointerCross.htext(top), 'VerticalAlignment', vabt{2});
        
        bgColor = [251 248 230]/255;
        set(mPointerCross.htext(left), 'BackgroundColor', bgColor);
        set(mPointerCross.htext(right), 'BackgroundColor', bgColor);
        set(mPointerCross.htext(bottom), 'BackgroundColor', bgColor);
        set(mPointerCross.htext(top), 'BackgroundColor', bgColor);
        
        edColor = [180 180 180]/255;
        set(mPointerCross.htext(left), 'EdgeColor', edColor);
        set(mPointerCross.htext(right), 'EdgeColor', edColor);
        set(mPointerCross.htext(bottom), 'EdgeColor', edColor);
        set(mPointerCross.htext(top), 'EdgeColor', edColor);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function PointerCrossUpdate()
        %PointerCrossUpdate
        
        if fIsPointerCross
            [xlim, ylim] = GetAxesLimits();
            [acx, acy] = GetCursorCoordOnAxes();
            
            left = mPointerCross.left;
            right = mPointerCross.right;
            bottom = mPointerCross.bottom;
            top = mPointerCross.top;
                        
            if fIsImage
                xValStr = sprintf(' %d ', round(acx));
                yValStr = sprintf(' %d ', round(acy));
            else
                xtick = get(hAx, 'XTick');
                ytick = get(hAx, 'YTick');
                
                %FIXME:
                prth = 5;
                
                [lenTick, maxi] = max(arrayfun(@(x) length(num2str(x)), xtick));
                atick = abs(xtick(maxi));
                flt = mod(atick, 1);
                
                if (flt == 0)
                    countDigX = lenTick + 1;
                    if countDigX > prth
                        countDigX = prth;
                    end
                else
                    countDigX = length(num2str(atick));
                    if (fix(acx) == 0)
                        countDigX = countDigX - 1;
                    end
                end
                
                [lenTick, maxi] = max(arrayfun(@(x) length(num2str(x)), ytick));
                atick = abs(ytick(maxi));
                flt = mod(atick, 1);
                
                if (flt == 0)
                    countDigY = lenTick + 1;
                    if countDigY > prth
                        countDigY = prth;
                    end
                else
                    countDigY = length(num2str(atick));
                    if (fix(acy) == 0)
                        countDigY = countDigY - 1;
                    end
                end
                
                xValStr = sprintf(' %.*g ', countDigX, acx);
                yValStr = sprintf(' %.*g ', countDigY, acy);
            end
            
            set(mPointerCross.htext(left), 'String', yValStr);
            set(mPointerCross.htext(right), 'String', yValStr);
            set(mPointerCross.htext(bottom), 'String', xValStr);
            set(mPointerCross.htext(top), 'String', xValStr);
            
            extent = get(mPointerCross.htext(left), 'Extent');
            xx = extent(3);
            
            if strcmp(get(hAx, 'xscale'), 'log')
                leftx = xlim(1);
            else
                leftx = xlim(1) - xx;
            end
            
            set(mPointerCross.htext(left), 'Position', [leftx, acy]);
            set(mPointerCross.htext(right), 'Position', [xlim(2) acy]);
            set(mPointerCross.htext(bottom), 'Position', [acx, ylim(1)]);
            set(mPointerCross.htext(top), 'Position', [acx, ylim(2)]);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RubberBandBegin()
        %RubberBandBegin
        
        if (~fIsRubberBandOn && ~fIsMagnifierOn)
            [acx, acy] = GetCursorCoordOnAxes();
            
            % create rubber band struct
            mRubberBand = struct(...
                'obj',	[patch('Parent', hAx), patch('Parent', hAx)], ...
                'x1',  	acx, ...
                'y1',  	acy, ...
                'x2',  	acx, ...
                'y2',  	acy);
            
            hAxes2d = GetHandlesAxes2D();
            if ~isempty(hAxes2d)
                set(hAxes2d, ...
                    'XLimMode', 'manual', ...
                    'YLimMode', 'manual');
            end
            
            RubberBandSetPos();
            RubberBandSetup();
            fIsRubberBandOn = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RubberBandEnd()
        %RubberBandEnd
        
        if fIsRubberBandOn
            fIsRubberBandOn = false;
            
            delete(mRubberBand.obj);          
            RubberBandZoomAxes();
            PointerCrossUpdate();
            mRubberBand = [];
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RubberBandUpdate()
        %RubberBandUpdate
        
        if fIsRubberBandOn
            [acx, acy] = GetCursorCoordOnAxes();
            
            mRubberBand.x2 = acx;
            mRubberBand.y2 = acy;
            RubberBandSetPos();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RubberBandSetPos()
        %RubberBandSetPos set position of rubber band
        
        x1 = mRubberBand.x1;
        y1 = mRubberBand.y1;
        x2 = mRubberBand.x2;
        y2 = mRubberBand.y2;
        
        set(mRubberBand.obj, ...
            'XData', [x1 x2 x2 x1], ...
            'YData', [y1 y1 y2 y2]);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RubberBandSetup()
        %RubberBandSetup
        
        set(mRubberBand.obj(1), ...
            'EdgeColor', 'w', ...
            'FaceColor', 'none', ...
            'LineWidth', 1.5, ...
            'LineStyle', '-');
        
        set(mRubberBand.obj(2), ...
            'EdgeColor', mRbEdgeColor, ...
            'FaceColor', mRbFaceColor, ...
            'FaceAlpha', mRbFaceAlpha, ...
            'LineWidth', 0.5, ...
            'LineStyle', '-');    
    end
%--------------------------------------------------------------------------

%==========================================================================
    function RubberBandZoomAxes()
        %RubberBandZoomAxes apply zoom from rubber band
        
        xLim = sort([mRubberBand.x1, mRubberBand.x2]);
        yLim = sort([mRubberBand.y1, mRubberBand.y2]);
        
        if (range(xLim) == 0 || range(yLim) == 0)
            return;
        end
        
        [zoomPctX, zoomPctY] = GetCurrentZoomAxesPercent(xLim, yLim);
        
        if fIsImage
            zoomPctX = min(zoomPctX, zoomPctY);
            zoomPctY = zoomPctX;
        end
        
        cx = mean(xLim);
        cy = mean(yLim);
        
        xLim = RecalcZoomAxesLimits('x', xLim, mDefaultXLim, cx, zoomPctX);
        yLim = RecalcZoomAxesLimits('y', yLim, mDefaultYLim, cy, zoomPctY);
        
        SetAxesLimits(xLim, yLim);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierOn()
        %MagnifierCreate
        
        if ~fIsMagnifierOn    
            if fIsPointerCross
                isPointerCross = true;
                PointerCrossOff();
            else
                isPointerCross = false;
            end
            
            mMgDirection = 'plus';
            
            % create magnifier struct
            mMagnifier = struct(...
                'obj',          copyobj(hAx, hFig), ...
                'frame_obj',    [], ...
                'size',         mMgSize, ...
                'zoom',         mMgZoom);
            
            fIsMagnifierOn = true;
            MagnifierSetup();
            MagnifierUpdate();
            
            if isPointerCross
                PointerCrossOn();
            end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierOff()
        %MagnifierOff
        
        if fIsMagnifierOn
            fIsMagnifierOn = false;
            set(hAx, 'Color', get(mMagnifier.obj, 'Color'));
            
            delete(mMagnifier.obj);
            mMagnifier = [];
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierUpdate()
        %MagnifierUpdate
        
        if fIsMagnifierOn            
            % see original idea of magnify by Rick Hindman -- 7/29/04
            % http://www.mathworks.com/matlabcentral/fileexchange/5961
            
            [acx, acy] = GetCursorCoordOnAxes();
            [wcx, wcy] = GetCursorCoordOnWindow('pixels');
            
            [xLim, yLim] = GetAxesLimits();
            
            if strcmp(get(hAx, 'xscale'), 'log')
                xLim = log10(xLim);
                xLim = FixInfLogLimits('x', xLim);
                acx = log10(acx);
                isXLog = true;
            else
                isXLog = false;
            end
            if strcmp(get(hAx, 'yscale'), 'log')
                yLim = log10(yLim);
                yLim = FixInfLogLimits('y', yLim);
                acy = log10(acy);
                isYLog = true;
            else
                isYLog = false;
            end
            
            figPos = GetObjPos(hFig, 'pixels');
            axPos = GetObjPos(hAx, 'normalized');
            
            % always square magnifier
            pbar = get(hAx, 'PlotBoxAspectRatio');
            af = pbar(1) / pbar(2);
            if (af == 1 && (pbar(1) == 1 && pbar(2) == 1))
                af = figPos(3) / figPos(4);
            end
            
            mgSizePix = round(mMagnifier.size);
            mgZoom = mMagnifier.zoom;
            
            mgSize = mgSizePix / figPos(3); % normalized size
            
            mgPos(3) = mgSize * 2;
            mgPos(4) = mgPos(3) * af;
            
            mg3 = round(mgPos(3) * figPos(3));
            mg4 = round(mgPos(4) * figPos(4));
            
            if (mg4 < mg3)
                mgSize = (mgSizePix * (mg3 / mg4)) / figPos(3);
            end
            
            mgPos(3) = mgSize * 2;
            mgPos(4) = mgPos(3) * af;
            
            mgPos(1) = wcx / figPos(3) - mgSize;
            mgPos(2) = wcy / figPos(4) - mgSize * af;
            
            mgXLim = acx + (1 / mgZoom) * (mgPos(3) / axPos(3)) * diff(xLim) * [-0.5 0.5];
            mgYLim = acy + (1 / mgZoom) * (mgPos(4) / axPos(4)) * diff(yLim) * [-0.5 0.5];
            
            SetObjPos(mMagnifier.obj, mgPos, 'normalized');
            
            if isXLog
                mgXLim = 10.^mgXLim;
            end
            if isYLog
                mgYLim = 10.^mgYLim;
            end
            
            set(mMagnifier.obj, ...
                'XLim', mgXLim, ...
                'YLim', mgYLim);
            
            MagnifierBorderUpdate();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierSetup()
        %MagnifierSetup

        set(mMagnifier.obj, ...
            'Box', 'on', ...
            'XMinorTick', 'on', ...
            'YMinorTick', 'on');
        
        title(mMagnifier.obj, '');
        xlabel(mMagnifier.obj, ''); 
        ylabel(mMagnifier.obj, '');
        
        if fIsImage
            mMagnifier.frame_obj = ...
                [patch('Parent', mMagnifier.obj), ...
                patch('Parent', mMagnifier.obj)];
            
            set(mMagnifier.frame_obj, 'FaceColor', 'none');
            
            set(mMagnifier.frame_obj(1), ...
                'LineWidth', 1.5, ...
                'EdgeColor', 'w')
            set(mMagnifier.frame_obj(2), ...
                'LineWidth', 1, ...
                'EdgeColor', 'k')
            
            MagnifierBorderUpdate();
        end
        
        hLines = findobj(mMagnifier.obj, 'Type', 'line');
        if ~isempty(hLines)
            if (mMgLinesWidth ~= 1)
                set(hLines, 'LineWidth', mMgLinesWidth);
            end
        end
        
        set(hAx, 'Color', get(hAx, 'Color')*mMgShadow);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierBorderUpdate()
        %MagnifierBorderUpdate
        
        if fIsImage
            x = get(mMagnifier.obj, 'XLim');
            y = get(mMagnifier.obj, 'YLim');
            
            set(mMagnifier.frame_obj, ...
                'XData', [x(1) x(2) x(2) x(1)], ...
                'YData', [y(1) y(1) y(2) y(2)]);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierSizeChange(direction)
        %MagnifierSizeChange
        
        if fIsMagnifierOn
            switch direction
                case 'plus'
                    if (mMagnifier.size < mMgMaxSize)
                        mMagnifier.size = mMagnifier.size + mMgSizeStep;
                    end
                case 'minus'
                    if (mMagnifier.size > mMgMinSize)
                        mMagnifier.size = mMagnifier.size - mMgSizeStep;
                    end
            end
            
            MagnifierUpdate();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierZoomChange(direction)
        %MagnifierZoomChange
        
        if fIsMagnifierOn
            switch direction
                case 'plus'
                    if (mMagnifier.zoom < mMgMaxZoom)
                        mMagnifier.zoom = mMagnifier.zoom * mMgZoomStep;
                    end
                case 'minus'
                    if (mMagnifier.zoom > mMgMinZoom)
                        mMagnifier.zoom = mMagnifier.zoom / mMgZoomStep;
                    end
            end
            
            MagnifierUpdate();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function MagnifierReset()
        %MagnifierReset
        
        if fIsMagnifierOn
            mMagnifier.size = mMgSize;
            mMagnifier.zoom = mMgZoom;
            MagnifierUpdate();
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetLinkAxesKeys(modifier)
        %SetLinkAxesKeys Set Linking of 2-D axes
        
        if isempty(modifier)
            mLinkOpt = 'xy';
        else
            switch modifier{1}
                case 'control'
                    mLinkOpt = 'x';
                case 'alt'
                    mLinkOpt = 'y';
            end
        end
        
        if fIsLinkAxesOn
            LinkAxesOff();
        else
            LinkAxesOn();
        end
        
        UserData = get(hFig, 'UserData');
        UserData.tools.islinkaxeson = fIsLinkAxesOn;
        set(hFig, 'UserData', UserData);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function LinkAxesOn()
        %LinkAxesOn On of 2-D axes linking
        
        if ~fIsLinkAxesOn
            hAxes2d = GetHandlesAxes2D();
            if (length(hAxes2d) > 1)
                linkaxes(hAxes2d, mLinkOpt);
                fIsLinkAxesOn = true;
                SetFigureName();
            end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function LinkAxesOff()
        %LinkAxesOff Off of 2-D axes linking
        
        if fIsLinkAxesOn
            fIsLinkAxesOn = false;
            
            SetFigureName();
            hAxes2d = GetHandlesAxes2D();
            linkaxes(hAxes2d, 'off');    
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DeleteOldTools()
        %DeleteOldTools
        
        UserData = get(hFig, 'UserData');
        
        if (~isempty(UserData) && isfield(UserData, 'tools'))
            if ~isempty(UserData.tools.pointercross)
                mPointerCross = UserData.tools.pointercross;
                fIsPointerCross = true;
                PointerCrossOff();
                UserData.tools.pointercross = [];
            end
            
            if UserData.tools.islinkaxeson
                fIsLinkAxesOn = true;
                LinkAxesOff();
                UserData.tools.islinkaxeson = false;
            end
            
            set(hFig, 'UserData', UserData);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function hAxes2d = GetHandlesAxes2D()
        %GetHandlesAxes2D Get handles of 2-D axes
        
        isAxes2d = arrayfun(@(x) x.is2d && ~x.islegend, mAxesInfo);
        hAxes2d = hAxes(isAxes2d);
        
        if ~isempty(hAxes2d)
            % Set current axes on first position
            hAxes2d(eq(hAxes2d, hAx)) = 0;
            hAxes2d = sort(hAxes2d);
            hAxes2d(eq(hAxes2d, 0)) = hAx;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ResetAxesToOrigView()
        %ResetAxesToOrigView reset axes to original limits
        
        SetAxesLimits(mDefaultXLim, mDefaultYLim);
        PointerCrossUpdate();
        
        mZoomIndexX = find(mZoomGrid == 100);
        mZoomIndexY = mZoomIndexX;
    end
%--------------------------------------------------------------------------

%==========================================================================
    function ResetAxesToOrigView3D()
        %ResetAxesToOrigView3D reset axes to original position in 3D mode
        zoomPct = 100;
        
        % position reset
        axi = GetCurrentAxesIndex();
        pos = mAxesInfo(axi).normposition;
        SetObjPos(hAx, pos, 'normalized');
        
        % view reset
        resetplotview(hAx, 'ApplyStoredView'); % (!!!) undocumented function
        
        if mAxesInfo(axi).isvis3d
            axis(hAx, 'vis3d');
        end
        
        % zoom reset
        mZoom3DIndex = find(mZoomGrid == 100);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function [x, y, z] = GetCursorCoordOnAxes()
        %GetCursorCoordOnAxImg
        
        crd = get(hAx, 'CurrentPoint');
        x = crd(2,1);
        y = crd(2,2);
        z = crd(2,3);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function [x, y] = GetCursorCoordOnWindow(units)
        %GetCursorCoordOnWindow
        
        if (nargin < 1), units = 'pixels'; end
        
        dfltUnits = get(hFig, 'Units');
        set(hFig, 'Units', units);
        
        crd = get(hFig, 'CurrentPoint');
        x = crd(1); 
        y = crd(2);
        
        set(hFig, 'Units', dfltUnits);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function pos = GetObjPos(h, units)
        %GetObjPos get object position
        
        if (nargin < 2), units = get(h, 'Units'); end
        
        dfltUnits = get(h, 'Units');
        set(h, 'Units', units);
        pos = get(h, 'Position');
        set(h, 'Units', dfltUnits);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetObjPos(h, pos, units)
        %SetObjPos set object position
        
        if (nargin < 3), units = get(h, 'Units'); end
        
        dfltUnits = get(h, 'Units');
        set(h, 'Units', units);
        set(h, 'Position', pos);
        set(h, 'Units', dfltUnits);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function [xLim, yLim] = GetAxesLimits()
        %GetAxesLimits
        
        xLim = get(hAx, 'XLim');
        yLim = get(hAx, 'YLim');
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetAxesLimits(xLim, yLim)
        %SetAxesLimits
        
        set(hAx, 'XLim', xLim);
        set(hAx, 'YLim', yLim);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetPointerCrossKeys()
        %SetPointerCrossKeys set pointer fullcross
        
        if fIsPointerCross
            PointerCrossOff();
        else
            PointerCrossOn();
        end
        
        UserData = get(hFig, 'UserData');
        UserData.tools.pointercross = mPointerCross;
        set(hFig, 'UserData', UserData);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetPointer(pointerType)
        %SetPointer set pointer symbol
        
        set(hFig, 'Pointer', pointerType);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetAxesGridKeys()
        %SetAxesGridKeys on/off axes grid
        
        if fIsAxesGrid
            action = 'off';
            fIsAxesGrid = false;
        else
            action = 'on';
            fIsAxesGrid = true;
        end
        
        set(hAx, 'XGrid', action, 'YGrid', action, 'ZGrid', action);
        
        if fIsMagnifierOn
            set(mMagnifier.obj, 'XGrid', action, 'YGrid', action);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetSmoothKeys()
        %SetSmoothKeys on/off cmoothing plots
        
        if fIsSmoothing
            action = 'off';
            fIsSmoothing = false;
        else
            action = 'on';
            fIsSmoothing = true;
        end
        
        if ~fIsImage
            %FIXME: bug with switching opengl/painter renderer here
            %Lost figure focus
            hLine = findobj(hAx, 'Type', 'Line');
            if ~isempty(hLine)
                set(hLine, 'LineSmooth', action);   % !!! Undocumented property
            end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function [zg, st] = ZoomLogGrid(a, b, n)
        %ZoomLogGrid log zoom grid
        
        zg = unique(round(logspace(a, b, n)));
        
        zg(zg<10) = [];	% begin zoom == 10%
        st = length(zg);
        
        if isempty(find(zg == 100, 1))
            error('dragzoom:badZoomGridOptions', 'Options for zoom grid is bad.')
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function tf = IsZoomMouseAllowed()
        %IsZoomMouseAllowed
        
        [wcx, wcy] = GetCursorCoordOnWindow();
        figPos = get(hFig, 'Position');
        
        if (wcx >= 1 && wcx <= figPos(3) && wcy >= 1 && wcy <= figPos(4))
            tf = true;
        else
            tf = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function tf = IsImageOnAxes(ax)
        %IsImageOnAxes
        
        if (nargin < 1), ax = hAx; end
        
        h = findobj(ax, 'Type', 'Image');
        
        if isempty(h)
            tf = false;
        else
            tf = true;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function tf = IsAxes2D(ax)
        %IsAxes2D
        
        if (nargin < 1), ax = hAx; end
        
        tf = is2D(ax); % (!!!) internal undocumented function
    end
%--------------------------------------------------------------------------

%==========================================================================
    function tf = IsLegendAxes(ax)
        %IsLegendAxes
        
        tf = strcmp(get(ax, 'Tag'), 'legend');
    end
%--------------------------------------------------------------------------

%==========================================================================
    function targetInBounds = IsInBoundsAxes(ax)
        %InBoundsAxes Check if the user clicked within the bounds of the axes. If not, do nothing
        
        targetInBounds = true;
        tol = 3e-16;
        cp = get(ax, 'CurrentPoint');
        
        XLims = get(ax, '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(ax, '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(ax, '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
    end
%--------------------------------------------------------------------------

%==========================================================================
    function tf = IsCurrentAxes(ax)
        %IsCurrentAxes
        
        hcAx = get(hFig, 'CurrentAxes');
        tf = eq(ax, hcAx);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function tf = IsAxesVis3D(ax)
        %IsAxesVis3D
        
        visProp = {
            get(ax, 'PlotBoxAspectRatioMode')
            get(ax, 'DataAspectRatioMode')
            get(ax, 'CameraViewAngleMode')
            };
        
        tf = all(strcmpi(visProp, 'manual'));
    end
%--------------------------------------------------------------------------

%==========================================================================
    function AxesInfo = GetAxesInfo()
        %GetAxesInfo make and get axes info struct
        
        countAxes = length(hAxes);
        
        AxesInfo = struct(...
            'handle',       cell(1, countAxes), ...
            'iscurrent',    cell(1, countAxes), ...
            'is2d',         cell(1, countAxes), ...
            'isimage',      cell(1, countAxes), ...  
            'isvisible',    cell(1, countAxes), ...  
            'isvis3d',      cell(1, countAxes), ...
            'islegend',     cell(1, countAxes), ...
            'position',     cell(1, countAxes), ...
            'normposition', cell(1, countAxes), ...
            'xlim',         cell(1, countAxes), ...
            'ylim',         cell(1, countAxes), ...
            'camtarget',    cell(1, countAxes), ...
            'camposition',  cell(1, countAxes));
        
        for i = 1:countAxes
            h = hAxes(i);
            
            AxesInfo(i).handle = h;
            AxesInfo(i).iscurrent = IsCurrentAxes(h);
            AxesInfo(i).is2d = IsAxes2D(h);
            AxesInfo(i).isimage = IsImageOnAxes(h);
            AxesInfo(i).isvisible = strcmpi(get(h, 'Visible'), 'on');
            AxesInfo(i).isvis3d = IsAxesVis3D(h);
            AxesInfo(i).islegend = IsLegendAxes(h);
            AxesInfo(i).position = GetObjPos(h, 'pixels');
            if p_memory.first_flag
                p_memory.first_flag = false;
                AxesInfo(i).normposition = GetObjPos(h, 'normalized');
                p_memory.AxesInfo(i).normposition = AxesInfo(i).normposition;
            else
                i_use = min(i,length(p_memory.AxesInfo));
                AxesInfo(i_use).normposition = p_memory.AxesInfo(i_use).normposition;
            end
            AxesInfo(i).xlim = get(h, 'XLim');
            AxesInfo(i).ylim = get(h, 'YLim');
            AxesInfo(i).camtarget = get(h, 'CameraTarget');
            AxesInfo(i).camposition = get(h, 'CameraPosition');
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SelectAxesUnderCursor()
        %SelectAxesUnderCursor select axes under cursor as current
        
        axi = GetAxesIndexUnderCursor();
        
        if (axi > 0)
            fIsEnableControl = true;
            
            if ~mAxesInfo(axi).iscurrent
                caxi = GetCurrentAxesIndex();
                
                if isempty(caxi)
                    DeleteInvalidAxesInfo();
                    
                    axi = GetAxesIndexUnderCursor();
                    isCax2d = mAxesInfo(axi).is2d;
                else
                    isCax2d = mAxesInfo(caxi).is2d;
                end
                
                SetCurrentAxes(axi);
                
                % for fix "legend" axes capture
                if mAxesInfo(axi).islegend;
                    fIsMouseOnLegend = true;
                else
                    fIsMouseOnLegend = false;
                end
                
                % check callbacks
                if (isCax2d ~= mAxesInfo(axi).is2d)
                    % if dimension of axes has changed
                    SetCallbacks();
                    
                    if fIsPointerCross
                        % disable pointer cross
                        PointerCrossOff()
                    end
                else
                    if fIsPointerCross
                        % reset pointer cross
                        PointerCrossOff()
                        SetPointerCrossKeys()
                    end
                end
            end
        else
            fIsEnableControl = false;
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function SetCurrentAxes(axi)
        %SetCurrentAxes set current axes and work mode
        
        hAx = mAxesInfo(axi).handle;
        
        set(hFig, 'CurrentAxes', hAx);
        for i = 1:numel(mAxesInfo)
            mAxesInfo(i).iscurrent = false;
        end
        mAxesInfo(axi).iscurrent = true;
        
        fIsAxes2D = mAxesInfo(axi).is2d;
        fIsImage = mAxesInfo(axi).isimage;
        
        mDefaultAxPos = mAxesInfo(axi).position;
        mDefaultXLim = mAxesInfo(axi).xlim;
        mDefaultYLim = mAxesInfo(axi).ylim;
        
        % save info to work correctly after saving figures
        UserData = get(hFig, 'UserData');
        UserData.axesinfo = mAxesInfo;
        set(hFig, 'UserData', UserData);
    end
%--------------------------------------------------------------------------

%==========================================================================
    function axi = GetCurrentAxesIndex()
        %GetCurrentAxesIndex
        
        axi = [];
        
        for i = 1:numel(mAxesInfo)
            if (ishandle(mAxesInfo(i).handle) && mAxesInfo(i).iscurrent)
                axi = i;
                return;
            end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function axi = GetAxesIndexUnderCursor()
        %FindAxesUnderCursor find current axes under cursor
        
        axi = GetCurrentAxesIndex();
        
        if ~fIsSelectedCurrentAxes
            caxi = GetCurrentAxesIndex();
            if ~IsInBoundsAxes(mAxesInfo(caxi).handle)
                axi = 0;
            end
            return;
        end
        
        for i = 1:numel(mAxesInfo)
            if (ishandle(mAxesInfo(i).handle) && IsInBoundsAxes(mAxesInfo(i).handle))
                axi = i;
                return;
            else
                axi = 0; % without axes
            end
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function axLim = FixInfLogLimits(ax, axLim)
        %FixInfLogLimits Fix Axes Inf Log Limits
        
        if (~all(isfinite(axLim)) || ~all(isreal(axLim)))
            
            % The following code has been taken from zoom.m
            % If any of the public limits are inf then we need the actual limits
            % by getting the hidden deprecated RenderLimits.
            oldstate = warning('off', 'MATLAB:HandleGraphics:NonfunctionalProperty:RenderLimits');
            renderlimits = get(hAx, 'RenderLimits');
            warning(oldstate);
            
            switch ax
                case 'x'
                    axLim = renderlimits(1:2);
                case 'y'
                    axLim = renderlimits(3:4);
            end
            axLim = log10(axLim);
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function DeleteInvalidAxesInfo()
        %DeleteInvalidAxesInfo
        
        invalidAxes = arrayfun(@(x) ~ishandle(x.handle), mAxesInfo);
        mAxesInfo(invalidAxes) = [];
        hAxes(invalidAxes) = [];
    end
%--------------------------------------------------------------------------

%==========================================================================
    function isWithStatus = ParseInputs(varargin)
        %ParseInputs parse input arguments
        
        isWithStatus = false;
        
        switch nargin
            case 0
                hObj = gcf;
                status = 'on';
                ih = 0;
            case 1
                if ischar(varargin{1})
                    isWithStatus = true;
                    hObj = gcf;
                    status = varargin{1};
                    ih = 2;
                    is = 1;
                else
                    hObj = varargin{1};
                    status = 'on';
                    ih = 1;
                    is = 2;
                end
            case 2
                isWithStatus = true;
                hObj = varargin{1};
                status = varargin{2};
                ih = 1;
                is = 2;
        end
        
        switch lower(status)
            case 'on'
                fIsEnabled = true;
            case 'off'
                fIsEnabled = false;
            otherwise
                error('dragzoom:invalidInputs', ...
                    'Input Argument %d must be a string "on" or "off".', is)
        end
        
        if all(ishandle(hObj))
            handleType = get(hObj(1), 'Type');
            
            switch handleType
                case 'axes'
                    hAxes = unique(hObj);
                    hFig = ancestor(hAxes(1), 'figure');
                    
                case 'figure'
                    hFig = hObj;
                    hAxes = findobj(hFig, 'Type', 'axes'); % all axes
                    
                    if isempty(hAxes)
                        % warning('dragzoom:notFoundAxes', 'Not found axes objects on figure.')
                    end
                    
                otherwise
                    error('dragzoom:invalidHandle', ...
                        'Input Argument %d must be figure or axes handle.', ih)
            end
        else
            error('dragzoom:invalidInputs', ...
                'Input Argument %d must be a figure or axes handle.', ih)
        end
    end
%--------------------------------------------------------------------------

%==========================================================================
    function res = range(x)
        %RANGE
        
        res = abs(diff([min(x) max(x)]));
    end
%--------------------------------------------------------------------------

end % DRAGZOOM

function plot_interactive_marker(varargin)

persistent h
global g_im
% Make enough handlers at the first
if isempty(h), for i = 1:10, for j = 1:10, h{i,j}.first_flag = true; end; end; end

% Parse input arguments
p = inputParser;
addParameter(p,'fig_idx',1);
addParameter(p,'subfig_idx',1);
addParameter(p,'T',eye(4,4));       % pose
addParameter(p,'clen',1.0);         % center length
addParameter(p,'clen_er',1.2);
addParameter(p,'tlw',4.0);          % translation line width
addParameter(p,'rlw',2.0);          % rotation line width
addParameter(p,'sr',0.1);           % sphere radius
addParameter(p,'fa',0.6);
addParameter(p,'USE_DRAGZOOM',true);
addParameter(p,'VERBOSE',false);
parse(p,varargin{:});
fig_idx         = p.Results.fig_idx;
subfig_idx      = p.Results.subfig_idx;
T               = p.Results.T;
clen            = p.Results.clen;
clen_er         = p.Results.clen_er;
tlw             = p.Results.tlw;
rlw             = p.Results.rlw;
sr              = p.Results.sr;
fa              = p.Results.fa;
USE_DRAGZOOM    = p.Results.USE_DRAGZOOM;
VERBOSE         = p.Results.VERBOSE;

% Helper functions for interactive marker
% ---------------------- X-translation ----------------------
    function bdf_xsphere(hcbo,evt) % x-translation click
        if VERBOSE
            fprintf('[%d-%d] x-translation click. \n',fig_idx,subfig_idx);
        end
        if 0
            h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        else
            h{fig_idx,subfig_idx}.ax = hcbo.Parent.Parent;
        end
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_xsphere;
        fig.WindowButtonUpFcn = @wbuf_xsphere;
        % Get direction?
        dir_temp = h{fig_idx,subfig_idx}.T(1:3,1:3)*[1,0,0]';
        h{fig_idx,subfig_idx}.dir2 = dir_temp / norm(dir_temp);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_xsphere(hcbo,evt) % x-translation move
        if VERBOSE
            fprintf('[%d-%d] x-translation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        p1 = cp(1,:)';
        p2 = cp(2,:)';
        dir1 = p1 - p2;
        dir2 = h{fig_idx,subfig_idx}.dir2;
        n = cr3(dir1)*dir2;
        n1 = cr3(dir1)*n;
        origin2 = h{fig_idx,subfig_idx}.T(1:3,4);
        c2 = origin2 + ( ((p1 - origin2)'*n1 )/(dir2'*n1)) * dir2;
        p = c2 - clen_er * clen * dir2;
        h{fig_idx,subfig_idx}.T(1:3,4) = p;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_xsphere(hcbo,evt) % x-translation unclick
        if VERBOSE
            fprintf('[%d-%d] x-translation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end
    function bdf_xsphere2(hcbo,evt) % x-translation click
        if VERBOSE
            fprintf('[%d-%d] x-translation click. \n',fig_idx,subfig_idx);
        end
        if 0
            h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        else
            h{fig_idx,subfig_idx}.ax = hcbo.Parent.Parent;
        end
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_xsphere2;
        fig.WindowButtonUpFcn = @wbuf_xsphere2;
        % Get direction?
        dir_temp = h{fig_idx,subfig_idx}.T(1:3,1:3)*[1,0,0]';
        h{fig_idx,subfig_idx}.dir2 = dir_temp / norm(dir_temp);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_xsphere2(hcbo,evt) % x-translation move
        if VERBOSE
            fprintf('[%d-%d] x-translation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        p1 = cp(1,:)';
        p2 = cp(2,:)';
        dir1 = p1 - p2;
        dir2 = h{fig_idx,subfig_idx}.dir2;
        n = cr3(dir1)*dir2;
        n1 = cr3(dir1)*n;
        origin2 = h{fig_idx,subfig_idx}.T(1:3,4);
        c2 = origin2 + ( ((p1 - origin2)'*n1 )/(dir2'*n1)) * dir2;
        p = c2 + clen * dir2;
        h{fig_idx,subfig_idx}.T(1:3,4) = p;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_xsphere2(hcbo,evt) % x-translation unclick
        if VERBOSE
            fprintf('[%d-%d] x-translation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end

% ---------------------- Y-translation ----------------------
    function bdf_ysphere(hcbo,evt) % y-translation click
        if VERBOSE
            fprintf('[%d-%d] y-translation click. \n',fig_idx,subfig_idx);
        end
        if 0
            h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        else
            h{fig_idx,subfig_idx}.ax = hcbo.Parent.Parent;
        end
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_ysphere;
        fig.WindowButtonUpFcn = @wbuf_ysphere;
        % Get direction?
        dir_temp = h{fig_idx,subfig_idx}.T(1:3,1:3)*[0,1,0]';
        h{fig_idx,subfig_idx}.dir2 = dir_temp / norm(dir_temp);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_ysphere(hcbo,evt) % y-translation move
        if VERBOSE
            fprintf('[%d-%d] y-translation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        p1 = cp(1,:)';
        p2 = cp(2,:)';
        dir1 = p1 - p2;
        dir2 = h{fig_idx,subfig_idx}.dir2;
        n = cr3(dir1)*dir2;
        n1 = cr3(dir1)*n;
        origin2 = h{fig_idx,subfig_idx}.T(1:3,4);
        c2 = origin2 + ( ((p1 - origin2)'*n1 )/(dir2'*n1)) * dir2;
        p = c2 - clen_er * clen * dir2;
        h{fig_idx,subfig_idx}.T(1:3,4) = p;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_ysphere(hcbo,evt) % y-translation unclick
        if VERBOSE
            fprintf('[%d-%d] y-translation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end
    function bdf_ysphere2(hcbo,evt) % y-translation click
        if VERBOSE
            fprintf('[%d-%d] y-translation click. \n',fig_idx,subfig_idx);
        end
        if 0
            h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        else
            h{fig_idx,subfig_idx}.ax = hcbo.Parent.Parent;
        end
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_ysphere2;
        fig.WindowButtonUpFcn = @wbuf_ysphere2;
        % Get direction?
        dir_temp = h{fig_idx,subfig_idx}.T(1:3,1:3)*[0,1,0]';
        h{fig_idx,subfig_idx}.dir2 = dir_temp / norm(dir_temp);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_ysphere2(hcbo,evt) % y-translation move
        if VERBOSE
            fprintf('[%d-%d] y-translation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        p1 = cp(1,:)';
        p2 = cp(2,:)';
        dir1 = p1 - p2;
        dir2 = h{fig_idx,subfig_idx}.dir2;
        n = cr3(dir1)*dir2;
        n1 = cr3(dir1)*n;
        origin2 = h{fig_idx,subfig_idx}.T(1:3,4);
        c2 = origin2 + ( ((p1 - origin2)'*n1 )/(dir2'*n1)) * dir2;
        p = c2 + clen * dir2;
        h{fig_idx,subfig_idx}.T(1:3,4) = p;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_ysphere2(hcbo,evt) % y-translation unclick
        if VERBOSE
            fprintf('[%d-%d] y-translation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end

% ---------------------- Z-translation ----------------------
    function bdf_zsphere(hcbo,evt) % z-translation click
        if VERBOSE
            fprintf('[%d-%d] z-translation click. \n',fig_idx,subfig_idx);
        end
        if 0
            h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        else
            h{fig_idx,subfig_idx}.ax = hcbo.Parent.Parent;
        end
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_zsphere;
        fig.WindowButtonUpFcn = @wbuf_zsphere;
        % Get direction?
        dir_temp = h{fig_idx,subfig_idx}.T(1:3,1:3)*[0,0,1]';
        h{fig_idx,subfig_idx}.dir2 = dir_temp / norm(dir_temp);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_zsphere(hcbo,evt) % z-translation move
        if VERBOSE
            fprintf('[%d-%d] z-translation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        p1 = cp(1,:)';
        p2 = cp(2,:)';
        dir1 = p1 - p2;
        dir2 = h{fig_idx,subfig_idx}.dir2;
        n = cr3(dir1)*dir2;
        n1 = cr3(dir1)*n;
        origin2 = h{fig_idx,subfig_idx}.T(1:3,4);
        c2 = origin2 + ( ((p1 - origin2)'*n1 )/(dir2'*n1)) * dir2;
        p = c2 - clen_er * clen * dir2;
        h{fig_idx,subfig_idx}.T(1:3,4) = p;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_zsphere(hcbo,evt) % z-translation unclick
        if VERBOSE
            fprintf('[%d-%d] z-translation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end
    function bdf_zsphere2(hcbo,evt) % z-translation click
        if VERBOSE
            fprintf('[%d-%d] z-translation click. \n',fig_idx,subfig_idx);
        end
        if 0
            h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        else
            h{fig_idx,subfig_idx}.ax = hcbo.Parent.Parent;
        end
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_zsphere2;
        fig.WindowButtonUpFcn = @wbuf_zsphere2;
        % Get direction?
        dir_temp = h{fig_idx,subfig_idx}.T(1:3,1:3)*[0,0,1]';
        h{fig_idx,subfig_idx}.dir2 = dir_temp / norm(dir_temp);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_zsphere2(hcbo,evt) % z-translation move
        if VERBOSE
            fprintf('[%d-%d] z-translation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        p1 = cp(1,:)';
        p2 = cp(2,:)';
        dir1 = p1 - p2;
        dir2 = h{fig_idx,subfig_idx}.dir2;
        n = cr3(dir1)*dir2;
        n1 = cr3(dir1)*n;
        origin2 = h{fig_idx,subfig_idx}.T(1:3,4);
        c2 = origin2 + ( ((p1 - origin2)'*n1 )/(dir2'*n1)) * dir2;
        p = c2 + clen * dir2;
        h{fig_idx,subfig_idx}.T(1:3,4) = p;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_zsphere2(hcbo,evt) % z-translation unclick
        if VERBOSE
            fprintf('[%d-%d] z-translation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end

% ---------------------- X-rotation ----------------------
    function bdf_xrot(hcbo,evt) % x-rotation button click
        if VERBOSE
            fprintf('[%d-%d] x-rotation click. \n',fig_idx,subfig_idx);
        end
        x = hcbo.XData;
        y = hcbo.YData;
        z = hcbo.ZData;
        P = [x;y;z];
        p1 = P(:,1);
        p2 = P(:,2);
        p3 = P(:,3);
        sn_temp = cr3(p1-p2)*(p2-p3);
        h{fig_idx,subfig_idx}.sn = sn_temp/norm(sn_temp);
        h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        q1 = cp(1,:)';
        q2 = cp(2,:)';
        nume = -h{fig_idx,subfig_idx}.sn'*(q1 - p1);
        deno = h{fig_idx,subfig_idx}.sn'*(q2 - q1);
        h{fig_idx,subfig_idx}.apoc = p1;
        s = nume/deno;
        h{fig_idx,subfig_idx}.intersecini = q1 + s*(q2-q1);
        qa = P(:,1);
        qb = P(:,33);
        h{fig_idx,subfig_idx}.cc = 0.5*(qa +qb);
        h{fig_idx,subfig_idx}.R0 = h{fig_idx,subfig_idx}.T(1:3, 1:3);
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_xrot;
        fig.WindowButtonUpFcn = @wbuf_xrot;
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_xrot(hcbo,evt)
        if VERBOSE
            fprintf('[%d-%d] x-rotation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        q1 = cp(1,:)';
        q2 = cp(2,:)';
        p1 = h{fig_idx,subfig_idx}.apoc;
        nume = -h{fig_idx,subfig_idx}.sn'*(q1 - p1);
        deno = h{fig_idx,subfig_idx}.sn'*(q2 - q1);
        s = nume/deno;
        current_intersec = q1 + s*(q2-q1);
        aa = (current_intersec - h{fig_idx,subfig_idx}.cc);
        aa = aa/norm(aa);
        bb = (h{fig_idx,subfig_idx}.intersecini - h{fig_idx,subfig_idx}.cc);
        bb =  bb/norm(bb);
        costheta = aa'*bb;
        st = -cr3(aa)*bb;
        for i_idx = 1:3
            if abs(h{fig_idx,subfig_idx}.sn(i_idx)) > 1e-10
                sintheta = st(i_idx)/h{fig_idx,subfig_idx}.sn(i_idx);
                break
            end
        end
        theta = atan2(sintheta, costheta);
        R = expm(theta*cr3(h{fig_idx,subfig_idx}.sn));
        h{fig_idx,subfig_idx}.T(1:3, 1:3) = R*h{fig_idx,subfig_idx}.R0;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_xrot(hcbo,evt)
        if VERBOSE
            fprintf('[%d-%d] x-rotation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end

% ---------------------- Y-rotation ----------------------
    function bdf_yrot(hcbo,evt) % y-rotation button click
        if VERBOSE
            fprintf('[%d-%d] y-rotation click. \n',fig_idx,subfig_idx);
        end
        x = hcbo.XData;
        y = hcbo.YData;
        z = hcbo.ZData;
        P = [x;y;z];
        p1 = P(:,1);
        p2 = P(:,2);
        p3 = P(:,3);
        sn_temp = cr3(p1-p2)*(p2-p3);
        h{fig_idx,subfig_idx}.sn = sn_temp/norm(sn_temp);
        h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        q1 = cp(1,:)';
        q2 = cp(2,:)';
        nume = -h{fig_idx,subfig_idx}.sn'*(q1 - p1);
        deno = h{fig_idx,subfig_idx}.sn'*(q2 - q1);
        h{fig_idx,subfig_idx}.apoc = p1;
        s = nume/deno;
        h{fig_idx,subfig_idx}.intersecini = q1 + s*(q2-q1);
        qa = P(:,1);
        qb = P(:,33);
        h{fig_idx,subfig_idx}.cc = 0.5*(qa + qb);
        h{fig_idx,subfig_idx}.R0 = h{fig_idx,subfig_idx}.T(1:3, 1:3);
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_yrot;
        fig.WindowButtonUpFcn = @wbuf_yrot;
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_yrot(hcbo,evt)
        if VERBOSE
            fprintf('[%d-%d] y-rotation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        q1 = cp(1,:)';
        q2 = cp(2,:)';
        p1 = h{fig_idx,subfig_idx}.apoc;
        nume = -h{fig_idx,subfig_idx}.sn'*(q1 - p1);
        deno = h{fig_idx,subfig_idx}.sn'*(q2 - q1);
        s = nume/deno;
        current_intersec = q1 + s*(q2-q1);
        aa = (current_intersec - h{fig_idx,subfig_idx}.cc);
        aa = aa/norm(aa);
        bb = (h{fig_idx,subfig_idx}.intersecini - h{fig_idx,subfig_idx}.cc);
        bb =  bb/norm(bb);
        costheta = aa'*bb;
        st = -cr3(aa)*bb;
        for i_idx = 1:3
            if abs(h{fig_idx,subfig_idx}.sn(i_idx)) > 1e-10
                sintheta = st(i_idx)/h{fig_idx,subfig_idx}.sn(i_idx);
                break
            end
        end
        theta = atan2(sintheta, costheta);
        R = expm(theta*cr3(h{fig_idx,subfig_idx}.sn));
        h{fig_idx,subfig_idx}.T(1:3, 1:3) = R*h{fig_idx,subfig_idx}.R0;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_yrot(hcbo,evt)
        if VERBOSE
            fprintf('[%d-%d] y-rotation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end

% ---------------------- Z-rotation ----------------------
    function bdf_zrot(hcbo,evt) % z-rotation button click
        if VERBOSE
            fprintf('[%d-%d] z-rotation click. \n',fig_idx,subfig_idx);
        end
        x = hcbo.XData;
        y = hcbo.YData;
        z = hcbo.ZData;
        P = [x;y;z];
        p1 = P(:,1);
        p2 = P(:,2);
        p3 = P(:,3);
        sn_temp = cr3(p1-p2)*(p2-p3);
        h{fig_idx,subfig_idx}.sn = sn_temp/norm(sn_temp);
        h{fig_idx,subfig_idx}.ax = hcbo.Parent;
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        q1 = cp(1,:)';
        q2 = cp(2,:)';
        nume = -h{fig_idx,subfig_idx}.sn'*(q1 - p1);
        deno = h{fig_idx,subfig_idx}.sn'*(q2 - q1);
        h{fig_idx,subfig_idx}.apoc = p1;
        s = nume/deno;
        h{fig_idx,subfig_idx}.intersecini = q1 + s*(q2-q1);
        qa = P(:,1);
        qb = P(:,33);
        h{fig_idx,subfig_idx}.cc = 0.5*(qa + qb);
        h{fig_idx,subfig_idx}.R0 = h{fig_idx,subfig_idx}.T(1:3, 1:3);
        fig = h{fig_idx,subfig_idx}.ax.Parent;
        fig.WindowButtonMotionFcn = @wbmf_zrot;
        fig.WindowButtonUpFcn = @wbuf_zrot;
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'clicked';
    end
    function wbmf_zrot(hcbo,evt)
        if VERBOSE
            fprintf('[%d-%d] z-rotation move. \n',fig_idx,subfig_idx);
        end
        cp = h{fig_idx,subfig_idx}.ax.CurrentPoint;
        q1 = cp(1,:)';
        q2 = cp(2,:)';
        p1 = h{fig_idx,subfig_idx}.apoc;
        nume = -h{fig_idx,subfig_idx}.sn'*(q1 - p1);
        deno = h{fig_idx,subfig_idx}.sn'*(q2 - q1);
        s = nume/deno;
        current_intersec = q1 + s*(q2-q1);
        aa = (current_intersec - h{fig_idx,subfig_idx}.cc);
        aa = aa/norm(aa);
        bb = (h{fig_idx,subfig_idx}.intersecini - h{fig_idx,subfig_idx}.cc);
        bb =  bb/norm(bb);
        costheta = aa'*bb;
        st = -cr3(aa)*bb;
        for i_idx = 1:3
            if abs(h{fig_idx,subfig_idx}.sn(i_idx)) > 1e-10
                sintheta = st(i_idx)/h{fig_idx,subfig_idx}.sn(i_idx);
                break
            end
        end
        theta = atan2(sintheta, costheta);
        R = expm(theta*cr3(h{fig_idx,subfig_idx}.sn));
        h{fig_idx,subfig_idx}.T(1:3, 1:3) = R*h{fig_idx,subfig_idx}.R0;
        % Update plot
        update_plot(h{fig_idx,subfig_idx}.T);
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updating';
    end
    function wbuf_zrot(hcbo,evt)
        if VERBOSE
            fprintf('[%d-%d] z-rotation unclick. \n',fig_idx,subfig_idx);
        end
        % Clear button motion and button up function handlers
        set(hcbo,'WindowButtonMotionFcn','')
        set(hcbo,'WindowButtonUpFcn','')
        if USE_DRAGZOOM
            dragzoom; % go back to use dragzoom
        end
        % Set status
        g_im{fig_idx,subfig_idx}.status = 'updated';
    end


    function pcross = cr3( p )
        pcross = [0, -p(3), p(2); p(3), 0, -p(1); -p(2), p(1), 0];
    end

    function [Xs,Ys,Zs,X_xtrn,X_xtrn2,X_ytrn,X_ytrn2,X_ztrn,X_ztrn2,X_xrot,X_yrot,X_zrot,...
            xyz_fv,xyz_fv_small] = ...
            get_ingredients(T)
        p = t2p(T)';
        R = t2r(T);
        % Plot interactive marker
        theta = (0:pi/32:2*pi)';
        x = clen*cos(theta);
        y = clen*sin(theta);
        l = length(x);
        z = zeros(l,1);
        if 0
            [Xs, Ys, Zs] = sphere(40);
        else
            [Xs, Ys, Zs] = ellipsoid(0,0,0,1,1,1,20);
            xyz_fv = surf2patch(sr*Xs, sr*Ys, sr*Zs);
            xyz_fv_small = surf2patch(sr*Xs*0.6,sr*Ys*0.6,sr*Zs*0.6);
        end
        Xs = sr*Xs;
        Ys = sr*Ys;
        Zs = sr*Zs;
        X_xtrn = [-clen, 0, 0; clen_er*clen, 0, 0];
        X_xtrn = X_xtrn*R' + ones(2,1)*p;
        X_xtrn2 = [-0, 0, 0; clen_er*clen, 0, 0];
        X_xtrn2 = X_xtrn2*R' + ones(2,1)*p;
        X_ytrn = [0, -clen, 0; 0, clen_er*clen, 0];
        X_ytrn = X_ytrn*R' + ones(2,1)*p;
        X_ytrn2 = [0, -0, 0; 0, clen_er*clen, 0];
        X_ytrn2 = X_ytrn2*R' + ones(2,1)*p;
        X_ztrn = [0, 0, -clen; 0, 0, clen_er*clen];
        X_ztrn = X_ztrn*R' + ones(2,1)*p;
        X_ztrn2 = [0, 0, -0; 0, 0, clen_er*clen];
        X_ztrn2 = X_ztrn2*R' + ones(2,1)*p;
        X_xrot = [z,x,y]*R' + ones(l,1)*p;
        X_yrot = [y,z,x]*R' + ones(l,1)*p;
        X_zrot = [x,y,z]*R' + ones(l,1)*p;
    end

    function update_plot(T)
        [Xs,Ys,Zs,X_xtrn,X_xtrn2,X_ytrn,X_ytrn2,X_ztrn,X_ztrn2,X_xrot,X_yrot,X_zrot,...
            xyz_fv,xyz_fv_small] = ...
            get_ingredients(T);
        % x-translation
        h{fig_idx,subfig_idx}.xaxis.XData = X_xtrn2(:,1);
        h{fig_idx,subfig_idx}.xaxis.YData = X_xtrn2(:,2);
        h{fig_idx,subfig_idx}.xaxis.ZData = X_xtrn2(:,3);
        if 0
            h{fig_idx,subfig_idx}.xsphere.XData = Xs+X_xtrn(2,1);
            h{fig_idx,subfig_idx}.xsphere.YData = Ys+X_xtrn(2,2);
            h{fig_idx,subfig_idx}.xsphere.ZData = Zs+X_xtrn(2,3);
            h{fig_idx,subfig_idx}.xsphere2.XData = 0.5*Xs+X_xtrn(1,1);
            h{fig_idx,subfig_idx}.xsphere2.YData = 0.5*Ys+X_xtrn(1,2);
            h{fig_idx,subfig_idx}.xsphere2.ZData = 0.5*Zs+X_xtrn(1,3);
        else
            tform = p2t(X_xtrn(2,:));
            set(h{fig_idx,subfig_idx}.xsphere_t,'Matrix',tform);
            tform = p2t(X_xtrn(1,:));
            set(h{fig_idx,subfig_idx}.xsphere2_t,'Matrix',tform);
        end
        % y-translation
        h{fig_idx,subfig_idx}.yaxis.XData = X_ytrn2(:,1);
        h{fig_idx,subfig_idx}.yaxis.YData = X_ytrn2(:,2);
        h{fig_idx,subfig_idx}.yaxis.ZData = X_ytrn2(:,3);
        if 0
            h{fig_idx,subfig_idx}.ysphere.XData = Xs+X_ytrn(2,1);
            h{fig_idx,subfig_idx}.ysphere.YData = Ys+X_ytrn(2,2);
            h{fig_idx,subfig_idx}.ysphere.ZData = Zs+X_ytrn(2,3);
            h{fig_idx,subfig_idx}.ysphere2.XData = 0.5*Xs+X_ytrn(1,1);
            h{fig_idx,subfig_idx}.ysphere2.YData = 0.5*Ys+X_ytrn(1,2);
            h{fig_idx,subfig_idx}.ysphere2.ZData = 0.5*Zs+X_ytrn(1,3);
        else
            tform = p2t(X_ytrn(2,:));
            set(h{fig_idx,subfig_idx}.ysphere_t,'Matrix',tform);
            tform = p2t(X_ytrn(1,:));
            set(h{fig_idx,subfig_idx}.ysphere2_t,'Matrix',tform);
        end
        % z-translation
        h{fig_idx,subfig_idx}.zaxis.XData = X_ztrn2(:,1);
        h{fig_idx,subfig_idx}.zaxis.YData = X_ztrn2(:,2);
        h{fig_idx,subfig_idx}.zaxis.ZData = X_ztrn2(:,3);
        if 0
            h{fig_idx,subfig_idx}.zsphere.XData = Xs+X_ztrn(2,1);
            h{fig_idx,subfig_idx}.zsphere.YData = Ys+X_ztrn(2,2);
            h{fig_idx,subfig_idx}.zsphere.ZData = Zs+X_ztrn(2,3);
            h{fig_idx,subfig_idx}.zsphere2.XData = 0.5*Xs+X_ztrn(1,1);
            h{fig_idx,subfig_idx}.zsphere2.YData = 0.5*Ys+X_ztrn(1,2);
            h{fig_idx,subfig_idx}.zsphere2.ZData = 0.5*Zs+X_ztrn(1,3);
        else
            tform = p2t(X_ztrn(2,:));
            set(h{fig_idx,subfig_idx}.zsphere_t,'Matrix',tform);
            tform = p2t(X_ztrn(1,:));
            set(h{fig_idx,subfig_idx}.zsphere2_t,'Matrix',tform);
        end
        % x-rotation
        h{fig_idx,subfig_idx}.xrot.XData = X_xrot(:,1);
        h{fig_idx,subfig_idx}.xrot.YData = X_xrot(:,2);
        h{fig_idx,subfig_idx}.xrot.ZData = X_xrot(:,3);
        % y-rotation
        h{fig_idx,subfig_idx}.yrot.XData = X_yrot(:,1);
        h{fig_idx,subfig_idx}.yrot.YData = X_yrot(:,2);
        h{fig_idx,subfig_idx}.yrot.ZData = X_yrot(:,3);
        % z-rotation
        h{fig_idx,subfig_idx}.zrot.XData = X_zrot(:,1);
        h{fig_idx,subfig_idx}.zrot.YData = X_zrot(:,2);
        h{fig_idx,subfig_idx}.zrot.ZData = X_zrot(:,3);
        drawnow limitrate;
        % Update global interactive marker T
        g_im{fig_idx,subfig_idx}.T = h{fig_idx,subfig_idx}.T;
    end

if h{fig_idx,subfig_idx}.first_flag || ... % first flag
        ~ishandle(h{fig_idx,subfig_idx}.fig)
    h{fig_idx,subfig_idx}.first_flag = false;
    % Initialize figure
    h{fig_idx,subfig_idx}.fig = figure(fig_idx);
    % Initialize T
    h{fig_idx,subfig_idx}.T = T;
    g_im{fig_idx,subfig_idx}.T = T;
    [Xs,Ys,Zs,X_xtrn,X_xtrn2,X_ytrn,X_ytrn2,X_ztrn,X_ztrn2,X_xrot,X_yrot,X_zrot,...
        xyz_fv,xyz_fv_small] = ...
        get_ingredients(T);
    
    % x-translation (axis and sphere)
    h{fig_idx,subfig_idx}.xaxis = line(X_xtrn2(:,1), X_xtrn2(:,2), X_xtrn2(:,3),...
        'color','r', 'LineWidth',tlw,'ButtonDownFcn','');
    if 0
        h{fig_idx,subfig_idx}.xsphere = surface(Xs+X_xtrn(2,1), Ys+X_xtrn(2,2), Zs+X_xtrn(2,3),...
            'facecolor','r', 'linestyle','none','facelighting','gouraud','ButtonDownFcn',@bdf_xsphere);
        h{fig_idx,subfig_idx}.xsphere2 = surface(0.5*Xs+X_xtrn(1,1), 0.5*Ys+X_xtrn(1,2), 0.5*Zs+X_xtrn(1,3),...
            'facecolor','r', 'linestyle','none','facelighting','gouraud','ButtonDownFcn',@bdf_xsphere2);
    else
        h{fig_idx,subfig_idx}.xsphere = patch(xyz_fv,...
            'FaceColor','r','FaceAlpha',fa,'EdgeColor','none',...
            'facelighting','gouraud','ButtonDownFcn',@bdf_xsphere);
        h{fig_idx,subfig_idx}.xsphere_t = hgtransform;
        set(h{fig_idx,subfig_idx}.xsphere,'parent',h{fig_idx,subfig_idx}.xsphere_t);
        tform = p2t(X_xtrn(2,:));
        set(h{fig_idx,subfig_idx}.xsphere_t,'Matrix',tform);
        h{fig_idx,subfig_idx}.xsphere2 = patch(xyz_fv_small,...
            'FaceColor','r','FaceAlpha',fa,'EdgeColor','none',...
            'facelighting','gouraud','ButtonDownFcn',@bdf_xsphere2);
        h{fig_idx,subfig_idx}.xsphere2_t = hgtransform;
        set(h{fig_idx,subfig_idx}.xsphere2,'parent',h{fig_idx,subfig_idx}.xsphere2_t);
        tform = p2t(X_xtrn(1,:));
        set(h{fig_idx,subfig_idx}.xsphere2_t,'Matrix',tform);
    end
    
    % y-translation
    h{fig_idx,subfig_idx}.yaxis = line(X_ytrn2(:,1), X_ytrn2(:,2), X_ytrn2(:,3),...
        'color','g', 'LineWidth',tlw,'ButtonDownFcn','');
    if 0
        h{fig_idx,subfig_idx}.ysphere = surface(Xs+X_ytrn(2,1), Ys+X_ytrn(2,2), Zs+X_ytrn(2,3),...
            'facecolor','g', 'linestyle','none','facelighting','gouraud','ButtonDownFcn',@bdf_ysphere);
        h{fig_idx,subfig_idx}.ysphere2 = surface(0.5*Xs+X_ytrn(1,1), 0.5*Ys+X_ytrn(1,2), 0.5*Zs+X_ytrn(1,3),...
            'facecolor','g', 'linestyle','none','facelighting','gouraud','ButtonDownFcn',@bdf_ysphere2);
    else
        h{fig_idx,subfig_idx}.ysphere = patch(xyz_fv,...
            'FaceColor','g','FaceAlpha',fa,'EdgeColor','none',...
            'facelighting','gouraud','ButtonDownFcn',@bdf_ysphere);
        h{fig_idx,subfig_idx}.ysphere_t = hgtransform;
        set(h{fig_idx,subfig_idx}.ysphere,'parent',h{fig_idx,subfig_idx}.ysphere_t);
        tform = p2t(X_ytrn(2,:));
        set(h{fig_idx,subfig_idx}.ysphere_t,'Matrix',tform);
        h{fig_idx,subfig_idx}.ysphere2 = patch(xyz_fv_small,...
            'FaceColor','g','FaceAlpha',fa,'EdgeColor','none',...
            'facelighting','gouraud','ButtonDownFcn',@bdf_ysphere2);
        h{fig_idx,subfig_idx}.ysphere2_t = hgtransform;
        set(h{fig_idx,subfig_idx}.ysphere2,'parent',h{fig_idx,subfig_idx}.ysphere2_t);
        tform = p2t(X_ytrn(1,:));
        set(h{fig_idx,subfig_idx}.ysphere2_t,'Matrix',tform);
    end
    
    % z-translation
    h{fig_idx,subfig_idx}.zaxis = line(X_ztrn2(:,1), X_ztrn2(:,2), X_ztrn2(:,3),...
        'color','b', 'LineWidth',tlw,'ButtonDownFcn','');
    if 0
        h{fig_idx,subfig_idx}.zsphere = surface(Xs+X_ztrn(2,1), Ys+X_ztrn(2,2), Zs+X_ztrn(2,3),...
            'facecolor','b', 'linestyle','none','facelighting','gouraud','ButtonDownFcn',@bdf_zsphere);
        h{fig_idx,subfig_idx}.zsphere2 = surface(0.5*Xs+X_ztrn(1,1), 0.5*Ys+X_ztrn(1,2), 0.5*Zs+X_ztrn(1,3),...
            'facecolor','b', 'linestyle','none','facelighting','gouraud','ButtonDownFcn',@bdf_zsphere2);
    else
        h{fig_idx,subfig_idx}.zsphere = patch(xyz_fv,...
            'FaceColor','b','FaceAlpha',fa,'EdgeColor','none',...
            'facelighting','gouraud','ButtonDownFcn',@bdf_zsphere);
        h{fig_idx,subfig_idx}.zsphere_t = hgtransform;
        set(h{fig_idx,subfig_idx}.zsphere,'parent',h{fig_idx,subfig_idx}.zsphere_t);
        tform = p2t(X_ztrn(2,:));
        set(h{fig_idx,subfig_idx}.zsphere_t,'Matrix',tform);
        h{fig_idx,subfig_idx}.zsphere2 = patch(xyz_fv_small,...
            'FaceColor','b','FaceAlpha',fa,'EdgeColor','none',...
            'facelighting','gouraud','ButtonDownFcn',@bdf_zsphere2);
        h{fig_idx,subfig_idx}.zsphere2_t = hgtransform;
        set(h{fig_idx,subfig_idx}.zsphere2,'parent',h{fig_idx,subfig_idx}.zsphere2_t);
        tform = p2t(X_ztrn(1,:));
        set(h{fig_idx,subfig_idx}.zsphere2_t,'Matrix',tform);
    end
    
    % x-rotation
    h{fig_idx,subfig_idx}.xrot = line(X_xrot(:,1), X_xrot(:,2), X_xrot(:,3),...
        'color','r', 'LineWidth',rlw,'ButtonDownFcn',@bdf_xrot);
    
    % y-rotation
    h{fig_idx,subfig_idx}.yrot = line(X_yrot(:,1), X_yrot(:,2), X_yrot(:,3),...
        'color','g', 'LineWidth',rlw,'ButtonDownFcn',@bdf_yrot);
    
    % z-rotation
    h{fig_idx,subfig_idx}.zrot = line(X_zrot(:,1), X_zrot(:,2), X_zrot(:,3),...
        'color','b', 'LineWidth',rlw,'ButtonDownFcn',@bdf_yrot);
    
    % Set status
    g_im{fig_idx,subfig_idx}.status = 'initialized';
    
    
else
    % Update T
    if VERBOSE
        fprintf('[%d-%d] update T. \n',fig_idx,subfig_idx);
    end
    h{fig_idx,subfig_idx}.T = T;
    update_plot(h{fig_idx,subfig_idx}.T);
    
    % Set status
    g_im{fig_idx,subfig_idx}.status = 'updated';
end


%%
function p = t2p(T)
%
% Get p and R from T
%

p = T([1,2,3],4);
end

function R = t2r(T)
%
% Get p and R from T
%

R = T([1,2,3],[1,2,3]);
end

function T = p2t(p)
%
% Get T from p and R
%

R = eye(3,3);
T = [R,reshape(p,[3,1]);...
    0,0,0,1];
end

end

function plot_title(title_str,varargin)
%
% Plot a title
%
persistent h

% Make enough handlers at the first
if isempty(h), for i = 1:10, h{i}.first_flag = true; end; end

% Parse options
ps = inputParser;
addParameter(ps,'fig_idx',1);
addParameter(ps,'tfs',13);               % title font size
addParameter(ps,'tfn','consolas');       % title font name 
addParameter(ps,'tfc','k');              % title font color
addParameter(ps,'interpreter','none');   % 'none'
parse(ps,varargin{:});
fig_idx         = ps.Results.fig_idx;
tfs             = ps.Results.tfs;
tfn             = ps.Results.tfn;
tfc             = ps.Results.tfc;
interpreter     = ps.Results.interpreter;

% Plot title 
if isequal(interpreter,'latex')
    title_str =  strrep(title_str,'_','-'); % change '_' to '-' for latex formatting
end
if h{fig_idx}.first_flag || ~ishandle(h{fig_idx}.fig)
    h{fig_idx}.first_flag = false;
    h{fig_idx}.fig = figure(fig_idx);
    h{fig_idx}.title = title(title_str,...
        'fontsize',tfs,'fontname',tfn,'interpreter',interpreter,'color',tfc);
else
    h{fig_idx}.title.String = title_str;
    h{fig_idx}.title.Color  = tfc;
end

 

function T = pr2t(p,R)
%
% Get T from p and R
%

if isempty(p)
    p = cv([0,0,0]);
end

if isempty(R)
    R = eye(3,3);
end

T = [R,reshape(p,[3,1]);...
    0,0,0,1];

 

function rpy_deg = r2rpy(R)
%
% Get roll, pitch, and yaw [in degree] from a rotation matrix
%  alpha: yaw
%  beta: pitch
%  gamma: roll 
%

r = atan2(R(3,2),R(3,3));
p = atan2(-R(3,1),sqrt(R(3,2)*R(3,2)+R(3,3)*R(3,3)));
y = atan2(R(2,1),R(1,1));

rpy_deg = [r,p,y];
function fig = set_fig(fig,varargin)
%
% Set figure
%
% Usage:
% set_fig(figure(1),'pos',[0.5,0.4,0.3,0.55],...
%     'view_info',view_info,'axis_info',1.5*[-1,+1,-1,+1,-1,+1],'AXIS_EQUAL',1,'GRID_ON',1,...
%     'REMOVE_MENUBAR',1,'USE_DRAGZOOM',1,'SET_CAMLIGHT',1,'SET_MATERIAL','METAL',...
%     'SET_AXISLABEL',1,'afs',18);
%
persistent h

% Make enough handlers at the first
if isempty(h), for i = 1:100,h{i}.first_flag = true; end; end

% Parse options
ps = inputParser;
addParameter(ps,'pos',[0.0,0.5,0.3,0.5]);            % position of the figure
addParameter(ps,'view_info','');                     % view information
addParameter(ps,'axis_info','');                     % axis information
addParameter(ps,'HOLD_ON',1);                        % hold on
addParameter(ps,'AXIS_EQUAL',1) ;                    % axis equal
addParameter(ps,'AXIS_OFF',0);                       % axis off
addParameter(ps,'GRID_ON',1);                        % grid on
addParameter(ps,'REMOVE_MENUBAR',1);                 % remove menubar
addParameter(ps,'USE_DRAGZOOM',1);                   % use dragzoome
addParameter(ps,'SET_CAMLIGHT',1);                   % set camera light
addParameter(ps,'SET_MATERIAL','METAL');             % set material property 'SHINY' 'DULL' 'METAL'
addParameter(ps,'SET_AXISLABEL',1);                  % axis label
addParameter(ps,'ax_str','X');                       % x label string
addParameter(ps,'ay_str','Y');                       % y label string
addParameter(ps,'az_str','Z');                       % z label string
addParameter(ps,'afs',13);                           % axis font size
addParameter(ps,'interpreter','latex');               % text interpreter
addParameter(ps,'gcf_color','w');                    % background color
addParameter(ps,'NO_MARGIN',0);                      % no margin on axes
parse(ps,varargin{:});
pos             = ps.Results.pos;
view_info       = ps.Results.view_info;
axis_info       = ps.Results.axis_info;
HOLD_ON         = ps.Results.HOLD_ON;
AXIS_EQUAL      = ps.Results.AXIS_EQUAL;
AXIS_OFF        = ps.Results.AXIS_OFF;
GRID_ON         = ps.Results.GRID_ON;
REMOVE_MENUBAR  = ps.Results.REMOVE_MENUBAR;
USE_DRAGZOOM    = ps.Results.USE_DRAGZOOM;
SET_CAMLIGHT    = ps.Results.SET_CAMLIGHT;
SET_MATERIAL    = ps.Results.SET_MATERIAL;
SET_AXISLABEL   = ps.Results.SET_AXISLABEL;
ax_str          = ps.Results.ax_str;
ay_str          = ps.Results.ay_str;
az_str          = ps.Results.az_str;
afs             = ps.Results.afs;
interpreter     = ps.Results.interpreter;
gcf_color       = ps.Results.gcf_color;
NO_MARGIN       = ps.Results.NO_MARGIN;

% Set figure configuration
if h{fig.Number}.first_flag || ~ishandle(h{fig.Number}.fig)
    h{fig.Number}.first_flag = false;
    h{fig.Number}.fig = fig;
    
    % No margin
    if NO_MARGIN
        axes('Parent',fig,'Position',[0,0,1,1]);
    end
    
    % Set figure position
    if ~isempty(pos)
        sz = get(0, 'ScreenSize');
        fig_pos = [pos(1)*sz(3),pos(2)*sz(4),pos(3)*sz(3),pos(4)*sz(4)];
        set(fig,'Position',fig_pos);
    end
    if HOLD_ON
        hold on;
    end
    
    % View information
    if ~isempty(view_info)
        switch length(view_info)
            case 1, view(view_info);
            case 2, view(view_info(1),view_info(2));
            case 3, view(view_info);
        end
    end
    
    % Make axis equal
    if AXIS_EQUAL
        axis equal;
    end
    
    % Axis information (this should come AFTER axis equal)
    if ~isempty(axis_info)
        axis(axis_info);
    end
    
    % Axis off
    if AXIS_OFF
        axis off;
    end
    
    % Grid on
    if GRID_ON
        grid on;
    end
    
    % Remove menubar
    if REMOVE_MENUBAR
        set(h{fig.Number}.fig,'MenuBar','none');
    end
    
    % Use dragzoom
    if USE_DRAGZOOM
        dragzoom;
    end
    
    % Set camera light
    if SET_CAMLIGHT
        camlight('infinite');
    end
    
    % Set material
    if ~isempty(SET_MATERIAL)
        material(SET_MATERIAL);
    end
    
    % Set axis label
    if SET_AXISLABEL
        xlabel(ax_str,'fontname','consolas','fontsize',afs,'interpreter',interpreter);
        ylabel(ay_str,'fontname','consolas','fontsize',afs,'interpreter',interpreter);
        zlabel(az_str,'fontname','consolas','fontsize',afs,'interpreter',interpreter);
    end
    
    % Set background color
    if ~isempty(gcf_color)
        set(gcf,'color',gcf_color);
    end
    
    
else
    
end

fig = h{fig.Number}.fig;
function p = t2p(T)
%
% Get p and R from T
%

p = T([1,2,3],4);

function str = vec2str(vec,format)
% vector to string
%
% [1 2 3] => [1, 2, 3]
%
if nargin == 1
    format = '%.4f';
end
str = strjoin(cellstr(num2str(vec(:),format)),', ');
str = ['[',str,']'];

以上是所有依赖的代码。

(3)具体执行demo演示的代码如下:

%% Plot interactive markers
% ccc
R2D = 180/pi;
D2R = pi/180;
global g_im  
% Plot interactive markers---pos【起点xy,长高】
fig = set_fig(figure(1),'pos',[0.2,0.2,0.5,0.7],...
    'view_info',[88,16],'axis_info',5*[-1,+1,-2,+2,-1,+1],'AXIS_EQUAL',1,'GRID_ON',1,...
    'REMOVE_MENUBAR',1,'USE_DRAGZOOM',1,'SET_CAMLIGHT',1,'SET_MATERIAL','METAL',...
    'SET_AXISLABEL',1,'afs',18,'interpreter','latex');
plot_interactive_marker('subfig_idx',1,'T',pr2t([0,0,-2],rpy2r(zeros(1,3))),'clen',2.0);
plot_interactive_marker('subfig_idx',2,'T',pr2t([0,0,+2],rpy2r(zeros(1,3))),'clen',2.0);
tick = 0;
while ishandle(fig)
    tick = tick + 1;
    title_str = sprintf('[%d] p1:%s rpy1:%s \np2:%s rpy2:%s',...
        tick,...
        vec2str(t2p(g_im{1}.T)','%.1f'),...
        vec2str(R2D*r2rpy(t2r(g_im{1}.T)),'%.0f'),...
        vec2str(t2p(g_im{2}.T)','%.1f'),...
        vec2str(R2D*r2rpy(t2r(g_im{2}.T)),'%.0f'));
    plot_title(title_str);
    drawnow;
end

执行效果如下:【鼠标拖动交互,相对于世界坐标系的变化如下显示】

 (4)将上述效果中的位置作为机器人模型的末端输入机器人逆解中实现拖动仿真

代码如下:【提前安装机器人工具箱robotics toolbox 10.3】

clc
clear
mdl_puma560;
fig = figure(1);
view(3)
hold on
 axis([-1 1 -1 1 -1 2])
 T=p560.fkine(qz*pi/180);
 R=[T.n T.o  T.a];
 numMats = size(R,3);H = zeros(4,4,numMats,'like',R);
           H(1:3,1:3,:) = R;
           H(4,4,:) = ones(1,1,numMats,'like',R);
           H(1:3,4)=T.t;%SE3 xyzMovement To Homogeneous matrix
              Hinit=H;
 p560.plot(qz)
 hold on
global g_im  
 plot_interactive_marker('subfig_idx',1,'T',Hinit,'clen',0.3);
 tick=0;
 while ishandle(fig)
    tick = tick + 1;
    title_str = sprintf('[%d] p1:%s rpy1:%s ',...
        tick,...
        vec2str(t2p(g_im{1}.T)','%.1f'),...
        vec2str(180/pi*r2rpy(t2r(g_im{1}.T)),'%.0f'));
    plot_title(title_str);hold on
    drawnow;
    figure(2)
    view(3)
    hold on
     axis([-1 1 -1 1 -1.1 1.2])
Tchange=g_im{1}.T;
Q=p560.ikine(Tchange);

 p560.plot(Q)
plot3(Tchange(1,4),Tchange(2,4),Tchange(3,4),'.','LineWidth',3,'color','r');
 end

效果:【当然要想效果更好需要较好的逆运动学算法,这里调用的是机器人工具箱的逆解函数】

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JianRobSim

嘤嘤其名,求其友声!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值