| function mvtk_write(M,filename,format) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
|
|
|
|
| |
| |
| if nargin < 2 || isempty(filename), filename = 'untitled'; end |
| if nargin < 3 || isempty(format) |
| [pth,name,ext] = fileparts(filename); |
| switch ext |
| case {'','.vtk'} |
| ext = '.vtk'; |
| format = 'legacy-ascii'; |
| case 'vtp' |
| format = 'xml-ascii'; |
| case {'.vti','.vtr','.vts','.vtu'} |
| format = 'xml-ascii'; |
| warning('Only partially handled.'); |
| otherwise |
| error('Unknown file extension.'); |
| end |
| else |
| switch lower(format) |
| case {'legacy','legacy-ascii','legacy-binary'} |
| ext = '.vtk'; |
| case {'xml','xml-ascii','xml-binary','xml-appended'} |
| ext = '.vtp'; |
| otherwise |
| error('Unknown file format.'); |
| end |
| end |
|
|
| |
| |
| [pth,name,e] = fileparts(filename); |
| if ~strcmpi(e,ext) |
| warning('Changing file extension from %s to %s.',e,ext); |
| end |
| filename = fullfile(pth,[name ext]); |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
| if ~isfield(M,'normals') |
| M.normals = compute_normals(M); |
| end |
|
|
| |
| |
| switch lower(format) |
| case {'legacy','legacy-ascii'} |
| mvtk_write_legacy(M,filename,'ASCII'); |
| case {'legacy-binary'} |
| mvtk_write_legacy(M,filename,'BINARY'); |
| case {'xml','xml-ascii'} |
| mvtk_write_xml(M,filename,'ASCII'); |
| case {'xml-binary'} |
| mvtk_write_xml(M,filename,'BINARY'); |
| case {'xml-appended'} |
| mvtk_write_xml(M,filename,'APPENDED'); |
| otherwise |
| error('Unknown file format.'); |
| end |
|
|
|
|
| |
| |
| |
| function fid = mvtk_write_legacy(s,filename,format) |
|
|
| |
| |
| if nargin == 2, format = 'ASCII'; else format = upper(format); end |
| switch format |
| case 'ASCII' |
| fopen_opts = {'wt'}; |
| write_data = @(fid,fmt,prec,dat) fprintf(fid,fmt,dat); |
| case 'BINARY' |
| fopen_opts = {'wb','ieee-be'}; |
| write_data = @(fid,fmt,prec,dat) [fwrite(fid,dat,prec);fprintf(fid,'\n');]; |
| otherwise |
| error('Unknown file format.'); |
| end |
| fid = fopen(filename,fopen_opts{:}); |
| if fid == -1 |
| error('Unable to write file %s: permission denied.',filename); |
| end |
|
|
| |
| |
|
|
| |
| |
| fprintf(fid,'# vtk DataFile Version 2.0\n'); |
|
|
| |
| |
| hdr = 'Saved using mVTK'; |
| fprintf(fid,'%s\n',hdr(1:min(length(hdr),256))); |
|
|
| |
| |
| fprintf(fid,'%s\n',format); |
|
|
| |
| |
| |
| |
| if isfield(s,'vertices') || isfield(s,'faces') |
| type = 'POLYDATA'; |
| elseif isfield(s,'spacing') |
| type = 'STRUCTURED_POINTS'; |
| |
| |
| else |
| error('Unknown dataset structure.'); |
| end |
| fprintf(fid,'DATASET %s\n',type); |
| if isfield(s,'vertices') |
| fprintf(fid,'POINTS %d %s\n',size(s.vertices,1),'float'); |
| write_data(fid,'%f %f %f\n','float32',s.vertices'); |
| end |
| if isfield(s,'faces') |
| nFaces = size(s.faces,1); |
| nConn = size(s.faces,2); |
| fprintf(fid,'POLYGONS |
| dat = uint32([repmat(nConn,1,nFaces); (s.faces'-1)]); |
| fmt = repmat(' |
| write_data(fid,[fmt '\n'],'uint32',dat); |
| end |
| if isfield(s,'spacing') |
| fprintf(fid,'DIMENSIONS %d %d %d\n',size(s.cdata)); |
| fprintf(fid,'ORIGIN %f %f %f\n',s.origin); |
| fprintf(fid,'SPACING %f %f %f\n',s.spacing); |
| s.cdata = s.cdata(:); |
| end |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| fprintf(fid,'\n'); |
|
|
| |
| |
| point_data_hdr = false; |
|
|
| |
| if isfield(s,'cdata') && ~isempty(s.cdata) |
| if ~point_data_hdr |
| fprintf(fid,'POINT_DATA %d\n',size(s.cdata,1)); |
| point_data_hdr = true; |
| end |
| if ~isfield(s,'lut') |
| lut_name = 'default'; |
| else |
| lut_name = 'my_lut'; |
| if size(s.lut,2) == 3 |
| s.lut = [s.lut ones(size(s.lut,1),1)]; |
| end |
| end |
| dataName = 'cdata'; |
| fprintf(fid,'SCALARS %s %s %d\n',dataName,'float',size(s.cdata,2)); |
| fprintf(fid,'LOOKUP_TABLE %s\n',lut_name); |
| fmt = repmat('%f ',1,size(s.cdata,2)); fmt(end) = ''; |
| write_data(fid,[fmt '\n'],'float32',s.cdata'); |
| if ~strcmp(lut_name,'default') |
| fprintf(fid,'LOOKUP_TABLE |
| if strcmp(format,'ASCII') |
| |
| write_data(fid,'%f %f %f %f\n','float32',s.lut'); % rescale |
| else |
| % four unsigned char values per table entry |
| write_data(fid,'','uint8',uint8(s.lut')); |
| end |
| end |
| end |
|
|
| |
| if isfield(s,'color') && ~isempty(s.color) |
| if ~point_data_hdr |
| fprintf(fid,'POINT_DATA %d\n',size(s.color,1)); |
| point_data_hdr = true; |
| end |
| dataName = 'color'; |
| fprintf(fid,'COLOR_SCALARS %s %d\n',dataName,size(s.color,2)); |
| if strcmp(format,'ASCII') |
| |
| fmt = repmat('%f ',1,size(s.color,2)); fmt(end) = ''; |
| write_data(fid,[fmt '\n'],'float32',s.color'); % rescale |
| else |
| % nValues unsigned char values per scalar value |
| write_data(fid,'','uint8',uint8(s.color')); |
| end |
| end |
|
|
| |
| if isfield(s,'vectors') && ~isempty(s.vectors) |
| if ~point_data_hdr |
| fprintf(fid,'POINT_DATA %d\n',size(s.vectors,1)); |
| point_data_hdr = true; |
| end |
| dataName = 'vectors'; |
| fprintf(fid,'VECTORS %s %s\n',dataName,'float'); |
| write_data(fid,'%f %f %f\n','float32',s.vectors'); |
| end |
| |
| %-NORMALS |
| if isfield(s,'normals') && ~isempty(s.normals) |
| if ~point_data_hdr |
| fprintf(fid,'POINT_DATA |
| point_data_hdr = true; |
| end |
| dataName = 'normals'; |
| fprintf(fid,'NORMALS %s %s\n',dataName,'float'); |
| write_data(fid,'%f %f %f\n','float32',-s.normals'); |
| end |
| |
| %-TENSORS |
| if isfield(s,'tensors') && ~isempty(s.tensors) |
| if ~point_data_hdr |
| fprintf(fid,'POINT_DATA |
| point_data_hdr = true; |
| end |
| dataName = 'tensors'; |
| fprintf(fid,'TENSORS %s %s\n',dataName,'float'); |
| write_data(fid,repmat('%f %f %f\n',1,3),'float32',s.tensors'); |
| end |
| |
| %-Close file |
| %-------------------------------------------------------------------------- |
| fclose(fid); |
| |
| |
| %========================================================================== |
| % function fid = mvtk_write_xml(s,filename,format) |
| %========================================================================== |
| function fid = mvtk_write_xml(s,filename,format) |
| |
| %-Open file |
| %-------------------------------------------------------------------------- |
| if nargin == 2, format = 'ascii'; else format = lower(format); end |
| clear store_appended_data |
| switch format |
| case 'ascii' |
| fopen_opts = {'wt'}; |
| write_data = @(fmt,dat) deal(NaN,sprintf(fmt,dat)); |
| case 'binary' |
| fopen_opts = {'wb','ieee-le'}; |
| write_data = @(fmt,dat) deal(NaN,[... |
| base64encode(typecast(uint32(numel(dat)*numel(typecast(dat(1),'uint8'))),'uint8')) ... |
| base64encode(typecast(dat(:),'uint8'))]); |
| case 'appended' |
| fopen_opts = {'wt'}; |
| store_appended_data('start'); |
| store_appended_data('base64'); % format: raw, [base64] |
| store_appended_data('none'); % compression: none, [zlib] |
| write_data = @(fmt,dat) deal(store_appended_data(fmt,dat),''); |
| otherwise |
| error('Unknown format.'); |
| end |
| fid = fopen(filename,fopen_opts{:}); |
| if fid == -1 |
| error('Unable to write file |
| end |
|
|
| |
| |
| o = @(x) blanks(x*3); |
|
|
| |
| |
| fprintf(fid,'<?xml version="1.0"?>\n'); |
|
|
| |
| |
| VTKFile = struct; |
| VTKFile.type = 'PolyData'; |
| VTKFile.version = '0.1'; |
| VTKFile.byte_order = 'LittleEndian'; |
| VTKFile.header_type = 'UInt32'; |
| if strcmp(store_appended_data('compression'),'zlib') |
| VTKFile.compressor = 'vtkZLibDataCompressor'; |
| end |
| fprintf(fid,'<VTKFile'); |
| for i=fieldnames(VTKFile)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>\n'); |
|
|
| |
| |
| fprintf(fid,'%s<PolyData>\n',o(1)); |
| Piece = struct; |
| Piece.NumberOfPoints = sprintf('%d',size(s.vertices,1)); |
| Piece.NumberOfVerts = sprintf('%d',0); |
| Piece.NumberOfLines = sprintf('%d',0); |
| Piece.NumberOfStrips = sprintf('%d',0); |
| Piece.NumberOfPolys = sprintf('%d',size(s.faces,1)); |
| fprintf(fid,'%s<Piece',o(2)); |
| for i=fieldnames(Piece)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>\n'); |
|
|
| |
| |
| PointData = struct; |
| if isfield(s,'cdata') && ~isempty(s.cdata) |
| PointData.Scalars = 'scalars'; |
| end |
| if isfield(s,'normals') && ~isempty(s.normals) |
| PointData.Normals = 'normals'; |
| end |
| fprintf(fid,'%s<PointData',o(3)); |
| for i=fieldnames(PointData)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>\n'); |
|
|
| |
| if isfield(s,'cdata') && ~isempty(s.cdata) |
| [offset,dat] = write_data('%f ',single(s.cdata')); |
| DataArray = struct; |
| DataArray.type = 'Float32'; |
| DataArray.Name = 'scalars'; |
| DataArray.NumberOfComponents = sprintf(' |
| DataArray.format = format; |
| if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end |
| fprintf(fid,'%s<DataArray',o(4)); |
| for i=fieldnames(DataArray)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>%s</DataArray>\n',dat); |
| end |
|
|
| |
| if isfield(s,'normals') && ~isempty(s.normals) |
| [offset,dat] = write_data('%f ',single(-s.normals')); |
| DataArray = struct; |
| DataArray.type = 'Float32'; |
| DataArray.Name = 'normals'; |
| DataArray.NumberOfComponents = sprintf(' |
| DataArray.format = format; |
| if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end |
| fprintf(fid,'%s<DataArray',o(4)); |
| for i=fieldnames(DataArray)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>%s</DataArray>\n',dat); |
| end |
|
|
| fprintf(fid,'%s</PointData>\n',o(3)); |
|
|
| |
| |
| fprintf(fid,'%s<CellData/>\n',o(3)); |
|
|
| |
| |
| fprintf(fid,'%s<Points>\n',o(3)); |
| if isfield(s,'vertices') |
| [offset,dat] = write_data('%f ',single(s.vertices')); |
| DataArray = struct; |
| DataArray.type = 'Float32'; |
| DataArray.Name = 'Vertices'; |
| DataArray.NumberOfComponents = sprintf(' |
| DataArray.format = format; |
| if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end |
| fprintf(fid,'%s<DataArray',o(4)); |
| for i=fieldnames(DataArray)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>%s</DataArray>\n',dat); |
| end |
| fprintf(fid,'%s</Points>\n',o(3)); |
|
|
| |
| |
| fprintf(fid,'%s<Verts/>\n',o(3)); |
|
|
| |
| |
| fprintf(fid,'%s<Lines/>\n',o(3)); |
|
|
| |
| |
| fprintf(fid,'%s<Strips/>\n',o(3)); |
|
|
| |
| |
| fprintf(fid,'%s<Polys>\n',o(3)); |
| if isfield(s,'faces') |
| [offset,dat] = write_data('%d ',uint32(s.faces'-1)); |
| DataArray = struct; |
| DataArray.type = 'UInt32'; |
| DataArray.Name = 'connectivity'; |
| DataArray.format = format; |
| if ~isnan(offset), DataArray.offset = sprintf(' |
| fprintf(fid,'%s<DataArray',o(4)); |
| for i=fieldnames(DataArray)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>%s</DataArray>\n',dat); |
| |
| [offset,dat] = write_data('%d ',uint32(3:3:3*size(s.faces,1))); |
| DataArray = struct; |
| DataArray.type = 'UInt32'; |
| DataArray.Name = 'offsets'; |
| DataArray.format = format; |
| if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end |
| fprintf(fid,'%s<DataArray',o(4)); |
| for i=fieldnames(DataArray)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>%s</DataArray>\n',dat); |
| end |
| fprintf(fid,'%s</Polys>\n',o(3)); |
|
|
| fprintf(fid,'%s</Piece>\n',o(2)); |
| fprintf(fid,'%s</PolyData>\n',o(1)); |
|
|
| |
| |
| if strcmp(format,'appended') |
| dat = store_appended_data('retrieve'); |
| store_appended_data('stop'); |
| AppendedData = struct; |
| AppendedData.encoding = store_appended_data('encoding'); |
| fprintf(fid,'%s<AppendedData',o(1)); |
| for i=fieldnames(AppendedData)' |
| fprintf(fid,' |
| end |
| fprintf(fid,'>\n%s_',o(2)); |
| fwrite(fid,dat); |
| fprintf(fid,'\n%s</AppendedData>\n',o(1)); |
| end |
|
|
| fprintf(fid,'</VTKFile>\n'); |
|
|
| |
| |
| fclose(fid); |
|
|
|
|
| |
| |
| |
| function varargout = store_appended_data(fmt,dat) |
|
|
| persistent fid encoding compression |
|
|
| if isempty(encoding), encoding = 'raw'; end |
| if isempty(compression), compression = 'none'; end |
| if ~nargin, fmt = 'start'; end |
| if nargin < 2 |
| varargout = {}; |
| switch lower(fmt) |
| case 'start' |
| filename = tempname; |
| fid = fopen(filename,'w+b'); |
| if fid == -1 |
| error('Cannot open temporary file.'); |
| end |
| case 'stop' |
| filename = fopen(fid); |
| fclose(fid); |
| delete(filename); |
| fid = -1; |
| case 'retrieve' |
| frewind(fid); |
| varargout = {fread(fid)}; |
| case 'encoding' |
| varargout = {encoding}; |
| case 'compression' |
| varargout = {compression}; |
| case {'raw','base64'} |
| encoding = fmt; |
| case {'none','zlib'} |
| compression = fmt; |
| otherwise |
| error('Unknown action.'); |
| end |
| return; |
| end |
|
|
| varargout = {ftell(fid)}; |
| N = uint32(numel(dat)*numel(typecast(dat(1),'uint8'))); |
| switch encoding |
| case 'raw' |
| switch compression |
| case 'none' |
| dat = typecast(dat(:),'uint8'); |
| hdr = N; |
| case 'zlib' |
| dat = zstream('C',typecast(dat(:),'uint8')); |
| hdr = uint32([1 N N numel(dat)]); |
| otherwise |
| error('Unknown compression.'); |
| end |
| fwrite(fid,hdr,'uint32'); |
| fwrite(fid,dat,class(dat)); |
| case 'base64' |
| switch compression |
| case 'none' |
| dat = typecast(dat(:),'uint8'); |
| hdr = N; |
| case 'zlib' |
| dat = zstream('C',typecast(dat(:),'uint8')); |
| hdr = uint32([1 N N numel(dat)]); |
| otherwise |
| error('Unknown compression.'); |
| end |
| fwrite(fid,base64encode(typecast(hdr,'uint8'))); |
| fwrite(fid,base64encode(dat)); |
| otherwise |
| error('Unknown encoding.'); |
| end |
|
|
|
|
| |
| |
| |
| function N = compute_normals(S) |
| try |
| t = triangulation(double(S.faces),double(S.vertices)); |
| N = -double(t.vertexNormal); |
| normN = sqrt(sum(N.^2,2)); |
| normN(normN < eps) = 1; |
| N = N ./ repmat(normN,1,3); |
| catch |
| N = []; |
| end |
|
|