You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
9.1 KiB
272 lines
9.1 KiB
function [coordVERTICES,varargout] = READ_stl(stlFILENAME,varargin)
|
|
% READ_stlascii Read mesh data in the form of an <*.stl> file
|
|
%==========================================================================
|
|
% FILENAME: READ_stl.m
|
|
% AUTHOR: Adam H. Aitkenhead
|
|
% INSTITUTION: The Christie NHS Foundation Trust
|
|
% CONTACT: adam.aitkenhead@physics.cr.man.ac.uk
|
|
% DATE: 29th March 2010
|
|
% PURPOSE: Read mesh data in the form of an <*.stl> file.
|
|
%
|
|
% USAGE:
|
|
%
|
|
% [coordVERTICES,coordNORMALS,stlNAME] = READ_stl(stlFILENAME,stlFORMAT)
|
|
%
|
|
% INPUT PARAMETERS:
|
|
%
|
|
% stlFILENAME - String - Mandatory - The filename of the STL file.
|
|
%
|
|
% stlFORMAT - String - Optional - The format of the STL file:
|
|
% 'ascii' or 'binary'
|
|
%
|
|
% OUTPUT PARAMETERS:
|
|
%
|
|
% coordVERTICES - Nx3x3 array - Mandatory
|
|
% - An array defining the vertex positions
|
|
% for each of the N facets, with:
|
|
% 1 row for each facet
|
|
% 3 cols for the x,y,z coordinates
|
|
% 3 pages for the three vertices
|
|
%
|
|
% coordNORMALS - Nx3 array - Optional
|
|
% - An array defining the normal vector for
|
|
% each of the N facets, with:
|
|
% 1 row for each facet
|
|
% 3 cols for the x,y,z components of the vector
|
|
%
|
|
% stlNAME - String - Optional - The name of the STL object.
|
|
%
|
|
%==========================================================================
|
|
|
|
%==========================================================================
|
|
% VERSION: USER: CHANGES:
|
|
% ------- ----- --------
|
|
% 100329 AHA Original version
|
|
% 100513 AHA Totally reworked the code. Now use textscan to read the
|
|
% file all at once, rather than one line at a time with
|
|
% fgetl. Major speed improvment.
|
|
% 100623 AHA Combined code which reads ascii STLS and code which
|
|
% reads binary STLs into a single function.
|
|
%==========================================================================
|
|
|
|
%==========================================================================
|
|
% STL ascii file format
|
|
%======================
|
|
% ASCII STL files have the following structure. Technically each facet
|
|
% could be any 2D shape, but in practice only triangular facets tend to be
|
|
% used. The present code ONLY works for meshes composed of triangular
|
|
% facets.
|
|
%
|
|
% solid object_name
|
|
% facet normal x y z
|
|
% outer loop
|
|
% vertex x y z
|
|
% vertex x y z
|
|
% vertex x y z
|
|
% endloop
|
|
% endfacet
|
|
%
|
|
% <Repeat for all facets...>
|
|
%
|
|
% endsolid object_name
|
|
%==========================================================================
|
|
|
|
%==========================================================================
|
|
% STL binary file format
|
|
%=======================
|
|
% Binary STL files have an 84 byte header followed by 50-byte records, each
|
|
% describing a single facet of the mesh. Technically each facet could be
|
|
% any 2D shape, but that would screw up the 50-byte-per-facet structure, so
|
|
% in practice only triangular facets are used. The present code ONLY works
|
|
% for meshes composed of triangular facets.
|
|
%
|
|
% HEADER:
|
|
% 80 bytes: Header text
|
|
% 4 bytes: (int) The number of facets in the STL mesh
|
|
%
|
|
% DATA:
|
|
% 4 bytes: (float) normal x
|
|
% 4 bytes: (float) normal y
|
|
% 4 bytes: (float) normal z
|
|
% 4 bytes: (float) vertex1 x
|
|
% 4 bytes: (float) vertex1 y
|
|
% 4 bytes: (float) vertex1 z
|
|
% 4 bytes: (float) vertex2 x
|
|
% 4 bytes: (float) vertex2 y
|
|
% 4 bytes: (float) vertex2 z
|
|
% 4 bytes: (float) vertex3 x
|
|
% 4 bytes: (float) vertex3 y
|
|
% 4 bytes: (float) vertex3 z
|
|
% 2 bytes: Padding to make the data for each facet 50-bytes in length
|
|
% ...and repeat for next facet...
|
|
%==========================================================================
|
|
|
|
if nargin==2
|
|
stlFORMAT = lower(varargin{1});
|
|
else
|
|
stlFORMAT = 'auto';
|
|
end
|
|
|
|
%If necessary, identify whether the STL is ascii or binary:
|
|
if strcmp(stlFORMAT,'ascii')==0 && strcmp(stlFORMAT,'binary')==0
|
|
stlFORMAT = IDENTIFY_stl_format(stlFILENAME);
|
|
end
|
|
|
|
%Load the STL file:
|
|
if strcmp(stlFORMAT,'ascii')
|
|
[coordVERTICES,coordNORMALS,stlNAME] = READ_stlascii(stlFILENAME);
|
|
elseif strcmp(stlFORMAT,'binary')
|
|
[coordVERTICES,coordNORMALS] = READ_stlbinary(stlFILENAME);
|
|
stlNAME = 'unnamed_object';
|
|
end %if
|
|
|
|
%Prepare the output arguments
|
|
if nargout == 2
|
|
varargout(1) = {coordNORMALS};
|
|
elseif nargout == 3
|
|
varargout(1) = {coordNORMALS};
|
|
varargout(2) = {stlNAME};
|
|
end
|
|
|
|
end %function
|
|
|
|
|
|
|
|
%==========================================================================
|
|
function [stlFORMAT] = IDENTIFY_stl_format(stlFILENAME)
|
|
% IDENTIFY_stl_format Test whether an stl file is ascii or binary
|
|
|
|
% Open the file:
|
|
fidIN = fopen(stlFILENAME);
|
|
|
|
% Check the file size first, since binary files MUST have a size of 84+(50*n)
|
|
fseek(fidIN,0,1); % Go to the end of the file
|
|
fidSIZE = ftell(fidIN); % Check the size of the file
|
|
|
|
if rem(fidSIZE-84,50) > 0
|
|
|
|
stlFORMAT = 'ascii';
|
|
|
|
else
|
|
|
|
% Files with a size of 84+(50*n), might be either ascii or binary...
|
|
|
|
% Read first 80 characters of the file.
|
|
% For an ASCII file, the data should begin immediately (give or take a few
|
|
% blank lines or spaces) and the first word must be 'solid'.
|
|
% For a binary file, the first 80 characters contains the header.
|
|
% It is bad practice to begin the header of a binary file with the word
|
|
% 'solid', so it can be used to identify whether the file is ASCII or
|
|
% binary.
|
|
fseek(fidIN,0,-1); % Go to the start of the file
|
|
firsteighty = char(fread(fidIN,80,'uchar')');
|
|
|
|
% Trim leading and trailing spaces:
|
|
firsteighty = strtrim(firsteighty);
|
|
|
|
% Take the first five remaining characters, and check if these are 'solid':
|
|
firstfive = firsteighty(1:min(5,length(firsteighty)));
|
|
|
|
% Double check by reading the last 80 characters of the file.
|
|
% For an ASCII file, the data should end (give or take a few
|
|
% blank lines or spaces) with 'endsolid <object_name>'.
|
|
% If the last 80 characters contains the word 'endsolid' then this
|
|
% confirms that the file is indeed ASCII.
|
|
if strcmp(firstfive,'solid')
|
|
|
|
fseek(fidIN,-80,1); % Go to the end of the file minus 80 characters
|
|
lasteighty = char(fread(fidIN,80,'uchar')');
|
|
|
|
if findstr(lasteighty,'endsolid')
|
|
stlFORMAT = 'ascii';
|
|
else
|
|
stlFORMAT = 'binary';
|
|
end
|
|
|
|
else
|
|
stlFORMAT = 'binary';
|
|
end
|
|
|
|
end
|
|
|
|
% Close the file
|
|
fclose(fidIN);
|
|
|
|
end %function
|
|
%==========================================================================
|
|
|
|
|
|
|
|
%==========================================================================
|
|
function [coordVERTICES,coordNORMALS,stlNAME] = READ_stlascii(stlFILENAME)
|
|
% READ_stlascii Read mesh data in the form of an ascii <*.stl> file
|
|
|
|
% Read the ascii STL file
|
|
fidIN = fopen(stlFILENAME);
|
|
fidCONTENTcell = textscan(fidIN,'%s','delimiter','\n'); %Read all the file
|
|
fidCONTENT = fidCONTENTcell{:}(logical(~strcmp(fidCONTENTcell{:},''))); %Remove all blank lines
|
|
fclose(fidIN);
|
|
|
|
% Read the STL name
|
|
if nargout == 3
|
|
line1 = char(fidCONTENT(1));
|
|
if (size(line1,2) >= 7)
|
|
stlNAME = line1(7:end);
|
|
else
|
|
stlNAME = 'unnamed_object';
|
|
end
|
|
end
|
|
|
|
% Read the vector normals
|
|
if nargout >= 2
|
|
stringNORMALS = char(fidCONTENT(logical(strncmp(fidCONTENT,'facet normal',12))));
|
|
coordNORMALS = str2num(stringNORMALS(:,13:end));
|
|
end
|
|
|
|
% Read the vertex coordinates
|
|
facetTOTAL = sum(strcmp(fidCONTENT,'endfacet'));
|
|
stringVERTICES = char(fidCONTENT(logical(strncmp(fidCONTENT,'vertex',6))));
|
|
coordVERTICESall = str2num(stringVERTICES(:,7:end));
|
|
cotemp = zeros(3,facetTOTAL,3);
|
|
cotemp(:) = coordVERTICESall;
|
|
coordVERTICES = shiftdim(cotemp,1);
|
|
|
|
end %function
|
|
%==========================================================================
|
|
|
|
|
|
|
|
%==========================================================================
|
|
function [coordVERTICES,coordNORMALS] = READ_stlbinary(stlFILENAME)
|
|
% READ_stlbinary Read mesh data in the form of an binary <*.stl> file
|
|
|
|
% Open the binary STL file
|
|
fidIN = fopen(stlFILENAME);
|
|
|
|
% Read the header
|
|
fseek(fidIN,80,-1); % Move to the last 4 bytes of the header
|
|
facetcount = fread(fidIN,1,'int32'); % Read the number of facets in the stl file
|
|
|
|
% Initialise arrays into which the STL data will be loaded:
|
|
coordNORMALS = zeros(facetcount,3);
|
|
coordVERTICES = zeros(facetcount,3,3);
|
|
|
|
% Read the data for each facet:
|
|
for loopF = 1:facetcount
|
|
|
|
tempIN = fread(fidIN,3*4,'float');
|
|
|
|
coordNORMALS(loopF,1:3) = tempIN(1:3); % x,y,z components of the facet's normal vector
|
|
coordVERTICES(loopF,1:3,1) = tempIN(4:6); % x,y,z coordinates of vertex 1
|
|
coordVERTICES(loopF,1:3,2) = tempIN(7:9); % x,y,z coordinates of vertex 2
|
|
coordVERTICES(loopF,1:3,3) = tempIN(10:12); % x,y,z coordinates of vertex 3
|
|
|
|
fread(fidIN,1,'int16'); % Move to the start of the next facet. Using fread is much quicker than using fseek(fidIN,2,0);
|
|
|
|
end %for
|
|
|
|
% Close the binary STL file
|
|
fclose(fidIN);
|
|
|
|
end %function
|
|
%==========================================================================
|
|
|