Code covered by the BSD License  

Highlights from
Elliptical Fourier Shape Descriptors GUI

image thumbnail
from Elliptical Fourier Shape Descriptors GUI by David Thomas
Provides a GUI for the EFSD routines by the same author

fsdv221(varargin)
function varargout = fsdv221(varargin)
% FSDV221 M-file for fsdv221.fig
%      FSDV221, by itself, creates a fsdv221 FSDV221 or raises the existing
%      singleton*.
%
%      H = FSDV221 returns the handle to a fsdv221 FSDV221 or the handle to
%      the existing singleton*.
%
%      FSDV221('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in FSDV221.M with the given input arguments.
%
%      FSDV221('Property','Value',...) creates a fsdv221 FSDV221 or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before fsdv221_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to fsdv221_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help fsdv221

% Last Modified by GUIDE v2.5 28-Nov-2005 15:16:13

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @fsdv221_OpeningFcn, ...
                   'gui_OutputFcn',  @fsdv221_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before fsdv221 is made visible.
function fsdv221_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to fsdv221 (see VARARGIN)

% Choose default command line output for fsdv221
handles.output = hObject;
% initialise globals needed by many functions
handles.bHasBeenMirrored = 0;
handles.bReconstructState = 0;

handles.sInputFileName = [];
handles.sInputFilePath = [];
handles.bValidInputDataLoaded = 0;
handles.bAnalysisDone = 0;
handles.bValidInputDataLoaded = 0;
handles.bReconDone = 0;
handles.iSymmetryType = 3; % defaults to "Mirror", 1 indicates symmetry
                          % about the x axis, 2 symmetry about y

handles.rFSDs = [];
handles.rDiffs = [];
handles.rInputData = [];
handles.ReconnedOutline = [];
handles.rMirroredInputData = [];
handles.iInputDataLengths = [];

handles.iOutlineNo = 0;
handles.rColourArray = [0 0 0; 0 0 0; 0 0 1; 0 1 0; 0 1 1; 1 0 0; 1 0 1; 1 1 0; 1 1 1];


% Update handles structure
guidata(hObject, handles);

% --- Outputs from this function are returned to the command line.
function varargout = fsdv221_OutputFcn(hObject, eventdata, handles) 
varargout{1} = handles.output;


% --------------------------------------------------------------------
% Checkbox callbacks and create functions
% --------------------------------------------------------------------
% --- Executes on button press in checkbox5.
function checkbox5_Callback(hObject, eventdata, handles)
% the Symmetry checkbox

% --- Executes on button press in checkbox6.
function checkbox6_Callback(hObject, eventdata, handles)
% the Normalse for size checkbox

% --- Executes on button press in checkbox7.
function checkbox7_Callback(hObject, eventdata, handles)
% the Normalise for orientation checkbox

% --- Executes on button press in checkbox8.
function checkbox8_Callback(hObject, eventdata, handles)
% the Symmetry: checkbox

function uipanel2_SelectionChangeFcn(hObject, eventdata, handles)
% radio buttons to select x, y axis for symmetry or overlay
% radiobutton1 = x, 2=y, 3=overlay
% hObject    handle to uipanel1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

switch get(hObject,'Tag')   % Get Tag of selected object
    case 'radiobutton1'
       handles.iSymmetryType = 1;
    case 'radiobutton2'
       handles.iSymmetryType = 2;
    case 'radiobutton3'
       % this is the same as the earlier "Mirror" operation
       handles.iSymmetryType = 3;
end
guidata(hObject, handles); % update handles objecgt

% --- Executes on button press in checkbox9.
function checkbox9_Callback(hObject, eventdata, handles)
% the Plot coefficient values checkbox

% --------------------------------------------------------------------
% Edit box callbacks and create functions
% --------------------------------------------------------------------
function edit2_Callback(hObject, eventdata, handles)
% # of harmonics to use for analysis
NoOfHarmonicsAnalyse = str2double(get(hObject,'String'));
set(hObject,'Value', NoOfHarmonicsAnalyse);
NoOfHarmonicsRecon = get(handles.edit2, 'Value');
% force the number of harmonics for reconstruction to be no more than the
% number for analysis
if NoOfHarmonicsRecon > NoOfHarmonicsAnalyse
    set(handles.edit2, ...
        'String', num2str(NoOfHarmonicsAnalyse), ...
        'Value', NoOfHarmonicsAnalyse);
end

% --- Executes during object creation, after setting all properties.
function edit2_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit3_Callback(hObject, eventdata, handles)
NoOfHarmonicsRecon = str2double(get(hObject,'String'));
set(hObject,'Value', NoOfHarmonicsRecon);
NoOfHarmonicsAnalyse = get(handles.edit2, 'Value');
% force the number of harmonics for reconstruction to be no more than the
% number for analysis
if NoOfHarmonicsRecon > NoOfHarmonicsAnalyse
    warndlg('You cannot reconstruct using more harmonics than have been created', 'Be careful!');
    set(handles.edit3, ...
        'String', num2str(NoOfHarmonicsAnalyse), ...
        'Value', NoOfHarmonicsAnalyse);
end

% --- Executes during object creation, after setting all properties.
function edit3_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit4_Callback(hObject, eventdata, handles)
% Mean eror per point display only

% --- Executes during object creation, after setting all properties.
function edit4_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit5_Callback(hObject, eventdata, handles)
% Error SD display only

% --- Executes during object creation, after setting all properties.
function edit5_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% the Plot input data button
rInputData = handles.rInputData; % a cell array
rMirroredInputData = handles.rMirroredInputData; % a cell array
sInputFileName = handles.sInputFileName;

bValidInputDataLoaded = handles.bValidInputDataLoaded;
bMirrorState = get(handles.checkbox8, 'value');
iOutlineNo = handles.iOutlineNo;
rColourArray = handles.rColourArray;

if bValidInputDataLoaded
h0 = figure('Units','points', ...
	'Color',[0 0.8 0.8], ...
	'MenuBar','figure', ...
	'Name','Input Data Plot', ...
	'NumberTitle','off', ...
	'PaperPosition',[18 180 576 432], ...
	'PaperType','A4', ...
	'PaperUnits','points', ...
	'Pointer','crosshair', ...
	'Position', [12 114 318 296], ...
	'Tag','InputDataFigure');
h1 = axes('Parent',h0, ...
	'Box','off', ...
	'CameraUpVector',[0 1 0], ...
	'Color',[1 1 1], ...
	'DataAspectRatioMode','manual', ...   
	'DataAspectRatio',[80 80 1], ...
	'Position', [0.2 0.1 0.5 0.8], ...
	'Tag','Axes1', ...
	'WarpToFill','off', ...
	'XColor',[0 0 0], ...
	'YColor',[0 0 0], ...
    'ZColor',[0 0 0]);

	for iCount = 1 : iOutlineNo  % plot all input outlines on the same axes
   	    if bMirrorState == 1
   	        rLocalDataCell = rMirroredInputData{iCount};
        else
            rLocalDataCell = rInputData{iCount};
        end
        for i=1:size(rLocalDataCell{1},1)  % convert from cell to numeric array
            for j=1:2
                rLocalData(i,j) = rLocalDataCell{1}(i,j);
            end
        end
        rLocalDataCell = {};
        rColour = rColourArray(rem(iCount,8)+1,:);
        h2 = line('Parent',h1, ...
			'Color', rColour, ...  
			'Tag','Axes1Line1', ...
			'XData',rLocalData(:,1), ...
  		 	'YData',rLocalData(:,2));
        rLocalData = [];
    end % for iCount = 1 : iOutlineNo
else
   errordlg('There is no data to plot!', 'Input data error' );
end; % "if bValidInputDataLoaded"


% --- Executes on button press in pushbutton2.
function pushbutton2_Callback(hObject, eventdata, handles)
% the Analyse button
% Calculates a set of elliptical Fourier shape descriptors -
% see Giardina and Kuhl.
% Input is from a file into rLocalData.
% Output is via rFSDs.
% Input data list is mirrored if bMirrorState is TRUE - see Kuhl and Giardina.
% Mirroring helps deal with outline figures that are open-ended
% The output FSDs will be normalised for location, size and orientation
% if bNormaliseState is TRUE

rInputData = handles.rInputData; % a cell array
rMirroredInputData = handles.rMirroredInputData; % a cell array
sInputFileName = handles.sInputFileName;
bValidInputDataLoaded = handles.bValidInputDataLoaded;
iOutlineNo = handles.iOutlineNo;
rColourArray = handles.rColourArray;

bMirrorState = get(handles.checkbox8, 'value'); % read the state of the 'Symmetry" checkbox
bNormaliseSizeState = get(handles.checkbox6, 'Value');
bNormaliseOrientationState = get(handles.checkbox7, 'Value');
bPlotCoeffs = get(handles.checkbox9, 'Value');
iNoOfHarmonicsAnalyse = get(handles.edit2, 'Value');

rFSDs = 0; % clear all shape descriptors

if bValidInputDataLoaded % only do the analysis if there is something to analyse!
   % analyse all figures that have been loaded
   % choose between the mirrored and ordinary data sets
   for iInputFigCount = 1 : iOutlineNo
       rLocalData = []; % clear this each time
       if bMirrorState == 1
        	rLocalDataCell = rMirroredInputData{iInputFigCount};
        else
     		rLocalDataCell = rInputData{iInputFigCount};
        end
        for i=1:size(rLocalDataCell{1},1)  % convert from cell to numeric array
            for j=1:2
                rLocalData(i,j) = rLocalDataCell{1}(i,j);
            end
        end
        rLocalDataCell = {}; % finished with this, clear it

		iNoOfPoints = size(rLocalData,1); % gives the number of rows in the matrix
	   
		% Pre-calculate some constant arrays
		% n * 2 * pi
		% n^2 * 2* pi^2
		% where n is the number of harmonics to be used in the analysis
		rTwoNPi = (1:1:iNoOfHarmonicsAnalyse)* 2 * pi;
		rTwoNSqPiSq = (1:1:iNoOfHarmonicsAnalyse) .* (1:1:iNoOfHarmonicsAnalyse)* 2 * pi * pi;
	
		% the following loops are copied from the Excel implementation
		% there undoubtably is a more efficient vector form for all this
		% but "one thing at a time"...
		iNoOfPoints = size(rLocalData,1) - 1; % hence there is 1 more data point in rLocalData than iNoOfPoints
		for iCount = 2 : iNoOfPoints + 1
		   rDeltaX(iCount-1) = rLocalData(iCount,1) - rLocalData(iCount-1,1);
	   	rDeltaY(iCount-1) = rLocalData(iCount,2) - rLocalData(iCount-1,2);
		end

		% Calculate 'time' differences from point to point - actually distances, but we are
		% carrying on the fiction of a point running around the closed figure at constant speed.	
		% We are analysing the projections on to the x and y axes of this point's path around the figure
		for iCount = 1 : iNoOfPoints
   		rDeltaT(iCount) = sqrt((rDeltaX(iCount)^2) + (rDeltaY(iCount)^2));
		end

		% now sum the incremental times to get the time at any point
		rTime(1) = 0;
		for iCount = 2 : iNoOfPoints + 1
		   rTime(iCount) = rTime(iCount - 1) + rDeltaT(iCount-1);
		end

		rPeriod = rTime(iNoOfPoints+1); % rPeriod defined for readability

		% calculate the A-sub-0 coefficient
		rSum1 = 0;
		rIncr1 = 0;
		for iP = 2 : iNoOfPoints + 1 
   		    rSum2 = 0;
		    rSum3 = 0;
   		    rInnerDiff = 0;
	   	% calculate the partial sums - these are 0 for iCount = 1
		   if iP > 1 
   		   for iJ = 2 : iP-1
      		   rSum2 = rSum2 + rDeltaX(iJ-1);
         		rSum3 = rSum3 + rDeltaT(iJ-1);
		   end
   		   rInnerDiff = rSum2 - ((rDeltaX(iP-1) / rDeltaT(iP-1)) * rSum3);
	   	end
	   	rIncr1 = ((rDeltaX(iP-1) / (2*rDeltaT(iP-1)))*(rTime(iP)^2-rTime(iP-1)^2) + rInnerDiff*(rTime(iP)-rTime(iP-1)));
		   rSum1 = rSum1 + rIncr1;
		end   
		rFSDs(iInputFigCount,1,1) = ((1 / rPeriod) * rSum1) + rLocalData(1,1); % store A-sub-0 in output FSDs array - this array will be 4 x iNoOfHarmonicsAnalyse

		% calculate the a-sub-n coefficients
		for iHNo = 2 : iNoOfHarmonicsAnalyse
   		rSum1 = 0;
		   rIncr1 = 0;
   		for iP = 1 : iNoOfPoints
      		rIncr1 = (rDeltaX(iP) / rDeltaT(iP))*((cos(rTwoNPi(iHNo-1)*rTime(iP+1)/rPeriod) - cos(rTwoNPi(iHNo-1)*rTime(iP)/rPeriod)));
         	rSum1 = rSum1 + rIncr1;
	   	end
			rFSDs(iInputFigCount,1,iHNo) = (rPeriod / rTwoNSqPiSq(iHNo-1)) * rSum1;
		end % "foriHNo = 1 :..."
   
   	    rFSDs(iInputFigCount,2,1) = 0; % there is no 0th order sine coefficient
   
		% calculate the b-sub-n coefficients
		for iHNo = 2 : iNoOfHarmonicsAnalyse
   		rSum1 = 0;
	   	rIncr1 = 0;
	   	for iP = 1 : iNoOfPoints
   	   	    rIncr1 = (rDeltaX(iP) / rDeltaT(iP))*((sin(rTwoNPi(iHNo-1)*rTime(iP+1)/rPeriod) - sin(rTwoNPi(iHNo-1)*rTime(iP)/rPeriod)));
      	    rSum1 = rSum1 + rIncr1;
	   	end
			rFSDs(iInputFigCount,2,iHNo) = (rPeriod / rTwoNSqPiSq(iHNo-1)) * rSum1;
	    end % "foriHNo = 1 :..."
   
  		% calculate the C-sub-0 coefficient
		rSum1 = 0;
		rIncr1 = 0;
		for iP = 2 : iNoOfPoints + 1 
   		rSum2 = 0;
	   	rSum3 = 0;
	   	rInnerDiff = 0;
		   % calculate the partial sums - these are 0 for iCount = 1
	   	if iP > 1 
   	   	for iJ = 2 : iP-1
      	   	rSum2 = rSum2 + rDeltaY(iJ-1);
	         	rSum3 = rSum3 + rDeltaT(iJ-1);
		   end
   		   rInnerDiff = rSum2 - ((rDeltaY(iP-1) / rDeltaT(iP-1)) * rSum3);
		   end
   		rIncr1 = ((rDeltaY(iP-1) / (2*rDeltaT(iP-1)))*(rTime(iP)^2-rTime(iP-1)^2) + rInnerDiff*(rTime(iP)-rTime(iP-1)));
	   	rSum1 = rSum1 + rIncr1;
		end   
   	rFSDs(iInputFigCount,3,1) = ((1 / rPeriod) * rSum1) + rLocalData(1,2); % store C-sub-0 in output FSDs array - this array will be 4 x iNoOfHarmonicsAnalyse
   
   	% calculate the C-sub-n coefficients
		for iHNo = 2 : iNoOfHarmonicsAnalyse
   		rSum1 = 0;
	   	rIncr1 = 0;
	   	for iP = 1 : iNoOfPoints
   	   	rIncr1 = (rDeltaY(iP) / rDeltaT(iP))*((cos(rTwoNPi(iHNo-1)*rTime(iP+1)/rPeriod) - cos(rTwoNPi(iHNo-1)*rTime(iP)/rPeriod)));
      	   rSum1 = rSum1 + rIncr1;
	   	end
			rFSDs(iInputFigCount,3,iHNo) = (rPeriod / rTwoNSqPiSq(iHNo-1)) * rSum1;
		end % "foriHNo = 1 :..."
   
   	rFSDs(iInputFigCount,4,1) = 0; % there is no 0th order sine coefficient
   
   	% calculate the D-sub-n coefficients
		for iHNo = 2 : iNoOfHarmonicsAnalyse
   		rSum1 = 0;
	   	rIncr1 = 0;
	   	for iP = 1 : iNoOfPoints
   	   	rIncr1 = (rDeltaY(iP) / rDeltaT(iP))*((sin(rTwoNPi(iHNo-1)*rTime(iP+1)/rPeriod) - sin(rTwoNPi(iHNo-1)*rTime(iP)/rPeriod)));
      	   rSum1 = rSum1 + rIncr1;
	   	end
			rFSDs(iInputFigCount,4,iHNo) = (rPeriod / rTwoNSqPiSq(iHNo-1)) * rSum1;
	   end % "foriHNo = 1 :...
   
   	% the non-normalised coefficients are now in rFSDs
	   % if we want the normalised ones, this is where it happens
   	if (bNormaliseSizeState == 1) || (bNormaliseOrientationState == 1)
        % rTheta1 is the angle through which the starting position of the first
	    % harmonic phasor must be rotated  to be aligned with the major axis of
   	    % the first harmonic ellipse
        rFSDsTemp = rFSDs;
      	rTheta1 = 0.5 * atan(2 * (rFSDsTemp(iInputFigCount,1,2) * rFSDsTemp(iInputFigCount,2,2) + rFSDsTemp(iInputFigCount,3,2) * rFSDsTemp(iInputFigCount,4,2)) / ...
         	(rFSDsTemp(iInputFigCount,1,2)^2 + rFSDsTemp(iInputFigCount,3,2)^2 - rFSDsTemp(iInputFigCount,2,2)^2 - rFSDsTemp(iInputFigCount,4,2)^2));
	   % calculate the partially normalised coefficients - normalised for
	   % starting point
   	   for iHNo = 1 : iNoOfHarmonicsAnalyse
      	    rStarFSDs(iInputFigCount,1,iHNo) = cos((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,1,iHNo) + sin((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,2,iHNo);
            rStarFSDs(iInputFigCount,2,iHNo) = -sin((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,1,iHNo) + cos((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,2,iHNo);
	        rStarFSDs(iInputFigCount,3,iHNo) = cos((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,3,iHNo) + sin((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,4,iHNo);
   	        rStarFSDs(iInputFigCount,4,iHNo) = -sin((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,3,iHNo) + cos((iHNo-1) * rTheta1) * rFSDsTemp(iInputFigCount,4,iHNo);
	   end % for iHNo = 1 : iNoOfHarmonicsAnalyse
      
   	   rPsi1 = atan(rStarFSDs(iInputFigCount,3,2) / rStarFSDs(iInputFigCount,1,2));
       rSemiMajor = sqrt(rStarFSDs(iInputFigCount,1,2)^2 + rStarFSDs(iInputFigCount,3,2)^2); % find the semi-major axis of the first ellipse
       
       rFSDs(iInputFigCount,:,:) = rStarFSDs(iInputFigCount,:,:) ./ rSemiMajor; % if we haven't asked for normalisation of orientation, 
                                                                             % return the coefficients normalised for starting point and size 
       
       if bNormaliseOrientationState == 1
           % now find the orientation normalised values - return them in rFSDs
   	       for iHNo = 1 : iNoOfHarmonicsAnalyse
      	       rFSDsTemp(iInputFigCount,1,iHNo) = (cos(rPsi1) * rStarFSDs(iInputFigCount,1,iHNo) + sin(rPsi1) * rStarFSDs(iInputFigCount,3,iHNo)) / rSemiMajor;
       	       rFSDsTemp(iInputFigCount,2,iHNo) = (cos(rPsi1) * rStarFSDs(iInputFigCount,2,iHNo) + sin(rPsi1) * rStarFSDs(iInputFigCount,4,iHNo)) / rSemiMajor;
	           rFSDsTemp(iInputFigCount,3,iHNo) = (-sin(rPsi1) * rStarFSDs(iInputFigCount,1,iHNo) + cos(rPsi1) * rStarFSDs(iInputFigCount,3,iHNo)) / rSemiMajor;
   	           rFSDsTemp(iInputFigCount,4,iHNo) = (-sin(rPsi1) * rStarFSDs(iInputFigCount,2,iHNo) + cos(rPsi1) * rStarFSDs(iInputFigCount,4,iHNo)) / rSemiMajor;
      	   end % for iHNo = 1 : iNoOfHarmonicsAnalyse
           rFSDs = rFSDsTemp; % return fully normlised coefficients
       end
	end % if (bNormaliseSizeState == 1) || (bNormaliseOrientationState == 1)
  end   %for iInputFigCount = 1 : iOutlineNo
rLocalData = {}; % finished with this, clear it
bAnalysisDone = 1;
      
rPowerSpectrum = []; % clear all intermediate variables - their size may need to change from run to run
rPhaseSpectrum = [];
% now calculate all derived values:
for iInputFigCount = 1 : iOutlineNo
	rPowerSpectrum(iInputFigCount,1,:) = real(sqrt(rFSDs(iInputFigCount,1,:).^2.0 + rFSDs(iInputFigCount,2,:)));
	rPowerSpectrum(iInputFigCount,2,:) = real(sqrt(rFSDs(iInputFigCount,3,:).^2.0 + rFSDs(iInputFigCount,4,:)));
   
   rFSDs(iInputFigCount,2,:) = rFSDs(iInputFigCount,2,:) + (eps*5); % small, but not zero
   rFSDs(iInputFigCount,4,:) = rFSDs(iInputFigCount,4,:) + (eps*5); % to prevent "divide by 0" errors
   rPhaseSpectrum(iInputFigCount,1,:) = atan(rFSDs(iInputFigCount,1,:)./rFSDs(iInputFigCount,2,:));
	rPhaseSpectrum(iInputFigCount,2,:) = atan(rFSDs(iInputFigCount,3,:)./rFSDs(iInputFigCount,4,:));
   
   rFSDs(iInputFigCount,2,:) = rFSDs(iInputFigCount,2,:) - (eps*5); % put it back the way it was
   rFSDs(iInputFigCount,4,:) = rFSDs(iInputFigCount,4,:) - (eps*5);
end

rXCosProduct = []; % clear all intermediate variables - their size may need to change from run to run
rYCosProduct = [];
rXSinProduct = [];
rYSinProduct = [];
rXCosDiff = [];
rYCosDiff = [];
rXSinDiff = [];
rYSinDiff = [];
rXHarmonicDistance = [];
rYHarmonicDistance = [];
if bNormaliseSizeState
    iStartHarmonic = 3;
else
    iStartHarmonic = 2;
end
% calculate harmonic distances (only if there is more than one outline)
% use the normalised RMS distance method
if iOutlineNo > 1 %ie there are distances to calculate
	rXCosDiff = [];
	rYCosDiff = [];
	rXSinDiff = [];
	rYSinDiff = [];
	rXRMSDistance = [];
	rYRMSDistance = [];
   for iLeft = 1 : iOutlineNo
      for iRight = (iLeft+1) : iOutlineNo
   		rXCosDiff = (rFSDs(iLeft,1,iStartHarmonic:iNoOfHarmonicsAnalyse) - rFSDs(iRight,1,iStartHarmonic:iNoOfHarmonicsAnalyse)).^2;
   		rYCosDiff = (rFSDs(iLeft,3,iStartHarmonic:iNoOfHarmonicsAnalyse) - rFSDs(iRight,3,iStartHarmonic:iNoOfHarmonicsAnalyse)).^2;
   		rXSinDiff = (rFSDs(iLeft,2,iStartHarmonic:iNoOfHarmonicsAnalyse) - rFSDs(iRight,2,iStartHarmonic:iNoOfHarmonicsAnalyse)).^2;
   		rYSinDiff = (rFSDs(iLeft,4,iStartHarmonic:iNoOfHarmonicsAnalyse) - rFSDs(iRight,4,iStartHarmonic:iNoOfHarmonicsAnalyse)).^2;
        % iStartHarmonic = 2 is used to avoid adding in
        % differences in the location of the centroids of the outlines
        % iStartHarmonic = 3 removes the 1 | -1 for the normalised size as
        % well
   		rXRMSDistance(iLeft,iRight) = sqrt(sum(rXCosDiff + rXSinDiff) ./ iNoOfHarmonicsAnalyse);
      	rYRMSDistance(iLeft,iRight) = sqrt(sum(rYCosDiff + rYSinDiff) ./ iNoOfHarmonicsAnalyse);
      end
   end
   rDiffs = rXRMSDistance;
   rDiffs = [rDiffs; rYRMSDistance];
   rXData = [];
   rYData = [];
   iPlotSize = size(rXRMSDistance,1) * size(rXRMSDistance,2);
   rXData = reshape(rXRMSDistance, 1, iPlotSize);% convert matrix to row vector
   rYData = reshape(rYRMSDistance, 1, iPlotSize);
   % plot all distances (there will be one fewer distances than there are outlines)
h0 = figure('Units','points', ...
	'Color',[0 0.8 0.8], ...
	'MenuBar','figure', ...
	'Name',['Harmonic distances plot - RMS distances method'], ...
	'NumberTitle','off', ...
	'Pointer','crosshair', ...
	'Position', [12 114 318 296], ...
	'Tag','DistancePlotFigure');
h1 = axes('Parent',h0, ...
	'Box','off', ...
	'CameraUpVector',[0 1 0], ...
	'Color',[1 1 1], ...
	'Tag','Axes1', ...
	'WarpToFill','off', ...
	'XColor',[0 0 0], ...
	'YColor',[0 0 0], ...
   'ZColor',[0 0 0]);
set(h1,'Xlabel',text('String','X projection distance'));
set(h1,'Ylabel',text('String','Y projection distance'));
h2 = line('Parent',h1, ...
   'Color', 'r', ...
   'LineStyle', 'none', ...
   'Marker', 'x', ...
	'Tag','Axes1Line1', ...
	'XData',rXData, ...
   'YData',rYData);
   for iLeft = 1 : iOutlineNo
      cLLabel = num2str(iLeft);
      rColourLLabel = rColourArray(iLeft,:);
      for iRight = (iLeft+1) : iOutlineNo
         cRLabel = num2str(iRight);
         rColourRLabel = rColourArray(iRight,:);
         h3 = text('Parent', h1, ...
            'Units', 'data', ...
	   		'Color', rColourLLabel, ...
   	      'Position', [rXRMSDistance(iLeft,iRight) rYRMSDistance(iLeft,iRight)], ...
      	   'VerticalAlignment', 'bottom', ...
   			'String', [cLLabel '-' cRLabel] );
      end
   end
   handles.rDiffs = rDiffs;
end %if iOutlineNo > 1

if bPlotCoeffs == 1  % Plot the four sets of raw harmonic coefficients
 	h0 = figure('Units','normalized', ...
		'Color',[0 0.8 0.8], ...
	   'MenuBar','figure', ...
     	'Name','Harmonic coefficient values', ...
      'NumberTitle','off', ...
		'Pointer','crosshair', ...
		'Position',[.35 .1 .6 .85], ...
      'ToolBar','figure');
   
	h1 = axes('Parent',h0, ...
		'Box','off', ...
	  	'Color',[1 1 1], ...
      'GridLineStyle', '-', ...
      'Position',[0.1 0.8 0.8 0.18], ...
     	'XLim',[1,iNoOfHarmonicsAnalyse]);
   set(h1,'Ylabel',text('String','X cosine'));
   for iInputFigCount = 1 : iOutlineNo
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8),:), ...
	  		'Tag','Axes1Line1', ...
	   		'Linewidth', 0.3, ...
		   	'Marker','.', ...
   			'MarkerSize', 8, ...
	   		'XData', [1:1:iNoOfHarmonicsAnalyse], ...
         	'YData',rFSDs(iInputFigCount,1,:));
	end % for iInputFigCount = 1 : iOutlineNo
	grid on;
     
	h1 = axes('Parent',h0, ...
		'Box','off', ...
  		'Color',[1 1 1], ...
      'GridLineStyle', '-', ...
	   'Position',[0.1 0.55 0.8 0.18], ...               
  	   'XLim',[1,iNoOfHarmonicsAnalyse]);
   set(h1,'Ylabel',text('String','X sine'));
   for iInputFigCount = 1 : iOutlineNo
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8),:), ...
			'Tag','Axes1Line1', ...
		   	'Linewidth', 0.3, ...
			'Marker','.', ...
  			'MarkerSize', 8, ...
		   	'XData', [1:1:iNoOfHarmonicsAnalyse], ...
 			'YData',rFSDs(iInputFigCount,2,:));
   	end % for iInputFigCount = 1 : iOutlineNo
	grid on;
         
  	h1 = axes('Parent',h0, ...
		'Box','off', ...
   	'Color',[1 1 1], ...
      'GridLineStyle', '-', ...
  	   'Position',[0.1 0.3 0.8 0.18], ...                  
     	'XLim',[1,iNoOfHarmonicsAnalyse]);
   set(h1,'Ylabel',text('String','Y cosine')); 
   for iInputFigCount = 1 : iOutlineNo
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8),:), ...
   		'Tag','Axes1Line1', ...
		   'Linewidth', 0.3, ...
		   'Marker','.', ...
  			'MarkerSize', 8, ...
 			'XData', [1:1:iNoOfHarmonicsAnalyse], ...
         'YData',rFSDs(iInputFigCount,3,:));
   end % for iInputFigCount = 1 : iOutlineNo
   grid on;

	h1 = axes('Parent',h0, ...
		'Box','off', ...
		'Color',[1 1 1], ...
     	'GridLineStyle', '-', ...
      'Position',[0.1 0.05 0.8 0.18], ...              
      'XLim',[1,iNoOfHarmonicsAnalyse]);
   set(h1,'Ylabel',text('String','Y sine')); 
   for iInputFigCount = 1 : iOutlineNo
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8),:), ...
   		'Tag','Axes1Line1', ...
		   'Linewidth', 0.3, ...
			'Marker','.', ...
 			'MarkerSize', 8, ...
  			'XData', [1:1:iNoOfHarmonicsAnalyse], ...
         'YData',rFSDs(iInputFigCount,4,:));
   end % for iInputFigCount = 1 : iOutlineNo
   grid on;
   
   h0 = figure('Units','normalized', ... %plot the x and y power spectra
		'Color',[0 0.8 0.8], ...
	   'MenuBar','figure', ...
     	'Name','Power spectra', ...
      'NumberTitle','off', ...
		'Pointer','crosshair', ...
		'Position',[.3 .1 .6 .45], ...
      'ToolBar','figure');
   
	h1 = axes('Parent',h0, ...
		'Box','off', ...
	  	'Color',[1 1 1], ...
      'GridLineStyle', '-', ...
      'Position',[0.1 0.55 0.8 0.35], ...
     	'XLim',[1,iNoOfHarmonicsAnalyse]);
   set(h1,'Ylabel',text('String','X projection'));
   for iInputFigCount = 1 : iOutlineNo
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8),:), ...
	  		'Tag','Axes1Line1', ...
	   	'Linewidth', 0.3, ...
		   'Marker','.', ...
   		'MarkerSize', 8, ...
	   	'XData', [1:1:iNoOfHarmonicsAnalyse], ...
         'YData',rPowerSpectrum(iInputFigCount,1,:));
   end % for iInputFigCount = 1 : iOutlineNo
	grid on;
     
	h1 = axes('Parent',h0, ...
		'Box','off', ...
  		'Color',[1 1 1], ...
        'GridLineStyle', '-', ...
	    'Position',[0.1 0.1 0.8 0.35], ...               
  	    'XLim',[1,iNoOfHarmonicsAnalyse]);
   set(h1,'Ylabel',text('String','Y projection'));
   for iInputFigCount = 1 : iOutlineNo
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8),:), ...
			'Tag','Axes1Line1', ...
	   	    'Linewidth', 0.3, ...
			'Marker','.', ...
  			'MarkerSize', 8, ...
	   	    'XData', [1:1:iNoOfHarmonicsAnalyse], ...
  	        'YData',rPowerSpectrum(iInputFigCount,2,:));
   end % for iInputFigCount = 1 : iOutlineNo
   grid on;
