function digitize(action,varargin)
%DIGITIZE: Image Digitization Tool
%
%Description:
%
%digitize is a user interfase that allows
%to import image files (.bmp, etc) into MatLab
%to digitize data from. Previous sessions can 
%also be loaded.
%
%Intructions:
% First load a new image by entering the menu "Load"
% and clicking on "Load New Image".
% First the Referennce Points (points on the image whos 
% coordinates are known) must be selected.
% This is done by entering the "Transformation" menu
% and then selecting "Add Points".
% Left-click on the points reference points and Right-click
% when finished.
% Then Right-click on any reference point to enter it's
% "true" coordinate.
% When the coordinated for all the reference points have been
% entered the "Reference Data Complete" is checked inside the
% "Transformation" menu.
% To digitize, select "Add Points" from the "Digitization" menu.
% You may start digitizing points on the image at any time,
% but their transformed coordinated are not calculated until
% the reference data is complete. To see a digitized point's
% transformed coordinate, right-click on a point. 
%
%A session .mat file contains:
%jt,kt - reference points in image axes coordinates
%xt,yt - reference points in transformed coordinates
%img - the RGB 'Cdata' of the image
%jd,kd - digitized points in image axes coordinates
%xd,yd - digitized points in transformed coordinates
%R - the transformation matrix
%The transformation matrix is used to convert from 
%the image axes coordinate system to the desired
%coordinate system by entered the "true" of transformed
%coordinates manually.
%
%The first order linear transformation matrix is:
%   R = [sx ry 0;... 
%       ry sy 0;... 
%       tx ty 1]
%   where s -> scaling, r -> rotation, t -> translation
%
%A second and third order fits are also available in the
%"Transformation" menu. These are useful for distorted images
%or maps of unknown projection. The drawback is that larger
%orders requiere more data.
%
%Click on "Residuals" in the "Transformation" menu
%to see the residuals of the reference points.
%The residuals are defined as the "true" or "given"
%value minus the calculated value using the transformation
%matrix. The residuals should be of the order of magnitude
%of the pixcel size. 
%
%The pixcel size can be seen by clicking on 
%"Pixcel Size" in the "Transformation" menu. 
%The pixcel size is the horizontal x vertical direction. 
%Note that these are approximate because the 
%image is assumed to be rotated. 
%
%The directory is automatically changed to the directory of
%the image file.
%
%An image can be loaded directly by using for example:
%digitize('ImageFileName.bmp')
%
% The userdata of the figure is set to a structure called ud.
% If the userdata already contains a structure,
% new fields are added for internal use of the program.
% Some of fields are describes bellow
%   hAx - Axis handle
%   hAddTrnPts - Add Points uimenu handle
%   hRmvTrnPts - Remove Points uimenu handle
%   hRDC - Reference Data Complete uimenu handle
%   hRes - Residuals uimenu handle
%   hResTxt - Residual labels handle
%   hPix - Pixcel Size uimenu handle
%   hDltDgzPts - Delete Digitized Points uimenu handle
%   hDgzNumPts - Number Digitized Points
%   hNumber - Number uimenu handle
%   hDltAllDgzPts - Delete all Points uimenu handle
%   hCntPts - Connect Points uimenu handle
%   hImg - Image handle
%   hTrnPts - Tranformation Points handle
%   hDgzPts - Digitizated Points handle
%   hHiPt - Highlight line object handle
%   xt - x-coordinate of Transformation Points
%   yt - y-coordinate of Transformation Points
%   xd - x-coordinate of Digitizated Points
%   yd - y-coordinate of Digitizated Points
%   order - Order of fit
%   index - Indexed used for sorting of Points
%   ImgFileName - Image File Name
%   
% A list of the subroutines is given below
%   Initialize(fullname)
%   LoadImageSession(fullname)
%   UpdateNumber
%   UpdatePoints(LineSpec,op)
%   AddPoints(op)
%   DeletePoints(op)
%   ReferencePoints(op)
%   ExpSession
%   ExpPts(op)
%   ExpTrnsMat
%   DeleteAllPts
%   LoadSession
%   HiRefPts
%   Residuals
%   PixcelSize
%   UIOrder
%   NumberPoints
%   [x,y] = jk2xy(j,k,R,order)
%   [R,order] = jkxy2R(j,k,x,y,order)
%   ConnectPts
%
%Copy-Left, Alejandro Sanchez

if nargin==0
    action = 'Initialize';    
else
    [pathstr,name,ext,versn] = fileparts(action);
    %if the ext is not empty it is a file
    if ~isempty(ext)
        Initialize(action)
        return
    end
end

feval(action,varargin{:});

return

%---------------------------------------------------

function Initialize(fullname)

% h = findobj(allchild(0),'tag','hFig');
% if ~isempty(h)
%     close(h)
% end
%'Tag','hFig', ...

try
    editingtool('Off')
end

screensize = get(0,'ScreenSize');
figpos = screensize*0.8 + [0.1*screensize(3),0.1*screensize(4),0,0];
hFig = figure('Name','DIGITIZE: Image Digitization Tool', ...
    'NumberTitle','off','Visible','off',... 
    'HandleVisibility','on','Position',figpos, ...
    'BusyAction','Queue','Interruptible','off', ...
    'Color', 0.8*[1,1,1],'NextPlot','Add', ...
    'DoubleBuffer','On','IntegerHandle','off');

ud.hAx = axes('Position',[0,0,1,1],'Ydir','Reverse',...
    'ButtonDownFcn','digitize(''clean'')');

%------------------ Image -------------------------------------------

hLoad = uimenu('Label','Load');

uimenu(hLoad,'Label','Image File',...
    'Callback','digitize(''LoadImageSession'')');

uimenu(hLoad,'Label','Session',...
    'Callback','digitize(''LoadSession'')');


%------------------ Transformation -----------------------------------

hTrn = uimenu('Label','Transformation');
ud.hAddTrnPts = uimenu(hTrn,'Label','Add Points',...
    'Callback','digitize(''AddPoints'',''t'')');

ud.hRmvTrnPts = uimenu(hTrn,'Label','Delete Points',...
    'Enable','Off','Callback','digitize(''DeletePoints'',''t'')');

hTrnPtsMarker = uimenu(hTrn,'Label','Marker');

hTrnPtsColor = uimenu(hTrnPtsMarker,'Label','Color');

