function [ Elements, varargout ] = PLY_READ ( Path, Str )
%*****************************************************************************80
%
%% PLY_READ reads a PLY 3D data file.
%
% [DATA,COMMENTS] = PLY_READ(FILENAME) reads a version 1.0 PLY file
% FILENAME and returns a structure DATA. The fields in this structure
% are defined by the PLY header; each element type is a field and each
% element property is a subfield. If the file contains any comments,
% they are returned in a cell string array COMMENTS.
%
% [TRI,PTS] = PLY_READ(FILENAME,'tri') or
% [TRI,PTS,DATA,COMMENTS] = PLY_READ(FILENAME,'tri') converts vertex
% and face data into triangular connectivity and vertex arrays. The
% mesh can then be displayed using the TRISURF command.
%
% Note: This function is slow for large mesh files (+50K faces),
% especially when reading data with list type properties.
%
% Example:
% [Tri,Pts] = PLY_READ('cow.ply','tri');
% [Tri,Pts] = PLY_READ('bunny.ply','tri');
% trisurf(Tri,Pts(:,1),Pts(:,2),Pts(:,3));
% colormap(gray); axis equal;
%
% Discussion:
%
% The original version of this program had a mistake that meant it
% did not properly triangulate files whose faces were not already triangular.
% This has been corrected (JVB, 25 February 2007).
%
% Glenn Ramsey pointed out and corrected a problem that occurred
% with an uninitialized value of Type2, 27 August 2012.
%
% Licensing:
%
% This code is distributed under the GNU LGPL license.
%
% Modified:
%
% 27 August 2012
%
% Author:
%
% Pascal Getreuer 2004
%
% Parameters:
%
% Local Parameters:
%
% COMMENTS, any comments from the file.
%
% ELEMENTCOUNT, the number of each type of element in file.
%
% ELEMENTS, the element data.
%
% PROPERTYTYPES, the element property types.
%
% SIZEOF, size in bytes of each type.
%
%
% Open the input file in "read text" mode.
%
[ fid, Msg ] = fopen ( Path, 'rt' );
if ( fid == -1 )
error ( Msg );
end
Buf = fscanf ( fid, '%s', 1 );
if ( ~strcmp ( Buf, 'ply' ) )
fclose ( fid );
error('Not a PLY file.');
end
%
% Read the header.
%
Position = ftell(fid);
Format = '';
NumComments = 0;
Comments = {};
NumElements = 0;
NumProperties = 0;
Elements = [];
ElementCount = [];
PropertyTypes = [];
ElementNames = {}; % list of element names in the order they are stored in the file
PropertyNames = []; % structure of lists of property names
while ( 1 )
%
% Read a line from the file.
%
Buf = fgetl ( fid );
BufRem = Buf;
Token = {};
Count = 0;
%
% Split the line into tokens.
%
while ( ~isempty(BufRem) )
[ tmp, BufRem ] = strtok(BufRem);
%
% Count the tokens.
%
if ( ~isempty ( tmp ) )
Count = Count + 1;
Token{Count} = tmp;
end
end
%
% Parse the line.
%
if ( Count )
switch lower ( Token{1} )
%
% Read the data format.
%
case 'format'
if ( 2 <= Count )
Format = lower ( Token{2} );
if ( Count == 3 & ~strcmp ( Token{3}, '1.0' ) )
fclose ( fid );
error('Only PLY format version 1.0 supported.');
end
end
%
% Read a comment.
%
case 'comment'
NumComments = NumComments + 1;
Comments{NumComments} = '';
for i = 2 : Count
Comments{NumComments} = [Comments{NumComments},Token{i},' '];
end
%
% Read an element name.
%
case 'element'
if ( 3 <= Count )
if ( isfield(Elements,Token{2}) )
fclose ( fid );
error(['Duplicate element name, ''',Token{2},'''.']);
end
NumElements = NumElements + 1;
NumProperties = 0;
Elements = setfield(Elements,Token{2},[]);
PropertyTypes = setfield(PropertyTypes,Token{2},[]);
ElementNames{NumElements} = Token{2};
PropertyNames = setfield(PropertyNames,Token{2},{});
CurElement = Token{2};
ElementCount(NumElements) = str2double(Token{3});
if ( isnan(ElementCount(NumElements)) )
fclose ( fid );
error(['Bad element definition: ',Buf]);
end
else
error(['Bad element definition: ',Buf]);
end
%
% Read an element property.
%
case 'property'
if ( ~isempty(CurElement) & Count >= 3 )
NumProperties = NumProperties + 1;
eval(['tmp=isfield(Elements.',CurElement,',Token{Count});'],...
'fclose(fid);error([''Error reading property: '',Buf])');
if ( tmp )
error(['Duplicate property name, ''',CurElement,'.',Token{2},'''.']);
end
%
% Add property subfield to Elements.
%
eval(['Elements.',CurElement,'.',Token{Count},'=[];'], ...
'fclose(fid);error([''Error reading property: '',Buf])');
%
% Add property subfield to PropertyTypes and save type.
%
eval(['PropertyTypes.',CurElement,'.',Token{Count},'={Token{2:Count-1}};'], ...
'fclose(fid);error([''Error reading property: '',Buf])');
%
% Record property name order.
%
eval(['PropertyNames.',CurElement,'{NumProperties}=Token{Count};'], ...
'fclose(fid);error([''Error reading property: '',Buf])');
else
fclose ( fid );
if ( isempty(CurElement) )
error(['Property definition without element definition: ',Buf]);
else
error(['Bad property definition: ',Buf]);
end
end
%
% End of header.
%
case 'end_header'
break;
end
end
end
%
% Set reading for specified data format.
%
if ( isempty ( Format ) )
warning('Data format unspecified, assuming ASCII.');
Format = 'ascii';
end
switch Format
case 'ascii'
Format = 0;
case 'binary_little_endian'
Format = 1;
case 'binary_big_endian'
Format = 2;
otherwise
fclose ( fid );
error(['Data format ''',Format,''' not supported.']);
end
%
% Read the rest of the file as ASCII data...
%
if ( ~Format )
Buf = fscanf ( fid, '%f' );
BufOff = 1;
else
%
% ...or, close the file, and reopen in "read binary" mode.
%
fclose ( fid );
%
% Reopen the binary file as LITTLE_ENDIAN or BIG_ENDIAN.
%
if ( Format == 1 )
fid = fopen ( Path, 'r', 'ieee-le.l64' );
else
fid = fopen ( Path, 'r', 'ieee-be.l64' );
end
%
% Find the end of the header again.
% Using ftell on the old handle doesn't give the correct position.
%
BufSize = 8192;
Buf = [ blanks(10), char(fread(fid,BufSize,'uchar')') ];
i = [];
tmp = -11;
while ( isempty(i) )
i = findstr(Buf,['end_header',13,10]); % look for end_header + CR/LF
i = [i,findstr(Buf,['end_header',10])]; % look for end_header + LF
if ( isempty(i) )
tmp = tmp + BufSize;
Buf = [Buf(BufSize+1:BufSize+10),char(fread(fid,BufSize,'uchar')')];
end
end
%
% seek to just after the line feed
%
fseek ( fid, i + tmp + 11 + (Buf(i + 10) == 13), -1 );
end
%
% Read element data.
%
% PLY and MATLAB data types (for fread)
%
PlyTypeNames = {'char','uchar','short','ushort','int','uint','float','double', ...
'char8','uchar8','short16','ushort16','int32','uint32','float32','double64'};
MatlabTypeNames = {'schar','uchar','int16','uint16','int32','uint32','single','double'};
SizeOf = [1,1,2,2,4,4,4,8];
for i = 1 : NumElements
%
% get current element property information
%
eval(['CurPropertyNames=PropertyNames.',ElementNames{i},';']);
eval(['CurPropertyTypes=PropertyTypes.',ElementNames{i},';']);
NumPro