end % if bPlotCoeffs == 1

handles.rDeltaT = rDeltaT;
handles.rFSDs = rFSDs;
handles.rPowerSpectrum = rPowerSpectrum;
handles.rPhaseSpectrum = rPhaseSpectrum;
handles.bAnalysisDone = 1;
guidata(hObject, handles);

else
   errordlg('There is no data loaded to analyse!', 'Data input error' );
end;% "if bValidInputDataLoaded"

% --- Executes on button press in pushbutton3.
function pushbutton3_Callback(hObject, eventdata, handles)
% the Reconstruct button
% Reconstructs the outline figure using the specified
% number of harmonics. Plots an x-y graph of the result

bAnalysisDone = handles.bAnalysisDone;

rFSDs = handles.rFSDs;

rDeltaT = handles.rDeltaT; % time increments used in the analysis will be used
% for the reconstruction. Could use any arbitrary points (eg equally
% spaced) but there is no need for spacing to be equal or for there to be
% any particular number of points.
% We should store seperate rDeltaT vectors for each outline when there are
% multiple outlines
iNoOfHarmonicsReconstruct = get(handles.edit3, 'Value');

iOutlineNo = handles.iOutlineNo;
rColourArray = handles.rColourArray;

sInputFileName = handles.sInputFileName;