uimenu(hTrnPtsColor,'Label','Red', ...
    'Callback','digitize(''UpdatePoints'',''r'',''t'')');
uimenu(hTrnPtsColor,'Label','Blue', ...
    'Callback','digitize(''UpdatePoints'',''b'',''t'')');
uimenu(hTrnPtsColor,'Label','Green', ...
    'Callback','digitize(''UpdatePoints'',''g'',''t'')');
uimenu(hTrnPtsColor,'Label','Yellow', ...
    'Callback','digitize(''UpdatePoints'',''y'',''t'')');
uimenu(hTrnPtsColor,'Label','Magenta', ...
    'Callback','digitize(''UpdatePoints'',''m'',''t'')');

hTrnPtsSize = uimenu(hTrnPtsMarker,'Label','Size');

uimenu(hTrnPtsSize,'Label','1', ...
    'Callback','digitize(''UpdatePoints'',1,''t'')');
uimenu(hTrnPtsSize,'Label','2', ...
    'Callback','digitize(''UpdatePoints'',2,''t'')');
uimenu(hTrnPtsSize,'Label','3', ...
    'Callback','digitize(''UpdatePoints'',3,''t'')');
uimenu(hTrnPtsSize,'Label','4', ...
    'Callback','digitize(''UpdatePoints'',4,''t'')');
uimenu(hTrnPtsSize,'Label','5', ...
    'Callback','digitize(''UpdatePoints'',5,''t'')');
uimenu(hTrnPtsSize,'Label','6', ...
    'Callback','digitize(''UpdatePoints'',6,''t'')');
uimenu(hTrnPtsSize,'Label','7', ...
    'Callback','digitize(''UpdatePoints'',7,''t'')');
uimenu(hTrnPtsSize,'Label','8', ...
    'Callback','digitize(''UpdatePoints'',8,''t'')');
uimenu(hTrnPtsSize,'Label','9', ...
    'Callback','digitize(''UpdatePoints'',9,''t'')');
uimenu(hTrnPtsSize,'Label','10', ...
    'Callback','digitize(''UpdatePoints'',10,''t'')');

hTrnPtsStyle = uimenu(hTrnPtsMarker,'Label','Style');

uimenu(hTrnPtsStyle,'Label','none', ...
    'Callback','digitize(''UpdatePoints'',''none'',''t'')');
uimenu(hTrnPtsStyle,'Label','Circle', ...
    'Callback','digitize(''UpdatePoints'',''o'',''t'')');
uimenu(hTrnPtsStyle,'Label','Plus', ...
    'Callback','digitize(''UpdatePoints'',''+'',''t'')');
uimenu(hTrnPtsStyle,'Label','Square', ...
    'Callback','digitize(''UpdatePoints'',''s'',''t'')');
uimenu(hTrnPtsStyle,'Label','Star', ...
    'Callback','digitize(''UpdatePoints'',''*'',''t'')');

uimenu(hTrn,'Label','Order of Fit ...', ...
    'Separator','On','Callback','digitize(''UIOrder'')');

ud.hHRP = uimenu(hTrn,'Label','Highlight Referenced Points', ...
    'Checked','off','Callback','digitize(''HiRefPts'')');

ud.hRDC = uimenu(hTrn,'Label','Reference Data Complete', ...
    'Checked','off');

ud.hRes = uimenu(hTrn,'Label','Residuals','Enable','Off', ...
    'Checked','off','Callback','digitize(''Residuals'')');

ud.hResTxt = [];

ud.hPix = uimenu(hTrn,'Label','Pixcel Size', ...
    'Callback','digitize(''PixcelSize'')');

%------------- Digitization ------------------------------------------

hDgz = uimenu('Label','Digitization');

uimenu(hDgz,'Label','Add Points',...
    'Callback','digitize(''AddPoints'',''d'')');

ud.hDltDgzPts = uimenu(hDgz,'Label','Delete Points',...
    'Enable','Off','Callback','digitize(''DeletePoints'',''d'')');

ud.hDgzNumPts =  uimenu(hDgz,'Label','Number Points', ...
    'Enable','Off','Checked','off','Callback','digitize(''NumberPoints'')');

hDgzPtsMarker = uimenu(hDgz,'Label','Marker');

hDgzPtsColor = uimenu(hDgzPtsMarker,'Label','Color');

uimenu(hDgzPtsColor,'Label','Red', ...
    'Callback','digitize(''UpdatePoints'',''r'',''d'')');
uimenu(hDgzPtsColor,'Label','Blue', ...
    'Callback','digitize(''UpdatePoints'',''b'',''d'')');
uimenu(hDgzPtsColor,'Label','Green', ...
    'Callback','digitize(''UpdatePoints'',''g'',''d'')');
uimenu(hDgzPtsColor,'Label','Yellow', ...
    'Callback','digitize(''UpdatePoints'',''y'',''d'')');
uimenu(hDgzPtsColor,'Label','Magenta', ...
    'Callback','digitize(''UpdatePoints'',''m'',''d'')');

hDgzPtsSize = uimenu(hDgzPtsMarker,'Label','Size');

uimenu(hDgzPtsSize,'Label','2', ...
    'Callback','digitize(''UpdatePoints'',2,''d'')');
uimenu(hDgzPtsSize,'Label','3', ...
    'Callback','digitize(''UpdatePoints'',3,''d'')');
uimenu(hDgzPtsSize,'Label','4', ...
    'Callback','digitize(''UpdatePoints'',4,''d'')');
uimenu(hDgzPtsSize,'Label','5', ...
    'Callback','digitize(''UpdatePoints'',5,''d'')');
uimenu(hDgzPtsSize,'Label','6', ...
    'Callback','digitize(''UpdatePoints'',6,''d'')');
uimenu(hDgzPtsSize,'Label','7', ...
    'Callback','digitize(''UpdatePoints'',7,''d'')');
uimenu(hDgzPtsSize,'Label','8', ...
    'Callback','digitize(''UpdatePoints'',8,''d'')');
uimenu(hDgzPtsSize,'Label','9', ...
    'Callback','digitize(''UpdatePoints'',9,''d'')');
uimenu(hDgzPtsSize,'Label','10', ...
    'Callback','digitize(''UpdatePoints'',10,''d'')');

hDgzPtsStyle = uimenu(hDgzPtsMarker,'Label','Style');

uimenu(hDgzPtsStyle,'Label','none', ...
    'Callback','digitize(''UpdatePoints'',''none'',''d'')');
