├── example.mat ├── make_STL_of_Array_Example.m ├── README.md ├── stlwrite.m └── make_STL_of_Array.m /example.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmirHoseinSafari/Make-STL-of-3D-array-Optimal-for-3d-printing-/HEAD/example.mat -------------------------------------------------------------------------------- /make_STL_of_Array_Example.m: -------------------------------------------------------------------------------- 1 | % LOADING DATA 2 | arr = load('example.mat'); 3 | Data = arr.Data; 4 | 5 | scaleX = 0.084; 6 | scaleY = 0.084; 7 | scaleZ = 0.03; 8 | 9 | [Vertices, Triangle, Quads] = make_STL_of_Array('example.stl',Data,scaleX,scaleY,scaleZ); 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrainingDataFor3D_Printing
2 | 3 | ### make_STL_of_Array Convert a voxelised object contained within a 3D logical array into an STL surface mesh 4 | ========================================================================== 5 |
AUTHOR:
Amir-Hosein Safari

6 | CONTACT:
amirsfr5353@gmail.com

7 | INSTITUTION:
Max-Planck institute for informatic

8 | DATE:
25th Aug 2018

9 | 10 | EXAMPLE:
make_STL_of_Array(FileName,Data,scaleX,scaleY,scaleZ) 11 | ..or..
[Vertices, Triangle, Quads] = make_STL_of_Array(FileName,Data,scaleX,scaleY,scaleZ) 12 | 13 | INPUTS 14 | 15 | FileName - string - Filename of the STL file. 16 | 17 | Data - 3D logical array - Voxelised data 18 | 1 => Inside the object 19 | 0 => Outside the object 20 | (FOR PRINTING WITH TWO MATERIALS YOU SHOULD INVERSE 21 | Data (Data = ~Data) AND RUN CODE AGAIN TO HAVE THE SECOND TYPE) 22 | 23 | scaleX - A number which means the X size of every 24 | voxel in mm 25 | scaleY - A number which means the Y size of every 26 | voxel in mm 27 | scaleZ - A number which means the Z size of every 28 | voxel in mm 29 | 30 | 31 | OUTPUTS 32 | 33 | vertices - Nx3 array - A list of the x,y,z coordinates of 34 | each vertex in the mesh. 35 | 36 | Triangle - Nx3 array - A list of the vertices used in 37 | each Triangle of the mesh, identified using the row 38 | number in the array vertices. 39 | 40 | Quads - Nx4 array - A list of the vertices used in 41 | each Quads of the mesh, identified using the row 42 | number in the array vertices. 43 | ========================================================================== 44 | 45 | -------------------------------------------------------------------------------- /stlwrite.m: -------------------------------------------------------------------------------- 1 | function stlwrite(filename, varargin) 2 | %STLWRITE Write STL file from patch or surface data. 3 | % 4 | % STLWRITE(FILE, FV) writes a stereolithography (STL) file to FILE for a 5 | % triangulated patch defined by FV (a structure with fields 'vertices' 6 | % and 'faces'). 7 | % 8 | % STLWRITE(FILE, FACES, VERTICES) takes faces and vertices separately, 9 | % rather than in an FV struct 10 | % 11 | % STLWRITE(FILE, X, Y, Z) creates an STL file from surface data in X, Y, 12 | % and Z. STLWRITE triangulates this gridded data into a triangulated 13 | % surface using triangulation options specified below. X, Y and Z can be 14 | % two-dimensional arrays with the same size. If X and Y are vectors with 15 | % length equal to SIZE(Z,2) and SIZE(Z,1), respectively, they are passed 16 | % through MESHGRID to create gridded data. If X or Y are scalar values, 17 | % they are used to specify the X and Y spacing between grid points. 18 | % 19 | % STLWRITE(...,'PropertyName',VALUE,'PropertyName',VALUE,...) writes an 20 | % STL file using the following property values: 21 | % 22 | % MODE - File is written using 'binary' (default) or 'ascii'. 23 | % 24 | % TITLE - Header text (max 80 chars) written to the STL file. 25 | % 26 | % TRIANGULATION - When used with gridded data, TRIANGULATION is either: 27 | % 'delaunay' - (default) Delaunay triangulation of X, Y 28 | % 'f' - Forward slash division of grid quads 29 | % 'b' - Back slash division of quadrilaterals 30 | % 'x' - Cross division of quadrilaterals 31 | % Note that 'f', 'b', or 't' triangulations now use an 32 | % inbuilt version of FEX entry 28327, "mesh2tri". 33 | % 34 | % FACECOLOR - Single colour (1-by-3) or one-colour-per-face (N-by-3) 35 | % vector of RGB colours, for face/vertex input. RGB range 36 | % is 5 bits (0:31), stored in VisCAM/SolidView format 37 | % (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL) 38 | % 39 | % Example 1: 40 | % % Write binary STL from face/vertex data 41 | % tmpvol = false(20,20,20); % Empty voxel volume 42 | % tmpvol(8:12,8:12,5:15) = 1; % Turn some voxels on 43 | % fv = isosurface(~tmpvol, 0.5); % Make patch w. faces "out" 44 | % stlwrite('test.stl',fv) % Save to binary .stl 45 | % 46 | % Example 2: 47 | % % Write ascii STL from gridded data 48 | % [X,Y] = deal(1:40); % Create grid reference 49 | % Z = peaks(40); % Create grid height 50 | % stlwrite('test.stl',X,Y,Z,'mode','ascii') 51 | % 52 | % Example 3: 53 | % % Write binary STL with coloured faces 54 | % cVals = fv.vertices(fv.faces(:,1),3); % Colour by Z height. 55 | % cLims = [min(cVals) max(cVals)]; % Transform height values 56 | % nCols = 255; cMap = jet(nCols); % onto an 8-bit colour map 57 | % fColsDbl = interp1(linspace(cLims(1),cLims(2),nCols),cMap,cVals); 58 | % fCols8bit = fColsDbl*255; % Pass cols in 8bit (0-255) RGB triplets 59 | % stlwrite('testCol.stl',fv,'FaceColor',fCols8bit) 60 | 61 | % Original idea adapted from surf2stl by Bill McDonald. Huge speed 62 | % improvements implemented by Oliver Woodford. Non-Delaunay triangulation 63 | % of quadrilateral surface courtesy of Kevin Moerman. FaceColor 64 | % implementation by Grant Lohsen. 65 | % 66 | % Author: Sven Holcombe, 11-24-11 67 | 68 | 69 | % Check valid filename path 70 | path = fileparts(filename); 71 | if ~isempty(path) && ~exist(path,'dir') 72 | error('Directory "%s" does not exist.',path); 73 | end 74 | 75 | % Get faces, vertices, and user-defined options for writing 76 | [faces, vertices, options] = parseInputs(varargin{:}); 77 | asciiMode = strcmp( options.mode ,'ascii'); 78 | 79 | % Create the facets 80 | facets = single(vertices'); 81 | facets = reshape(facets(:,faces'), 3, 3, []); 82 | 83 | % Compute their normals 84 | V1 = squeeze(facets(:,2,:) - facets(:,1,:)); 85 | V2 = squeeze(facets(:,3,:) - facets(:,1,:)); 86 | normals = V1([2 3 1],:) .* V2([3 1 2],:) - V2([2 3 1],:) .* V1([3 1 2],:); 87 | clear V1 V2 88 | normals = bsxfun(@times, normals, 1 ./ sqrt(sum(normals .* normals, 1))); 89 | facets = cat(2, reshape(normals, 3, 1, []), facets); 90 | clear normals 91 | 92 | % Open the file for writing 93 | permissions = {'w','wb+'}; 94 | fid = fopen(filename, permissions{asciiMode+1}); 95 | if (fid == -1) 96 | error('stlwrite:cannotWriteFile', 'Unable to write to %s', filename); 97 | end 98 | 99 | % Write the file contents 100 | if asciiMode 101 | % Write HEADER 102 | fprintf(fid,'solid %s\r\n',options.title); 103 | % Write DATA 104 | fprintf(fid,[... 105 | 'facet normal %.7E %.7E %.7E\r\n' ... 106 | 'outer loop\r\n' ... 107 | 'vertex %.7E %.7E %.7E\r\n' ... 108 | 'vertex %.7E %.7E %.7E\r\n' ... 109 | 'vertex %.7E %.7E %.7E\r\n' ... 110 | 'endloop\r\n' ... 111 | 'endfacet\r\n'], facets); 112 | % Write FOOTER 113 | fprintf(fid,'endsolid %s\r\n',options.title); 114 | 115 | else % BINARY 116 | % Write HEADER 117 | fprintf(fid, '%-80s', options.title); % Title 118 | fwrite(fid, size(facets, 3), 'uint32'); % Number of facets 119 | % Write DATA 120 | % Add one uint16(0) to the end of each facet using a typecasting trick 121 | facets = reshape(typecast(facets(:), 'uint16'), 12*2, []); 122 | % Set the last bit to 0 (default) or supplied RGB 123 | facets(end+1,:) = options.facecolor; 124 | fwrite(fid, facets, 'uint16'); 125 | end 126 | 127 | % Close the file 128 | fclose(fid); 129 | fprintf('Wrote %d faces\n',size(faces, 2)); 130 | 131 | 132 | %% Input handling subfunctions 133 | function [faces, vertices, options] = parseInputs(varargin) 134 | % Determine input type 135 | if isstruct(varargin{1}) % stlwrite('file', FVstruct, ...) 136 | if ~all(isfield(varargin{1},{'vertices','faces'})) 137 | error( 'Variable p must be a faces/vertices structure' ); 138 | end 139 | faces = varargin{1}.faces; 140 | vertices = varargin{1}.vertices; 141 | options = parseOptions(varargin{2:end}); 142 | 143 | elseif isnumeric(varargin{1}) 144 | firstNumInput = cellfun(@isnumeric,varargin); 145 | firstNumInput(find(~firstNumInput,1):end) = 0; % Only consider numerical input PRIOR to the first non-numeric 146 | numericInputCnt = nnz(firstNumInput); 147 | 148 | options = parseOptions(varargin{numericInputCnt+1:end}); 149 | switch numericInputCnt 150 | case 3 % stlwrite('file', X, Y, Z, ...) 151 | % Extract the matrix Z 152 | Z = varargin{3}; 153 | 154 | % Convert scalar XY to vectors 155 | ZsizeXY = fliplr(size(Z)); 156 | for i = 1:2 157 | if isscalar(varargin{i}) 158 | varargin{i} = (0:ZsizeXY(i)-1) * varargin{i}; 159 | end 160 | end 161 | 162 | % Extract X and Y 163 | if isequal(size(Z), size(varargin{1}), size(varargin{2})) 164 | % X,Y,Z were all provided as matrices 165 | [X,Y] = varargin{1:2}; 166 | elseif numel(varargin{1})==ZsizeXY(1) && numel(varargin{2})==ZsizeXY(2) 167 | % Convert vector XY to meshgrid 168 | [X,Y] = meshgrid(varargin{1}, varargin{2}); 169 | else 170 | error('stlwrite:badinput', 'Unable to resolve X and Y variables'); 171 | end 172 | 173 | % Convert to faces/vertices 174 | if strcmp(options.triangulation,'delaunay') 175 | faces = delaunay(X,Y); 176 | vertices = [X(:) Y(:) Z(:)]; 177 | else 178 | if ~exist('mesh2tri','file') 179 | error('stlwrite:missing', '"mesh2tri" is required to convert X,Y,Z matrices to STL. It can be downloaded from:\n%s\n',... 180 | 'http://www.mathworks.com/matlabcentral/fileexchange/28327') 181 | end 182 | [faces, vertices] = mesh2tri(X, Y, Z, options.triangulation); 183 | end 184 | 185 | case 2 % stlwrite('file', FACES, VERTICES, ...) 186 | faces = varargin{1}; 187 | vertices = varargin{2}; 188 | 189 | otherwise 190 | error('stlwrite:badinput', 'Unable to resolve input types.'); 191 | end 192 | end 193 | 194 | if size(faces,2)~=3 195 | errorMsg = { 196 | sprintf('The FACES input array should hold triangular faces (N x 3), but was detected as N x %d.',size(faces,2)) 197 | 'The STL format is for triangulated surfaces (i.e., surfaces made from 3-sided triangles).' 198 | 'The Geom3d package (https://www.mathworks.com/matlabcentral/fileexchange/24484-geom3d) contains' 199 | 'a "triangulateFaces" function which can be used convert your faces into triangles.' 200 | }; 201 | error('stlwrite:nonTriangles', '%s\n',errorMsg{:}) 202 | end 203 | 204 | if ~isempty(options.facecolor) % Handle colour preparation 205 | facecolor = uint16(options.facecolor); 206 | %Set the Valid Color bit (bit 15) 207 | c0 = bitshift(ones(size(faces,1),1,'uint16'),15); 208 | %Red color (10:15), Blue color (5:9), Green color (0:4) 209 | c0 = bitor(bitshift(bitand(2^6-1, facecolor(:,1)),10),c0); 210 | c0 = bitor(bitshift(bitand(2^11-1, facecolor(:,2)),5),c0); 211 | c0 = bitor(bitand(2^6-1, facecolor(:,3)),c0); 212 | options.facecolor = c0; 213 | else 214 | options.facecolor = 0; 215 | end 216 | 217 | function options = parseOptions(varargin) 218 | IP = inputParser; 219 | IP.addParamValue('mode', 'binary', @ischar) 220 | IP.addParamValue('title', sprintf('Created by stlwrite.m %s',datestr(now)), @ischar); 221 | IP.addParamValue('triangulation', 'delaunay', @ischar); 222 | IP.addParamValue('facecolor',[], @isnumeric) 223 | IP.addParamValue('facecolour',[], @isnumeric) 224 | IP.parse(varargin{:}); 225 | options = IP.Results; 226 | if ~isempty(options.facecolour) 227 | options.facecolor = options.facecolour; 228 | end 229 | 230 | function [F,V]=mesh2tri(X,Y,Z,tri_type) 231 | % function [F,V]=mesh2tri(X,Y,Z,tri_type) 232 | % 233 | % Available from http://www.mathworks.com/matlabcentral/fileexchange/28327 234 | % Included here for convenience. Many thanks to Kevin Mattheus Moerman 235 | % kevinmoerman@hotmail.com 236 | % 15/07/2010 237 | %------------------------------------------------------------------------ 238 | 239 | [J,I]=meshgrid(1:1:size(X,2)-1,1:1:size(X,1)-1); 240 | 241 | switch tri_type 242 | case 'f'%Forward slash 243 | TRI_I=[I(:),I(:)+1,I(:)+1; I(:),I(:),I(:)+1]; 244 | TRI_J=[J(:),J(:)+1,J(:); J(:),J(:)+1,J(:)+1]; 245 | F = sub2ind(size(X),TRI_I,TRI_J); 246 | case 'b'%Back slash 247 | TRI_I=[I(:),I(:)+1,I(:); I(:)+1,I(:)+1,I(:)]; 248 | TRI_J=[J(:)+1,J(:),J(:); J(:)+1,J(:),J(:)+1]; 249 | F = sub2ind(size(X),TRI_I,TRI_J); 250 | case 'x'%Cross 251 | TRI_I=[I(:)+1,I(:); I(:)+1,I(:)+1; I(:),I(:)+1; I(:),I(:)]; 252 | TRI_J=[J(:),J(:); J(:)+1,J(:); J(:)+1,J(:)+1; J(:),J(:)+1]; 253 | IND=((numel(X)+1):numel(X)+prod(size(X)-1))'; 254 | F = sub2ind(size(X),TRI_I,TRI_J); 255 | F(:,3)=repmat(IND,[4,1]); 256 | Fe_I=[I(:),I(:)+1,I(:)+1,I(:)]; Fe_J=[J(:),J(:),J(:)+1,J(:)+1]; 257 | Fe = sub2ind(size(X),Fe_I,Fe_J); 258 | Xe=mean(X(Fe),2); Ye=mean(Y(Fe),2); Ze=mean(Z(Fe),2); 259 | X=[X(:);Xe(:)]; Y=[Y(:);Ye(:)]; Z=[Z(:);Ze(:)]; 260 | end 261 | 262 | V=[X(:),Y(:),Z(:)]; -------------------------------------------------------------------------------- /make_STL_of_Array.m: -------------------------------------------------------------------------------- 1 | function [Vertices, Triangle, Quads] = make_STL_of_Array(FileName,Data,scaleX,scaleY,scaleZ) 2 | % make_STL_of_Array Convert a voxelised object contained within a 3D logical array into an STL surface mesh 3 | %========================================================================== 4 | % AUTHOR Amir-Hosein Safari 5 | % CONTACT amirsfr5353@gmail.com 6 | % INSTITUTION Max-Planck institute for informatic 7 | % DATE 25th Aug 2018 8 | % 9 | % EXAMPLE make_STL_of_Array(FileName,Data,scaleX,scaleY,scaleZ) 10 | % ..or.. [Vertices, Triangle, Quads] = make_STL_of_Array(FileName,Data,scaleX,scaleY,scaleZ) 11 | % 12 | % INPUTS FileName - string - Filename of the STL file. 13 | % 14 | % Data - 3D logical array - Voxelised data 15 | % 1 => Inside the object 16 | % 0 => Outside the object 17 | % (FOR PRINTING WITH TWO MATERIALS YOU SHOULD INVERSE 18 | % Data (Data = ~Data) AND RUN CODE AGAIN TO HAVE THE SECOND TYPE) 19 | % 20 | % scaleX - A number which means the X size of every 21 | % voxel in mm 22 | % scaleY - A number which means the Y size of every 23 | % voxel in mm 24 | % scaleZ - A number which means the Z size of every 25 | % voxel in mm 26 | % 27 | % 28 | % OUTPUTS vertices - Nx3 array - A list of the x,y,z coordinates of 29 | % each vertex in the mesh. 30 | % 31 | % Triangle - Nx3 array - A list of the vertices used in 32 | % each Triangle of the mesh, identified using the row 33 | % number in the array vertices. 34 | % 35 | % Quads - Nx4 array - A list of the vertices used in 36 | % each Quads of the mesh, identified using the row 37 | % number in the array vertices. 38 | %========================================================================== 39 | 40 | %====================================================== 41 | % ALLOCATING THE MEMORY FOR MATRIXES 42 | %====================================================== 43 | 44 | Vertices = zeros(8*size(Data,1)* size(Data,2)* size(Data,3), 3); 45 | Quads = zeros(6*size(Data,1)* size(Data,2)* size(Data,3), 4); 46 | Triangle = zeros(12*size(Data,1)* size(Data,2)* size(Data,3), 3); 47 | 48 | Vertices_Pointer = 1; 49 | Quads_Pointer = 1; 50 | Triangle_Pointer = 1; 51 | GoIn3D = 0; 52 | ZlayerVertexNum = 0; 53 | PrevZlayerVertexNum = 0; 54 | PrevVertexPointer = 0; 55 | %====================================================== 56 | % LOOPING THROUGH EVERY ELEMENTS OF 3D LOGICAL ARRAY(Data) 57 | %====================================================== 58 | 59 | for i3 = 1 : size(Data,3) 60 | ZlayerVertexNum = 0; 61 | for i2 = 1 : size(Data,2) 62 | for i1 = 1 : size(Data,1) 63 | % i1 and i2 will change inside a for loop so we use i11 and 64 | % i22 to save the value before changing and we will use them 65 | i11 = i1; 66 | i22 = i2; 67 | 68 | % check that Data(i1,i2,i3) is not surronded by 1s! 69 | if i1 ~= 1 && i2 ~= 1 && i3 ~= 1 && i1 ~= size(Data,1) && i2 ~= size(Data,2) && i3 ~= size(Data,3) && Data(i1 - 1,i2,i3) == 2 && Data(i1,i2 - 1,i3) == 2 && Data(i1,i2,i3 - 1) == 2 && (Data(i1 + 1,i2,i3) == 1 || Data(i1 + 1,i2,i3) == 2 ) && (Data(i1,i2 + 1,i3) == 1 || Data(i1,i2 + 1,i3) == 2 ) && (Data(i1,i2,i3 + 1) == 1 || Data(i1,i2,i3 + 1) == 2 ) && Data(i1,i2,i3) ~= 0 70 | Data(i1,i2,i3) = 2; 71 | continue; 72 | end 73 | 74 | 75 | if Data(i1,i2,i3) == 1 76 | %====================================================== 77 | % LOOKING FOR THE BIGGEST RECTANGLE (BY ITS AREA) FROM THIS PONT 78 | % IN 2D! 79 | % FOR THIS WE LOOPING THROUGH X AND Y TILL FINDING A ZERO IN THAT DIRECTION! 80 | % WE SAVE THE INDEX OF X AND Y OF THE BIGGEST AREA IN 81 | % maxX, maxY 82 | %====================================================== 83 | 84 | maxX = 0; 85 | maxY = 0; 86 | 87 | loop2Len = size(Data,2); 88 | for i1 = i1 : size(Data,1) 89 | i2 = i22; 90 | % IF WE REACH A VOXEL FROM OUTSIDE WE BREAK FROM THE 91 | % LOOP 92 | if Data(i1,i2,i3) ~= 1 93 | i1 = i1 - 1; 94 | break; 95 | end 96 | for i2 = i2 : loop2Len 97 | % IF WE REACH A VOXEL FROM OUTSIDE WE BREAK FROM THE 98 | % LOOP 99 | if Data(i1,i2,i3) ~= 1 100 | loop2Len = i2 -1; 101 | i2 = i2 - 1; 102 | break; 103 | else 104 | if maxX*maxY < (i1 - i11 + 1) * (i2 - i22 + 1) 105 | maxX = i1 - i11; 106 | maxY = i2 - i22; 107 | end 108 | end 109 | end 110 | end 111 | 112 | % WE CHANGE THE VALUE OF THE BIGGEST AREA TO 2, IT 113 | % MEANS THAT WE'LL NOT CONSIDER THIS AREA AGAIN 114 | Data(i11:i11 + maxX, i22:i22 + maxY, i3) = 2; 115 | 116 | % CREATE THE VERTICES AND ADD THEM TO Vertices MATRIX 117 | % AND INCREAS THE Vertices_Pointer 118 | V1 = [ (i11 - 1) * scaleX , (i22 - 1) * scaleY, (i3 - 1) * scaleZ]; 119 | V2 = [ (i11 + maxX) * scaleX , (i22 - 1) * scaleY, (i3 - 1) * scaleZ]; 120 | V3 = [ (i11 - 1) * scaleX , (i22 + maxY) * scaleY, (i3 - 1) * scaleZ]; 121 | V4 = [ (i11 + maxX) * scaleX , (i22 + maxY) * scaleY, (i3 - 1) * scaleZ]; 122 | 123 | V5 = [ (i11 - 1) * scaleX , (i22 - 1) * scaleY, (i3) * scaleZ]; 124 | V6 = [ (i11 + maxX) * scaleX , (i22 - 1) * scaleY, (i3) * scaleZ]; 125 | V7 = [ (i11 - 1) * scaleX , (i22 + maxY) * scaleY, (i3) * scaleZ]; 126 | V8 = [ (i11 + maxX) * scaleX , (i22 + maxY) * scaleY, (i3) * scaleZ]; 127 | 128 | % CHCK IF IT CAN GO IN DEPTH OR NOT 129 | GoIn3D = 0; 130 | if PrevVertexPointer > 0 131 | for i4 = PrevVertexPointer - PrevZlayerVertexNum : 8 : Vertices_Pointer - 8 132 | if size(Vertices) > 0 133 | if Vertices(i4,1) == V1(1) && Vertices(i4,2) == V1(2) && Vertices(i4 + 1,1) == V2(1) && Vertices(i4 + 1,2) == V2(2) && Vertices(i4 + 2,1) == V3(1) && Vertices(i4 + 2,2) == V3(2) && Vertices(i4 + 3,1) == V4(1) && Vertices(i4 + 3,2) == V4(2) 134 | GoIn3D = 1; 135 | Vertices(i4 + 4,3) = Vertices(i4 + 4,3) + scaleZ; 136 | Vertices(i4 + 5,3) = Vertices(i4 + 5,3) + scaleZ; 137 | Vertices(i4 + 6,3) = Vertices(i4 + 6,3) + scaleZ; 138 | Vertices(i4 + 7,3) = Vertices(i4 + 7,3) + scaleZ; 139 | break; 140 | end 141 | end 142 | end 143 | end 144 | 145 | if GoIn3D == 1 146 | continue; 147 | end 148 | 149 | ZlayerVertexNum = ZlayerVertexNum + 8; 150 | 151 | if Vertices_Pointer == 0 152 | Vertices = cat(1,Vertices,V1); 153 | end 154 | 155 | Vertices(Vertices_Pointer,:) = V1; 156 | V_pointer1 = Vertices_Pointer; 157 | Vertices_Pointer = Vertices_Pointer + 1; 158 | 159 | Vertices(Vertices_Pointer,:) = V2; 160 | V_pointer2 = Vertices_Pointer; 161 | Vertices_Pointer = Vertices_Pointer + 1; 162 | 163 | Vertices(Vertices_Pointer,:) = V3; 164 | V_pointer3 = Vertices_Pointer; 165 | Vertices_Pointer = Vertices_Pointer + 1; 166 | 167 | Vertices(Vertices_Pointer,:) = V4; 168 | V_pointer4 = Vertices_Pointer; 169 | Vertices_Pointer = Vertices_Pointer + 1; 170 | 171 | Vertices(Vertices_Pointer,:) = V5; 172 | V_pointer5 = Vertices_Pointer; 173 | Vertices_Pointer = Vertices_Pointer + 1; 174 | 175 | Vertices(Vertices_Pointer,:) = V6; 176 | V_pointer6 = Vertices_Pointer; 177 | Vertices_Pointer = Vertices_Pointer + 1; 178 | 179 | Vertices(Vertices_Pointer,:) = V7; 180 | V_pointer7 = Vertices_Pointer; 181 | Vertices_Pointer = Vertices_Pointer + 1; 182 | 183 | Vertices(Vertices_Pointer,:) = V8; 184 | V_pointer8 = Vertices_Pointer; 185 | Vertices_Pointer = Vertices_Pointer + 1; 186 | end 187 | end 188 | end 189 | PrevZlayerVertexNum = ZlayerVertexNum; 190 | PrevVertexPointer = Vertices_Pointer; 191 | end 192 | 193 | % CUT THE UNUSED ELEMENTS OF Vertices 194 | Vertices = Vertices(1:Vertices_Pointer - 1,:); 195 | 196 | % CREATE THE QUADS AND ADD THEM TO Quads MATRIX 197 | % AND INCREAS THE Quads_Pointer 198 | Quads_Pointer = 1; 199 | for iInVertices = 1 : 8: size(Vertices) 200 | Quads(Quads_Pointer,:) = [iInVertices, iInVertices + 1, iInVertices + 2, iInVertices + 3]; 201 | Quads_Pointer = Quads_Pointer + 1; 202 | Quads(Quads_Pointer,:) = [iInVertices + 4, iInVertices + 5, iInVertices + 6, iInVertices + 7]; 203 | Quads_Pointer = Quads_Pointer + 1; 204 | Quads(Quads_Pointer,:) = [iInVertices + 4, iInVertices + 5, iInVertices, iInVertices + 1]; 205 | Quads_Pointer = Quads_Pointer + 1; 206 | Quads(Quads_Pointer,:) = [iInVertices + 2, iInVertices + 3, iInVertices + 6, iInVertices + 7]; 207 | Quads_Pointer = Quads_Pointer + 1; 208 | Quads(Quads_Pointer,:) = [iInVertices + 4, iInVertices, iInVertices + 6, iInVertices + 2]; 209 | Quads_Pointer = Quads_Pointer + 1; 210 | Quads(Quads_Pointer,:) = [iInVertices + 1, iInVertices + 5, iInVertices + 3, iInVertices + 7]; 211 | Quads_Pointer = Quads_Pointer + 1; 212 | end 213 | 214 | % CUT THE UNUSED ELEMENTS OF Quads 215 | Quads = Quads(1:Quads_Pointer - 1,:); 216 | 217 | % REMOVE REPETITIOUS QUADS 218 | Quads = unique(Quads,'rows','stable'); 219 | 220 | % CREATE THE TRIAGNLES FROM QUADS AND ADD THEM TO Triangle MATRIX 221 | % AND INCREAS THE Triangle_Pointer 222 | for i = 1 : size(Quads,1) 223 | Triangle(Triangle_Pointer,:) = [Quads(i,1) Quads(i,2) Quads(i,3)]; 224 | Triangle_Pointer = Triangle_Pointer + 1; 225 | Triangle(Triangle_Pointer,:) = [Quads(i,4) Quads(i,2) Quads(i,3)]; 226 | Triangle_Pointer = Triangle_Pointer + 1; 227 | end 228 | 229 | % CUT THE UNUSED ELEMENTS OF MATRIXES 230 | Triangle = Triangle(1:Triangle_Pointer - 1,:); 231 | 232 | % WRITE THE STL FILE 233 | stlwrite(FileName, Triangle, Vertices); 234 | --------------------------------------------------------------------------------