iStartHarmonic = 2; % start at 2  - No.1 is just a location,could be added in

m = size(rDeltaT, 2) + 1; 
rTimeSteps(1) = 0;
rTimeSteps = [rTimeSteps rDeltaT];
for i=2:m
    rTimeSteps(i) = rTimeSteps(i) + rTimeSteps(i-1);
end
rTimeSteps = (rTimeSteps/sum(rDeltaT)) * pi; % fractions of pi up to 100%

ReconnedOutline = 0;
if bAnalysisDone == 1 % don't try to reconstruct if the analysis hasn't been done on the currently loaded data
   for iInputFigCount = 1 : iOutlineNo   
		% reconstruct the x-projection
		for iTime = 1:size(rTimeSteps, 2)
   		rSum = 0.0;
	   	for iHNo = iStartHarmonic:iNoOfHarmonicsReconstruct
         	rSum = rSum + (rFSDs(iInputFigCount,1,iHNo) * cos(2*(iHNo-1)* rTimeSteps(iTime)) + ...
            		 rFSDs(iInputFigCount,2,iHNo) * sin(2*(iHNo-1)* rTimeSteps(iTime)));
		   end % for iHNo = 1 : iNoOfHarmonicsReconstruct
   		ReconnedOutline(iInputFigCount,iTime,1) = rFSDs(iInputFigCount,1,1) + rSum;
		end % for iTime = 1 : size(rTimeSteps, 2)
        
		% reconstruct the y-projection
		for iTime = 1:size(rTimeSteps, 2)
   		rSum = 0.0;
		   for iHNo = iStartHarmonic:iNoOfHarmonicsReconstruct
   	      rSum = rSum + (rFSDs(iInputFigCount,3,iHNo) * cos(2*(iHNo-1)* rTimeSteps(iTime)) + ...
      	      	 rFSDs(iInputFigCount,4,iHNo) * sin(2*(iHNo-1)* rTimeSteps(iTime)));
		   end % for iHNo = 1 : iNoOfHarmonicsReconstruct
   	   ReconnedOutline(iInputFigCount,iTime,2) = rFSDs(iInputFigCount,3,1) + rSum;
	   end % for iTime = 1 : size(rTimeSteps, 2)
   end   %for iInputFigCount = 1 : iOutlineNo

	h0 = figure('Units','points', ...
		'Color',[0 0.8 0.8], ...
		'MenuBar','figure', ...
		'Name',['Reconstructed figure plot:  ' sInputFileName], ...
		'NumberTitle','off', ...
		'PaperPosition',[18 180 576 432], ...
		'PaperType','A4', ...
		'PaperUnits','points', ...
		'Pointer','crosshair', ...
		'Position', [12 114 318 296], ...
		'Tag','ReconnedDataFigure', ...
		'ToolBar','figure');
	h1 = axes('Parent',h0, ...
		'Box','off', ...
		'CameraUpVector',[0 1 0], ...
		'Color',[1 1 1], ...
		'DataAspectRatioMode','manual', ...   
		'DataAspectRatio',[80 80 1], ...
		'Position', [0.2 0.1 0.5 0.8], ...
		'Tag','Axes1', ...
		'WarpToFill','off', ...
		'XColor',[0 0 0], ...
		'YColor',[0 0 0], ...
        'ZColor',[0 0 0]);
   for iInputFigCount = 1 : iOutlineNo  % plot all reconstructed outlines
		h2 = line('Parent',h1, ...
			'Color',rColourArray(rem(iInputFigCount,8)+1,:), ...
			'Tag','Axes1Line1', ...
			'XData',ReconnedOutline(iInputFigCount,:,1), ...
   	        'YData',ReconnedOutline(iInputFigCount,:,2));
   end % for iInputFigCount = 1 : iOutlineNo