uimenu(hDgzPtsStyle,'Label','Circle', ...
    'Callback','digitize(''UpdatePoints'',''o'',''d'')');
uimenu(hDgzPtsStyle,'Label','Plus', ...
    'Callback','digitize(''UpdatePoints'',''+'',''d'')');
uimenu(hDgzPtsStyle,'Label','Square', ...
    'Callback','digitize(''UpdatePoints'',''s'',''d'')');
uimenu(hDgzPtsStyle,'Label','Star', ...
    'Callback','digitize(''UpdatePoints'',''*'',''d'')');
ud.hNumber = uimenu(hDgzPtsStyle,'Label','Number', ...
    'Checked','off','Callback','digitize(''UpdateNumber'')');

ud.hDltAllDgzPts = uimenu(hDgz,'Label','Delete All Points', ...
    'Enable','Off','Callback','digitize(''DeleteAllPts'')');

ud.hCntPts = uimenu(hDgz,'Label','Connect Points','Enable','Off', ...
    'Separator','On','Checked','Off','Callback','digitize(''ConnectPts'')');

%------------------- Export --------------------------------

hExp = uimenu('Label','Export');

uimenu(hExp,'Label','Session', ...
    'Callback','digitize(''ExpSession'')');

uimenu(hExp,'Label','Digitized Points to Workspace', ...
    'Callback','digitize(''ExpPts'',''WS'')');

uimenu(hExp,'Label','Digitized Points to ASCII File', ...
    'Callback','digitize(''ExpPts'',''ASCII'')');

uimenu(hExp,'Label','Transformation Matrix to Workspace', ...
    'Callback','digitize(''ExpTrnsMat'')');


%============== Plotting =========================

%----------- Image ----------------------
cImgMenu = uicontextmenu;

ud.hImg = image('Parent',ud.hAx, ...
    'UIContextMenu',cImgMenu,'ButtonDownFcn', ...
    'digitize(''clean'')');

uimenu(cImgMenu,'Label','Add Digitization Points', ...
    'Callback','digitize(''AddPoints'',''dc'')');
uimenu(cImgMenu,'Label','Add Transformation Points', ...
    'Callback','digitize(''AddPoints'',''tc'')');

%---------- Transformation Points --------------
jt = [];
kt = [];

cTrnPtsMenu = uicontextmenu;

ud.hTrnPts = line('Xdata',jt,'Ydata',kt, ...
    'LineStyle','None','Parent',ud.hAx, ...
    'Marker','o','Color','r','MarkerSize',6, ...
    'UIContextMenu',cTrnPtsMenu);

set(ud.hTrnPts,'ButtonDownFcn','digitize(''BtnDown'',''t'')')

uimenu(cTrnPtsMenu,'Label','Delete', ...
    'Callback','digitize(''DeletePoints'',''tc'')');
uimenu(cTrnPtsMenu,'Label','Reference', ...
    'Callback','digitize(''ReferencePoints'',''tc'')');

%------------- Digitization Points -----------------
jd = [];
kd = [];

cDgzPtsMenu = uicontextmenu;

ud.hDgzPts = line('Xdata',jd,'Ydata',kd, ...
    'LineStyle','None','Parent',ud.hAx, ...
    'Marker','+','Color','b','MarkerSize',6, ...
    'Parent',ud.hAx,'UIContextMenu',cDgzPtsMenu);

set(ud.hDgzPts,'ButtonDownFcn','digitize(''BtnDown'',''d'')')

uimenu(cDgzPtsMenu,'Label','Delete', ...
    'Callback','digitize(''DeletePoints'',''dc'')');
uimenu(cDgzPtsMenu,'Label','Coordinates', ...
    'Callback','digitize(''ReferencePoints'',''dc'')');

%Highlight Point
ud.hHiPt = line('Xdata',[],'Ydata',[], ...
    'LineStyle','None','Parent',ud.hAx, ...
    'Marker','none','Color','c',...
    'ButtonDownFcn','digitize(''BtnDown'',''h'')');

%Transformation Coordinates of jt and kt
ud.xt = [];
ud.yt = [];

%Digitized Transformed Points of jd and kd
ud.xd = [];
ud.yd = [];

%Transformation Matrix
ud.R = [];

%Order of Fit
ud.order = 1;

%Index of Order of Points
ud.index = [];

set(hFig,'UserData',ud)
if nargin==0
    list = dir('*.bmp');
    if isstruct(list) && ~isempty(list)
        fullname = list(1).name;    
    else
        fullname = 'detail.mat';  
    end
end
LoadImageSession(fullname)
set(hFig,'Visible','On')
return

%----------------------------------------------------
function LoadImageSession(fullname)
% hFig = findobj(allchild(0),'tag','hFig');
% hFig = hFig(end); %if several are open use last
hFig = get(0,'CurrentFigure');
ud = get(hFig,'UserData');
if nargin<1 %Callback
    oldname = ud.ImgFileName;
    [iname,ipath] = uigetfile('*.*','Select an Image-File:');
	if ~any(iname)
        return
    end
    if isempty(ipath)
        ipath = [cd,filesep];
    end
    fullname = [ipath,iname];
%     if strcmpi(oldname,fullname)
%         return
%     end
end
if exist(fullname,'file')~=2
    hw = msgbox('Invalid file name or file does not exist','DIGITIZE','error');
    uiwait(hw)
    drawnow
    [iname,ipath] = uigetfile('*.*','Locate Image-File:');
    if ~any(iname)
        return
    end
    if isempty(ipath)
        ipath = [cd,filesep];
    end
    fullname = [ipath,iname];
    if exist(fullname,'file')~=2
        errordlg('Invalid file name or file does not exist','DIGITIZE')
    end
end 
[pathstr,name,ext] = fileparts(fullname);
if ~isempty(pathstr)
    try
        cd(pathstr) %change to directory of file
    catch
       error([fullname,' has an invalid path'])
    end
end
if strcmpi(ext,'.mat')
    try
        a = load(fullname);
    catch
        error([fullname,' not found'])
    end
    fn = fieldnames(a);
    img = [];
    map = [];
    %---------- Sesssion --------------
    if isfield(a,'R')
