From 7f19e13be212b7c61bea67756806dd1dc9888b5a Mon Sep 17 00:00:00 2001 From: Qianqian Fang Date: Sat, 26 Oct 2024 23:19:24 -0400 Subject: [PATCH] [i2m] update i2m gui, works after deploytool build --- .github/workflows/run_test.yml | 6 +- i2m.prj | 59 ++++----- img2mesh.m | 226 +++++++++++++++++++-------------- iso2meshver.m | 2 +- transposemat.m | 40 ++++++ 5 files changed, 199 insertions(+), 134 deletions(-) create mode 100644 transposemat.m diff --git a/.github/workflows/run_test.yml b/.github/workflows/run_test.yml index 76c2b97..4c3d874 100644 --- a/.github/workflows/run_test.yml +++ b/.github/workflows/run_test.yml @@ -27,9 +27,9 @@ jobs: brew install gnuplot brew install octave else - curl --retry 3 -kL http://cdimage.debian.org/mirror/gnu.org/gnu/octave/windows/octave-5.2.0-w64-64.7z --output octave_5.2.0.7z - 7z x octave_5.2.0.7z -ooctave -y - echo "$PWD/octave/octave-5.2.0-w64-64/mingw64/bin" >> $GITHUB_PATH + curl --retry 3 -kL http://cdimage.debian.org/mirror/gnu.org/gnu/octave/windows/octave-6.2.0-w64-64.7z --output octave_6.2.0.7z + 7z x octave_6.2.0.7z -ooctave -y + echo "$PWD/octave/octave-6.2.0-w64-64/mingw64/bin" >> $GITHUB_PATH fi - name: Install octave-image (Windows) if: ${{ runner.os == 'Windows' }} diff --git a/i2m.prj b/i2m.prj index bea4ce7..b0eac45 100644 --- a/i2m.prj +++ b/i2m.prj @@ -3,11 +3,11 @@ i2m - 0.8 + 0.9 Qianqian Fang q.fang at neu.edu Northeastern University - I2M is an integrated GUI for the 3D meshing toolbox Iso2Mesh developed by Qianqian Fang + I2M is an integrated graphical user interface (GUI) for the 3D meshing toolbox Iso2Mesh developed by Qianqian Fang Iso2Mesh is a free matlab/octave-based mesh generation and processing toolbox. It can create 3D tetrahedral finite element (FE) mesh from surfaces, 3D binary and gray-scale volumetric images such as segmented MRI/CT scans. @@ -103,7 +103,6 @@ ${PROJECT_ROOT}/fallbackexeext.m ${PROJECT_ROOT}/finddisconnsurf.m ${PROJECT_ROOT}/getexeext.m - ${PROJECT_ROOT}/getoptkey.m ${PROJECT_ROOT}/getplanefrom3pt.m ${PROJECT_ROOT}/getvarfrom.m ${PROJECT_ROOT}/img2mesh.fig @@ -140,7 +139,6 @@ ${PROJECT_ROOT}/readinr.m ${PROJECT_ROOT}/readmedit.m ${PROJECT_ROOT}/readmptiff.m - ${PROJECT_ROOT}/readnifti.m ${PROJECT_ROOT}/readoff.m ${PROJECT_ROOT}/readsmf.m ${PROJECT_ROOT}/readtetgen.m @@ -152,7 +150,6 @@ ${PROJECT_ROOT}/rotmat2vec.m ${PROJECT_ROOT}/s2m.m ${PROJECT_ROOT}/s2v.m - ${PROJECT_ROOT}/savegts.m ${PROJECT_ROOT}/saveinr.m ${PROJECT_ROOT}/savejmesh.m ${PROJECT_ROOT}/savejson.m @@ -178,6 +175,31 @@ ${PROJECT_ROOT}/vol2restrictedtri.m ${PROJECT_ROOT}/vol2surf.m ${PROJECT_ROOT}/volface.m + ${PROJECT_ROOT}/base64decode.m + ${PROJECT_ROOT}/base64encode.m + ${PROJECT_ROOT}/blosc2decode.m + ${PROJECT_ROOT}/blosc2encode.m + ${PROJECT_ROOT}/decodevarname.m + ${PROJECT_ROOT}/encodevarname.m + ${PROJECT_ROOT}/fast_match_bracket.m + ${PROJECT_ROOT}/fillholes3d.m + ${PROJECT_ROOT}/fillsurf.m + ${PROJECT_ROOT}/filterjsonmmap.m + ${PROJECT_ROOT}/getfromjsonpath.m + ${PROJECT_ROOT}/jdatadecode.m + ${PROJECT_ROOT}/jdataencode.m + ${PROJECT_ROOT}/loadbj.m + ${PROJECT_ROOT}/loadh5.m + ${PROJECT_ROOT}/loadmsgpack.m + ${PROJECT_ROOT}/match_bracket.m + ${PROJECT_ROOT}/meshquality.m + ${PROJECT_ROOT}/meshreorient.m + ${PROJECT_ROOT}/nestbracket2dim.m + ${PROJECT_ROOT}/regrouph5.m + ${PROJECT_ROOT}/remeshsurf.m + ${PROJECT_ROOT}/savebj.m + ${PROJECT_ROOT}/surfreorient.m + ${PROJECT_ROOT}/transposemat.m ${PROJECT_ROOT}/i2m/for_testing/readme.txt @@ -187,33 +209,6 @@ - /drives/hoyi1/users/shared/MATLAB/R2016a - - - - - - - - - true - - - - - true - - - - - true - - - - - true - - true diff --git a/img2mesh.m b/img2mesh.m index 54a5da7..67b4809 100644 --- a/img2mesh.m +++ b/img2mesh.m @@ -35,6 +35,10 @@ gui_State.gui_Callback = str2func(varargin{1}); end +if (isdeployed) + assignin('base', 'ISO2MESH_BIN', [pwd filesep 'bin']); +end + if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else @@ -89,14 +93,28 @@ function i2m_OpeningFcn(hObject, eventdata, handles, varargin) mimeshing.Separator = 'on'; mirefresh.Separator = 'on'; +hbackground = axes('units', 'normalized', 'position', [0 0 1 1]); +uistack(hbackground, 'down'); +text(0.97, 0, 'Iso2Mesh', 'FontSize', 40, 'Color', [1 1 1], 'Parent', hbackground, ... + 'HorizontalAlignment', 'right', 'VerticalAlignment', 'bottom', ... + 'FontWeight', 'bold', 'FontName', 'sans', 'FontAngle', 'italic'); +hold(hbackground, 'on'); +text(0.972, 0.002, 'Iso2Mesh', 'FontSize', 40, 'Color', [1 1 1] * 0.8, 'Parent', hbackground, ... + 'HorizontalAlignment', 'right', 'VerticalAlignment', 'bottom', ... + 'FontWeight', 'bold', 'FontName', 'sans', 'FontAngle', 'italic'); +set(hbackground, 'handlevisibility', 'off', 'visible', 'off'); + root = get(handles.fgI2M, 'userdata'); if (isempty(root)) root = struct('graph', digraph, 'menu', cm); end set(handles.fgI2M, 'userdata', root); -set(handles.axFlow, 'position', [0 0 1 1]); -set(handles.axPreview, 'position', [0 0 1 1]); +set(handles.fgI2M, 'color', [0.78, 0.91, 1.0]); +set(handles.axFlow, 'position', [0.01 0.01 1 - 0.02 1 - 0.02]); + +set(handles.axPreview, 'position', [0.01 0.01 1 - 0.02 1 - 0.02]); +set(handles.axPreview, 'color', get(handles.fgI2M, 'color')); set(handles.fgI2M, 'UIContextMenu', handles.meCreate); @@ -135,21 +153,21 @@ function processdata(source, callbackdata, handles) [newdata, newtype] = v2sgui(nodedata); prefix = 'Vol2Surf'; else - error('no volume data found'); + errordlg('no volume data found'); end case 'Volume to mesh' if (nodetype.hasvol) [newdata, newtype] = v2mgui(nodedata); prefix = 'Vol2Mesh'; else - error('no volume data found'); + errordlg('no volume data found'); end case 'Surface to mesh' if (nodetype.hasnode && nodetype.hasface) [newdata, newtype] = s2mgui(nodedata); prefix = 'Surf2Mesh'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Surface to volume' if (nodetype.hasnode && nodetype.hasface) @@ -158,20 +176,20 @@ function processdata(source, callbackdata, handles) if (isempty(ndiv)) return end - newdata.vol = s2v(nodedata.node, nodedata.face, str2num(ndiv{1})); + newdata.vol = s2v(nodedata.node, nodedata.face, str2double(ndiv{1})); newtype.hasvol = 1; prefix = 'Surf2Vol'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Close and fill volume' if (nodetype.hasvol) rad = inputdlg('maximum gap length in voxel (scalar)', 'Close and fill a volume', 1, {'1'}); - newdata.vol = fillholes3d(nodedata.vol, str2num(rad{1})); + newdata.vol = fillholes3d(nodedata.vol, str2double(rad{1})); newtype = nodetype; prefix = 'FillVol'; else - error('no volume data found'); + errordlg('no volume data found'); end case 'Extract surface' if (isstruct(nodetype) && isfield(nodetype, 'hasvol') && nodetype.hasvol) @@ -194,7 +212,7 @@ function processdata(source, callbackdata, handles) newtype = nodetype; prefix = 'CleanSurf'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Repair surface' if (nodetype.hasnode && nodetype.hasface) @@ -202,7 +220,7 @@ function processdata(source, callbackdata, handles) newtype = nodetype; prefix = 'RepairSurf'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Smooth surface' if (nodetype.hasnode && nodetype.hasface) @@ -213,23 +231,23 @@ function processdata(source, callbackdata, handles) end newdata = nodedata; newtype = nodetype; - newdata.node = sms(nodedata.node, nodedata.face, str2num(res{2}), str2num(res{3}), res{1}); + newdata.node = sms(nodedata.node, nodedata.face, str2double(res{2}), str2double(res{3}), res{1}); prefix = 'SmoothSurf'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Simplify surface' if (nodetype.hasnode && nodetype.hasface) res = inputdlg('Percentage of edges to keep (0-1):', ... - 'Simplify mesh', [1], {'1'}); + 'Simplify mesh', 1, {'1'}); if (isempty(res)) return end - [newdata.node, newdata.face] = meshresample(nodedata.node, nodedata.face, str2num(res{1})); + [newdata.node, newdata.face] = meshresample(nodedata.node, nodedata.face, str2double(res{1})); newtype = nodetype; prefix = 'SimplifySurf'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Remesh surface' if (nodetype.hasnode && nodetype.hasface) @@ -238,15 +256,15 @@ function processdata(source, callbackdata, handles) if (isempty(res)) return end - opt.gridsize = str2num(res{1}); - opt.closesize = str2num(res{2}); - opt.elemsize = str2num(res{3}); + opt.gridsize = str2double(res{1}); + opt.closesize = str2double(res{2}); + opt.elemsize = str2double(res{3}); [newdata.node, newdata.face] = remeshsurf(nodedata.node, nodedata.face, opt); newtype.hasnode = 1; newtype.hasface = 1; prefix = 'RemeshSurf'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Tessellate surface' if (nodetype.hasnode && nodetype.hasface) @@ -256,7 +274,7 @@ function processdata(source, callbackdata, handles) newtype.haselem = 1; prefix = 'TessMesh'; else - error('no surface data found'); + errordlg('no surface data found'); end case 'Reorient mesh elements' if (nodetype.hasnode && nodetype.haselem) @@ -268,7 +286,7 @@ function processdata(source, callbackdata, handles) newtype = nodetype; prefix = 'ReorientSurf'; else - error('no surface or tetrahedral mesh data found'); + errordlg('no surface or tetrahedral mesh data found'); end case 'Mesh quality histogram' if (nodetype.hasnode && nodetype.haselem) @@ -342,7 +360,7 @@ function processdata(source, callbackdata, handles) if (nodetype2.hasnode && nodetype2.haselem) nodedata2.face = volface(nodedata2.elem); else - error('Second operand does not contain a surface'); + errordlg('Second operand does not contain a surface'); end end op = source.Label; @@ -369,21 +387,20 @@ function processdata(source, callbackdata, handles) updategraph(root, handles); case 'Rename' newname = inputdlg('Define a new name:', ... - 'Rename', 1, {root.graph.Nodes.Name{nodeid}}); + 'Rename', 1, root.graph.Nodes.Name(nodeid)); if (isempty(newname)) return end if (isempty(newname{1}) || ~isempty(cell2mat(regexp(root.graph.Nodes.Name, ['^' newname{1} '$'])))) - error('empty or duplicated node name'); + errordlg('empty or duplicated node name'); end root.graph.Nodes.Name{nodeid} = newname{1}; updategraph(root, handles); case 'Save as' if (~nodetype.haselem && ~nodetype.hasnode) - error('selected data does not have a mesh'); - return + errordlg('selected data does not have a mesh'); end - filter = {'*.jmesh'; '*.*'}; + filter = {'*.jmsh'; '*.*'}; [file, path] = uiputfile(filter, 'Export mesh'); if ~isequal(file, 0) && ~isequal(path, 0) if (nodetype.haselem) @@ -397,11 +414,11 @@ function processdata(source, callbackdata, handles) if (exist('newdata', 'var') && exist('newtype', 'var')) cla(handles.axPreview); cla(handles.axFlow); - newdata.preview = getpreview(newdata, newtype, [400, 400]); + newdata.preview = getpreview(newdata, newtype, [800, 800]); [newkey, root.graph] = addnodewithdata(handles, newdata, newtype, prefix); - root.graph = addedge(root.graph, {root.graph.Nodes.Name{nodeid}}, {newkey}); + root.graph = addedge(root.graph, root.graph.Nodes.Name(nodeid), {newkey}); if (strcmp(source.Parent.Type, 'uimenu') && strcmp(source.Parent.Label, 'Surface boolean')) - root.graph = addedge(root.graph, {root.graph.Nodes.Name{nodeid2}}, {newkey}); + root.graph = addedge(root.graph, root.graph.Nodes.Name(nodeid2), {newkey}); end updategraph(root, handles); end @@ -483,7 +500,7 @@ function plotfigevent(hobject, event) return end -opt = struct('radbound', str2num(res{2}), 'distbound', str2num(res{3})); +opt = struct('radbound', str2double(res{2}), 'distbound', str2double(res{3})); [newdata.node, newdata.face] = v2s(data.vol, eval(res{1}), opt, res{4}); newtype.hasnode = 1; newtype.hasface = 1; @@ -505,7 +522,7 @@ function plotfigevent(hobject, event) return end -opt = struct('radbound', str2num(res{2}), 'distbound', str2num(res{3})); +opt = struct('radbound', str2double(res{2}), 'distbound', str2double(res{3})); [newdata.node, newdata.elem, newdata.face] = v2m(data.vol, eval(res{1}), ... opt, res{4}); newtype.hasnode = 1; @@ -514,7 +531,7 @@ function plotfigevent(hobject, event) % ---------------------------------------------------------------- function img = getpreview(nodedata, nodetype, imsize) -hfpreview = figure('visible', 'off'); +hfpreview = figure('visible', 'off', 'position', [0, 0, imsize(1), imsize(2)]); ax = axes('parent', hfpreview, 'Units', 'pixels', 'position', [1, 1, imsize(1), imsize(2)]); if (isfield(nodetype, 'haselem') && nodetype.haselem) plotmesh(nodedata.node, [], nodedata.elem, 'linestyle', ':', 'edgealpha', 0.3, 'parent', ax); @@ -552,8 +569,8 @@ function plotfigevent(hobject, event) end [newdata.node, newdata.elem, newdata.face] = ... - s2m(data.node, data.face, str2num(res{1}), ... - str2num(res{2}), res{3}, eval(res{4}), eval(res{5})); + s2m(data.node, data.face, str2double(res{1}), ... + str2double(res{2}), res{3}, eval(res{4}), eval(res{5})); newtype.hasnode = 1; newtype.hasface = 1; newtype.haselem = 1; @@ -582,14 +599,14 @@ function miAbout_Callback(hObject, eventdata, handles) helpmsg = { '\bf\fontsize{12}I2M: An Integrated GUI for Iso2Mesh Meshing Toolbox\rm\fontsize{10}' '' - 'Copyright (c) 2018 Qianqian Fang ' + 'Copyright (c) 2018-2024 Qianqian Fang ' '' 'Computational Optics&Translational Imaging Lab (http://fanglab.org)' 'Department of Bioengineering' 'Northeastern University' '360 Huntington Ave, Boston, MA 02115, USA' '' - 'URL: http://iso2mesh.sourceforge.net' + 'URL: http://iso2mesh.sf.net' ''}; opt.Interpreter = 'tex'; @@ -610,20 +627,20 @@ function miSphere_Callback(hObject, eventdata, handles) return end newtype = dummytype; -opt = str2num(res{3}); -if (str2num(res{4}) == 0) +opt = str2double(res{3}); +if (str2double(res{4}) == 0) [newdata.node, newdata.face] = meshasphere(eval(res{1}), ... - str2num(res{2}), opt); + str2double(res{2}), opt); else [newdata.node, newdata.face, newdata.elem] = meshasphere(eval(res{1}), ... - str2num(res{2}), opt, str2num(res{4})); + str2double(res{2}), opt, str2double(res{4})); newtype.haselem = 1; end newtype.hasnode = 1; newtype.hasface = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Sphere'); + addnodewithdata(handles, newdata, newtype, 'Sphere'); end % -------------------------------------------------------------------- @@ -639,19 +656,19 @@ function miBox_Callback(hObject, eventdata, handles) return end newtype = dummytype; -opt = str2num(res{3}); -if (str2num(res{4}) == 0) +opt = str2double(res{3}); +if (str2double(res{4}) == 0) [newdata.node, newdata.face] = meshabox(eval(res{1}), eval(res{2}), opt); else [newdata.node, newdata.face, newdata.elem] = meshabox(eval(res{1}), ... - eval(res{2}), opt, str2num(res{4})); + eval(res{2}), opt, str2double(res{4})); newtype.haselem = 1; end newtype.hasnode = 1; newtype.hasface = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Box'); + addnodewithdata(handles, newdata, newtype, 'Box'); end % -------------------------------------------------------------------- @@ -668,22 +685,22 @@ function miCylinder_Callback(hObject, eventdata, handles) return end newtype = dummytype; -opt = str2num(res{4}); -maxvol = str2num(res{5}); +opt = str2double(res{4}); +maxvol = str2double(res{5}); if (maxvol == 0) [newdata.node, newdata.face] = meshacylinder(eval(res{1}), eval(res{2}), ... - str2num(res{3}), opt); + str2double(res{3}), opt); else [newdata.node, newdata.face, newdata.elem] = meshacylinder(eval(res{1}), eval(res{2}), ... - str2num(res{3}), opt, maxvol); + str2double(res{3}), opt, maxvol); newtype.haselem = 1; end newtype.hasnode = 1; newtype.hasface = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Cyl'); + addnodewithdata(handles, newdata, newtype, 'Cyl'); end % -------------------------------------------------------------------- @@ -691,33 +708,38 @@ function miLoadVol_Callback(hObject, eventdata, handles) nodedata = struct; nodetype = dummytype; -filters = {'*.nii;*.hdr;*.img;*.tif;*.tiff;*.inr;*.bin;*.ubj', '3D volume file (*.nii;*.hdr;*.img;*.tif;*.tiff;*.inr;*.bin;*.ubj)'; ... - '*.nii', 'Nifti file (*.nii)'; ... +filters = {'*.jnii;*.nii;*.hdr;*.img;*.tif;*.tiff;*.inr;*.bin;*.ubj', '3D volume file (*.jnii;*.nii;*.hdr;*.img;*.tif;*.tiff;*.inr;*.bin;*.ubj)'; ... + '*.jnii', 'JNIFTI file (*.jnii)'; ... + '*.nii', 'NIFTI file (*.nii)'; ... '*.hdr;*.img', 'Analyze 7.5 file (*.hdr;*.img)'; ... '*.tif;*.tiff', 'Multipage TIFF file (*.tif)'; ... '*.inr', 'INR image (*.inr)'; ... '*.bin', 'Binary file (*.bin)'; ... - '*.ubj', 'Universal JSON (*.ubj)'; ... + '*.jdb', 'Binary JSON (*.jdb)'; ... '*.*', 'All (*.*)'}; -[file, path, idx] = uigetfile(filters); +[file, path] = uigetfile(filters); if isequal(file, 0) return else - if (regexp(file, '\.[Nn][Ii][Ii]$')) - im = readnifti(fullfile(path, file)); - nodedata.vol = im.img; + if (~isempty(regexpi(file, '\.j*nii(\.gz)*$', 'once'))) + im = loadnifti(fullfile(path, file)); + if (isfield(im, 'NIFTIData')) + nodedata.vol = im.NIFTIData; + else + nodedata.vol = im.img; + end nodetype.hasvol = 1; - elseif (regexp(file, '(\.[Hh][Dd][Rr]$|\.[Ii][Mm][Gg]$)')) - im = readnifti(fullfile(path, file)); + elseif (~isempty(regexpi(file, '(\.hdr$|\.[img$)', 'once'))) + im = loadnifti(fullfile(path, file)); nodedata.vol = im.img; nodetype.hasvol = 1; - elseif (regexp(file, '\.[Tt][Ii][Ff][Ff]*$')) + elseif (~isempty(regexpi(file, '\.tiff*$', 'once'))) nodedata.vol = readmptiff(fullfile(path, file)); nodetype.hasvol = 1; - elseif (regexp(file, '\.[Ii][Nn][Rr]$')) + elseif (~isempty(regexpi(file, '\.inr$', 'once'))) nodedata.vol = readinr(fullfile(path, file)); nodetype.hasvol = 1; - elseif (regexp(file, '\.[Bb][Ii][Nn]$')) + elseif (~isempty(regexpi(file, '\.bin$', 'once'))) prompt = {'Dimension (1x3 vector):', ... 'Datatype (short,float,double,integer,...):'}; title = 'Load generic binary file'; @@ -727,10 +749,15 @@ function miLoadVol_Callback(hObject, eventdata, handles) if (isok == 0) return end - nodedata.vol = loadmc2(fullfile(path, file), eval(res{1}), res{2}); + fid = fopen(fullfile(path, file), 'rb'); + if (fid == 0) + errordlg('can not open the specified file'); + end + nodedata.vol = fread(fid, eval(res{1}), res{2}); + fclose(fid); nodetype.hasvol = 1; - elseif (regexp(file, '\.[Uu][Bb][Jj]$')) - nodedata = loadbj(fullfile(path, file)); + elseif (~isempty(regexpi(file, '\.jdb$', 'once'))) + nodedata = loadjd(fullfile(path, file)); if (isstruct(nodedata) && isfield(nodedata, 'vol')) nodetype.hasvol = 1; end @@ -751,27 +778,28 @@ function miLoadMesh_Callback(hObject, eventdata, handles) nodedata = struct; nodetype = dummytype; -filters = {'*.jmesh;*.off;*.medit;*.smf;*.json', '3D Mesh files (*.jmesh;*.off;*.medit;*.smf;*.json)'; ... - '*.jmesh', 'JSON mesh (*.jmesh)'; ... +filters = {'*.jmsh;*.bmsh,*.off;*.medit;*.smf;*.json', '3D Mesh files (*.jmsh;*.bmsh;*.off;*.medit;*.smf;*.json)'; ... + '*.jmsh', 'JSON mesh (*.jmsh)'; ... + '*.bmsh', 'Binary JSON mesh (*.bmsh)'; ... '*.off', 'OFF file (*.off)'; ... '*.medit', 'Medit file (*.medit)'; ... '*.ele', 'Tetgen element mesh file (*.ele)'; ... '*.json', 'JSON file (*.json)'; '*.*', 'All (*.*)'}; -[file, path, idx] = uigetfile(filters); +[file, path] = uigetfile(filters); if isequal(file, 0) return else - if (regexp(file, '\.[Oo][Ff][Ff]$')) + if (~isempty(regexpi(file, '\.off$', 'once'))) [nodedata.node, nodedata.face] = readoff(fullfile(path, file)); - elseif (regexp(file, '\.[Mm][Ee][Dd][Ii][Tt]$')) + elseif (~isempty(regexpi(file, '\.medit$', 'once'))) [nodedata.node, nodedata.elem] = readmedit(fullfile(path, file)); - elseif (regexp(file, '\.[Ee][Ll][Ee]$')) - [pathstr, name, ext] = fileparts(fullfile(path, file)); + elseif (~isempty(regexpi(file, '\.ele$', 'once'))) + [pathstr, name] = fileparts(fullfile(path, file)); [nodedata.node, nodedata.elem] = readtetgen(fullfile(pathstr, name)); - elseif (regexp(file, '\.[Jj][Mm][Ee][Ss][Hh]$')) + elseif (~isempty(regexpi(file, '\.[bj]me*sh$', 'once'))) nodedata = importjmesh(fullfile(path, file)); - elseif (regexp(file, '\.[Jj][Ss][Oo][Nn]$')) - nodedata = loadjson(fullfile(path, file)); + elseif (~isempty(regexpi(file, '\.json$', 'once'))) + nodedata = loadjd(fullfile(path, file)); end end if (exist('nodedata', 'var')) @@ -784,29 +812,30 @@ function miLoadMesh_Callback(hObject, eventdata, handles) function miLoadSurf_Callback(hObject, eventdata, handles) nodedata = struct; nodetype = dummytype; -filters = {'*.jmesh;*.off;*.asc;*.smf;*.smf;*.json', '3D Mesh files (*.jmesh;*.off;*.asc;*.smf;*.smf;*.json)'; ... - '*.jmesh', 'JSON mesh (*.jmesh)'; ... +filters = {'*.jmsh;*.bmsh;*.off;*.asc;*.smf;*.smf;*.json', '3D Mesh files (*.jmsh;*.bmsh;*.off;*.asc;*.smf;*.smf;*.json)'; ... + '*.jmsh', 'JSON mesh (*.jmsh)'; ... + '*.bmsh', 'Binary JSON mesh (*.bmsh)'; ... '*.off', 'OFF file (*.off)'; ... '*.asc', 'ASC file (*.asc)'; ... '*.gts', 'GNU Trangulated Surface file (*.gts)'; ... '*.smf', 'Simple Model Format (*.smf)'; ... '*.json', 'JSON file (*.json)'; '*.*', 'All (*.*)'}; -[file, path, idx] = uigetfile(filters); +[file, path] = uigetfile(filters); if isequal(file, 0) return else - if (regexp(file, '\.[Oo][Ff][Ff]$')) + if (~isempty(regexpi(file, '\.off$', 'once'))) [nodedata.node, nodedata.face] = readoff(fullfile(path, file)); - elseif (regexp(file, '\.[Aa][Ss][Cc]$')) + elseif (~isempty(regexpi(file, '\.asc$', 'once'))) [nodedata.node, nodedata.face] = readasc(fullfile(path, file)); - elseif (regexp(file, '\.[Gg][Tt][Ss]$')) + elseif (~isempty(regexpi(file, '\.gts$', 'once'))) [nodedata.node, nodedata.face] = readgts(fullfile(path, file)); - elseif (regexp(file, '\.[Ss][Mm][Ff]$')) + elseif (~isempty(regexpi(file, '\.smf$', 'once'))) [nodedata.node, nodedata.face] = readsmf(fullfile(path, file)); - elseif (regexp(file, '\.[Jj][Mm][Ee][Ss][Hh]$')) + elseif (~isempty(regexpi(file, '\.[bj]me*sh$', 'once'))) nodedata = importjmesh(fullfile(path, file)); - elseif (regexp(file, '\.[Jj][Ss][Oo][Nn]$')) - nodedata = loadjson(fullfile(path, file)); + elseif (~isempty(regexpi(file, '\.json$', 'once'))) + nodedata = loadjd(fullfile(path, file)); end end if (exist('nodedata', 'var')) @@ -817,7 +846,7 @@ function miLoadSurf_Callback(hObject, eventdata, handles) % -------------------------------------------------------------------- function nodedata = importjmesh(filename) -data = loadjson(filename); +data = loadjd(filename); nodedata = struct; if (isfield(data, 'MeshNode')) nodedata.node = data.MeshNode; @@ -908,7 +937,7 @@ function fgI2M_CreateFcn(hObject, eventdata, handles) cla(handles.axPreview); cla(handles.axFlow); -nodedata.preview = getpreview(nodedata, nodetype, [400, 400]); +nodedata.preview = getpreview(nodedata, nodetype, [800, 800]); nodeprop = table({key}, {nodedata}, {nodetype}, 'VariableNames', {'Name', 'Data', 'Type'}); root.graph = addnode(root.graph, nodeprop); @@ -938,8 +967,9 @@ function fgI2M_CreateFcn(hObject, eventdata, handles) for i = 1:nn if (isfield(root.graph.Nodes.Data{i}, 'preview')) - hobj(i) = imagesc(nx(i) + [-2 * wd 0], ny(i) + [-wd wd] * (wfig / hfig), imresize(root.graph.Nodes.Data{i}.preview, 0.25), ... - 'parent', handles.axPreview); + previewdata = imresize(root.graph.Nodes.Data{i}.preview, 0.5); + hobj(i) = imagesc(nx(i) + [-1.8 0] * wd, ny(i) + 0.9 * [-wd wd] * (wfig / hfig), previewdata, ... + 'parent', handles.axPreview, 'AlphaData', sum(previewdata, 3) < (240 * 3)); end end set(hobj, 'userdata', obj); @@ -981,8 +1011,8 @@ function miEllipsoid_Callback(hObject, eventdata, handles) return end newtype = dummytype; -opt = str2num(res{3}); -maxvol = str2num(res{4}); +opt = str2double(res{3}); +maxvol = str2double(res{4}); if (maxvol == 0) [newdata.node, newdata.face] = meshanellip(eval(res{1}), eval(res{2}), opt); @@ -995,7 +1025,7 @@ function miEllipsoid_Callback(hObject, eventdata, handles) newtype.hasface = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Cyl'); + addnodewithdata(handles, newdata, newtype, 'Cyl'); end % -------------------------------------------------------------------- @@ -1012,7 +1042,7 @@ function miLattice_Callback(hObject, eventdata, handles) return end newtype = dummytype; -maxvol = str2num(res{4}); +maxvol = str2double(res{4}); if (maxvol == 0) [newdata.node, newdata.face] = latticegrid(eval(res{1}), eval(res{2}), eval(res{3})); else @@ -1024,7 +1054,7 @@ function miLattice_Callback(hObject, eventdata, handles) newtype.hasface = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Lattice'); + addnodewithdata(handles, newdata, newtype, 'Lattice'); end % -------------------------------------------------------------------- @@ -1048,7 +1078,7 @@ function miMeshgrid5_Callback(hObject, eventdata, handles) newtype.haselem = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Meshgrid5_'); + addnodewithdata(handles, newdata, newtype, 'Meshgrid5_'); end % -------------------------------------------------------------------- @@ -1072,7 +1102,7 @@ function miMeshgrid6_Callback(hObject, eventdata, handles) newtype.haselem = 1; if (exist('newdata', 'var') && exist('newtype', 'var')) - newkey = addnodewithdata(handles, newdata, newtype, 'Meshgrid6_'); + addnodewithdata(handles, newdata, newtype, 'Meshgrid6_'); end % -------------------------------------------------------------------- diff --git a/iso2meshver.m b/iso2meshver.m index d3a4062..1068d9c 100644 --- a/iso2meshver.m +++ b/iso2meshver.m @@ -22,7 +22,7 @@ major = 1; minor = 9; -patchnum = 5; +patchnum = 8; extra = '$Rev:: $'; extra = regexprep(extra, '[\s$:]', ''); diff --git a/transposemat.m b/transposemat.m new file mode 100644 index 0000000..04593a0 --- /dev/null +++ b/transposemat.m @@ -0,0 +1,40 @@ +function data = transposemat(input) +% +% data=transposemat(input) +% +% Iterate over struct/cell and transpose 2D or higher-dimensional numerical +% array to match Octave loaded HDF5 array elements with loadh5 default setting +% +% author: Qianqian Fang (q.fang neu.edu) +% +% input: +% name: a matlab variable, can be a cell, struct, containers.Map, numeric array or strings +% +% output: +% newname: the restored original string +% +% example: +% a=struct('a', ones(2,3), 'b', 'a string', 'c', uint8(zeros(2,3,4))); +% b=transposemat(a) +% +% this file is part of EasyH5 Toolbox: https://github.com/NeuroJSON/easyh5 +% +% License: GPLv3 or 3-clause BSD license, see https://github.com/NeuroJSON/easyh5 for details +% + +if (isstruct(input)) + data = structfun(@transposemat, input, 'UniformOutput', false); +elseif (iscell(input)) + data = cellfun(@transposemat, input, 'UniformOutput', 'false'); +elseif (isa(input, 'containers.Map')) + allkeys = keys(input); + for i = 1:length(allkeys) + input(allkeys(i)) = transposemat(allkeys(i)); + end +elseif (isnumeric(input) && (ndims(input) > 2 || all(size(input) > 1))) + data = permute(input, ndims(input):-1:1); +elseif (ischar(input) && ndims(input) == 2 && size(input, 1) == 1 && size(input, 2) > 1 && input(end) == ' ') + data = input(1:end - 1); +else + data = input; +end