else
   errordlg('Cannot reconstruct - there are no valid FSDs!', 'Reconstruction error' );
end %

% recover the original data to calculate the error estimate
rInputData = handles.rInputData; % a cell array

rLocalData = []; % clear this each time
rLocalDataCell = rInputData{iInputFigCount};
for i=1:size(rLocalDataCell{1},1)  % convert from cell to numeric array
    for j=1:2
        rLocalData(i,j) = rLocalDataCell{1}(i,j);
    end
end
%remove the last point from the input data - was added to "close" the figure
iNoOfPoints = size(rLocalData,1)-1;
rLocalData = rLocalData(1:iNoOfPoints, :);

ro = ReconnedOutline(:,:,1)';
ro = [ro ReconnedOutline(:,:,2)'];
bMirrorState = get(handles.checkbox8, 'value');
if bMirrorState
    ro = ro(1:iNoOfPoints, :);
end

errors = [];
for m=1:iNoOfPoints
    errors(m) = EuclidDist(rLocalData(m,:), ro(m,:));
end
totalError = sum(errors);
meanErrorPerPoint = totalError / iNoOfPoints;

errorDeviations = (errors - meanErrorPerPoint).^2;
errorVariance = sum(errorDeviations)/(iNoOfPoints-1);
errorSD = sqrt(errorVariance);

set(handles.edit5, ...
    'String', num2str(errorSD), ...
    'Value', errorSD);

set(handles.edit4, ...
    'String', num2str(meanErrorPerPoint), ...
    'Value', meanErrorPerPoint);

handles.ReconnedOutline = ReconnedOutline;
handles.bReconDone = 1;
% Update handles structure
guidata(hObject, handles);


% --- Executes on button press in pushbutton4.
function pushbutton4_Callback(hObject, eventdata, handles)
% the Clear outlines button
% Clears all outlines and FSDs from memory
% Specific to the multiple outlines version
% in the single outlines version, loading a
% fresh outline or set of FSDs automatically
% over-writes the previous data.

handles.rInputData = {};
handles.rMirroredInputData = {};
handles.rFSDs = [];
handles.ReconnedOutline = [];
handles.iOutlineNo = 0;
handles.ReconnedOutline = 0;

handles.bValidInputDataLoaded = 0;
handles.bAnalysisDone = 0;
guidata(hObject, handles);

% --- Executes on button press in pushbutton5.
function pushbutton5_Callback(hObject, eventdata, handles)
% the Close button
delete(handles.figure1);

% --------------------------------------------------------------------
% Menu callbacks
% --------------------------------------------------------------------
function FileMenu_Callback(hObject, eventdata, handles)

% --------------------------------------------------------------------
function OpenXyMenu_Callback(hObject, eventdata, handles)
% Prompts for filename using a dialog box
% Opens the selected file (assumes it is TAB delimited text)
% and reads it into a matrix
% A "mirrored" version is also created
% Intended to be the callback for the file-open menu entry in 'Figure1.m'

iOutlineNo = handles.iOutlineNo;
sInputFilePath = handles.sInputFilePath;
sInputFileName = handles.sInputFileName;

if iOutlineNo == 0 %ie we are here for the first time
	[fname,sInputFilePath] = uigetfile('*.txt;*.csv;*.xls', 'Find the input data file to open');
	if fname == 0
      return; % bail out if user Cancels
   else
      iOutlineNo = iOutlineNo + 1;
	end
	sInputFileName = fullfile(sInputFilePath, fname);
    [pathstr,name,inputFileExtention,versn] = fileparts(sInputFileName);
   [hFileHandle, message] = fopen(sInputFileName, 'r');
   sOpenFileList{iOutlineNo} = fname;
else % we have opened one already - go back to the same directory path
  	[fname,sInputFilePath] = uigetfile([sInputFilePath '*.txt;*.csv;*.xls'], 'Find the input data file to open');
	if fname == 0
      return; % bail out if user Cancels
    else
      iOutlineNo = iOutlineNo + 1;
	end
	sInputFileName = fullfile(sInputFilePath, fname);
    [pathstr,name,inputFileExtention,versn] = fileparts(sInputFileName);
    [hFileHandle, message] = fopen(sInputFileName, 'r');
    sOpenFileList{iOutlineNo} = fname;
end
rThisInputData = [];
rThisMirroredInputData = [];
%read x,y data - dlmread uses textread that is only available as a MEX file and cannot yet (R11) be compiled
switch lower(inputFileExtention)
    case '.txt'
        while feof(hFileHandle) == 0
            line = fgetl(hFileHandle);
            xy = sscanf(line, '%g%g');
            xyt = xy';
            rThisInputData = [rThisInputData;xyt];
        end
        fclose(hFileHandle);
    case '.csv'
        while feof(hFileHandle) == 0
            line = fgetl(hFileHandle);
            xy = sscanf(line, '%g,%g');
            xyt = xy';
            rThisInputData = [rThisInputData;xyt];
        end
        fclose(hFileHandle);
    case '.xls'
        rThisInputData = xlsread(sInputFileName);
end

iNoOfPoints = size(rThisInputData,1); % gives the number of columns in the matrix
rFirstPt = rThisInputData(1, :);
rLastPt = rThisInputData(iNoOfPoints, :); % these Pts are x-y pairs

if EuclidDist(rFirstPt, rLastPt) < .1 % millimetres are the usual units
 	rThisInputData = rThisInputData(1:iNoOfPoints-1, :); 
	% removes last point from rInputData
   % if it is too close to the first (ie figure is "closed")
end

iSymmetryType = handles.iSymmetryType;
switch iSymmetryType
    case 1
       rSecondHalfOfMirroredData(:,1) = rThisInputData(iNoOfPoints-1:-1:2, 1)*-1;
       rSecondHalfOfMirroredData(:,2) = rThisInputData(iNoOfPoints-1:-1:2, 2);
       % flips the sign of the x coordinates and reverses the data
    case 2
       rSecondHalfOfMirroredData(:,1) = rThisInputData(iNoOfPoints-1:-1:2, 1);
       rSecondHalfOfMirroredData(:,2) = rThisInputData(iNoOfPoints-1:-1:2, 2)*-1;
       % flips the sign of the y coordinates and reverses the data
    case 3
       rSecondHalfOfMirroredData(:,1) = rThisInputData(iNoOfPoints-1:-1:2, 1);
       rSecondHalfOfMirroredData(:,2) = rThisInputData(iNoOfPoints-1:-1:2, 2);
       % this is the same as the earlier "Mirror" operation - overlays the
       % data with a reverse order copy of itself
end

%if iSymmetryType == 1
%    rSecondHalfOfMirroredData(:,1) = rThisInputData(iNoOfPoints-1:-1:2, 1) *-1;
%else
%    rSecondHalfOfMirroredData(:,1) = rThisInputData(iNoOfPoints-1:-1:2, 1);
%end
%rSecondHalfOfMirroredData(:,2) = rThisInputData(iNoOfPoints-1:-1:2, 2);

rThisMirroredInputData = [rThisInputData; rSecondHalfOfMirroredData];
% puts the two halves of the data together to form the symmetrical data

% Duplicate the first point to close the figure.
% I know this may just be a reversal of the removal of the
% duplicate point done above, BUT this way it doesn't matter whether someone
% has already closed the input figure or not.
% Really just input data validation
rThisInputData = [rThisInputData; rThisInputData(1,:)];
rThisMirroredInputData = [rThisMirroredInputData; rThisMirroredInputData(1,:)];

% when we get here there are two alternative sets of input data
% one is mirrored, the other not. Both  are "closed" correctly.
handles.iOutlineNo = iOutlineNo;
handles.rInputData{iOutlineNo} = {rThisInputData}; % a cell array
handles.rMirroredInputData{iOutlineNo} = {rThisMirroredInputData}; % a cell array
handles.sInputFileName = sInputFileName;
handles.bValidInputDataLoaded = 1;
handles.bAnalysisDone = 0; % new data is loaded but not analysed
handles.sInputFilePath = sInputFilePath;
% Update handles structure
guidata(hObject, handles);


% --------------------------------------------------------------------
function OpenFSDsMenu_Callback(hObject, eventdata, handles)
% callback for "Load FSDs from text file" menu entry
% Loads the FSDs from a TAB delimited text file
iOutlineNo = handles.iOutlineNo;

[fname,pname] = uigetfile('*.txt', 'Specify the file from which to load the FSDs');
if fname == 0
  	return; % bail out if user Cancels
end
sInputFileName = fullfile(pname, fname);

hFileHandle = fopen(sInputFileName, 'rt');
if hFileHandle == -1
   return; % bail out if fopen fails for any reason
end

rFSDs = [];
rFSDsTemp = [];
iOutlineNo = 0;
while feof(hFileHandle) == 0
    iOutlineNo = iOutlineNo + 1;
    sLine1 = fgetl(hFileHandle);
	sLine2 = fgetl(hFileHandle);
	sLine3 = fgetl(hFileHandle);
	sLine4 = fgetl(hFileHandle);
    rFSDsTemp = sscanf(sLine1, '%g', [1 inf]);
    iNoOfHarmonicsAnalyse = size(rFSDsTemp,2);
	rFSDsTemp = [rFSDsTemp ; sscanf(sLine2, '%g', [1 iNoOfHarmonicsAnalyse])]; % concatenate the next row
	rFSDsTemp = [rFSDsTemp ; sscanf(sLine3, '%g', [1 iNoOfHarmonicsAnalyse])]; % and again
    rFSDsTemp = [rFSDsTemp ; sscanf(sLine4, '%g', [1 iNoOfHarmonicsAnalyse])]; % and again
    rFSDs(iOutlineNo,:,:) = rFSDsTemp;
end   %while feof(hFileHandle)
fclose(hFileHandle);

set(handles.edit2, ...
    'String', num2str(iNoOfHarmonicsAnalyse), ...
    'Value', iNoOfHarmonicsAnalyse); % force edit box contents
set(handles.edit3, ...
    'String', num2str(iNoOfHarmonicsAnalyse), ...
    'Value', iNoOfHarmonicsAnalyse);

handles.rFSDs = rFSDs;
handles.iOutlineNo = iOutlineNo;
handles.bValidInputDataLoaded = 0; % we have loaded FSDs - cannot assume there is anything to analyse
handles.bAnalysisDone = 1; % but there are valid FSDs to reconstruct
guidata(hObject, handles);


% --------------------------------------------------------------------
function SaveFSDsMenu_Callback(hObject, eventdata, handles)
% callback for "Save FSDs to text file" menu entry
% saves the FSDs in a TAB delimited text file

rFSDs = handles.rFSDs;
bAnalysisDone = handles.bAnalysisDone;
iOutlineNo = handles.iOutlineNo;

if bAnalysisDone == 1  % ie there should be valid data in rFSDs
	[fname,pname] = uiputfile('*.txt', 'Specify the file in which to save the FSDs');
	if fname == 0
   	    return; % bail out if user Cancels
	end
sOutputFileName = fullfile(pname, fname);
hFileHandle = fopen(sOutputFileName, 'w');
for iInputFigCount = 1 : iOutlineNo   
	rLine1 = rFSDs(iInputFigCount,1,:);
	rLine2 = rFSDs(iInputFigCount,2,:);
	rLine3 = rFSDs(iInputFigCount,3,:);
	rLine4 = rFSDs(iInputFigCount,4,:);

	fprintf(hFileHandle, '%7.5f\t', rLine1);
	fprintf(hFileHandle, '\r\n');
	fprintf(hFileHandle, '%7.5f\t', rLine2);
	fprintf(hFileHandle, '\r\n');
	fprintf(hFileHandle, '%7.5f\t', rLine3);
	fprintf(hFileHandle, '\r\n');
	fprintf(hFileHandle, '%7.5f\t', rLine4);
	fprintf(hFileHandle, '\r\n');
end   %for iInputFigCount = 1 : iOutlineNo
%	dlmwrite(sOutputFileName, rFSDs, '\t'); % dlmwrite leaves blanks for 0s - not what we want!
fclose(hFileHandle);
else
   errordlg('There are no FSDs to write out!', 'File output error' );
end % if bAnalysisDone == 1


% --------------------------------------------------------------------
function SaveRecon_Callback(hObject, eventdata, handles)
% callback for "Save reconstructed outline" menu entry
% saves the outline  in a TAB delimited text file
ReconnedOutline = handles.ReconnedOutline;
bReconDone = handles.bReconDone;
iOutlineNo = handles.iOutlineNo;

if bReconDone == 1  % ie there should be valid data in rDiffs
	[fname,pname] = uiputfile('*.txt', 'Specify the file in which to save the reconstruction');
	if fname == 0
   	    return; % bail out if user Cancels
	end
sOutputFileName = fullfile(pname, fname);
hFileHandle = fopen(sOutputFileName, 'w');
for iThisOutline = 1:iOutlineNo
    for iCol = 1 : size(ReconnedOutline,2)
        rLine1 = ReconnedOutline(iOutlineNo,iCol, :);
        fprintf(hFileHandle, '%7.5f\t', rLine1);
        fprintf(hFileHandle, '\r\n');
    end   %for iRow = 1 : iOutlineNo
end

fclose(hFileHandle);
else
   errordlg('No reconstruction has been done, hence there is no data to write!', 'File output error' );
end % if bAnalysisDone == 1


% --------------------------------------------------------------------
function SaveDiffsToFile_Callback(hObject, eventdata, handles)
% callback for "Save differences to file" menu entry
% saves the RMS differences in a TAB delimited text file
rDiffs = handles.rDiffs;
bAnalysisDone = handles.bAnalysisDone;
iOutlineNo = handles.iOutlineNo;

if bAnalysisDone == 1  % ie there should be valid data in rDiffs
	[fname,pname] = uiputfile('*.txt', 'Specify the file in which to save the differences');
	if fname == 0
   	    return; % bail out if user Cancels
	end
sOutputFileName = fullfile(pname, fname);
hFileHandle = fopen(sOutputFileName, 'w');
for iRow = 1 : 2 .* (iOutlineNo - 1)
    rLine1 = rDiffs(iRow, :);
    fprintf(hFileHandle, '%7.5f\t', rLine1);
    fprintf(hFileHandle, '\r\n');
end   %for iRow = 1 : iOutlineNo

fclose(hFileHandle);
else
   errordlg('There are no differences calculated! Cannot write the file', 'File output error' );
end % if bAnalysisDone == 1


% --------------------------------------------------------------------
function ExitMenu_Callback(hObject, eventdata, handles)
delete(handles.figure1);


% --------------------------------------------------------------------
function AboutMenu_Callback(hObject, eventdata, handles)
msgbox(['Fourier shape descriptor program - Version V2.21. C David L Thomas, Oral ' ...
        'Anatomy, Medicine & Surgery Unit, School of Dental Science, ' ...
        'University of Melbourne. November 2005'], 'About FSD');
    
    
% --------------------------------------------------------------------
% Helper functions
% --------------------------------------------------------------------
function rDistance = EuclidDist(rPt1, rPt2)
% returns the Euclidian distance between rPt1 and rPt2
% which are assumed to be x-y pairs of floating-point numbers
rXDist = abs(rPt1(1) - rPt2(1));
rYDist = abs(rPt1(2) - rPt2(2));
rDistance = sqrt(rXDist*rXDist + rYDist*rYDist);

Contact us at files@mathworks.com