%         try
            ud.ImgFileName = a.ImgFileName;
            set(hFig,'Name',['DIGITIZE: Image Digitization Tool - ', ...
                ud.ImgFileName])
            ud.jt = a.jt;
            ud.kt = a.kt;
            ud.jd = a.jd;
            ud.kd = a.kd;
            set(ud.hTrnPts,'Xdata',ud.jt,'Ydata',ud.kt)
            set(ud.hDgzPts,'Xdata',ud.jd,'Ydata',ud.kd)
            ud.xt = a.xt;
            ud.yt = a.yt;
            ud.xd = a.xd;
            ud.yd = a.yd;
            ud.R = a.R;

            [img,map] = imread(ud.ImgFileName);
            if ~isempty(map) %always use true color
                img = ind2rgb(img,map);
            end
            s = size(img);
            xdata = [1,s(2)];
            ydata = [1,s(1)];
            set(ud.hImg,'Xdata',xdata,'Ydata',ydata,'Cdata',img)
            xlim = [0.5,s(2)+0.5];
            ylim = [0.5,s(1)+0.5];
            set(ud.hAx,'Xlim',xlim,'Ylim',ylim)
            
            %check if all transformation data is complete
            if all(isfinite(ud.xt))
                set(ud.hRDC,'Checked','On')
                set(ud.hRes,'Enable','On')
                set(ud.hPix,'Enable','On')
            else
                set(ud.hRDC,'Checked','Off')
                set(ud.hRes,'Enable','Off')
                set(ud.hPix,'Enable','Off')
            end
            set(ud.hResTxt,'Visible','Off')
            set(hFig,'UserData',ud)
            return

    %------- Image File -----------------
    else 
        for k=1:length(fn)
            s = size(a.(fn{k}));
            num = isnumeric(a.(fn{k}));
            if s(2)==3 && num
                map = a.(fn{k});
            elseif num && all(s>20)
                img = a.(fn{k});
            end %if
        end %for
        if isempty(img)
            errordlg('Invalid .mat file','DIGITIZE')
            return
        end %if
    end %if
else
    [img,map] = imread(fullname);
end %if
ud.ImgFileName = fullname;
if ~isempty(map) %always use true color
    img = ind2rgb(img,map);
end
s = size(img);
xdata = [1,s(2)];
ydata = [1,s(1)];
set(ud.hImg,'Xdata',xdata,'Ydata',ydata,'Cdata',img)
xlim = [0.5,s(2)+0.5];
ylim = [0.5,s(1)+0.5];
set(ud.hAx,'Xlim',xlim,'Ylim',ylim)
set(ud.hTrnPts,'Xdata',[],'Ydata',[])
set(ud.hDgzPts,'Xdata',[],'Ydata',[])
set(ud.hHiPt,'Xdata',[],'Ydata',[])
ud.xt = [];
ud.yt = [];
ud.xd = [];
ud.yd = [];
ud.R = [];
set(ud.hRDC,'Checked','Off')
set(ud.hRes,'Enable','Off')
set(ud.hPix,'Enable','Off')
set(ud.hResTxt,'Visible','Off')
set(hFig,'UserData',ud)
set(hFig,'Name',['DIGITIZE: Image Digitization Tool - ',fullname])
return

%----------------------------------------------------
function UpdateNumber
ud = get(gcbf,'UserData');
if strcmpi(get(ud.hNumber,'Checked'),'On')
    set(ud.hNumber,'Checked','Off')
else
    set(ud.hNumber,'Checked','On')
end
set(hFig,'UserData',ud)
return

%--------------------------------------------------------
function UpdatePoints(LineSpec,op)
hFig = gcbf;
ud = get(hFig,'UserData');
isDgz = any(op=='d');
if isDgz %Digitization
    h = ud.hDgzPts;
else %Transformation
    h = ud.hTrnPts;
end %if
if isnumeric(LineSpec)
    set(h,'MarkerSize',LineSpec)
    return
end
if strcmpi(LineSpec,'none')
    set(h,'Marker','none')
    return
end
[style,color,marker,msg] = colstyle(LineSpec);
if ~isempty(msg)
    errordlg(msg,'DIGITIZE')
    return
end
if ~isempty(color)
    set(h,'Color',color)
end
if ~isempty(marker)
    set(h,'Marker',marker)  
end
return

%---------------------------------------------------------
function AddPoints(op)
hFig = gcbf;
ud = get(hFig,'UserData');
isDgz = any(op=='d');
if isDgz %Digitization
    h = ud.hDgzPts;
    x = ud.xd;
    y = ud.yd;
    if ~isempty(ud.index)
        index = ud.index;
        [dummy,newindex] = sort(index);
        j = get(ud.hDgzPts,'XData');
        k = get(ud.hDgzPts,'YData');
        n = length(newindex);
        j(1:n) = j(newindex);
        k(1:n) = k(newindex);
        set(ud.hCntPts,'Checked','Off')
        set(ud.hDgzPts,'XData',j,'YData',k,'LineStyle','None')
        ud.index = [];
    end
else    %Transformation
    h = ud.hTrnPts;
    x = ud.xt;
    y = ud.yt;
end
j = get(h,'Xdata');
k = get(h,'Ydata');
pointer = get(gcf,'pointer');
state = uisuspend(hFig); %Remove figure button functions
set(hFig,'pointer','fullcrosshair');
while 1 %This is much faster than using ginput
    keydown = waitforbuttonpress;
    button = get(hFig,'SelectionType');
    drawnow
    pt = get(gca,'CurrentPoint');
    switch lower(button)
        case {'open','normal'}
            j(end+1) = pt(1,1);
            k(end+1) = pt(1,2);
            set(h,'Xdata',j,'Ydata',k)
        case {'extend','alt'}
            break
    end %switch
end %while
uirestore(state);
set(hFig,'pointer',pointer)
n = length(j);
%DataWrap x and y with NaN's
nn = length(x);
x(nn+1:n) = NaN;
y(nn+1:n) = NaN;
if isDgz %Digitization
    if ~isempty(ud.R)
        [x,y] = jk2xy(j,k,ud.R,ud.order);
    end
    ud.xd = x;
    ud.yd = y;
else %Transformation
    ud.xt = x;
    ud.yt = y;
    if nn<n
        set(ud.hRDC,'Checked','Off')
        set([ud.hRes,ud.hPix],'Enable','Off')
        chck = get(ud.hRes,'Checked');
        if chck
            set(ud.hRes,'Checked','Off')
            set(ud.hResTxt,'Visible','Off')
        end
    end
end
if n>0
    if isDgz %Digitization
        set([ud.hDltDgzPts,ud.hDltAllDgzPts, ...
            ud.hDgzNumPts,ud.hCntPts],'Enable','On')
    else    %Transformation
        set(ud.hRmvTrnPts,'Enable','On')
    end
else
    if isDgz %Digitization
        set([ud.hDltDgzPts,ud.hDltAllDgzPts, ...
            ud.hDgzNumPts,ud.hCntPts],'Enable','Off')
    else    %Transformation
        set(ud.hRmvTrnPts,'Enable','Off')
    end
end
set(hFig,'UserData',ud)
return

%---------------------------------------------------------
function DeletePoints(op)
hFig = gcbf;
ud = get(hFig,'UserData');
isDgz = any(op=='d');
if any(op=='c') %from Mouse
    pt = get(ud.hAx, 'CurrentPoint');
    jp = pt(1,1);
    kp = pt(1,2);
    if isDgz %Digitization
        h = ud.hDgzPts;
        if ~isempty(ud.index)
            index = ud.index;
            [dummy,newindex] = sort(index);
            j = get(ud.hDgzPts,'XData');
            k = get(ud.hDgzPts,'YData');
            n = length(newindex);
            j(1:n) = j(newindex);
            k(1:n) = k(newindex);
            ud.xd(1:n) = ud.xd(ud.index);
            ud.yd(1:n) = ud.yd(ud.index);
            set(ud.hCntPts,'Checked','Off')
            set(ud.hDgzPts,'XData',j,'YData',k,'LineStyle','None')
            ud.index = [];
        end
    else %Transformation
        h = ud.hTrnPts;
    end %if
    j = get(h,'Xdata');
    k = get(h,'Ydata');
    %Delaunay Does not work well
    %when there are few points
    tri = [];
    try
        tri = delaunay(j,k);
        c = dsearch(j,k,tri,jp,kp);
    catch
        d = sqrt((jp-j).^2 + (kp-k).^2);
        [m,c] = min(d);
    end
    j(c) = [];
    k(c) = [];
    set(h,'Xdata',j,'Ydata',k)
    if isDgz %Digitization
        ud.xd(c) = [];
        ud.yd(c) = [];
    else %Transformation
        ud.xt(c) = [];
        ud.yt(c) = [];  
        %Check for complete reference points
        if strcmpi(get(ud.hRDC,'Checked'),'On')
            ud.R = jkxy2R(j,k,ud.xt,ud.yt,ud.order);
        end
    end
else %from Menu
    if isDgz %Digitization
        h = ud.hDgzPts;
        if ~isempty(ud.index)
            index = ud.index;
            [dummy,newindex] = sort(index);
            j = get(ud.hDgzPts,'XData');
            k = get(ud.hDgzPts,'YData');
            n = length(newindex);
            j(1:n) = j(newindex);
            k(1:n) = k(newindex);
            ud.xd(1:n) = ud.xd(ud.index);
            ud.yd(1:n) = ud.yd(ud.index);
            set(ud.hCntPts,'Checked','Off')
            set(ud.hDgzPts,'XData',j,'YData',k,'LineStyle','None')
            ud.index = [];
        end
    else %Transformation
        h = ud.hTrnPts;
    end %if
    j = get(h,'Xdata');
    k = get(h,'Ydata');
    marker = get(h,'Marker');
    set(ud.hHiPt,'Marker',marker)
    n = length(j);
    %Delaunay Does not work well
    %when there are few points
    tri = [];
    if n>30
        try
            tri = delaunay(j,k);
        end
    end
    ind = true(n,1);
    f = false(1);
    while 1
        [jp,kp,button] = ginput(1);
        if button==1
            if ~isempty(tri)
                c = dsearch(j,k,tri,jp,kp);
            else
                d = sqrt((jp-j).^2 + (kp-k).^2);
                [m,c] = min(d);
            end
            set(ud.hHiPt,'Xdata',j(c),'Ydata',k(c))
            [xd,yd,button] = ginput(1);
            if button==1
                ind(c) = f;
                set(h,'Xdata',j(ind),'Ydata',k(ind))
            end %if
        else
            break
        end %if
        set(ud.hHiPt,'Xdata',[],'Ydata',[])
    end %while
    if isDgz
        ud.xd = ud.xd(ind);
        ud.yd = ud.yd(ind);
    else
        ud.xt = ud.xt(ind);
        ud.yt = ud.yt(ind);
        x = ud.xt;
        y = ud.yt;
        j = j(ind);
        k = k(ind);
        ud.R = jkxy2R(j,k,x,y,ud.order);
        if isempty(ud.R)
            ud.xd = [];
            ud.yd = [];
        end
    end %if
end %if
set(hFig,'UserData',ud)
return

%----------------------------------------------------------
function ReferencePoints(op)
hFig = gcbf;
ud = get(hFig,'UserData');
pt = get(ud.hAx, 'CurrentPoint');
jp = pt(1,1);
kp = pt(1,2);
isDgz = any(op=='d');
if isDgz
    if ~isempty(ud.R)
        h = ud.hDgzPts;
        x = ud.xd;
        y = ud.yd;
    else
        errordlg('Transformation Data is not complete','DIGITIZE')
        return
    end
else
    h = ud.hTrnPts;
    x = ud.xt;
    y = ud.yt;
end
j = get(h,'Xdata');
k = get(h,'Ydata');
tri = [];
n = length(j);
if n>30
    try
        tri = delaunay(j,k);
    end
end
if ~isempty(tri)
    c = dsearch(j,k,tri,jp,kp);
else
    d = sqrt((jp-j).^2 + (kp-k).^2);
    [m,c] = min(d);
end
m = get(ud.hTrnPts,'Marker');
ms = get(ud.hTrnPts,'MarkerSize');
set(ud.hHiPt,'Xdata',j(c),'Ydata',k(c), ...
    'Marker',m,'MarkerSize',ms+1,'visible','on')
if isDgz
    msgbox(['X: ',num2str(x(c)),', Y: ',num2str(y(c))])
    set(ud.hHiPt,'Xdata',[],'Ydata',[],'MarkerSize',ms)
    return
end
while 1
    if isnan(x(c))
        xydef = {'',''};
    else
        xydef = {num2str(x(c)),num2str(y(c))};
    end
    xy = inputdlg({'X-Coordinate:','Y-Coordinate:'}, ...
        'Reference',[1,35; 1,35],xydef);
    if isempty(xy)
        break
    end
    try
        xy = str2double(xy);
        x(c) = xy(1);
        y(c) = xy(2);
        break
    catch
        errordlg('Input must be numberic','DIGITIZE')
    end %try
end %while
ud.xt = x;
ud.yt = y;
%Check if All References are entered
if all(isfinite(x))
    set(ud.hRDC,'Checked','On')
    set(ud.hRes,'Enable','On')
    set(ud.hPix,'Enable','On')
    ud.R = jkxy2R(j,k,x,y,ud.order);
    jd = get(ud.hDgzPts,'XData');
    kd = get(ud.hDgzPts,'YData');
    if ~isempty(jd)
        [ud.xd,ud.yd] = jk2xy(jd,kd,ud.R,ud.order);
    end
end
set(hFig,'UserData',ud)
set(ud.hHiPt,'Xdata',[],'Ydata',[],'MarkerSize',ms)
return

%----------------------------------------------------------
function ExpSession
hFig = gcbf;
ud = get(hFig,'UserData');
ImgFileName = ud.ImgFileName;
jt = get(ud.hTrnPts,'Xdata');
kt = get(ud.hTrnPts,'Ydata');
jd = get(ud.hDgzPts,'Xdata');
kd = get(ud.hDgzPts,'Ydata');
xt = ud.xt;
yt = ud.yt;
xd = ud.xd;
yd = ud.yd;
R = ud.R;
[sname,spath] = uiputfile('*.mat','Save Session as:');
if ~any(sname)
    return
end
fullname = [spath,sname];
if exist(fullname,'file')==2
    fileattrib(fullname,'+w'); %make writeable
end
save(fullname,'ImgFileName','jt','kt', ...
    'jd','kd','xt','yt','xd','yd','R')
msgbox('Session Saved Successfully','DIGITIZE')
return

%----------------------------------------------------------
function ExpPts(op)
hFig = gcbf;
ud = get(hFig,'UserData');
if all(~isnan(ud.xd))
    if strcmp(op,'WS')
        assignin('base','xd',ud.xd)
        assignin('base','yd',ud.yd)
        msgbox('Digitized Points (xd,yd) Saved to Workspace','DIGITIZE')
    else %ASCII
        [aname,apath] = uiputfile('*.*','Save as:');
        if ~any(aname)
            return
        end
        fullname = [apath,aname];
        fid = fopen(fullname,'w');
        fprintf(fid,'%f\t%f\n',[ud.xd(:), ud.yd(:)]')
        fclose(fid);
        msgbox('ASCII File Saved Successfully','DIGITIZE')
    end
else
    errordlg('Transformation is not complete','DIGITIZE')
end
return

%-----------------------------------------------------------
function BtnDown(op)
hFig = gcbf;
Button = get(hFig,'SelectionType');
ud = get(hFig,'UserData');
hAx = ud.hAx;
ud.Button = Button;
ud.CurrentPoint = get(hAx,'CurrentPoint');
x0 = ud.CurrentPoint(1,1);
y0 = ud.CurrentPoint(1,2);
if op=='d'
    h = ud.hDgzPts;
    ud.hMod = h;
elseif op=='h'
    h = ud.hHiPt;
else
    h = ud.hTrnPts;
    ud.hMod = h;
end
switch ud.Button
    case 'normal' %Move
        %check if it is alredy highlighted
        if ~isempty(get(ud.hHiPt,'Xdata')) && (op=='h')
            setptr(hFig,'circle');
            set(hFig,'WindowButtonMotionFcn', ...
                'digitize(''BtnMotion'')',...
                'WindowButtonUpFcn', ...
                'digitize(''BtnUp'')')
        else
            clean
        end %if
    
    case 'open' %select
        %check if it is alredy highlighted
        if isempty(get(ud.hHiPt,'Xdata'))
            x = get(h,'Xdata');
            y = get(h,'Ydata');
            [dummy,indMove] = min(sqrt((x0-x).^2 + (y0-y).^2));
            marker = get(h,'Marker');
            ud.indMove = indMove;
            if strcmpi(marker,'.')
                marker = 'o';
            end
            markersize = get(h,'MarkerSize');
            color = get(h,'Color');
            markeredgecolor = get(h,'MarkerEdgeColor');
            markerfacecolor = get(h,'MarkerFaceColor');
            set(ud.hHiPt,'Parent',hAx, ...
                'Xdata',x(indMove),'Ydata',y(indMove), ...
                'Color',color,'Marker',marker, ...
                'MarkerSize',markersize, ...
                'MarkerEdgeColor',markeredgecolor, ...
                'MarkerFaceColor',markerfacecolor, ...
                'Visible','On','Selected','On');
            %Now bring hHiPt to the front
			chldrn = get(hAx,'Children');
			chldrn(chldrn == ud.hHiPt) = [];
			chldrn = [ud.hHiPt; chldrn(:)];
			set(hAx,'Children',chldrn)
        end %if
end %switch
set(hFig,'UserData',ud)
return

%----------------------------------------
function BtnMotion
hFig = gcbf;
ud = get(hFig,'UserData');
hAx = ud.hAx; 
LastPoint = ud.CurrentPoint;
CurrentPoint = get(hAx,'CurrentPoint');
if strcmpi(ud.Button,'normal') && ~isempty(ud.hMod)
    xb = get(ud.hMod,'Xdata');
    yb = get(ud.hMod,'Ydata');
    xb(ud.indMove) = CurrentPoint(1,1);
    yb(ud.indMove) = CurrentPoint(1,2);
    set(ud.hMod,'Xdata',xb,'Ydata',yb)
    set(ud.hHiPt,'Xdata',CurrentPoint(1,1), ...
        'Ydata',CurrentPoint(1,2))
end %if
ud.CurrentPoint = CurrentPoint;
set(hFig,'UserData',ud)
return

%-----------------------------------------------------------
function BtnUp
hFig = gcbf;
setptr(hFig,'arrow');
set(hFig,'WindowButtonMotionFcn','',...
    'WindowButtonUpFcn','');
ud = get(hFig,'UserData');
set(ud.hHiPt,'Xdata',[],'Ydata',[])
if strcmpi(get(ud.hRDC,'Checked'),'On')
    jt = get(ud.hTrnPts,'XData');
    kt = get(ud.hTrnPts,'YData');
    ud.R = jkxy2R(jt,kt,ud.xt,ud.yt,ud.order);
    jd = get(ud.hDgzPts,'XData');
    kd = get(ud.hDgzPts,'YData');
    if ~isempty(jd)
        [ud.xd,ud.yd] = jk2xy(jd,kd,ud.R,ud.order);
    end
end
ud.hMod = [];
ud.indMove = [];
set(hFig,'UserData',ud)
return

%----------------------------------------------------------
function clean
hFig = gcbf;
ud = get(hFig,'UserData');
hAx = ud.hAx;
setptr(hFig,'arrow')
set(hFig,'WindowButtonMotionFcn','', ...
    'WindowButtonUpFcn','', ...
    'WindowButtonDownFcn','')
ud.hMod = [];
ud.indMove = [];
set(ud.hHiPt,'Xdata',[],'Ydata',[])
set(hFig,'UserData',ud)
return

%---------------------------------------------------------
function ExpTrnsMat
hFig = gcbf;
ud = get(hFig,'UserData');
assignin('base','R',ud.R)
return

%----------------------------------------------------------
function DeleteAllPts
hFig = gcbf;
ud = get(hFig,'UserData');
nowork = isempty(ud.xd);
if nowork
   return 
end
button = questdlg('Digitized Points will be erased. Continue?',...
    'Warning','Yes','No','Yes');
if strcmpi(button,'No')
    return
end
ud.xd = [];
ud.yd = [];
set(ud.hDgzPts,'Xdata',[],'Ydata',[])
ud.index = [];
set(hFig,'UserData',ud)
return

%---------------------------------------------------------
function LoadSession
hFig = gcbf;
ud = get(hFig,'UserData');
[fname,fpath] = uigetfile('*.mat','Select a Session:');
if ~any(fname)
    return
end
fullname = [fpath,fname]; %session name
load(fullname) %xt yt xd yd R ImgFileName
if exist(ImgFileName,'file')~=2
    hw = msgbox('Invalid file name or file does not exist','DIGITIZE','error');
    uiwait(hw)
    drawnow
    [ipath,iname,ext] = fileparts(ImgFileName);
    [iname,ipath] = uigetfile('*.*','Locate Image-File:',[iname,ext]);
    if ~any(iname)
        return
    end
    if isempty(ipath)
        ipath = [cd,filesep];
    end
    ImgFileName = [ipath,iname]; %change image file name
    if exist(ImgFileName,'file')~=2
        errordlg('Invalid file name or file does not exist','DIGITIZE')
    end
    save(fullname,'ImgFileName','jt','kt', ...
        'jd','kd','xt','yt','xd','yd','R')
    hw = msgbox('ImgFileName updated','DIGITIZE');
    uiwait(hw)
end 
ud.ImgFileName = ImgFileName;
set(hFig,'UserData',ud)
LoadImageSession(fullname)
set(ud.hTrnPts,'Xdata',jt,'Ydata',kt)
set(ud.hDgzPts,'Xdata',jd,'Ydata',kd)
ud.xt = xt;
ud.yt = yt;
ud.xd = xd;
ud.yd = yd;
ud.R = R;
set(ud.hRDC,'Checked','On')
set(ud.hRes,'Enable','On','Checked','Off')
set(ud.hPix,'Enable','On')
if ~isempty(xd)
   set([ud.hDltDgzPts,ud.hDltAllDgzPts, ...
            ud.hDgzNumPts,ud.hCntPts],'Enable','On')
end
set(hFig,'UserData',ud)
hw = msgbox('Session Loaded Successfully','DIGITIZE');
uiwait(hw)
return

%-----------------------------------------
function HiRefPts
hFig = gcbf;
ud = get(hFig,'UserData');
chck = get(ud.hHRP,'Checked');
if strcmpi(chck,'On')
    set(ud.hHRP,'Checked','Off');
    set(ud.hHiPt,'Visible','Off')
    return
end
set(ud.hHRP,'Checked','On')
jt = get(ud.hTrnPts,'Xdata');
kt = get(ud.hTrnPts,'Ydata');
xt = ud.xt;
ind = isfinite(xt);
marker = get(ud.hTrnPts,'Marker');
if strcmpi(marker,'.')
    marker = 'o';
end
markersize = get(ud.hTrnPts,'MarkerSize');
markeredgecolor = get(ud.hTrnPts,'MarkerEdgeColor');
markerfacecolor = get(ud.hTrnPts,'MarkerFaceColor');
set(ud.hHiPt,'Xdata',jt(ind),'Ydata',kt(ind), ...
    'Color','c','Marker',marker, ...
    'MarkerSize',markersize, ...
    'MarkerEdgeColor',markeredgecolor, ...
    'MarkerFaceColor',markerfacecolor, ...
    'Visible','On','Selected','Off');
%Now bring hHiPt to the front
chldrn = get(ud.hAx,'Children');
chldrn(chldrn == ud.hHiPt) = [];
chldrn = [ud.hHiPt; chldrn(:)];
set(ud.hAx,'Children',chldrn)
return

%-----------------------------------------
function Residuals
hFig = gcbf;
ud = get(hFig,'UserData');
chck = get(ud.hRes,'Checked');
if strcmpi(chck,'On')
    set(ud.hRes,'Checked','Off');
    set(ud.hResTxt,'Visible','Off')
    return
end
set(ud.hRes,'Checked','On')
jt = get(ud.hTrnPts,'Xdata');
kt = get(ud.hTrnPts,'Ydata');
[xc,yc] = jk2xy(jt,kt,ud.R,ud.order);
rx = ud.xt(:) - xc; %Residuals
ry = ud.yt(:) - yc;
%Make Labels
for c=1:length(rx)
    rtxt = strvcat(['res-x: ',num2str(rx(c))],...
        ['res-y: ',num2str(ry(c))]);
    ud.hResTxt(c) = text(jt(c),kt(c),rtxt, ...
        'BackgroundColor','W','EdgeColor','k', ...
        'HorizontalAlignment','Left','Visible','On', ...
        'VerticalAlignment','Bottom','Color','k');
end
set(hFig,'UserData',ud)
return

%------------------------------------------------------------------
function PixcelSize
hFig = gcbf;
ud = get(hFig,'UserData');
j = [0;1];
k = [0;1];
onez = ones(2,1);
switch ud.order
    case 1
        jk = [j, k, onez];
    case 2
        jk = [j, k, j.^2, k.^2, j.*k, onez];
    case 3
        jk = [j, k, j.^2, k.^2, j.*k, j.^3, k.^3, j.^2.*k, j.*k.^2, onez];
end
PixSize = jk*ud.R;
PixSize = diff(PixSize,1,1);
PixSize = abs(PixSize(1:2));
msgbox(['The approximate pixcel size of the image is: [',...
    num2str(PixSize(1)),'x',...
    num2str(PixSize(2)),']'],'DIGITIZE')
return

%------------------------------------------------------------------
function UIOrder
hFig = gcbf;
ud = get(hFig,'UserData');
order = num2str(ud.order);
neworder = inputdlg('Order of Fit:','DIGITIZE',[1,30],{order});
if isempty(neworder)
    return
end
ud.order = str2double(neworder);
jt = get(ud.hTrnPts,'XData');
kt = get(ud.hTrnPts,'YData');
op = get(ud.hRDC,'Checked');
if length(jt)>2 && strcmpi(op,'On')
    [ud.R,ud.order] = jkxy2R(jt,kt,ud.xt,ud.yt,ud.order);
    jd = get(ud.hDgzPts,'XData');
    kd = get(ud.hDgzPts,'YData');
    if ~isempty(jd)
        [ud.xd,ud.yd] = jk2xy(jd,kd,ud.R,ud.order);
    end
end
set(hFig,'UserData',ud)
return

%-----------------------------------------------------------------
function NumberPoints
hFig = gcbf;
ud = get(hFig,'UserData');
chck = get(ud.hDgzNumPts,'Checked');
if strcmpi(chck,'On')
    set(ud.hDgzNumPts,'Checked','Off');
    set(ud.hNumPts,'Visible','Off')
    return
end
set(ud.hDgzNumPts,'Checked','On');
x = get(ud.hDgzPts,'Xdata');
y = get(ud.hDgzPts,'Ydata');
color = get(ud.hDgzPts,'Color');
Numbers = num2str((1:length(x))');
ud.hNumPts = text(x,y,Numbers,'HorizontalAlignment','Left', ...
    'VerticalAlignment','Bottom','Color',color,'FontSize',14);
set(hFig,'UserData',ud)
return

%------------------------------------------------------------------
function [x,y] = jk2xy(j,k,R,order)
j = j(:);
k = k(:);
n = length(j);
onez = ones(n,1);
switch order
    case 1
        jk = [j, k, onez];
    case 2
        jk = [j, k, j.^2, k.^2, j.*k, onez];
    case 3
        jk = [j, k, j.^2, k.^2, j.*k, j.^3, k.^3, j.^2.*k, j.*k.^2, onez];   
end %switch
xy = jk*R;
x = xy(:,1);
y = xy(:,2);
return

%----------------------------------------------------------------
function [R,order] = jkxy2R(j,k,x,y,order)
j = j(:);
k = k(:);
x = x(:);
y = y(:);
n = length(x);
onez = ones(n,1);
while 1
    switch order
        case 1
            if n<3
                R = [];
                warndlg('Not Enough Reference Points','DIGITIZE')
                return
            end
            A = [j, k, onez];
            B = [x, y, onez];
            break
        case 2
            if n<6
                s = char('Not Enough Reference Points.', ...
                    'Reducing Order of Fit.');
                warndlg(s,'DIGITIZE')
                order = 1;
                continue
            end
            A = [j, k, j.^2, k.^2, j.*k, onez];
            B = [x, y, x.^2, y.^2, x.*y, onez];
            break
        case 3
            if n<10
                s = char('Not Enough Reference Points.', ...
                    'Reducing Order of Fit.');
                warndlg(s,'DIGITIZE')
                if n<6
                    order = 1;
                else
                    order = 2;
                end
                continue
            end
            A = [j, k, j.^2, k.^2, j.*k, j.^3, k.^3, j.^2.*k, j.*k.^2, onez];
            B = [x, y, x.^2, y.^2, x.*y, x.^3, y.^3, x.^2.*y, x.*y.^2, onez];
            break
        otherwise
            s = char('Maximum Order is 3.', ...
                    'Reducing Order ...');
            warndlg(s,'DIGITIZE')
            order = 3;
    end %switch
end %while
R = A\B; %Left Division
R(1:end-1,3) = 0; %force these values
R(end,end) = 1;
return

%-----------------------------------------------------------------------
function ConnectPts
hFig = gcbf;
ud = get(hFig,'UserData');
j = get(ud.hDgzPts,'XData');
k = get(ud.hDgzPts,'YData');
chk = get(ud.hCntPts,'Checked');
if strcmpi(chk,'On')
    index = ud.index;
    [dummy,newindex] = sort(index);
    n = length(newindex);
    j(1:n) = j(newindex);
    k(1:n) = k(newindex);
    ud.xd(1:n) = ud.xd(newindex);
    ud.yd(1:n) = ud.yd(newindex);
    set(ud.hCntPts,'Checked','Off')
    set(ud.hDgzPts,'XData',j,'YData',k,'LineStyle','None')
else % strcmpi(chk,'Off')
    if isempty(ud.index)
        j = j(:);
        k = k(:);
        n = size(j,1);
        ik = 1; %Index of Starting Point
        ind = true(n,1); %logicals
        f = false(1);
        ind(ik) = f;
        index = NaN*ones(n,1);
        index(1) = ik;
        %Initialize 1/distances
        d = zeros(n,1);
        %The algorithm is made much faster by
        %only calculating d for the remaining
        %points of the line
        for c=2:n
            d(ind) = 1./sqrt((j(ik)-j(ind)).^2 ...
                + (k(ik)-k(ind)).^2);
            [dummy,ik] = max(d);
            ind(ik) = f; %elimates past points
            d(ik) = 0; %elimates past points
            index(c) = ik; %update final index
        end
        ud.index = index; %saves the sorted index
    end
    n = length(ud.index);
    j(1:n) = j(ud.index);
    k(1:n) = k(ud.index);
    ud.xd(1:n) = ud.xd(ud.index);
    ud.yd(1:n) = ud.yd(ud.index);
    set(ud.hDgzPts,'XData',j,'YData',k,'LineStyle','-');
    set(ud.hCntPts,'Checked','On')
end
set(hFig,'UserData',ud)
return



